diff options
author | Hans Goudey <h.goudey@me.com> | 2021-08-23 22:10:39 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-08-23 22:10:39 +0300 |
commit | b3639670ee001cc77480cdc8db54ce517d992e3b (patch) | |
tree | 6fd63d86a18915aac313c1e85853a809ed0df516 | |
parent | ea3ee04fa8ba4613007bfbed2206b9af9d3aaddd (diff) | |
parent | be1891e895c012b36ca574a90b5d90fc1433152e (diff) |
Merge branch 'master' into temp-geometry-nodes-fields-prototype
301 files changed, 7791 insertions, 6277 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2868324bf46..47712f0ac1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1598,6 +1598,9 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_PARAMETER -Wunused-parameter) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_ALL -Wall) + # Using C++20 features while having C++17 as the project language isn't allowed by MSVC. + ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_CXX20_DESIGNATOR -Wc++20-designator) + ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_AUTOLOGICAL_COMPARE -Wno-tautological-compare) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_UNKNOWN_PRAGMAS -Wno-unknown-pragmas) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_CHAR_SUBSCRIPTS -Wno-char-subscripts) diff --git a/build_files/cmake/Modules/FindZstd.cmake b/build_files/cmake/Modules/FindZstd.cmake new file mode 100644 index 00000000000..84606d01f44 --- /dev/null +++ b/build_files/cmake/Modules/FindZstd.cmake @@ -0,0 +1,66 @@ +# - Find Zstd library +# Find the native Zstd includes and library +# This module defines +# ZSTD_INCLUDE_DIRS, where to find zstd.h, Set when +# ZSTD_INCLUDE_DIR is found. +# ZSTD_LIBRARIES, libraries to link against to use Zstd. +# ZSTD_ROOT_DIR, The base directory to search for Zstd. +# This can also be an environment variable. +# ZSTD_FOUND, If false, do not try to use Zstd. +# +# also defined, but not for general use are +# ZSTD_LIBRARY, where to find the Zstd library. + +#============================================================================= +# Copyright 2019 Blender Foundation. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +# If ZSTD_ROOT_DIR was defined in the environment, use it. +IF(NOT ZSTD_ROOT_DIR AND NOT $ENV{ZSTD_ROOT_DIR} STREQUAL "") + SET(ZSTD_ROOT_DIR $ENV{ZSTD_ROOT_DIR}) +ENDIF() + +SET(_zstd_SEARCH_DIRS + ${ZSTD_ROOT_DIR} +) + +FIND_PATH(ZSTD_INCLUDE_DIR + NAMES + zstd.h + HINTS + ${_zstd_SEARCH_DIRS} + PATH_SUFFIXES + include +) + +FIND_LIBRARY(ZSTD_LIBRARY + NAMES + zstd + HINTS + ${_zstd_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + +# handle the QUIETLY and REQUIRED arguments and set ZSTD_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Zstd DEFAULT_MSG + ZSTD_LIBRARY ZSTD_INCLUDE_DIR) + +IF(ZSTD_FOUND) + SET(ZSTD_LIBRARIES ${ZSTD_LIBRARY}) + SET(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) +ENDIF() + +MARK_AS_ADVANCED( + ZSTD_INCLUDE_DIR + ZSTD_LIBRARY +) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index dceafb236de..2bfdc84ec2a 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -441,6 +441,9 @@ if(WITH_HARU) endif() endif() +set(ZSTD_ROOT_DIR ${LIBDIR}/zstd) +find_package(Zstd REQUIRED) + if(EXISTS ${LIBDIR}) without_system_libs_end() endif() diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 7f62399ac4f..fc0c37e4c8b 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -99,6 +99,7 @@ endif() find_package_wrapper(JPEG REQUIRED) find_package_wrapper(PNG REQUIRED) find_package_wrapper(ZLIB REQUIRED) +find_package_wrapper(Zstd REQUIRED) find_package_wrapper(Freetype REQUIRED) if(WITH_PYTHON) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 3773aaaffed..d44ef691d1b 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -873,3 +873,6 @@ if(WITH_HARU) set(WITH_HARU OFF) endif() endif() + +set(ZSTD_INCLUDE_DIRS ${LIBDIR}/zstd/include) +set(ZSTD_LIBRARIES ${LIBDIR}/zstd/lib/zstd_static.lib) diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index 611df59caec..5d1a24a30f1 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -51,9 +51,9 @@ buildbot: gcc: version: '9.0.0' cuda10: - version: '10.1.0' + version: '10.1.243' cuda11: - version: '11.4.0' + version: '11.4.1' optix: version: '7.1.0' cmake: diff --git a/doc/python_api/requirements.txt b/doc/python_api/requirements.txt index a854d2a267b..b5a9d15bf7b 100644 --- a/doc/python_api/requirements.txt +++ b/doc/python_api/requirements.txt @@ -1,13 +1,13 @@ -sphinx==3.5.4 +sphinx==4.1.1 # Sphinx dependencies that are important -Jinja2==2.11.3 -Pygments==2.9.0 -docutils==0.16 +Jinja2==3.0.1 +Pygments==2.10.0 +docutils==0.17.1 snowballstemmer==2.1.0 babel==2.9.1 -requests==2.25.1 +requests==2.26.0 # Only needed to match the theme used for the official documentation. # Without this theme, the default theme will be used. -sphinx_rtd_theme==0.5.2 +sphinx_rtd_theme==1.0.0rc1 diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index ac0aca57028..0c3af3fabeb 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -408,7 +408,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): adaptive_threshold: FloatProperty( name="Adaptive Sampling Threshold", - description="Noise level step to stop sampling at, lower values reduce noise the cost of render time. Zero for automatic setting based on number of AA samples", + description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples", min=0.0, max=1.0, default=0.0, precision=4, diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 2dbebac4cc3..a7eae421b55 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -523,6 +523,9 @@ void BlenderSync::sync_procedural(BL::Object &b_ob, procedural->set_scale(cache_file.scale()); + procedural->set_use_prefetch(cache_file.use_prefetch()); + procedural->set_prefetch_cache_size(cache_file.prefetch_cache_size()); + /* create or update existing AlembicObjects */ ustring object_path = ustring(b_mesh_cache.object_path()); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 3cf75b338dc..f6824f31b7b 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -424,7 +424,7 @@ static inline void set_enum(PointerRNA &ptr, const char *name, const string &ide static inline string get_string(PointerRNA &ptr, const char *name) { char cstrbuf[1024]; - char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf)); + char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf), NULL); string str(cstr); if (cstr != cstrbuf) MEM_freeN(cstr); diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 43646aaeb5b..d5d012068ff 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -36,10 +36,10 @@ static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf), ccl_device float3 calculate_principled_diffuse_brdf( const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float3 H, float *pdf) { - float NdotL = max(dot(N, L), 0.0f); - float NdotV = max(dot(N, V), 0.0f); + float NdotL = dot(N, L); + float NdotV = dot(N, V); - if (NdotL < 0 || NdotV < 0) { + if (NdotL <= 0 || NdotV <= 0) { *pdf = 0.0f; return make_float3(0.0f, 0.0f, 0.0f); } diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index 81f47256739..69bc0712674 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -25,6 +25,7 @@ #include "render/shader.h" #include "util/util_foreach.h" +#include "util/util_logging.h" #include "util/util_progress.h" #include "util/util_transform.h" #include "util/util_vector.h" @@ -211,6 +212,35 @@ void CachedData::set_time_sampling(TimeSampling time_sampling) } } +size_t CachedData::memory_used() const +{ + size_t mem_used = 0; + + mem_used += curve_first_key.memory_used(); + mem_used += curve_keys.memory_used(); + mem_used += curve_radius.memory_used(); + mem_used += curve_shader.memory_used(); + mem_used += num_ngons.memory_used(); + mem_used += shader.memory_used(); + mem_used += subd_creases_edge.memory_used(); + mem_used += subd_creases_weight.memory_used(); + mem_used += subd_face_corners.memory_used(); + mem_used += subd_num_corners.memory_used(); + mem_used += subd_ptex_offset.memory_used(); + mem_used += subd_smooth.memory_used(); + mem_used += subd_start_corner.memory_used(); + mem_used += transforms.memory_used(); + mem_used += triangles.memory_used(); + mem_used += uv_loops.memory_used(); + mem_used += vertices.memory_used(); + + for (const CachedAttribute &attr : attributes) { + mem_used += attr.data.memory_used(); + } + + return mem_used; +} + static M44d convert_yup_zup(const M44d &mtx, float scale_mult) { V3d scale, shear, rotation, translation; @@ -706,6 +736,9 @@ NODE_DEFINE(AlembicProcedural) SOCKET_NODE_ARRAY(objects, "Objects", AlembicObject::get_node_type()); + SOCKET_BOOLEAN(use_prefetch, "Use Prefetch", true); + SOCKET_INT(prefetch_cache_size, "Prefetch Cache Size", 4096); + return type; } @@ -823,6 +856,30 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) } } + if (use_prefetch_is_modified()) { + if (!use_prefetch) { + for (Node *node : objects) { + AlembicObject *object = static_cast<AlembicObject *>(node); + object->clear_cache(); + } + } + } + + if (prefetch_cache_size_is_modified()) { + /* Check whether the current memory usage fits in the new requested size, + * abort the render if it is any higher. */ + size_t memory_used = 0ul; + for (Node *node : objects) { + AlembicObject *object = static_cast<AlembicObject *>(node); + memory_used += object->get_cached_data().memory_used(); + } + + if (memory_used > get_prefetch_cache_size_in_bytes()) { + progress.set_error("Error: Alembic Procedural memory limit reached"); + return; + } + } + build_caches(progress); foreach (Node *node, objects) { @@ -1300,6 +1357,8 @@ void AlembicProcedural::walk_hierarchy( void AlembicProcedural::build_caches(Progress &progress) { + size_t memory_used = 0; + for (Node *node : objects) { AlembicObject *object = static_cast<AlembicObject *>(node); @@ -1353,7 +1412,18 @@ void AlembicProcedural::build_caches(Progress &progress) if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) { object->setup_transform_cache(object->get_cached_data(), scale); } + + memory_used += object->get_cached_data().memory_used(); + + if (use_prefetch) { + if (memory_used > get_prefetch_cache_size_in_bytes()) { + progress.set_error("Error: Alembic Procedural memory limit reached"); + return; + } + } } + + VLOG(1) << "AlembicProcedural memory usage : " << string_human_readable_size(memory_used); } CCL_NAMESPACE_END diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index 9c58af720f6..8e166a5ab04 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -272,6 +272,21 @@ template<typename T> class DataStore { node->set(*socket, value); } + size_t memory_used() const + { + if constexpr (is_array<T>::value) { + size_t mem_used = 0; + + for (const T &array : data) { + mem_used += array.size() * sizeof(array[0]); + } + + return mem_used; + } + + return data.size() * sizeof(T); + } + private: const TimeIndexPair &get_index_for_time(double time) const { @@ -332,6 +347,8 @@ struct CachedData { void invalidate_last_loaded_time(bool attributes_only = false); void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling); + + size_t memory_used() const; }; /* Representation of an Alembic object for the AlembicProcedural. @@ -482,6 +499,13 @@ class AlembicProcedural : public Procedural { * software. */ NODE_SOCKET_API(float, scale) + /* Cache controls */ + NODE_SOCKET_API(bool, use_prefetch) + + /* Memory limit for the cache, if the data does not fit within this limit, rendering is aborted. + */ + NODE_SOCKET_API(int, prefetch_cache_size) + AlembicProcedural(); ~AlembicProcedural(); @@ -531,6 +555,12 @@ class AlembicProcedural : public Procedural { void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); void build_caches(Progress &progress); + + size_t get_prefetch_cache_size_in_bytes() const + { + /* prefetch_cache_size is in megabytes, so convert to bytes. */ + return static_cast<size_t>(prefetch_cache_size) * 1024 * 1024; + } }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp index c53ec668938..b105af63b44 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/render/alembic_read.cpp @@ -44,9 +44,19 @@ static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc, return result; } - // load the data for the entire animation - const double start_frame = static_cast<double>(proc->get_start_frame()); - const double end_frame = static_cast<double>(proc->get_end_frame()); + double start_frame; + double end_frame; + + if (proc->get_use_prefetch()) { + // load the data for the entire animation + start_frame = static_cast<double>(proc->get_start_frame()); + end_frame = static_cast<double>(proc->get_end_frame()); + } + else { + // load the data for the current frame + start_frame = static_cast<double>(proc->get_frame()); + end_frame = start_frame; + } const double frame_rate = static_cast<double>(proc->get_frame_rate()); const double start_time = start_frame / frame_rate; diff --git a/intern/ghost/GHOST_IEvent.h b/intern/ghost/GHOST_IEvent.h index bcccd536ebd..239eea21088 100644 --- a/intern/ghost/GHOST_IEvent.h +++ b/intern/ghost/GHOST_IEvent.h @@ -32,10 +32,10 @@ class GHOST_IWindow; /** * Interface class for events received from GHOST. * You should not need to inherit this class. The system will pass these events - * to the GHOST_IEventConsumer::processEvent() method of event consumers.<br> - * Use the getType() method to retrieve the type of event and the getData() + * to the #GHOST_IEventConsumer::processEvent() method of event consumers.<br> + * Use the #getType() method to retrieve the type of event and the #getData() * method to get the event data out. Using the event type you can cast the - * event data to the correct event dat structure. + * event data to the correct event data structure. * \see GHOST_IEventConsumer#processEvent * \see GHOST_TEventType */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 347067eae50..f44107ee000 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1741,7 +1741,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_MOUSELEAVE: { window->m_mousePresent = false; if (window->getTabletData().Active == GHOST_kTabletModeNone) { - processCursorEvent(window); + event = processCursorEvent(window); } GHOST_Wintab *wt = window->getWintab(); if (wt) { diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 003f2f223ea..708701c4804 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -997,7 +997,6 @@ class USERPREF_PT_theme_text_style(ThemePanel, CenterAlignMixIn, Panel): flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) col = flow.column() - col.row().prop(font_style, "font_kerning_style", expand=True) col.prop(font_style, "points") col = flow.column(align=True) diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index eb3f9805240..4de7e704a7e 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -98,13 +98,13 @@ void BLF_batch_draw_flush(void); void BLF_batch_draw_end(void); /* Draw the string using the current font. */ -void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +void BLF_draw_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info) ATTR_NONNULL(2); -void BLF_draw(int fontid, const char *str, size_t len) ATTR_NONNULL(2); -void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +void BLF_draw(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2); +void BLF_draw_ascii_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info) ATTR_NONNULL(2); -void BLF_draw_ascii(int fontid, const char *str, size_t len) ATTR_NONNULL(2); -int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) ATTR_NONNULL(2); +void BLF_draw_ascii(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2); +int BLF_draw_mono(int fontid, const char *str, size_t str_len, int cwidth) ATTR_NONNULL(2); typedef bool (*BLF_GlyphBoundsFn)(const char *str, const size_t str_step_ofs, @@ -116,43 +116,45 @@ typedef bool (*BLF_GlyphBoundsFn)(const char *str, void BLF_boundbox_foreach_glyph_ex(int fontid, const char *str, - size_t len, + size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data, struct ResultBLF *r_info) ATTR_NONNULL(2); void BLF_boundbox_foreach_glyph(int fontid, const char *str, - size_t len, + size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data) ATTR_NONNULL(2); /* Get the string byte offset that fits within a given width */ -size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width) - ATTR_NONNULL(2); +size_t BLF_width_to_strlen( + int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2); /* Same as BLF_width_to_strlen but search from the string end */ -size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width) - ATTR_NONNULL(2); +size_t BLF_width_to_rstrlen( + int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2); /* This function return the bounding box of the string * and are not multiplied by the aspect. */ void BLF_boundbox_ex(int fontid, const char *str, - size_t len, + size_t str_len, struct rctf *box, struct ResultBLF *r_info) ATTR_NONNULL(2); -void BLF_boundbox(int fontid, const char *str, size_t len, struct rctf *box) ATTR_NONNULL(); +void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rctf *box) ATTR_NONNULL(); /* The next both function return the width and height * of the string, using the current font and both value * are multiplied by the aspect of the font. */ -float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +float BLF_width_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2); -float BLF_width(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +float BLF_width(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); +float BLF_height_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2); -float BLF_height(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BLF_height(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); /* Return dimensions of the font without any sample text. */ int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT; @@ -163,8 +165,8 @@ float BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT; /* The following function return the width and height of the string, but * just in one call, so avoid extra freetype2 stuff. */ -void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height) - ATTR_NONNULL(); +void BLF_width_and_height( + int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL(); /* For fixed width fonts only, returns the width of a * character. @@ -221,9 +223,9 @@ void BLF_buffer_col(int fontid, const float rgba[4]) ATTR_NONNULL(2); /* Draw the string into the buffer, this function draw in both buffer, * float and unsigned char _BUT_ it's not necessary set both buffer, NULL is valid here. */ -void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +void BLF_draw_buffer_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info) ATTR_NONNULL(2); -void BLF_draw_buffer(int fontid, const char *str, size_t len) ATTR_NONNULL(2); +void BLF_draw_buffer(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2); /* Add a path to the font dir paths. */ void BLF_dir_add(const char *path) ATTR_NONNULL(); @@ -254,8 +256,9 @@ void BLF_default_dpi(int dpi); void BLF_default_set(int fontid); int BLF_default(void); /* get default font ID so we can pass it to other functions */ /* Draw the string using the default font, size and dpi. */ -void BLF_draw_default(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL(); -void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL(); +void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL(); +void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t str_len) + ATTR_NONNULL(); /* Set size and DPI, and return default font ID. */ int BLF_set_default(void); diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 9168e7aa19c..86d67c80fd4 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -108,7 +108,6 @@ void BLF_cache_clear(void) FontBLF *font = global_font[i]; if (font) { blf_glyph_cache_clear(font); - blf_kerning_cache_clear(font); } } } @@ -522,7 +521,7 @@ static void blf_draw_gl__end(FontBLF *font) } } -void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +void BLF_draw_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info) { FontBLF *font = blf_get(fontid); @@ -531,27 +530,27 @@ void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_in if (font) { blf_draw_gl__start(font); if (font->flags & BLF_WORD_WRAP) { - blf_font_draw__wrap(font, str, len, r_info); + blf_font_draw__wrap(font, str, str_len, r_info); } else { - blf_font_draw(font, str, len, r_info); + blf_font_draw(font, str, str_len, r_info); } blf_draw_gl__end(font); } } -void BLF_draw(int fontid, const char *str, size_t len) +void BLF_draw(int fontid, const char *str, const size_t str_len) { - if (len == 0 || str[0] == '\0') { + if (str_len == 0 || str[0] == '\0') { return; } /* Avoid bgl usage to corrupt BLF drawing. */ GPU_bgl_end(); - BLF_draw_ex(fontid, str, len, NULL); + BLF_draw_ex(fontid, str, str_len, NULL); } -void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +void BLF_draw_ascii_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info) { FontBLF *font = blf_get(fontid); @@ -561,27 +560,27 @@ void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF blf_draw_gl__start(font); if (font->flags & BLF_WORD_WRAP) { /* Use non-ASCII draw function for word-wrap. */ - blf_font_draw__wrap(font, str, len, r_info); + blf_font_draw__wrap(font, str, str_len, r_info); } else { - blf_font_draw_ascii(font, str, len, r_info); + blf_font_draw_ascii(font, str, str_len, r_info); } blf_draw_gl__end(font); } } -void BLF_draw_ascii(int fontid, const char *str, size_t len) +void BLF_draw_ascii(int fontid, const char *str, const size_t str_len) { - if (len == 0 || str[0] == '\0') { + if (str_len == 0 || str[0] == '\0') { return; } - BLF_draw_ascii_ex(fontid, str, len, NULL); + BLF_draw_ascii_ex(fontid, str, str_len, NULL); } -int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) +int BLF_draw_mono(int fontid, const char *str, const size_t str_len, int cwidth) { - if (len == 0 || str[0] == '\0') { + if (str_len == 0 || str[0] == '\0') { return 0; } @@ -590,7 +589,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) if (font) { blf_draw_gl__start(font); - columns = blf_font_draw_mono(font, str, len, cwidth); + columns = blf_font_draw_mono(font, str, str_len, cwidth); blf_draw_gl__end(font); } @@ -607,7 +606,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) */ void BLF_boundbox_foreach_glyph_ex(int fontid, const char *str, - size_t len, + size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data, struct ResultBLF *r_info) @@ -622,25 +621,26 @@ void BLF_boundbox_foreach_glyph_ex(int fontid, BLI_assert(0); } else { - blf_font_boundbox_foreach_glyph(font, str, len, user_fn, user_data, r_info); + blf_font_boundbox_foreach_glyph(font, str, str_len, user_fn, user_data, r_info); } } } void BLF_boundbox_foreach_glyph( - int fontid, const char *str, size_t len, BLF_GlyphBoundsFn user_fn, void *user_data) + int fontid, const char *str, const size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data) { - BLF_boundbox_foreach_glyph_ex(fontid, str, len, user_fn, user_data, NULL); + BLF_boundbox_foreach_glyph_ex(fontid, str, str_len, user_fn, user_data, NULL); } -size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width) +size_t BLF_width_to_strlen( + int fontid, const char *str, const size_t str_len, float width, float *r_width) { FontBLF *font = blf_get(fontid); if (font) { const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f; size_t ret; - ret = blf_font_width_to_strlen(font, str, len, width / xa, r_width); + ret = blf_font_width_to_strlen(font, str, str_len, width / xa, r_width); if (r_width) { *r_width *= xa; } @@ -653,14 +653,15 @@ size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, return 0; } -size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width) +size_t BLF_width_to_rstrlen( + int fontid, const char *str, const size_t str_len, float width, float *r_width) { FontBLF *font = blf_get(fontid); if (font) { const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f; size_t ret; - ret = blf_font_width_to_rstrlen(font, str, len, width / xa, r_width); + ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, r_width); if (r_width) { *r_width *= xa; } @@ -674,7 +675,7 @@ size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width } void BLF_boundbox_ex( - int fontid, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info) + int fontid, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info) { FontBLF *font = blf_get(fontid); @@ -682,47 +683,48 @@ void BLF_boundbox_ex( if (font) { if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, r_box, r_info); + blf_font_boundbox__wrap(font, str, str_len, r_box, r_info); } else { - blf_font_boundbox(font, str, len, r_box, r_info); + blf_font_boundbox(font, str, str_len, r_box, r_info); } } } -void BLF_boundbox(int fontid, const char *str, size_t len, rctf *r_box) +void BLF_boundbox(int fontid, const char *str, const size_t str_len, rctf *r_box) { - BLF_boundbox_ex(fontid, str, len, r_box, NULL); + BLF_boundbox_ex(fontid, str, str_len, r_box, NULL); } -void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height) +void BLF_width_and_height( + int fontid, const char *str, const size_t str_len, float *r_width, float *r_height) { FontBLF *font = blf_get(fontid); if (font) { - blf_font_width_and_height(font, str, len, r_width, r_height, NULL); + blf_font_width_and_height(font, str, str_len, r_width, r_height, NULL); } else { *r_width = *r_height = 0.0f; } } -float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +float BLF_width_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info) { FontBLF *font = blf_get(fontid); BLF_RESULT_CHECK_INIT(r_info); if (font) { - return blf_font_width(font, str, len, r_info); + return blf_font_width(font, str, str_len, r_info); } return 0.0f; } -float BLF_width(int fontid, const char *str, size_t len) +float BLF_width(int fontid, const char *str, const size_t str_len) { - return BLF_width_ex(fontid, str, len, NULL); + return BLF_width_ex(fontid, str, str_len, NULL); } float BLF_fixed_width(int fontid) @@ -736,22 +738,22 @@ float BLF_fixed_width(int fontid) return 0.0f; } -float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +float BLF_height_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info) { FontBLF *font = blf_get(fontid); BLF_RESULT_CHECK_INIT(r_info); if (font) { - return blf_font_height(font, str, len, r_info); + return blf_font_height(font, str, str_len, r_info); } return 0.0f; } -float BLF_height(int fontid, const char *str, size_t len) +float BLF_height(int fontid, const char *str, const size_t str_len) { - return BLF_height_ex(fontid, str, len, NULL); + return BLF_height_ex(fontid, str, str_len, NULL); } int BLF_height_max(int fontid) @@ -895,24 +897,27 @@ void blf_draw_buffer__end(void) { } -void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info) +void BLF_draw_buffer_ex(int fontid, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) { FontBLF *font = blf_get(fontid); if (font && (font->buf_info.fbuf || font->buf_info.cbuf)) { blf_draw_buffer__start(font); if (font->flags & BLF_WORD_WRAP) { - blf_font_draw_buffer__wrap(font, str, len, r_info); + blf_font_draw_buffer__wrap(font, str, str_len, r_info); } else { - blf_font_draw_buffer(font, str, len, r_info); + blf_font_draw_buffer(font, str, str_len, r_info); } blf_draw_buffer__end(); } } -void BLF_draw_buffer(int fontid, const char *str, size_t len) +void BLF_draw_buffer(int fontid, const char *str, const size_t str_len) { - BLF_draw_buffer_ex(fontid, str, len, NULL); + BLF_draw_buffer_ex(fontid, str, str_len, NULL); } char *BLF_display_name_from_file(const char *filename) diff --git a/source/blender/blenfont/intern/blf_default.c b/source/blender/blenfont/intern/blf_default.c index 7bbc865128d..1b458e8aaef 100644 --- a/source/blender/blenfont/intern/blf_default.c +++ b/source/blender/blenfont/intern/blf_default.c @@ -68,23 +68,23 @@ int BLF_set_default(void) return global_font_default; } -void BLF_draw_default(float x, float y, float z, const char *str, size_t len) +void BLF_draw_default(float x, float y, float z, const char *str, const size_t str_len) { ASSERT_DEFAULT_SET; const uiStyle *style = UI_style_get(); BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi); BLF_position(global_font_default, x, y, z); - BLF_draw(global_font_default, str, len); + BLF_draw(global_font_default, str, str_len); } /* same as above but call 'BLF_draw_ascii' */ -void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) +void BLF_draw_default_ascii(float x, float y, float z, const char *str, const size_t str_len) { ASSERT_DEFAULT_SET; const uiStyle *style = UI_style_get(); BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi); BLF_position(global_font_default, x, y, z); - BLF_draw_ascii(global_font_default, str, len); /* XXX, use real length */ + BLF_draw_ascii(global_font_default, str, str_len); /* XXX, use real length */ } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 00d3cfb09eb..af68e1563d1 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -72,6 +72,38 @@ static SpinLock ft_lib_mutex; static SpinLock blf_glyph_cache_mutex; /* -------------------------------------------------------------------- */ +/** \name FreeType Utilities (Internal) + * \{ */ + +/** + * Convert a FreeType 26.6 value representing an unscaled design size to pixels. + * This is an exact copy of the scaling done inside FT_Get_Kerning when called + * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. + */ +static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) +{ + /* Scale value by font size using integer-optimized multiplication. */ + FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + + /* FreeType states that this '25' has been determined heuristically. */ + if (font->face->size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + } + + /* Copies of internal FreeType macros needed here. */ +#define FT_PIX_FLOOR(x) ((x) & ~63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) + + /* Round to even 64ths, then divide by 64. */ + return (int)FT_PIX_ROUND(scaled) >> 6; + +#undef FT_PIX_FLOOR +#undef FT_PIX_ROUND +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Glyph Batching * \{ */ @@ -257,67 +289,8 @@ static void blf_batch_draw_end(void) /** \} */ /* -------------------------------------------------------------------- */ - -int blf_font_init(void) -{ - memset(&g_batch, 0, sizeof(g_batch)); - BLI_spin_init(&ft_lib_mutex); - BLI_spin_init(&blf_glyph_cache_mutex); - return FT_Init_FreeType(&ft_lib); -} - -void blf_font_exit(void) -{ - FT_Done_FreeType(ft_lib); - BLI_spin_end(&ft_lib_mutex); - BLI_spin_end(&blf_glyph_cache_mutex); - blf_batch_draw_exit(); -} - -void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) -{ - GlyphCacheBLF *gc; - FT_Error err; - - blf_glyph_cache_acquire(font); - - gc = blf_glyph_cache_find(font, size, dpi); - if (gc) { - /* Optimization: do not call FT_Set_Char_Size if size did not change. */ - if (font->size == size && font->dpi == dpi) { - blf_glyph_cache_release(font); - return; - } - } - - err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); - if (err) { - /* FIXME: here we can go through the fixed size and choice a close one */ - printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); - - blf_glyph_cache_release(font); - return; - } - - font->size = size; - font->dpi = dpi; - - if (!gc) { - blf_glyph_cache_new(font); - } - blf_glyph_cache_release(font); -} - -static void blf_font_ensure_ascii_kerning(FontBLF *font, GlyphCacheBLF *gc) -{ - if (font->kerning_cache || !FT_HAS_KERNING(font->face)) { - return; - } - font->kerning_cache = blf_kerning_cache_find(font); - if (!font->kerning_cache) { - font->kerning_cache = blf_kerning_cache_new(font, gc); - } -} +/** \name Glyph Stepping Utilities (Internal) + * \{ */ /* Fast path for runs of ASCII characters. Given that common UTF-8 * input will consist of an overwhelming majority of ASCII @@ -355,26 +328,44 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const uint c, int *pen_x_p) { - /* `blf_font_ensure_ascii_kerning(font, gc);` must be called before this function. */ - BLI_assert(font->kerning_cache != NULL || !FT_HAS_KERNING(font->face)); + if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { + return; + } - if (g_prev != NULL && FT_HAS_KERNING(font->face)) { - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - *pen_x_p += font->kerning_cache->ascii_table[c][c_prev]; - } - else { - FT_Vector delta; - if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) { - *pen_x_p += (int)delta.x >> 6; - } - } + FT_Vector delta = {KERNING_ENTRY_UNSET}; + + /* Get unscaled kerning value from our cache if ASCII. */ + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + delta.x = font->kerning_cache->ascii_table[c][c_prev]; + } + + /* If not ASCII or not found in cache, ask FreeType for kerning. */ + if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + /* Note that this function sets delta values to zero on any error. */ + FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); + } + + /* If ASCII we save this value to our cache for quicker access next time. */ + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; + } + + if (delta.x != 0) { + /* Convert unscaled design units to pixels and move pen. */ + *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Drawing: GPU + * \{ */ + static void blf_font_draw_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, - size_t len, + const size_t str_len, struct ResultBLF *r_info, int pen_y) { @@ -383,16 +374,14 @@ static void blf_font_draw_ex(FontBLF *font, int pen_x = 0; size_t i = 0; - if (len == 0) { + if (str_len == 0) { /* early output, don't do any IMM OpenGL. */ return; } - blf_font_ensure_ascii_kerning(font, gc); - blf_batch_draw_begin(font); - while ((i < len) && str[i]) { + while ((i < str_len) && str[i]) { g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { @@ -418,16 +407,16 @@ static void blf_font_draw_ex(FontBLF *font, r_info->width = pen_x; } } -void blf_font_draw(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info) { GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_draw_ex(font, gc, str, len, r_info, 0); + blf_font_draw_ex(font, gc, str, str_len, r_info, 0); blf_glyph_cache_release(font); } /* faster version of blf_font_draw, ascii only for view dimensions */ static void blf_font_draw_ascii_ex( - FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info, int pen_y) + FontBLF *font, const char *str, size_t str_len, struct ResultBLF *r_info, int pen_y) { unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; @@ -435,11 +424,9 @@ static void blf_font_draw_ascii_ex( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_kerning(font, gc); - blf_batch_draw_begin(font); - while ((c = *(str++)) && len--) { + while ((c = *(str++)) && str_len--) { BLI_assert(c < GLYPH_ASCII_TABLE_SIZE); g = gc->glyph_ascii_table[c]; if (UNLIKELY(g == NULL)) { @@ -469,13 +456,16 @@ static void blf_font_draw_ascii_ex( blf_glyph_cache_release(font); } -void blf_font_draw_ascii(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +void blf_font_draw_ascii(FontBLF *font, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) { - blf_font_draw_ascii_ex(font, str, len, r_info, 0); + blf_font_draw_ascii_ex(font, str, str_len, r_info, 0); } /* use fixed column width, but an utf8 character may occupy multiple columns */ -int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) +int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth) { unsigned int c; GlyphBLF *g; @@ -487,7 +477,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) blf_batch_draw_begin(font); - while ((i < len) && str[i]) { + while ((i < str_len) && str[i]) { g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { @@ -515,11 +505,17 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) return columns; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Drawgin: Buffer + * \{ */ + /* Sanity checks are done by BLF_draw_buffer() */ static void blf_font_draw_buffer_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, - size_t len, + const size_t str_len, struct ResultBLF *r_info, int pen_y) { @@ -536,11 +532,9 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int chx, chy; int y, x; - blf_font_ensure_ascii_kerning(font, gc); - /* another buffer specific call for color conversion */ - while ((i < len) && str[i]) { + while ((i < str_len) && str[i]) { g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { @@ -655,13 +649,26 @@ static void blf_font_draw_buffer_ex(FontBLF *font, } } -void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +void blf_font_draw_buffer(FontBLF *font, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) { GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_draw_buffer_ex(font, gc, str, len, r_info, 0); + blf_font_draw_buffer_ex(font, gc, str, str_len, r_info, 0); blf_glyph_cache_release(font); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Width to Sting Length + * + * Use to implement exported functions: + * - #BLF_width_to_strlen + * - #BLF_width_to_rstrlen + * \{ */ + static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, const uint c_prev, const uint c, @@ -684,7 +691,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, } size_t blf_font_width_to_strlen( - FontBLF *font, const char *str, size_t len, float width, float *r_width) + FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; @@ -694,9 +701,7 @@ size_t blf_font_width_to_strlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - blf_font_ensure_ascii_kerning(font, gc); - - for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i]; + for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i]; i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -714,7 +719,7 @@ size_t blf_font_width_to_strlen( } size_t blf_font_width_to_rstrlen( - FontBLF *font, const char *str, size_t len, float width, float *r_width) + FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; @@ -725,9 +730,7 @@ size_t blf_font_width_to_rstrlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - blf_font_ensure_ascii_kerning(font, gc); - - i = BLI_strnlen(str, len); + i = BLI_strnlen(str, str_len); s = BLI_str_find_prev_char_utf8(str, &str[i]); i = (size_t)((s != NULL) ? s - str : 0); s_prev = BLI_str_find_prev_char_utf8(str, s); @@ -759,10 +762,16 @@ size_t blf_font_width_to_rstrlen( return i; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Glyph Bound Box with Callback + * \{ */ + static void blf_font_boundbox_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, - size_t len, + const size_t str_len, rctf *box, struct ResultBLF *r_info, int pen_y) @@ -778,9 +787,7 @@ static void blf_font_boundbox_ex(FontBLF *font, box->ymin = 32000.0f; box->ymax = -32000.0f; - blf_font_ensure_ascii_kerning(font, gc); - - while ((i < len) && str[i]) { + while ((i < str_len) && str[i]) { g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { @@ -828,15 +835,178 @@ static void blf_font_boundbox_ex(FontBLF *font, } } void blf_font_boundbox( - FontBLF *font, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info) + FontBLF *font, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info) +{ + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + blf_font_boundbox_ex(font, gc, str, str_len, r_box, r_info, 0); + blf_glyph_cache_release(font); +} + +void blf_font_width_and_height(FontBLF *font, + const char *str, + const size_t str_len, + float *r_width, + float *r_height, + struct ResultBLF *r_info) +{ + float xa, ya; + rctf box; + + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + ya = font->aspect[1]; + } + else { + xa = 1.0f; + ya = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, str_len, &box, r_info); + } + else { + blf_font_boundbox(font, str, str_len, &box, r_info); + } + *r_width = (BLI_rctf_size_x(&box) * xa); + *r_height = (BLI_rctf_size_y(&box) * ya); +} + +float blf_font_width(FontBLF *font, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) +{ + float xa; + rctf box; + + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + } + else { + xa = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, str_len, &box, r_info); + } + else { + blf_font_boundbox(font, str, str_len, &box, r_info); + } + return BLI_rctf_size_x(&box) * xa; +} + +float blf_font_height(FontBLF *font, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) +{ + float ya; + rctf box; + + if (font->flags & BLF_ASPECT) { + ya = font->aspect[1]; + } + else { + ya = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, str_len, &box, r_info); + } + else { + blf_font_boundbox(font, str, str_len, &box, r_info); + } + return BLI_rctf_size_y(&box) * ya; +} + +float blf_font_fixed_width(FontBLF *font) { + const unsigned int c = ' '; + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_boundbox_ex(font, gc, str, len, r_box, r_info, 0); + GlyphBLF *g = blf_glyph_search(gc, c); + if (!g) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); + + /* if we don't find the glyph. */ + if (!g) { + blf_glyph_cache_release(font); + return 0.0f; + } + } + blf_glyph_cache_release(font); + return g->advance; } +static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, + GlyphCacheBLF *gc, + const char *str, + const size_t str_len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + struct ResultBLF *r_info, + int pen_y) +{ + unsigned int c, c_prev = BLI_UTF8_ERR; + GlyphBLF *g, *g_prev = NULL; + int pen_x = 0; + size_t i = 0, i_curr; + rcti gbox; + + if (str_len == 0) { + /* early output. */ + return; + } + + while ((i < str_len) && str[i]) { + i_curr = i; + g = blf_utf8_next_fast(font, gc, str, &i, &c); + + if (UNLIKELY(c == BLI_UTF8_ERR)) { + break; + } + if (UNLIKELY(g == NULL)) { + continue; + } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + + gbox.xmin = pen_x; + gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); + gbox.ymin = pen_y; + gbox.ymax = gbox.ymin - g->dims[1]; + + pen_x += g->advance_i; + + if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { + break; + } + + g_prev = g; + c_prev = c; + } + + if (r_info) { + r_info->lines = 1; + r_info->width = pen_x; + } +} +void blf_font_boundbox_foreach_glyph(FontBLF *font, + const char *str, + const size_t str_len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + struct ResultBLF *r_info) +{ + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + blf_font_boundbox_foreach_glyph_ex(font, gc, str, str_len, user_fn, user_data, r_info, 0); + blf_glyph_cache_release(font); +} + +/** \} */ + /* -------------------------------------------------------------------- */ -/** \name Word-Wrap Support +/** \name Text Evaluation: Word-Wrap with Callback * \{ */ /** @@ -850,12 +1020,12 @@ void blf_font_boundbox( */ static void blf_font_wrap_apply(FontBLF *font, const char *str, - size_t len, + const size_t str_len, struct ResultBLF *r_info, void (*callback)(FontBLF *font, GlyphCacheBLF *gc, const char *str, - size_t len, + const size_t str_len, int pen_y, void *userdata), void *userdata) @@ -869,15 +1039,13 @@ static void blf_font_wrap_apply(FontBLF *font, GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_kerning(font, gc); - struct WordWrapVars { int wrap_width; size_t start, last[2]; } wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}}; - // printf("%s wrapping (%d, %d) `%s`:\n", __func__, len, strlen(str), str); - while ((i < len) && str[i]) { + // printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str); + while ((i < str_len) && str[i]) { /* wrap vars */ size_t i_curr = i; @@ -905,7 +1073,7 @@ static void blf_font_wrap_apply(FontBLF *font, if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) { do_draw = true; } - else if (UNLIKELY(((i < len) && str[i]) == 0)) { + else if (UNLIKELY(((i < str_len) && str[i]) == 0)) { /* need check here for trailing newline, else we draw it */ wrap.last[0] = i + ((g->c != '\n') ? 1 : 0); wrap.last[1] = i; @@ -956,268 +1124,179 @@ static void blf_font_wrap_apply(FontBLF *font, static void blf_font_draw__wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, - size_t len, + const size_t str_len, int pen_y, void *UNUSED(userdata)) { - blf_font_draw_ex(font, gc, str, len, NULL, pen_y); + blf_font_draw_ex(font, gc, str, str_len, NULL, pen_y); } -void blf_font_draw__wrap(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +void blf_font_draw__wrap(FontBLF *font, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) { - blf_font_wrap_apply(font, str, len, r_info, blf_font_draw__wrap_cb, NULL); + blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw__wrap_cb, NULL); } /* blf_font_boundbox__wrap */ -static void blf_font_boundbox_wrap_cb( - FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t len, int pen_y, void *userdata) +static void blf_font_boundbox_wrap_cb(FontBLF *font, + GlyphCacheBLF *gc, + const char *str, + const size_t str_len, + int pen_y, + void *userdata) { rctf *box = userdata; rctf box_single; - blf_font_boundbox_ex(font, gc, str, len, &box_single, NULL, pen_y); + blf_font_boundbox_ex(font, gc, str, str_len, &box_single, NULL, pen_y); BLI_rctf_union(box, &box_single); } void blf_font_boundbox__wrap( - FontBLF *font, const char *str, size_t len, rctf *box, struct ResultBLF *r_info) + FontBLF *font, const char *str, const size_t str_len, rctf *box, struct ResultBLF *r_info) { box->xmin = 32000.0f; box->xmax = -32000.0f; box->ymin = 32000.0f; box->ymax = -32000.0f; - blf_font_wrap_apply(font, str, len, r_info, blf_font_boundbox_wrap_cb, box); + blf_font_wrap_apply(font, str, str_len, r_info, blf_font_boundbox_wrap_cb, box); } /* blf_font_draw_buffer__wrap */ static void blf_font_draw_buffer__wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, - size_t len, + const size_t str_len, int pen_y, void *UNUSED(userdata)) { - blf_font_draw_buffer_ex(font, gc, str, len, NULL, pen_y); + blf_font_draw_buffer_ex(font, gc, str, str_len, NULL, pen_y); } void blf_font_draw_buffer__wrap(FontBLF *font, const char *str, - size_t len, + const size_t str_len, struct ResultBLF *r_info) { - blf_font_wrap_apply(font, str, len, r_info, blf_font_draw_buffer__wrap_cb, NULL); + blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw_buffer__wrap_cb, NULL); } /** \} */ -void blf_font_width_and_height(FontBLF *font, - const char *str, - size_t len, - float *r_width, - float *r_height, - struct ResultBLF *r_info) +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Count Missing Characters + * \{ */ + +int blf_font_count_missing_chars(FontBLF *font, + const char *str, + const size_t str_len, + int *r_tot_chars) { - float xa, ya; - rctf box; + int missing = 0; + size_t i = 0; - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - ya = font->aspect[1]; - } - else { - xa = 1.0f; - ya = 1.0f; - } + *r_tot_chars = 0; + while (i < str_len) { + unsigned int c; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); + if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { + i++; + } + else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { + if (FT_Get_Char_Index((font)->face, c) == 0) { + missing++; + } + } + (*r_tot_chars)++; } - *r_width = (BLI_rctf_size_x(&box) * xa); - *r_height = (BLI_rctf_size_y(&box) * ya); + return missing; } -float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) -{ - float xa; - rctf box; - - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - } - else { - xa = 1.0f; - } +/** \} */ - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_x(&box) * xa; -} +/* -------------------------------------------------------------------- */ +/** \name Font Query: Attributes + * \{ */ -float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +int blf_font_height_max(FontBLF *font) { - float ya; - rctf box; + int height_max; - if (font->flags & BLF_ASPECT) { - ya = font->aspect[1]; - } - else { - ya = 1.0f; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + height_max = gc->glyph_height_max; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_y(&box) * ya; + blf_glyph_cache_release(font); + return height_max; } -float blf_font_fixed_width(FontBLF *font) +int blf_font_width_max(FontBLF *font) { - const unsigned int c = ' '; + int width_max; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF *g = blf_glyph_search(gc, c); - if (!g) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); - - /* if we don't find the glyph. */ - if (!g) { - blf_glyph_cache_release(font); - return 0.0f; - } - } + width_max = gc->glyph_width_max; blf_glyph_cache_release(font); - return g->advance; + return width_max; } -/* -------------------------------------------------------------------- */ -/** \name Glyph Bound Box with Callback - * \{ */ - -static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, - GlyphCacheBLF *gc, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info, - int pen_y) +float blf_font_descender(FontBLF *font) { - unsigned int c, c_prev = BLI_UTF8_ERR; - GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; - size_t i = 0, i_curr; - rcti gbox; - - if (len == 0) { - /* early output. */ - return; - } - - blf_font_ensure_ascii_kerning(font, gc); - - while ((i < len) && str[i]) { - i_curr = i; - g = blf_utf8_next_fast(font, gc, str, &i, &c); - - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } - if (UNLIKELY(g == NULL)) { - continue; - } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + float descender; - gbox.xmin = pen_x; - gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); - gbox.ymin = pen_y; - gbox.ymax = gbox.ymin - g->dims[1]; + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + descender = gc->descender; - pen_x += g->advance_i; + blf_glyph_cache_release(font); + return descender; +} - if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { - break; - } +float blf_font_ascender(FontBLF *font) +{ + float ascender; - g_prev = g; - c_prev = c; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + ascender = gc->ascender; - if (r_info) { - r_info->lines = 1; - r_info->width = pen_x; - } + blf_glyph_cache_release(font); + return ascender; } -void blf_font_boundbox_foreach_glyph(FontBLF *font, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info) + +char *blf_display_name(FontBLF *font) { - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0); - blf_glyph_cache_release(font); + if (!font->face->family_name) { + return NULL; + } + return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); } /** \} */ -int blf_font_count_missing_chars(FontBLF *font, - const char *str, - const size_t len, - int *r_tot_chars) -{ - int missing = 0; - size_t i = 0; - - *r_tot_chars = 0; - while (i < len) { - unsigned int c; +/* -------------------------------------------------------------------- */ +/** \name Font Subsystem Init/Exit + * \{ */ - if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { - i++; - } - else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { - if (FT_Get_Char_Index((font)->face, c) == 0) { - missing++; - } - } - (*r_tot_chars)++; - } - return missing; +int blf_font_init(void) +{ + memset(&g_batch, 0, sizeof(g_batch)); + BLI_spin_init(&ft_lib_mutex); + BLI_spin_init(&blf_glyph_cache_mutex); + return FT_Init_FreeType(&ft_lib); } -void blf_font_free(FontBLF *font) +void blf_font_exit(void) { - BLI_spin_lock(&blf_glyph_cache_mutex); - GlyphCacheBLF *gc; - - while ((gc = BLI_pophead(&font->cache))) { - blf_glyph_cache_free(gc); - } - - blf_kerning_cache_clear(font); + FT_Done_FreeType(ft_lib); + BLI_spin_end(&ft_lib_mutex); + BLI_spin_end(&blf_glyph_cache_mutex); + blf_batch_draw_exit(); +} - FT_Done_Face(font->face); - if (font->filename) { - MEM_freeN(font->filename); - } - if (font->name) { - MEM_freeN(font->name); - } - MEM_freeN(font); +/** \} */ - BLI_spin_unlock(&blf_glyph_cache_mutex); -} +/* -------------------------------------------------------------------- */ +/** \name Font New/Free + * \{ */ static void blf_font_fill(FontBLF *font) { @@ -1246,7 +1325,6 @@ static void blf_font_fill(FontBLF *font) font->dpi = 0; font->size = 0; BLI_listbase_clear(&font->cache); - BLI_listbase_clear(&font->kerning_caches); font->kerning_cache = NULL; #if BLF_BLUR_ENABLE font->blur = 0; @@ -1307,6 +1385,17 @@ FontBLF *blf_font_new(const char *name, const char *filename) font->name = BLI_strdup(name); font->filename = BLI_strdup(filename); blf_font_fill(font); + + if (FT_HAS_KERNING(font->face)) { + /* Create kerning cache table and fill with value indicating "unset". */ + font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); + for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { + for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { + font->kerning_cache->ascii_table[i][j] = KERNING_ENTRY_UNSET; + } + } + } + return font; } @@ -1346,54 +1435,61 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m return font; } -int blf_font_height_max(FontBLF *font) +void blf_font_free(FontBLF *font) { - int height_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - height_max = gc->glyph_height_max; + BLI_spin_lock(&blf_glyph_cache_mutex); + GlyphCacheBLF *gc; - blf_glyph_cache_release(font); - return height_max; -} + while ((gc = BLI_pophead(&font->cache))) { + blf_glyph_cache_free(gc); + } -int blf_font_width_max(FontBLF *font) -{ - int width_max; + if (font->kerning_cache) { + MEM_freeN(font->kerning_cache); + } - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - width_max = gc->glyph_width_max; + FT_Done_Face(font->face); + if (font->filename) { + MEM_freeN(font->filename); + } + if (font->name) { + MEM_freeN(font->name); + } + MEM_freeN(font); - blf_glyph_cache_release(font); - return width_max; + BLI_spin_unlock(&blf_glyph_cache_mutex); } -float blf_font_descender(FontBLF *font) -{ - float descender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - descender = gc->descender; +/** \} */ - blf_glyph_cache_release(font); - return descender; -} +/* -------------------------------------------------------------------- */ +/** \name Font Configure + * \{ */ -float blf_font_ascender(FontBLF *font) +void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) { - float ascender; + blf_glyph_cache_acquire(font); - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - ascender = gc->ascender; + GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi); + if (gc && (font->size == size && font->dpi == dpi)) { + /* Optimization: do not call FT_Set_Char_Size if size did not change. */ + } + else { + const FT_Error err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); + if (err) { + /* FIXME: here we can go through the fixed size and choice a close one */ + printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); + } + else { + font->size = size; + font->dpi = dpi; + if (gc == NULL) { + blf_glyph_cache_new(font); + } + } + } blf_glyph_cache_release(font); - return ascender; } -char *blf_display_name(FontBLF *font) -{ - if (!font->face->family_name) { - return NULL; - } - return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); -} +/** \} */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 5fb69251466..6cdf5fc5996 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -55,56 +55,6 @@ #include "BLI_math_vector.h" #include "BLI_strict_flags.h" -KerningCacheBLF *blf_kerning_cache_find(FontBLF *font) -{ - return (KerningCacheBLF *)font->kerning_caches.first; -} - -/* Create a new glyph cache for the current kerning mode. */ -KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) -{ - KerningCacheBLF *kc = MEM_mallocN(sizeof(KerningCacheBLF), __func__); - kc->next = NULL; - kc->prev = NULL; - - GlyphBLF *g_table[KERNING_CACHE_TABLE_SIZE]; - for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { - GlyphBLF *g = blf_glyph_search(gc, i); - if (UNLIKELY(g == NULL)) { - FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); - g = blf_glyph_add(font, gc, glyph_index, i); - } - g_table[i] = g; - } - - memset(kc->ascii_table, 0, sizeof(kc->ascii_table)); - for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { - GlyphBLF *g = g_table[i]; - if (g == NULL) { - continue; - } - for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { - GlyphBLF *g_prev = g_table[j]; - if (g_prev == NULL) { - continue; - } - FT_Vector delta; - if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) { - kc->ascii_table[i][j] = (int)delta.x >> 6; - } - } - } - - BLI_addhead(&font->kerning_caches, kc); - return kc; -} - -void blf_kerning_cache_clear(FontBLF *font) -{ - font->kerning_cache = NULL; - BLI_freelistN(&font->kerning_caches); -} - GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi) { GlyphCacheBLF *p; diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 35a6d019eac..6fd5e8b7503 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -53,46 +53,55 @@ struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size); void blf_font_size(struct FontBLF *font, unsigned int size, unsigned int dpi); -void blf_font_draw(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info); +void blf_font_draw(struct FontBLF *font, + const char *str, + size_t str_len, + struct ResultBLF *r_info); void blf_font_draw__wrap(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, struct ResultBLF *r_info); void blf_font_draw_ascii(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, struct ResultBLF *r_info); -int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t len, int cwidth); +int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t str_len, int cwidth); void blf_font_draw_buffer(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, struct ResultBLF *r_info); void blf_font_draw_buffer__wrap(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, struct ResultBLF *r_info); size_t blf_font_width_to_strlen( - struct FontBLF *font, const char *str, size_t len, float width, float *r_width); + struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width); size_t blf_font_width_to_rstrlen( - struct FontBLF *font, const char *str, size_t len, float width, float *r_width); + struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width); void blf_font_boundbox(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, struct rctf *r_box, struct ResultBLF *r_info); void blf_font_boundbox__wrap(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, struct rctf *r_box, struct ResultBLF *r_info); void blf_font_width_and_height(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, float *r_width, float *r_height, struct ResultBLF *r_info); -float blf_font_width(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info); -float blf_font_height(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info); +float blf_font_width(struct FontBLF *font, + const char *str, + size_t str_len, + struct ResultBLF *r_info); +float blf_font_height(struct FontBLF *font, + const char *str, + size_t str_len, + struct ResultBLF *r_info); float blf_font_fixed_width(struct FontBLF *font); int blf_font_height_max(struct FontBLF *font); int blf_font_width_max(struct FontBLF *font); @@ -103,7 +112,7 @@ char *blf_display_name(struct FontBLF *font); void blf_font_boundbox_foreach_glyph(struct FontBLF *font, const char *str, - size_t len, + size_t str_len, bool (*user_fn)(const char *str, const size_t str_step_ofs, const struct rcti *glyph_step_bounds, @@ -116,15 +125,11 @@ void blf_font_boundbox_foreach_glyph(struct FontBLF *font, int blf_font_count_missing_chars(struct FontBLF *font, const char *str, - const size_t len, + const size_t str_len, int *r_tot_chars); void blf_font_free(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_find(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_new(struct FontBLF *font, struct GlyphCacheBLF *gc); -void blf_kerning_cache_clear(struct FontBLF *font); - struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, unsigned int size, unsigned int dpi); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index caa10b2b125..38d7d7b6e21 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -34,6 +34,9 @@ /* Number of characters in KerningCacheBLF.table. */ #define KERNING_CACHE_TABLE_SIZE 128 +/* A value in the kerning cache that indicates it is not yet set. */ +#define KERNING_ENTRY_UNSET INT_MAX + typedef struct BatchBLF { struct FontBLF *font; /* can only batch glyph from the same font */ struct GPUBatch *batch; @@ -50,7 +53,6 @@ typedef struct BatchBLF { extern BatchBLF g_batch; typedef struct KerningCacheBLF { - struct KerningCacheBLF *next, *prev; /** * Cache a ascii glyph pairs. Only store the x offset we are interested in, * instead of the full #FT_Vector since it's not used for drawing at the moment. @@ -223,10 +225,7 @@ typedef struct FontBLF { */ ListBase cache; - /* list of kerning cache for this font. */ - ListBase kerning_caches; - - /* current kerning cache for this font and kerning mode. */ + /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */ KerningCacheBLF *kerning_cache; /* freetype2 lib handle. */ diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h index 58d876b184b..836597f95da 100644 --- a/source/blender/blenkernel/BKE_cachefile.h +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -61,11 +61,6 @@ void BKE_cachefile_reader_open(struct CacheFile *cache_file, const char *object_path); void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader **reader); -/* Determine whether the CacheFile should use a render engine procedural. If so, data is not read - * from the file and bouding boxes are used to represent the objects in the Scene. Render engines - * will receive the bounding box as a placeholder but can instead load the data directly if they - * support it. - */ bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file, struct Scene *scene, const int dag_eval_mode); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 1f7aa4dd26f..c7864e8909d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -111,8 +111,7 @@ typedef struct bNodeSocketTemplate { #ifdef __cplusplus namespace blender { namespace nodes { -class SocketMFNetworkBuilder; -class NodeMFNetworkBuilder; +class NodeMultiFunctionBuilder; class GeoNodeExecParams; } // namespace nodes namespace fn { @@ -121,18 +120,16 @@ class MFDataType; } // namespace fn } // namespace blender -using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder); +using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder); using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); -using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder); #else -typedef void *NodeExpandInMFNetworkFunction; -typedef void *SocketExpandInMFNetworkFunction; +typedef void *NodeMultiFunctionBuildFunction; typedef void *NodeGeometryExecFunction; typedef void *SocketGetCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPTypeFunction; @@ -196,8 +193,6 @@ typedef struct bNodeSocketType { /* Callback to free the socket type. */ void (*free_self)(struct bNodeSocketType *stype); - /* Expands the socket into a multi-function node that outputs the socket value. */ - SocketExpandInMFNetworkFunction expand_in_mf_network; /* Return the CPPType of this socket. */ SocketGetCPPTypeFunction get_base_cpp_type; /* Get the value of this socket in a generic way. */ @@ -332,8 +327,8 @@ typedef struct bNodeType { /* gpu */ NodeGPUExecFunction gpu_fn; - /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */ - NodeExpandInMFNetworkFunction expand_in_mf_network; + /* Build a multi-function for this node. */ + NodeMultiFunctionBuildFunction build_multi_function; /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; @@ -351,7 +346,7 @@ typedef struct bNodeType { #define NODE_CLASS_OP_FILTER 5 #define NODE_CLASS_GROUP 6 // #define NODE_CLASS_FILE 7 -#define NODE_CLASS_CONVERTOR 8 +#define NODE_CLASS_CONVERTER 8 #define NODE_CLASS_MATTE 9 #define NODE_CLASS_DISTORT 10 // #define NODE_CLASS_OP_DYNAMIC 11 /* deprecated */ diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 164f921c7ac..707201207d9 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -1254,11 +1254,10 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, bool in_cache = bvhcache_find( bvh_cache_p, bvh_cache_type, &tree, &lock_started, mesh_eval_mutex); BVHCache *bvh_cache = *bvh_cache_p; - bvhtree_balance(tree, true); - if (in_cache == false) { tree = bvhtree_from_editmesh_looptri_create_tree( epsilon, tree_type, axis, em, looptri_mask, looptri_num_active); + bvhtree_balance(tree, true); /* Save on cache for later use */ // printf("BVHTree built and saved on cache\n"); diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 4a60564b4a1..87b1584d422 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -368,7 +368,7 @@ void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file #endif if (DEG_is_active(depsgraph)) { - /* Flush object paths back to original datablock for UI. */ + /* Flush object paths back to original data-block for UI. */ CacheFile *cache_file_orig = (CacheFile *)DEG_get_original_id(&cache_file->id); BLI_freelistN(&cache_file_orig->object_paths); BLI_duplicatelist(&cache_file_orig->object_paths, &cache_file->object_paths); @@ -411,6 +411,12 @@ float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, c return cache_file->is_sequence ? frame : frame / fps - time_offset; } +/** + * Determine whether the #CacheFile should use a render engine procedural. If so, data is not read + * from the file and bounding boxes are used to represent the objects in the Scene. + * Render engines will receive the bounding box as a placeholder but can instead + * load the data directly if they support it. + */ bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file, Scene *scene, const int dag_eval_mode) diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 0f218d6166c..5bca20ecd44 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -620,7 +620,10 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const } if (new_count == 1) { - BKE_gpencil_free_stroke_weights(gps); + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } MEM_freeN(gps->points); gps->points = nullptr; gps->dvert = nullptr; @@ -628,27 +631,24 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const return false; } - new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed"); - - for (int i = 0; i < new_count; i++) { - memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint)); - } + new_pt = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed"); + memcpy(new_pt, &pt[index_from], sizeof(bGPDspoint) * new_count); if (gps->dvert) { - new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count, + new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed"); for (int i = 0; i < new_count; i++) { dv = &gps->dvert[i + index_from]; new_dv[i].flag = dv->flag; new_dv[i].totweight = dv->totweight; - new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, "gp_stroke_dverts_dw_trimmed"); for (int j = 0; j < dv->totweight; j++) { new_dv[i].dw[j].weight = dv->dw[j].weight; new_dv[i].dw[j].def_nr = dv->dw[j].def_nr; } - BKE_defvert_clear(dv); } + BKE_gpencil_free_stroke_weights(gps); MEM_freeN(gps->dvert); gps->dvert = new_dv; } @@ -692,25 +692,21 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd, gpf, gps, gps->mat_nr, new_count, gps->thickness); new_pt = new_gps->points; /* Allocated from above. */ - - for (int i = 0; i < new_count; i++) { - memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint)); - } + memcpy(new_pt, &pt[before_index], sizeof(bGPDspoint) * new_count); if (gps->dvert) { - new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count, + new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_remaining(MDeformVert)"); for (int i = 0; i < new_count; i++) { dv = &gps->dvert[i + before_index]; new_dv[i].flag = dv->flag; new_dv[i].totweight = dv->totweight; - new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, "gp_stroke_dverts_dw_remaining(MDeformWeight)"); for (int j = 0; j < dv->totweight; j++) { new_dv[i].dw[j].weight = dv->dw[j].weight; new_dv[i].dw[j].def_nr = dv->dw[j].def_nr; } - BKE_defvert_clear(dv); } new_gps->dvert = new_dv; } diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 0f880d16358..495d5d79eb9 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1481,7 +1481,7 @@ static bool id_name_final_build(char *name, char *base_name, size_t base_name_le /* Code above may have generated invalid utf-8 string, due to raw truncation. * Ensure we get a valid one now. */ - base_name_len -= (size_t)BLI_utf8_invalid_strip(base_name, base_name_len); + base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len); /* Also truncate orig name, and start the whole check again. */ name[base_name_len] = '\0'; @@ -1731,7 +1731,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const boo else { /* disallow non utf8 chars, * the interface checks for this but new ID's based on file names don't */ - BLI_utf8_invalid_strip(name, strlen(name)); + BLI_str_utf8_invalid_strip(name, strlen(name)); } ID *id_sorting_hint = NULL; diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 5aac29c19a7..4c97ccdf8b1 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -49,14 +49,10 @@ #include "BKE_simulation.h" #include "NOD_geometry.h" -#include "NOD_node_tree_multi_function.hh" #include "BLI_map.hh" #include "BLT_translation.h" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 275cf0d4c38..06137f5d110 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -308,7 +308,7 @@ int txt_extended_ascii_as_utf8(char **str) int added = 0; while ((*str)[i]) { - if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) { + if ((bad_char = BLI_str_utf8_invalid_byte(*str + i, length - i)) == -1) { break; } @@ -322,7 +322,7 @@ int txt_extended_ascii_as_utf8(char **str) i = 0; while ((*str)[i]) { - if ((bad_char = BLI_utf8_invalid_byte((*str) + i, length - i)) == -1) { + if ((bad_char = BLI_str_utf8_invalid_byte((*str) + i, length - i)) == -1) { memcpy(newstr + mi, (*str) + i, length - i + 1); break; } diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 7cfecc798a7..906a56ce909 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -154,18 +154,17 @@ bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL bool BLI_file_touch(const char *file) ATTR_NONNULL(); bool BLI_file_alias_target(const char *filepath, char *r_targetpath) ATTR_WARN_UNUSED_RESULT; -#if 0 /* UNUSED */ -int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -#endif -char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(); -size_t BLI_gzip_mem_to_file_at_pos(void *buf, - size_t len, - FILE *file, - size_t gz_stream_offset, - int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) +bool BLI_file_magic_is_gzip(const char header[4]); + +size_t BLI_file_zstd_from_mem_at_pos(void *buf, + size_t len, + FILE *file, + size_t file_offset, + int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BLI_file_magic_is_zstd(const char header[4]); + size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT; size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h new file mode 100644 index 00000000000..8d1fa3d1596 --- /dev/null +++ b/source/blender/blenlib/BLI_filereader.h @@ -0,0 +1,81 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bli + * \brief Wrapper for reading from various sources (e.g. raw files, compressed files, memory...). + */ + +#pragma once + +#ifdef WIN32 +# include "BLI_winstuff.h" +#else +# include <sys/types.h> +#endif + +#include "BLI_compiler_attrs.h" +#include "BLI_utildefines.h" + +#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__) +typedef int64_t off64_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct FileReader; + +typedef ssize_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, size_t size); +typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence); +typedef void (*FileReaderCloseFn)(struct FileReader *reader); + +/* General structure for all FileReaders, implementations add custom fields at the end. */ +typedef struct FileReader { + FileReaderReadFn read; + FileReaderSeekFn seek; + FileReaderCloseFn close; + + off64_t offset; +} FileReader; + +/* Functions for opening the various types of FileReader. + * They either succeed and return a valid FileReader, or fail and return NULL. + * + * If a FileReader is created, it has to be cleaned up and freed by calling + * its close() function unless another FileReader has taken ownership - for example, + * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called. + */ + +/* Create FileReader from raw file descriptor. */ +FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT; +/* Create FileReader from raw file descriptor using memory-mapped IO. */ +FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT; +/* Create FileReader from a region of memory. */ +FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); +/* Create FileReader from applying Zstd decompression on an underlying file. */ +FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/* Create FileReader from applying Gzip decompression on an underlying file. */ +FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index e877503e835..dbdd28766a5 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -196,6 +196,8 @@ MINLINE unsigned int log2_ceil_u(unsigned int x); MINLINE int divide_round_i(int a, int b); MINLINE int mod_i(int i, int n); +MINLINE float round_to_even(float f); + MINLINE signed char round_fl_to_char(float a); MINLINE unsigned char round_fl_to_uchar(float a); MINLINE short round_fl_to_short(float a); diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index 65d9d7863c3..e1d7e2c58f7 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -31,8 +31,8 @@ char *BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t ATTR_NONNULL(); size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(); -ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL(); -int BLI_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL(); +ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL(); +int BLI_str_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL(); /* warning, can return -1 on bad chars */ int BLI_str_utf8_size(const char *p) ATTR_NONNULL(); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index b6603dce378..f98d15ad08b 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS} ) @@ -75,6 +76,10 @@ set(SRC intern/endian_switch.c intern/expr_pylike_eval.c intern/fileops.c + intern/filereader_file.c + intern/filereader_gzip.c + intern/filereader_memory.c + intern/filereader_zstd.c intern/fnmatch.c intern/freetypefont.c intern/gsqueue.c @@ -194,6 +199,7 @@ set(SRC BLI_enumerable_thread_specific.hh BLI_expr_pylike_eval.h BLI_fileops.h + BLI_filereader.h BLI_fileops_types.h BLI_float2.hh BLI_float3.hh @@ -323,6 +329,7 @@ set(LIB ${FREETYPE_LIBRARY} ${ZLIB_LIBRARIES} + ${ZSTD_LIBRARIES} ) if(WITH_MEM_VALGRIND) diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index ac034d2b5cd..532a29b8147 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -31,6 +31,7 @@ #include <errno.h> #include "zlib.h" +#include "zstd.h" #ifdef WIN32 # include "BLI_fileops_types.h" @@ -61,200 +62,124 @@ #include "BLI_sys_types.h" /* for intptr_t support */ #include "BLI_utildefines.h" -#if 0 /* UNUSED */ -/* gzip the file in from and write it to "to". - * return -1 if zlib fails, -2 if the originating file does not exist - * NOTE: will remove the "from" file - */ -int BLI_file_gzip(const char *from, const char *to) +size_t BLI_file_zstd_from_mem_at_pos( + void *buf, size_t len, FILE *file, size_t file_offset, int compression_level) { - char buffer[10240]; - int file; - int readsize = 0; - int rval = 0, err; - gzFile gzfile; + fseek(file, file_offset, SEEK_SET); - /* level 1 is very close to 3 (the default) in terms of file size, - * but about twice as fast, best use for speedy saving - campbell */ - gzfile = BLI_gzopen(to, "wb1"); - if (gzfile == NULL) { - return -1; - } - file = BLI_open(from, O_BINARY | O_RDONLY, 0); - if (file == -1) { - return -2; - } + ZSTD_CCtx *ctx = ZSTD_createCCtx(); + ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level); + + ZSTD_inBuffer input = {buf, len, 0}; - while (1) { - readsize = read(file, buffer, sizeof(buffer)); + size_t out_len = ZSTD_CStreamOutSize(); + void *out_buf = MEM_mallocN(out_len, __func__); + size_t total_written = 0; - if (readsize < 0) { - rval = -2; /* error happened in reading */ - fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno)); + /* Compress block and write it out until the input has been consumed. */ + while (input.pos < input.size) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue); + if (ZSTD_isError(ret)) { break; } - else if (readsize == 0) { - break; /* done reading */ + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { + break; } + total_written += output.pos; + } - if (gzwrite(gzfile, buffer, readsize) <= 0) { - rval = -1; /* error happened in writing */ - fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err)); + /* Finalize the Zstd frame. */ + size_t ret = 1; + while (ret != 0) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end); + if (ZSTD_isError(ret)) { + break; + } + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { break; } + total_written += output.pos; } - gzclose(gzfile); - close(file); + MEM_freeN(out_buf); + ZSTD_freeCCtx(ctx); - return rval; + return ZSTD_isError(ret) ? 0 : total_written; } -#endif -/* gzip the file in from_file and write it to memory to_mem, at most size bytes. - * return the unzipped size - */ -char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) { - gzFile gzfile; - int readsize, size, alloc_size = 0; - char *mem = NULL; - const int chunk_size = 512 * 1024; + fseek(file, file_offset, SEEK_SET); - size = 0; + ZSTD_DCtx *ctx = ZSTD_createDCtx(); - gzfile = BLI_gzopen(from_file, "rb"); - for (;;) { - if (mem == NULL) { - mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem"); - alloc_size = chunk_size; - } - else { - mem = MEM_reallocN(mem, size + chunk_size); - alloc_size += chunk_size; - } + size_t in_len = ZSTD_DStreamInSize(); + void *in_buf = MEM_mallocN(in_len, __func__); + ZSTD_inBuffer input = {in_buf, in_len, 0}; - readsize = gzread(gzfile, mem + size, chunk_size); - if (readsize > 0) { - size += readsize; - } - else { + ZSTD_outBuffer output = {buf, len, 0}; + + size_t ret = 0; + /* Read and decompress chunks of input data until we have enough output. */ + while (output.pos < output.size && !ZSTD_isError(ret)) { + input.size = fread(in_buf, 1, in_len, file); + if (input.size == 0) { break; } - } - gzclose(gzfile); + /* Consume input data until we run out or have enough output. */ + input.pos = 0; + while (input.pos < input.size && output.pos < output.size) { + ret = ZSTD_decompressStream(ctx, &output, &input); - if (size == 0) { - MEM_freeN(mem); - mem = NULL; - } - else if (alloc_size != size) { - mem = MEM_reallocN(mem, size); + if (ZSTD_isError(ret)) { + break; + } + } } - *r_size = size; + MEM_freeN(in_buf); + ZSTD_freeDCtx(ctx); - return mem; + return ZSTD_isError(ret) ? 0 : output.pos; } -#define CHUNK (256 * 1024) - -/* gzip byte array from memory and write it to file at certain position. - * return size of gzip stream. - */ -size_t BLI_gzip_mem_to_file_at_pos( - void *buf, size_t len, FILE *file, size_t gz_stream_offset, int compression_level) +bool BLI_file_magic_is_gzip(const char header[4]) { - int ret, flush; - unsigned have; - z_stream strm; - unsigned char out[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, compression_level); - if (ret != Z_OK) { - return 0; - } - - strm.avail_in = len; - strm.next_in = (Bytef *)buf; - flush = Z_FINISH; - - do { - strm.avail_out = CHUNK; - strm.next_out = out; - ret = deflate(&strm, flush); - if (ret == Z_STREAM_ERROR) { - return 0; - } - have = CHUNK - strm.avail_out; - if (fwrite(out, 1, have, file) != have || ferror(file)) { - deflateEnd(&strm); - return 0; - } - } while (strm.avail_out == 0); - - if (strm.avail_in != 0 || ret != Z_STREAM_END) { - return 0; - } - - deflateEnd(&strm); - return (size_t)strm.total_out; + /* GZIP itself starts with the magic bytes 0x1f 0x8b. + * The third byte indicates the compression method, which is 0x08 for DEFLATE. */ + return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08; } -/* read and decompress gzip stream from file at certain position to buffer. - * return size of decompressed data. - */ -size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) +bool BLI_file_magic_is_zstd(const char header[4]) { - int ret; - z_stream strm; - size_t chunk = 256 * 1024; - unsigned char in[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit(&strm); - if (ret != Z_OK) { - return 0; + /* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame. + * Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5* + * for skippable frames, with the * being anything from 0 to F. + * + * To check whether a file is Zstd-compressed, we just check whether the first frame matches + * either. Seeking through the file until a Zstd frame is found would make things more + * complicated and the probability of a false positive is rather low anyways. + * + * Note that LZ4 uses a compatible format, so even though its compressed frames have a + * different magic number, a valid LZ4 file might also start with a skippable frame matching + * the second check here. + * + * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md + */ + + uint32_t magic = *((uint32_t *)header); + if (magic == 0xFD2FB528) { + return true; } - - do { - strm.avail_in = fread(in, 1, chunk, file); - strm.next_in = in; - if (ferror(file)) { - inflateEnd(&strm); - return 0; - } - - do { - strm.avail_out = len; - strm.next_out = (Bytef *)buf + strm.total_out; - - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR) { - return 0; - } - } while (strm.avail_out == 0); - - } while (ret != Z_STREAM_END); - - inflateEnd(&strm); - return (size_t)strm.total_out; + if ((magic >> 4) == 0x184D2A5) { + return true; + } + return false; } -#undef CHUNK - /** * Returns true if the file with the specified name can be written. * This implementation uses access(2), which makes the check according diff --git a/source/blender/blenlib/intern/filereader_file.c b/source/blender/blenlib/intern/filereader_file.c new file mode 100644 index 00000000000..3a833871e27 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_file.c @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#ifndef WIN32 +# include <unistd.h> /* for read close */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include <io.h> /* for open close read */ +#endif + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + int filedes; +} RawFileReader; + +static ssize_t file_read(FileReader *reader, void *buffer, size_t size) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + ssize_t readsize = read(rawfile->filedes, buffer, size); + + if (readsize >= 0) { + rawfile->reader.offset += readsize; + } + + return readsize; +} + +static off64_t file_seek(FileReader *reader, off64_t offset, int whence) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + rawfile->reader.offset = BLI_lseek(rawfile->filedes, offset, whence); + return rawfile->reader.offset; +} + +static void file_close(FileReader *reader) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + close(rawfile->filedes); + MEM_freeN(rawfile); +} + +FileReader *BLI_filereader_new_file(int filedes) +{ + RawFileReader *rawfile = MEM_callocN(sizeof(RawFileReader), __func__); + + rawfile->filedes = filedes; + + rawfile->reader.read = file_read; + rawfile->reader.seek = file_seek; + rawfile->reader.close = file_close; + + return (FileReader *)rawfile; +} diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c new file mode 100644 index 00000000000..72eb153a8b9 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_gzip.c @@ -0,0 +1,108 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <zlib.h> + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + z_stream strm; + + void *in_buf; + size_t in_size; +} GzipReader; + +static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size) +{ + GzipReader *gzip = (GzipReader *)reader; + + gzip->strm.avail_out = size; + gzip->strm.next_out = buffer; + + while (gzip->strm.avail_out > 0) { + if (gzip->strm.avail_in == 0) { + /* Ran out of buffered input data, read some more. */ + size_t readsize = gzip->base->read(gzip->base, gzip->in_buf, gzip->in_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + gzip->strm.avail_in = readsize; + gzip->strm.next_in = gzip->in_buf; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + int ret = inflate(&gzip->strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + break; + } + } + + ssize_t read_len = size - gzip->strm.avail_out; + gzip->reader.offset += read_len; + return read_len; +} + +static void gzip_close(FileReader *reader) +{ + GzipReader *gzip = (GzipReader *)reader; + + if (inflateEnd(&gzip->strm) != Z_OK) { + printf("close gzip stream error\n"); + } + MEM_freeN((void *)gzip->in_buf); + + gzip->base->close(gzip->base); + MEM_freeN(gzip); +} + +FileReader *BLI_filereader_new_gzip(FileReader *base) +{ + GzipReader *gzip = MEM_callocN(sizeof(GzipReader), __func__); + gzip->base = base; + + if (inflateInit2(&gzip->strm, 16 + MAX_WBITS) != Z_OK) { + MEM_freeN(gzip); + return NULL; + } + + gzip->in_size = 256 * 2014; + gzip->in_buf = MEM_mallocN(gzip->in_size, "gzip in buf"); + + gzip->reader.read = gzip_read; + gzip->reader.seek = NULL; + gzip->reader.close = gzip_close; + + return (FileReader *)gzip; +} diff --git a/source/blender/blenlib/intern/filereader_memory.c b/source/blender/blenlib/intern/filereader_memory.c new file mode 100644 index 00000000000..150fed7d1cc --- /dev/null +++ b/source/blender/blenlib/intern/filereader_memory.c @@ -0,0 +1,145 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <string.h> + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" +#include "BLI_mmap.h" + +#include "MEM_guardedalloc.h" + +/* This file implements both memory-backed and memory-mapped-file-backed reading. */ +typedef struct { + FileReader reader; + + const char *data; + BLI_mmap_file *mmap; + size_t length; +} MemoryReader; + +static ssize_t memory_read_raw(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + memcpy(buffer, mem->data + mem->reader.offset, readsize); + mem->reader.offset += readsize; + + return readsize; +} + +static off64_t memory_seek(FileReader *reader, off64_t offset, int whence) +{ + MemoryReader *mem = (MemoryReader *)reader; + + off64_t new_pos; + if (whence == SEEK_CUR) { + new_pos = mem->reader.offset + offset; + } + else if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = mem->length + offset; + } + else { + return -1; + } + + if (new_pos < 0 || new_pos > mem->length) { + return -1; + } + + mem->reader.offset = new_pos; + return mem->reader.offset; +} + +static void memory_close_raw(FileReader *reader) +{ + MEM_freeN(reader); +} + +FileReader *BLI_filereader_new_memory(const void *data, size_t len) +{ + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->data = (const char *)data; + mem->length = len; + + mem->reader.read = memory_read_raw; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_raw; + + return (FileReader *)mem; +} + +/* Memory-mapped file reading. + * By using `mmap()`, we can map a file so that it can be treated like normal memory, + * meaning that we can just read from it with `memcpy()` etc. + * This avoids system call overhead and can significantly speed up file loading. + */ + +static ssize_t memory_read_mmap(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + if (!BLI_mmap_read(mem->mmap, buffer, mem->reader.offset, readsize)) { + return 0; + } + + mem->reader.offset += readsize; + + return readsize; +} + +static void memory_close_mmap(FileReader *reader) +{ + MemoryReader *mem = (MemoryReader *)reader; + BLI_mmap_free(mem->mmap); + MEM_freeN(mem); +} + +FileReader *BLI_filereader_new_mmap(int filedes) +{ + BLI_mmap_file *mmap = BLI_mmap_open(filedes); + if (mmap == NULL) { + return NULL; + } + + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->mmap = mmap; + mem->length = BLI_lseek(filedes, 0, SEEK_END); + + mem->reader.read = memory_read_mmap; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_mmap; + + return (FileReader *)mem; +} diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c new file mode 100644 index 00000000000..785a40cd1a1 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_zstd.c @@ -0,0 +1,335 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <string.h> +#include <zstd.h> + +#include "BLI_blenlib.h" +#include "BLI_endian_switch.h" +#include "BLI_filereader.h" +#include "BLI_math_base.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + ZSTD_DCtx *ctx; + ZSTD_inBuffer in_buf; + size_t in_buf_max_size; + + struct { + int num_frames; + size_t *compressed_ofs; + size_t *uncompressed_ofs; + + char *cached_content; + int cached_frame; + } seek; +} ZstdReader; + +static bool zstd_read_u32(FileReader *base, uint32_t *val) +{ + if (base->read(base, val, sizeof(uint32_t)) != sizeof(uint32_t)) { + return false; + } +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(val); +#endif + return true; +} + +static bool zstd_read_seek_table(ZstdReader *zstd) +{ + FileReader *base = zstd->base; + + /* The seek table frame is at the end of the file, so seek there + * and verify that there is enough data. */ + if (base->seek(base, -4, SEEK_END) < 13) { + return false; + } + uint32_t magic; + if (!zstd_read_u32(base, &magic) || magic != 0x8F92EAB1) { + return false; + } + + uint8_t flags; + if (base->seek(base, -5, SEEK_END) < 0 || base->read(base, &flags, 1) != 1) { + return false; + } + /* Bit 7 indicates checksums. Bits 5 and 6 must be zero. */ + bool has_checksums = (flags & 0x80); + if (flags & 0x60) { + return false; + } + + uint32_t num_frames; + if (base->seek(base, -9, SEEK_END) < 0 || !zstd_read_u32(base, &num_frames)) { + return false; + } + + /* Each frame has either 2 or 3 uint32_t, and after that we have + * num_frames, flags and magic for another 9 bytes. */ + uint32_t expected_frame_length = num_frames * (has_checksums ? 12 : 8) + 9; + /* The frame starts with another magic number and its length, but these + * two fields are not included when counting length. */ + off64_t frame_start_ofs = 8 + expected_frame_length; + /* Sanity check: Before the start of the seek table frame, + * there must be num_frames frames, each of which at least 8 bytes long. */ + off64_t seek_frame_start = base->seek(base, -frame_start_ofs, SEEK_END); + if (seek_frame_start < num_frames * 8) { + return false; + } + + if (!zstd_read_u32(base, &magic) || magic != 0x184D2A5E) { + return false; + } + + uint32_t frame_length; + if (!zstd_read_u32(base, &frame_length) || frame_length != expected_frame_length) { + return false; + } + + zstd->seek.num_frames = num_frames; + zstd->seek.compressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + zstd->seek.uncompressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + + size_t compressed_ofs = 0; + size_t uncompressed_ofs = 0; + for (int i = 0; i < num_frames; i++) { + uint32_t compressed_size, uncompressed_size; + if (!zstd_read_u32(base, &compressed_size) || !zstd_read_u32(base, &uncompressed_size)) { + break; + } + if (has_checksums && base->seek(base, 4, SEEK_CUR) < 0) { + break; + } + zstd->seek.compressed_ofs[i] = compressed_ofs; + zstd->seek.uncompressed_ofs[i] = uncompressed_ofs; + compressed_ofs += compressed_size; + uncompressed_ofs += uncompressed_size; + } + zstd->seek.compressed_ofs[num_frames] = compressed_ofs; + zstd->seek.uncompressed_ofs[num_frames] = uncompressed_ofs; + + /* Seek to the end of the previous frame for the following BHead frame detection. */ + if (seek_frame_start != compressed_ofs || base->seek(base, seek_frame_start, SEEK_SET) < 0) { + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.uncompressed_ofs); + memset(&zstd->seek, 0, sizeof(zstd->seek)); + return false; + } + + zstd->seek.cached_frame = -1; + + return true; +} + +/* Find out which frame contains the given position in the uncompressed stream. + * Basically just bisection. */ +static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos) +{ + int low = 0, high = zstd->seek.num_frames; + + if (pos >= zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + + while (low + 1 < high) { + int mid = low + ((high - low) >> 1); + if (zstd->seek.uncompressed_ofs[mid] <= pos) { + low = mid; + } + else { + high = mid; + } + } + + return low; +} + +/* Ensure that the currently loaded frame is the correct one. */ +static const char *zstd_ensure_cache(ZstdReader *zstd, int frame) +{ + if (zstd->seek.cached_frame == frame) { + /* Cached frame matches, so just return it. */ + return zstd->seek.cached_content; + } + + /* Cached frame doesn't match, so discard it and cache the wanted one onstead. */ + MEM_SAFE_FREE(zstd->seek.cached_content); + + size_t compressed_size = zstd->seek.compressed_ofs[frame + 1] - zstd->seek.compressed_ofs[frame]; + size_t uncompressed_size = zstd->seek.uncompressed_ofs[frame + 1] - + zstd->seek.uncompressed_ofs[frame]; + + char *uncompressed_data = MEM_mallocN(uncompressed_size, __func__); + char *compressed_data = MEM_mallocN(compressed_size, __func__); + if (zstd->base->seek(zstd->base, zstd->seek.compressed_ofs[frame], SEEK_SET) < 0 || + zstd->base->read(zstd->base, compressed_data, compressed_size) < compressed_size) { + MEM_freeN(compressed_data); + MEM_freeN(uncompressed_data); + return NULL; + } + + size_t res = ZSTD_decompressDCtx( + zstd->ctx, uncompressed_data, uncompressed_size, compressed_data, compressed_size); + MEM_freeN(compressed_data); + if (ZSTD_isError(res) || res < uncompressed_size) { + MEM_freeN(uncompressed_data); + return NULL; + } + + zstd->seek.cached_frame = frame; + zstd->seek.cached_content = uncompressed_data; + return uncompressed_data; +} + +static ssize_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + size_t end_offset = zstd->reader.offset + size, read_len = 0; + while (zstd->reader.offset < end_offset) { + int frame = zstd_frame_from_pos(zstd, zstd->reader.offset); + if (frame < 0) { + /* EOF is reached, so return as much as we can. */ + break; + } + + const char *framedata = zstd_ensure_cache(zstd, frame); + if (framedata == NULL) { + /* Error while reading the frame, so return as much as we can. */ + break; + } + + size_t frame_end_offset = min_zz(zstd->seek.uncompressed_ofs[frame + 1], end_offset); + size_t frame_read_len = frame_end_offset - zstd->reader.offset; + + size_t offset_in_frame = zstd->reader.offset - zstd->seek.uncompressed_ofs[frame]; + memcpy((char *)buffer + read_len, framedata + offset_in_frame, frame_read_len); + read_len += frame_read_len; + zstd->reader.offset = frame_end_offset; + } + + return read_len; +} + +static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence) +{ + ZstdReader *zstd = (ZstdReader *)reader; + off64_t new_pos; + if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = zstd->seek.uncompressed_ofs[zstd->seek.num_frames] + offset; + } + else { + new_pos = zstd->reader.offset + offset; + } + + if (new_pos < 0 || new_pos > zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + zstd->reader.offset = new_pos; + return zstd->reader.offset; +} + +static ssize_t zstd_read(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + ZSTD_outBuffer output = {buffer, size, 0}; + + while (output.pos < output.size) { + if (zstd->in_buf.pos == zstd->in_buf.size) { + /* Ran out of buffered input data, read some more. */ + zstd->in_buf.pos = 0; + ssize_t readsize = zstd->base->read( + zstd->base, (char *)zstd->in_buf.src, zstd->in_buf_max_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + zstd->in_buf.size = readsize; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + if (ZSTD_isError(ZSTD_decompressStream(zstd->ctx, &output, &zstd->in_buf))) { + break; + } + } + + zstd->reader.offset += output.pos; + return output.pos; +} + +static void zstd_close(FileReader *reader) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + ZSTD_freeDCtx(zstd->ctx); + if (zstd->reader.seek) { + MEM_freeN(zstd->seek.uncompressed_ofs); + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.cached_content); + } + else { + MEM_freeN((void *)zstd->in_buf.src); + } + + zstd->base->close(zstd->base); + MEM_freeN(zstd); +} + +FileReader *BLI_filereader_new_zstd(FileReader *base) +{ + ZstdReader *zstd = MEM_callocN(sizeof(ZstdReader), __func__); + + zstd->ctx = ZSTD_createDCtx(); + zstd->base = base; + + if (zstd_read_seek_table(zstd)) { + zstd->reader.read = zstd_read_seekable; + zstd->reader.seek = zstd_seek; + } + else { + zstd->reader.read = zstd_read; + zstd->reader.seek = NULL; + + zstd->in_buf_max_size = ZSTD_DStreamInSize(); + zstd->in_buf.src = MEM_mallocN(zstd->in_buf_max_size, "zstd in buf"); + zstd->in_buf.size = zstd->in_buf_max_size; + /* This signals that the buffer has run out, + * which will make the read function refill it on the first call. */ + zstd->in_buf.pos = zstd->in_buf_max_size; + } + zstd->reader.close = zstd_close; + + return (FileReader *)zstd; +} diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index a8b50b66f5f..e1e3aa273b5 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -302,7 +302,7 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) /* Get the name. */ if (face->family_name) { BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name); - BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name)); + BLI_str_utf8_invalid_strip(vfd->name, strlen(vfd->name)); } /* Select a character map. */ diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index a80c495ecf3..983fd3b6543 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -363,6 +363,14 @@ MINLINE signed char round_db_to_char_clamp(double a){ #undef _round_clamp_fl_impl #undef _round_clamp_db_impl +/** + * Round to closest even number, halfway cases are rounded away from zero. + */ +MINLINE float round_to_even(float f) +{ + return roundf(f * 0.5f) * 2.0f; +} + /* integer division that rounds 0.5 up, particularly useful for color blending * with integers, to avoid gradual darkening when rounding down */ MINLINE int divide_round_i(int a, int b) diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 19ff8764259..5710bd6b150 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -70,7 +70,7 @@ static const size_t utf8_skip_data[256] = { * * \return the offset of the first invalid byte. */ -ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) +ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length) { const unsigned char *p, *perr, *pend = (const unsigned char *)str + length; unsigned char c; @@ -200,14 +200,14 @@ utf8_error: * * \return number of stripped bytes. */ -int BLI_utf8_invalid_strip(char *str, size_t length) +int BLI_str_utf8_invalid_strip(char *str, size_t length) { ptrdiff_t bad_char; int tot = 0; BLI_assert(str[length] == '\0'); - while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) { + while ((bad_char = BLI_str_utf8_invalid_byte(str, length)) != -1) { str += bad_char; length -= (size_t)(bad_char + 1); diff --git a/source/blender/blenlib/tests/BLI_string_utf8_test.cc b/source/blender/blenlib/tests/BLI_string_utf8_test.cc index 9ddc372e6d1..13f5cb7f284 100644 --- a/source/blender/blenlib/tests/BLI_string_utf8_test.cc +++ b/source/blender/blenlib/tests/BLI_string_utf8_test.cc @@ -266,7 +266,7 @@ static const char *utf8_invalid_tests[][3] = { }; /* clang-format on */ -/* BLI_utf8_invalid_strip (and indirectly, BLI_utf8_invalid_byte). */ +/* BLI_str_utf8_invalid_strip (and indirectly, BLI_str_utf8_invalid_byte). */ TEST(string, Utf8InvalidBytes) { for (int i = 0; utf8_invalid_tests[i][0] != nullptr; i++) { @@ -277,7 +277,7 @@ TEST(string, Utf8InvalidBytes) char buff[80]; memcpy(buff, tst, sizeof(buff)); - const int num_errors_found = BLI_utf8_invalid_strip(buff, sizeof(buff) - 1); + const int num_errors_found = BLI_str_utf8_invalid_strip(buff, sizeof(buff) - 1); printf("[%02d] -> [%02d] \"%s\" -> \"%s\"\n", num_errors, num_errors_found, tst, buff); EXPECT_EQ(num_errors_found, num_errors); diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h index fc41a6e832f..4e240e2462b 100644 --- a/source/blender/blenloader/BLO_undofile.h +++ b/source/blender/blenloader/BLO_undofile.h @@ -24,6 +24,8 @@ * \ingroup blenloader */ +#include "BLI_filereader.h" + struct GHash; struct Scene; @@ -65,6 +67,16 @@ typedef struct MemFileUndoData { size_t undo_size; } MemFileUndoData; +/* FileReader-compatible wrapper for reading MemFiles */ +typedef struct { + FileReader reader; + + MemFile *memfile; + int undo_direction; + + bool memchunk_identical; +} UndoReader; + /* actually only used writefile.c */ void BLO_memfile_write_init(MemFileWriteData *mem_data, @@ -84,3 +96,5 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene); extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename); + +FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction); diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index f5baf0dcb83..89631588ed0 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -42,7 +42,7 @@ set(INC ) set(INC_SYS - ${ZLIB_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} ) set(SRC diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e48c305fc4b..49c3497f996 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -21,8 +21,6 @@ * \ingroup blenloader */ -#include "zlib.h" - #include <ctype.h> /* for isdigit. */ #include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */ #include <limits.h> @@ -71,7 +69,6 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_mempool.h" -#include "BLI_mmap.h" #include "BLI_threads.h" #include "PIL_time.h" @@ -788,7 +785,7 @@ static BHeadN *get_bhead(FileData *fd) */ if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) { bhead4.code = DATA; - readsize = fd->read(fd, &bhead4, sizeof(bhead4), NULL); + readsize = fd->file->read(fd->file, &bhead4, sizeof(bhead4)); if (readsize == sizeof(bhead4) || bhead4.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -811,7 +808,7 @@ static BHeadN *get_bhead(FileData *fd) } else { bhead8.code = DATA; - readsize = fd->read(fd, &bhead8, sizeof(bhead8), NULL); + readsize = fd->file->read(fd->file, &bhead8, sizeof(bhead8)); if (readsize == sizeof(bhead8) || bhead8.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -845,22 +842,22 @@ static BHeadN *get_bhead(FileData *fd) /* pass */ } #ifdef USE_BHEAD_READ_ON_DEMAND - else if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) { + else if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) { /* Delay reading bhead content. */ new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead"); if (new_bhead) { new_bhead->next = new_bhead->prev = NULL; - new_bhead->file_offset = fd->file_offset; + new_bhead->file_offset = fd->file->offset; new_bhead->has_data = false; new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; - off64_t seek_new = fd->seek(fd, bhead.len, SEEK_CUR); + off64_t seek_new = fd->file->seek(fd->file, bhead.len, SEEK_CUR); if (seek_new == -1) { fd->is_eof = true; MEM_freeN(new_bhead); new_bhead = NULL; } - BLI_assert(fd->file_offset == seek_new); + BLI_assert(fd->file->offset == seek_new); } else { fd->is_eof = true; @@ -878,14 +875,17 @@ static BHeadN *get_bhead(FileData *fd) new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; - readsize = fd->read( - fd, new_bhead + 1, (size_t)bhead.len, &new_bhead->is_memchunk_identical); + readsize = fd->file->read(fd->file, new_bhead + 1, (size_t)bhead.len); - if (readsize != (ssize_t)bhead.len) { + if (readsize != bhead.len) { fd->is_eof = true; MEM_freeN(new_bhead); new_bhead = NULL; } + + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } } else { fd->is_eof = true; @@ -964,17 +964,19 @@ static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf) bool success = true; BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock); BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0); - off64_t offset_backup = fd->file_offset; - if (UNLIKELY(fd->seek(fd, new_bhead->file_offset, SEEK_SET) == -1)) { + off64_t offset_backup = fd->file->offset; + if (UNLIKELY(fd->file->seek(fd->file, new_bhead->file_offset, SEEK_SET) == -1)) { success = false; } else { - if (fd->read(fd, buf, (size_t)new_bhead->bhead.len, &new_bhead->is_memchunk_identical) != - (ssize_t)new_bhead->bhead.len) { + if (fd->file->read(fd->file, buf, (size_t)new_bhead->bhead.len) != new_bhead->bhead.len) { success = false; } + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } } - if (fd->seek(fd, offset_backup, SEEK_SET) == -1) { + if (fd->file->seek(fd->file, offset_backup, SEEK_SET) == -1) { success = false; } return success; @@ -1017,7 +1019,7 @@ static void decode_blender_header(FileData *fd) ssize_t readsize; /* read in the header data */ - readsize = fd->read(fd, header, sizeof(header), NULL); + readsize = fd->file->read(fd->file, header, sizeof(header)); if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') && ELEM(header[8], 'v', 'V') && @@ -1147,210 +1149,12 @@ static int *read_file_thumbnail(FileData *fd) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name File Data API - * \{ */ - -/* Regular file reading. */ - -static ssize_t fd_read_data_from_file(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - ssize_t readsize = read(filedata->filedes, buffer, size); - - if (readsize < 0) { - readsize = EOF; - } - else { - filedata->file_offset += readsize; - } - - return readsize; -} - -static off64_t fd_seek_data_from_file(FileData *filedata, off64_t offset, int whence) -{ - filedata->file_offset = BLI_lseek(filedata->filedes, offset, whence); - return filedata->file_offset; -} - -/* GZip file reading. */ - -static ssize_t fd_read_gzip_from_file(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - BLI_assert(size <= INT_MAX); - - ssize_t readsize = gzread(filedata->gzfiledes, buffer, (uint)size); - - if (readsize < 0) { - readsize = EOF; - } - else { - filedata->file_offset += readsize; - } - - return readsize; -} - -/* Memory reading. */ - -static ssize_t fd_read_from_memory(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - /* don't read more bytes than there are available in the buffer */ - ssize_t readsize = (ssize_t)MIN2(size, filedata->buffersize - (size_t)filedata->file_offset); - - memcpy(buffer, filedata->buffer + filedata->file_offset, (size_t)readsize); - filedata->file_offset += readsize; - - return readsize; -} - -/* Memory-mapped file reading. - * By using mmap(), we can map a file so that it can be treated like normal memory, - * meaning that we can just read from it with memcpy() etc. - * This avoids system call overhead and can significantly speed up file loading. - */ - -static ssize_t fd_read_from_mmap(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - /* don't read more bytes than there are available in the buffer */ - size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset)); - - if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) { - return 0; - } - - filedata->file_offset += readsize; - - return readsize; -} - -static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence) -{ - off64_t new_pos; - if (whence == SEEK_CUR) { - new_pos = filedata->file_offset + offset; - } - else if (whence == SEEK_SET) { - new_pos = offset; - } - else if (whence == SEEK_END) { - new_pos = filedata->buffersize + offset; - } - else { - return -1; - } - - if (new_pos < 0 || new_pos > filedata->buffersize) { - return -1; - } - - filedata->file_offset = new_pos; - return filedata->file_offset; -} - -/* MemFile reading. */ - -static ssize_t fd_read_from_memfile(FileData *filedata, - void *buffer, - size_t size, - bool *r_is_memchunck_identical) -{ - static size_t seek = SIZE_MAX; /* the current position */ - static size_t offset = 0; /* size of previous chunks */ - static MemFileChunk *chunk = NULL; - size_t chunkoffset, readsize, totread; - - if (r_is_memchunck_identical != NULL) { - *r_is_memchunck_identical = true; - } - - if (size == 0) { - return 0; - } - - if (seek != (size_t)filedata->file_offset) { - chunk = filedata->memfile->chunks.first; - seek = 0; - - while (chunk) { - if (seek + chunk->size > (size_t)filedata->file_offset) { - break; - } - seek += chunk->size; - chunk = chunk->next; - } - offset = seek; - seek = (size_t)filedata->file_offset; - } - - if (chunk) { - totread = 0; - - do { - /* first check if it's on the end if current chunk */ - if (seek - offset == chunk->size) { - offset += chunk->size; - chunk = chunk->next; - } - - /* debug, should never happen */ - if (chunk == NULL) { - CLOG_ERROR(&LOG, "Illegal read, got a NULL chunk"); - return 0; - } - - chunkoffset = seek - offset; - readsize = size - totread; - - /* data can be spread over multiple chunks, so clamp size - * to within this chunk, and then it will read further in - * the next chunk */ - if (chunkoffset + readsize > chunk->size) { - readsize = chunk->size - chunkoffset; - } - - memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize); - totread += readsize; - filedata->file_offset += readsize; - seek += readsize; - if (r_is_memchunck_identical != NULL) { - /* `is_identical` of current chunk represents whether it changed compared to previous undo - * step. this is fine in redo case, but not in undo case, where we need an extra flag - * defined when saving the next (future) step after the one we want to restore, as we are - * supposed to 'come from' that future undo step, and not the one before current one. */ - *r_is_memchunck_identical &= filedata->undo_direction == STEP_REDO ? - chunk->is_identical : - chunk->is_identical_future; - } - } while (totread < size); - - return (ssize_t)totread; - } - - return 0; -} - static FileData *filedata_new(BlendFileReadReport *reports) { BLI_assert(reports != NULL); FileData *fd = MEM_callocN(sizeof(FileData), "FileData"); - fd->filedes = -1; - fd->gzfiledes = NULL; - fd->memsdna = DNA_sdna_current_get(); fd->datamap = oldnewmap_new(); @@ -1387,78 +1191,66 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports) static FileData *blo_filedata_from_file_descriptor(const char *filepath, BlendFileReadReport *reports, - int file) + int filedes) { - FileDataReadFn *read_fn = NULL; - FileDataSeekFn *seek_fn = NULL; /* Optional. */ - size_t buffersize = 0; - BLI_mmap_file *mmap_file = NULL; - - gzFile gzfile = (gzFile)Z_NULL; - char header[7]; + FileReader *rawfile = BLI_filereader_new_file(filedes); + FileReader *file = NULL; - /* Regular file. */ errno = 0; - if (read(file, header, sizeof(header)) != sizeof(header)) { + /* If opening the file failed or we can't read the header, give up. */ + if (rawfile == NULL || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { BKE_reportf(reports->reports, RPT_WARNING, "Unable to read '%s': %s", filepath, errno ? strerror(errno) : TIP_("insufficient content")); + if (rawfile) { + rawfile->close(rawfile); + } + else { + close(filedes); + } return NULL; } - /* Regular file. */ - if (memcmp(header, "BLENDER", sizeof(header)) == 0) { - read_fn = fd_read_data_from_file; - seek_fn = fd_seek_data_from_file; + /* Rewind the file after reading the header. */ + rawfile->seek(rawfile, 0, SEEK_SET); - mmap_file = BLI_mmap_open(file); - if (mmap_file != NULL) { - read_fn = fd_read_from_mmap; - seek_fn = fd_seek_from_mmap; - buffersize = BLI_lseek(file, 0, SEEK_END); + /* Check if we have a regular file. */ + if (memcmp(header, "BLENDER", sizeof(header)) == 0) { + /* Try opening the file with memory-mapped IO. */ + file = BLI_filereader_new_mmap(filedes); + if (file == NULL) { + /* mmap failed, so just keep using rawfile. */ + file = rawfile; + rawfile = NULL; } } - - BLI_lseek(file, 0, SEEK_SET); - - /* Gzip file. */ - errno = 0; - if ((read_fn == NULL) && - /* Check header magic. */ - (header[0] == 0x1f && header[1] == 0x8b)) { - gzfile = BLI_gzopen(filepath, "rb"); - if (gzfile == (gzFile)Z_NULL) { - BKE_reportf(reports->reports, - RPT_WARNING, - "Unable to open '%s': %s", - filepath, - errno ? strerror(errno) : TIP_("unknown error reading file")); - return NULL; + else if (BLI_file_magic_is_gzip(header)) { + file = BLI_filereader_new_gzip(rawfile); + if (file != NULL) { + rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */ + } + } + else if (BLI_file_magic_is_zstd(header)) { + file = BLI_filereader_new_zstd(rawfile); + if (file != NULL) { + rawfile = NULL; /* The Zstd FileReader takes ownership of `rawfile`. */ } - - /* 'seek_fn' is too slow for gzip, don't set it. */ - read_fn = fd_read_gzip_from_file; - /* Caller must close. */ - file = -1; } - if (read_fn == NULL) { + /* Clean up `rawfile` if it wasn't taken over. */ + if (rawfile != NULL) { + rawfile->close(rawfile); + } + if (file == NULL) { BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath); return NULL; } FileData *fd = filedata_new(reports); - - fd->filedes = file; - fd->gzfiledes = gzfile; - - fd->read = read_fn; - fd->seek = seek_fn; - fd->mmap_file = mmap_file; - fd->buffersize = buffersize; + fd->file = file; return fd; } @@ -1475,11 +1267,7 @@ static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileRead errno ? strerror(errno) : TIP_("unknown error reading file")); return NULL; } - FileData *fd = blo_filedata_from_file_descriptor(filepath, reports, file); - if ((fd == NULL) || (fd->filedes == -1)) { - close(file); - } - return fd; + return blo_filedata_from_file_descriptor(filepath, reports, file); } /* cannot be called with relative paths anymore! */ @@ -1513,50 +1301,6 @@ static FileData *blo_filedata_from_file_minimal(const char *filepath) return NULL; } -static ssize_t fd_read_gzip_from_memory(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - int err; - - filedata->strm.next_out = (Bytef *)buffer; - filedata->strm.avail_out = (uint)size; - - /* Inflate another chunk. */ - err = inflate(&filedata->strm, Z_SYNC_FLUSH); - - if (err == Z_STREAM_END) { - return 0; - } - if (err != Z_OK) { - CLOG_ERROR(&LOG, "ZLib error (code %d)", err); - return 0; - } - - filedata->file_offset += size; - - return (ssize_t)size; -} - -static int fd_read_gzip_from_memory_init(FileData *fd) -{ - - fd->strm.next_in = (Bytef *)fd->buffer; - fd->strm.avail_in = fd->buffersize; - fd->strm.total_out = 0; - fd->strm.zalloc = Z_NULL; - fd->strm.zfree = Z_NULL; - - if (inflateInit2(&fd->strm, (16 + MAX_WBITS)) != Z_OK) { - return 0; - } - - fd->read = fd_read_gzip_from_memory; - - return 1; -} - FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports) { if (!mem || memsize < SIZEOFBLENDERHEADER) { @@ -1565,24 +1309,24 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe return NULL; } - FileData *fd = filedata_new(reports); - const char *cp = mem; - - fd->buffer = mem; - fd->buffersize = memsize; + FileReader *mem_file = BLI_filereader_new_memory(mem, memsize); + FileReader *file = mem_file; - /* test if gzip */ - if (cp[0] == 0x1f && cp[1] == 0x8b) { - if (0 == fd_read_gzip_from_memory_init(fd)) { - blo_filedata_free(fd); - return NULL; - } + if (BLI_file_magic_is_gzip(mem)) { + file = BLI_filereader_new_gzip(mem_file); } - else { - fd->read = fd_read_from_memory; + else if (BLI_file_magic_is_zstd(mem)) { + file = BLI_filereader_new_zstd(mem_file); } - fd->flags |= FD_FLAGS_NOT_MY_BUFFER; + if (file == NULL) { + /* Compression initialization failed. */ + mem_file->close(mem_file); + return NULL; + } + + FileData *fd = filedata_new(reports); + fd->file = file; return blo_decode_and_check(fd, reports->reports); } @@ -1597,11 +1341,9 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, } FileData *fd = filedata_new(reports); - fd->memfile = memfile; + fd->file = BLO_memfile_new_filereader(memfile, params->undo_direction); fd->undo_direction = params->undo_direction; - - fd->read = fd_read_from_memfile; - fd->flags |= FD_FLAGS_NOT_MY_BUFFER; + fd->flags |= FD_FLAGS_IS_MEMFILE; return blo_decode_and_check(fd, reports->reports); } @@ -1609,30 +1351,7 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, void blo_filedata_free(FileData *fd) { if (fd) { - if (fd->filedes != -1) { - close(fd->filedes); - } - - if (fd->gzfiledes != NULL) { - gzclose(fd->gzfiledes); - } - - if (fd->strm.next_in) { - int err = inflateEnd(&fd->strm); - if (err != Z_OK) { - CLOG_ERROR(&LOG, "Close gzip stream error (code %d)", err); - } - } - - if (fd->buffer && !(fd->flags & FD_FLAGS_NOT_MY_BUFFER)) { - MEM_freeN((void *)fd->buffer); - fd->buffer = NULL; - } - - if (fd->mmap_file) { - BLI_mmap_free(fd->mmap_file); - fd->mmap_file = NULL; - } + fd->file->close(fd->file); /* Free all BHeadN data blocks */ #ifndef NDEBUG @@ -1640,7 +1359,7 @@ void blo_filedata_free(FileData *fd) #else /* Sanity check we're not keeping memory we don't need. */ LISTBASE_FOREACH_MUTABLE (BHeadN *, new_bhead, &fd->bhead_list) { - if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) { + if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) { BLI_assert(new_bhead->has_data == 0); } MEM_freeN(new_bhead); @@ -2096,7 +1815,7 @@ static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), void blo_cache_storage_init(FileData *fd, Main *bmain) { - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { BLI_assert(fd->cache_storage == NULL); fd->cache_storage = MEM_mallocN(sizeof(*fd->cache_storage), __func__); fd->cache_storage->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -2261,7 +1980,7 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname) * undo since DNA must match. */ static const void *peek_struct_undo(FileData *fd, BHead *bhead) { - BLI_assert(fd->memfile != NULL); + BLI_assert(fd->flags & FD_FLAGS_IS_MEMFILE); UNUSED_VARS_NDEBUG(fd); return (bhead->len) ? (const void *)(bhead + 1) : NULL; } @@ -3679,7 +3398,7 @@ static BHead *read_libblock(FileData *fd, * When datablocks are changed but still exist, we restore them at the old * address and inherit recalc flags for the dependency graph. */ ID *id_old = NULL; - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) { if (r_id) { *r_id = id_old; @@ -3980,13 +3699,14 @@ static void lib_link_all(FileData *fd, Main *bmain) continue; } - if (fd->memfile != NULL && GS(id->name) == ID_WM) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) && GS(id->name) == ID_WM) { /* No load UI for undo memfiles. * Only WM currently, SCR needs it still (see below), and so does WS? */ continue; } - if (fd->memfile != NULL && do_partial_undo && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo && + (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { /* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across * current undo step, and old IDs re-use their old memory address, we do not need to liblink * it at all. */ @@ -4165,7 +3885,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) BlendFileData *bfd; ListBase mainlist = {NULL, NULL}; - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step"); } @@ -4256,7 +3976,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) } /* do before read_libraries, but skip undo case */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) { do_versions(fd, NULL, bfd->main); } @@ -4278,7 +3998,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) fd->reports->duration.libraries = PIL_check_seconds_timer() - fd->reports->duration.libraries; /* Skip in undo case. */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { /* Note that we can't recompute user-counts at this point in undo case, we play too much with * IDs from different memory realms, and Main database is not in a fully valid state yet. */ @@ -4311,7 +4031,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) /* Now that all our data-blocks are loaded, * we can re-generate overrides from their references. */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { /* Do not apply in undo case! */ fd->reports->duration.lib_overrides = PIL_check_seconds_timer(); @@ -4391,7 +4111,7 @@ static void sort_bhead_old_map(FileData *fd) static BHead *find_previous_lib(FileData *fd, BHead *bhead) { /* Skip library data-blocks in undo, see comment in read_libblock. */ - if (fd->memfile) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { return NULL; } @@ -5850,7 +5570,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p) bool BLO_read_data_is_undo(BlendDataReader *reader) { - return reader->fd->memfile != NULL; + return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); } void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr) @@ -5870,7 +5590,7 @@ BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader) bool BLO_read_lib_is_undo(BlendLibReader *reader) { - return reader->fd->memfile != NULL; + return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); } Main *BLO_read_lib_get_main(BlendLibReader *reader) diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index b04043f9641..beeed8e45ae 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -28,10 +28,10 @@ # include "BLI_winstuff.h" #endif +#include "BLI_filereader.h" #include "DNA_sdna_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" /* for ReportType */ -#include "zlib.h" struct BLI_mmap_file; struct BLOCacheStorage; @@ -50,7 +50,7 @@ enum eFileDataFlag { FD_FLAGS_FILE_POINTSIZE_IS_4 = 1 << 1, FD_FLAGS_POINTSIZE_DIFFERS = 1 << 2, FD_FLAGS_FILE_OK = 1 << 3, - FD_FLAGS_NOT_MY_BUFFER = 1 << 4, + FD_FLAGS_IS_MEMFILE = 1 << 4, /* XXX Unused in practice (checked once but never set). */ FD_FLAGS_NOT_MY_LIBMAP = 1 << 5, }; @@ -60,44 +60,18 @@ enum eFileDataFlag { # pragma GCC poison off_t #endif -#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__) -typedef int64_t off64_t; -#endif - -typedef ssize_t(FileDataReadFn)(struct FileData *filedata, - void *buffer, - size_t size, - bool *r_is_memchunk_identical); -typedef off64_t(FileDataSeekFn)(struct FileData *filedata, off64_t offset, int whence); - typedef struct FileData { /** Linked list of BHeadN's. */ ListBase bhead_list; enum eFileDataFlag flags; bool is_eof; - size_t buffersize; - off64_t file_offset; - FileDataReadFn *read; - FileDataSeekFn *seek; + FileReader *file; - /** Regular file reading. */ - int filedes; - - /** Variables needed for reading from memory / stream / memory-mapped files. */ - const char *buffer; - struct BLI_mmap_file *mmap_file; - /** Variables needed for reading from memfile (undo). */ - struct MemFile *memfile; /** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use * to detect unchanged data from memfile. */ int undo_direction; /* eUndoStepDir */ - /** Variables needed for reading from file. */ - gzFile gzfiledes; - /** Gzip stream for memory decompression. */ - z_stream strm; - /** Now only in use for library appending. */ char relabase[FILE_MAX]; diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c index 2eeeac2e8d7..62072cf7df5 100644 --- a/source/blender/blenloader/intern/undofile.c +++ b/source/blender/blenloader/intern/undofile.c @@ -48,6 +48,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_undo_system.h" /* keep last */ #include "BLI_strict_flags.h" @@ -273,3 +274,97 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) } return true; } + +static ssize_t undo_read(FileReader *reader, void *buffer, size_t size) +{ + UndoReader *undo = (UndoReader *)reader; + + static size_t seek = SIZE_MAX; /* The current position. */ + static size_t offset = 0; /* Size of previous chunks. */ + static MemFileChunk *chunk = NULL; + size_t chunkoffset, readsize, totread; + + undo->memchunk_identical = true; + + if (size == 0) { + return 0; + } + + if (seek != (size_t)undo->reader.offset) { + chunk = undo->memfile->chunks.first; + seek = 0; + + while (chunk) { + if (seek + chunk->size > (size_t)undo->reader.offset) { + break; + } + seek += chunk->size; + chunk = chunk->next; + } + offset = seek; + seek = (size_t)undo->reader.offset; + } + + if (chunk) { + totread = 0; + + do { + /* First check if it's on the end if current chunk. */ + if (seek - offset == chunk->size) { + offset += chunk->size; + chunk = chunk->next; + } + + /* Debug, should never happen. */ + if (chunk == NULL) { + printf("illegal read, chunk zero\n"); + return 0; + } + + chunkoffset = seek - offset; + readsize = size - totread; + + /* Data can be spread over multiple chunks, so clamp size + * to within this chunk, and then it will read further in + * the next chunk. */ + if (chunkoffset + readsize > chunk->size) { + readsize = chunk->size - chunkoffset; + } + + memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize); + totread += readsize; + undo->reader.offset += (off64_t)readsize; + seek += readsize; + + /* `is_identical` of current chunk represents whether it changed compared to previous undo + * step. this is fine in redo case, but not in undo case, where we need an extra flag + * defined when saving the next (future) step after the one we want to restore, as we are + * supposed to 'come from' that future undo step, and not the one before current one. */ + undo->memchunk_identical &= undo->undo_direction == STEP_REDO ? chunk->is_identical : + chunk->is_identical_future; + } while (totread < size); + + return (ssize_t)totread; + } + + return 0; +} + +static void undo_close(FileReader *reader) +{ + MEM_freeN(reader); +} + +FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction) +{ + UndoReader *undo = MEM_callocN(sizeof(UndoReader), __func__); + + undo->memfile = memfile; + undo->undo_direction = undo_direction; + + undo->reader.read = undo_read; + undo->reader.seek = NULL; + undo->reader.close = undo_close; + + return (FileReader *)undo; +} diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index e56c1995363..436645c2241 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -23,8 +23,7 @@ #else # include "BLI_winstuff.h" # include "winsock2.h" -# include <io.h> /* for open close read */ -# include <zlib.h> /* odd include order-issue */ +# include <io.h> /* for open close read */ #endif /* allow readfile to use deprecated functionality */ diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 81371e1c1ed..6ba27b6ee9e 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -28,8 +28,7 @@ #else # include "BLI_winstuff.h" # include "winsock2.h" -# include <io.h> /* for open close read */ -# include <zlib.h> /* odd include order-issue */ +# include <io.h> /* for open close read */ #endif /* allow readfile to use deprecated functionality */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 99246603e9a..90d58514eb5 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -83,7 +83,6 @@ # include "BLI_winstuff.h" # include "winsock2.h" # include <io.h> -# include <zlib.h> /* odd include order-issue */ #else # include <unistd.h> /* FreeBSD, for write() and close(). */ #endif @@ -101,7 +100,12 @@ #include "BLI_bitmap.h" #include "BLI_blenlib.h" #include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_link_utils.h" +#include "BLI_linklist.h" +#include "BLI_math_base.h" #include "BLI_mempool.h" +#include "BLI_threads.h" #include "MEM_guardedalloc.h" /* MEM_freeN */ #include "BKE_blender_version.h" @@ -129,14 +133,21 @@ #include <errno.h> +#include <zstd.h> + /* Make preferences read-only. */ #define U (*((const UserDef *)&U)) /* ********* my write, buffered writing with minimum size chunks ************ */ /* Use optimal allocation since blocks of this size are kept in memory for undo. */ -#define MYWRITE_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */ -#define MYWRITE_MAX_CHUNK (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */ +#define MEM_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */ +#define MEM_CHUNK_SIZE (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */ + +#define ZSTD_BUFFER_SIZE (1 << 21) /* 2mb */ +#define ZSTD_CHUNK_SIZE (1 << 20) /* 1mb */ + +#define ZSTD_COMPRESSION_LEVEL 3 /** Use if we want to store how many bytes have been written to the file. */ // #define USE_WRITE_DATA_LEN @@ -147,9 +158,16 @@ typedef enum { WW_WRAP_NONE = 1, - WW_WRAP_ZLIB, + WW_WRAP_ZSTD, } eWriteWrapType; +typedef struct ZstdFrame { + struct ZstdFrame *next, *prev; + + uint32_t compressed_size; + uint32_t uncompressed_size; +} ZstdFrame; + typedef struct WriteWrap WriteWrap; struct WriteWrap { /* callbacks */ @@ -161,15 +179,23 @@ struct WriteWrap { bool use_buf; /* internal */ - union { - int file_handle; - gzFile gz_handle; - } _user_data; + int file_handle; + struct { + ListBase threadpool; + ListBase tasks; + ThreadMutex mutex; + ThreadCondition condition; + int next_frame; + int num_frames; + + int level; + ListBase frames; + + bool write_error; + } zstd; }; /* none */ -#define FILE_HANDLE(ww) (ww)->_user_data.file_handle - static bool ww_open_none(WriteWrap *ww, const char *filepath) { int file; @@ -177,7 +203,7 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath) file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666); if (file != -1) { - FILE_HANDLE(ww) = file; + ww->file_handle = file; return true; } @@ -185,39 +211,170 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath) } static bool ww_close_none(WriteWrap *ww) { - return (close(FILE_HANDLE(ww)) != -1); + return (close(ww->file_handle) != -1); } static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len) { - return write(FILE_HANDLE(ww), buf, buf_len); + return write(ww->file_handle, buf, buf_len); } -#undef FILE_HANDLE -/* zlib */ -#define FILE_HANDLE(ww) (ww)->_user_data.gz_handle +/* zstd */ + +typedef struct { + struct ZstdWriteBlockTask *next, *prev; + void *data; + size_t size; + int frame_number; + WriteWrap *ww; +} ZstdWriteBlockTask; -static bool ww_open_zlib(WriteWrap *ww, const char *filepath) +static void *zstd_write_task(void *userdata) { - gzFile file; + ZstdWriteBlockTask *task = userdata; + WriteWrap *ww = task->ww; - file = BLI_gzopen(filepath, "wb1"); + size_t out_buf_len = ZSTD_compressBound(task->size); + void *out_buf = MEM_mallocN(out_buf_len, "Zstd out buffer"); + size_t out_size = ZSTD_compress( + out_buf, out_buf_len, task->data, task->size, ZSTD_COMPRESSION_LEVEL); - if (file != Z_NULL) { - FILE_HANDLE(ww) = file; - return true; + MEM_freeN(task->data); + + BLI_mutex_lock(&ww->zstd.mutex); + + while (ww->zstd.next_frame != task->frame_number) { + BLI_condition_wait(&ww->zstd.condition, &ww->zstd.mutex); } - return false; + if (ZSTD_isError(out_size)) { + ww->zstd.write_error = true; + } + else { + if (ww_write_none(ww, out_buf, out_size) == out_size) { + ZstdFrame *frameinfo = MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo"); + frameinfo->uncompressed_size = task->size; + frameinfo->compressed_size = out_size; + BLI_addtail(&ww->zstd.frames, frameinfo); + } + else { + ww->zstd.write_error = true; + } + } + + ww->zstd.next_frame++; + + BLI_mutex_unlock(&ww->zstd.mutex); + BLI_condition_notify_all(&ww->zstd.condition); + + MEM_freeN(out_buf); + return NULL; +} + +static bool ww_open_zstd(WriteWrap *ww, const char *filepath) +{ + if (!ww_open_none(ww, filepath)) { + return false; + } + + /* Leave one thread open for the main writing logic, unless we only have one HW thread. */ + int num_threads = max_ii(1, BLI_system_thread_count() - 1); + BLI_threadpool_init(&ww->zstd.threadpool, zstd_write_task, num_threads); + BLI_mutex_init(&ww->zstd.mutex); + BLI_condition_init(&ww->zstd.condition); + + return true; } -static bool ww_close_zlib(WriteWrap *ww) + +static void zstd_write_u32_le(WriteWrap *ww, uint32_t val) +{ +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&val); +#endif + ww_write_none(ww, (char *)&val, sizeof(uint32_t)); +} + +/* In order to implement efficient seeking when reading the .blend, we append + * a skippable frame that encodes information about the other frames present + * in the file. + * The format here follows the upstream spec for seekable files: + * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md + * If this information is not present in a file (e.g. if it was compressed + * with external tools), it can still be opened in Blender, but seeking will + * not be supported, so more memory might be needed. */ +static void zstd_write_seekable_frames(WriteWrap *ww) +{ + /* Write seek table header (magic number and frame size). */ + zstd_write_u32_le(ww, 0x184D2A5E); + + /* The actual frame number might not match ww->zstd.num_frames if there was a write error. */ + const uint32_t num_frames = BLI_listbase_count(&ww->zstd.frames); + /* Each frame consists of two u32, so 8 bytes each. + * After the frames, a footer containing two u32 and one byte (9 bytes total) is written. */ + const uint32_t frame_size = num_frames * 8 + 9; + zstd_write_u32_le(ww, frame_size); + + /* Write seek table entries. */ + LISTBASE_FOREACH (ZstdFrame *, frame, &ww->zstd.frames) { + zstd_write_u32_le(ww, frame->compressed_size); + zstd_write_u32_le(ww, frame->uncompressed_size); + } + + /* Write seek table footer (number of frames, option flags and second magic number). */ + zstd_write_u32_le(ww, num_frames); + const char flags = 0; /* We don't store checksums for each frame. */ + ww_write_none(ww, &flags, 1); + zstd_write_u32_le(ww, 0x8F92EAB1); +} + +static bool ww_close_zstd(WriteWrap *ww) { - return (gzclose(FILE_HANDLE(ww)) == Z_OK); + BLI_threadpool_end(&ww->zstd.threadpool); + BLI_freelistN(&ww->zstd.tasks); + + BLI_mutex_end(&ww->zstd.mutex); + BLI_condition_end(&ww->zstd.condition); + + zstd_write_seekable_frames(ww); + BLI_freelistN(&ww->zstd.frames); + + return ww_close_none(ww) && !ww->zstd.write_error; } -static size_t ww_write_zlib(WriteWrap *ww, const char *buf, size_t buf_len) + +static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len) { - return gzwrite(FILE_HANDLE(ww), buf, buf_len); + if (ww->zstd.write_error) { + return 0; + } + + ZstdWriteBlockTask *task = MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__); + task->data = MEM_mallocN(buf_len, __func__); + memcpy(task->data, buf, buf_len); + task->size = buf_len; + task->frame_number = ww->zstd.num_frames++; + task->ww = ww; + + BLI_mutex_lock(&ww->zstd.mutex); + BLI_addtail(&ww->zstd.tasks, task); + + /* If there's a free worker thread, just push the block into that thread. + * Otherwise, we wait for the earliest thread to finish. + * We look up the earliest thread while holding the mutex, but release it + * before joining the thread to prevent a deadlock. */ + ZstdWriteBlockTask *first_task = ww->zstd.tasks.first; + BLI_mutex_unlock(&ww->zstd.mutex); + if (!BLI_available_threads(&ww->zstd.threadpool)) { + BLI_threadpool_remove(&ww->zstd.threadpool, first_task); + + /* If the task list was empty before we pushed our task, there should + * always be a free thread. */ + BLI_assert(first_task != task); + BLI_remlink(&ww->zstd.tasks, first_task); + MEM_freeN(first_task); + } + BLI_threadpool_insert(&ww->zstd.threadpool, task); + + return buf_len; } -#undef FILE_HANDLE /* --- end compression types --- */ @@ -226,11 +383,11 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww) memset(r_ww, 0, sizeof(*r_ww)); switch (ww_type) { - case WW_WRAP_ZLIB: { - r_ww->open = ww_open_zlib; - r_ww->close = ww_close_zlib; - r_ww->write = ww_write_zlib; - r_ww->use_buf = false; + case WW_WRAP_ZSTD: { + r_ww->open = ww_open_zstd; + r_ww->close = ww_close_zstd; + r_ww->write = ww_write_zstd; + r_ww->use_buf = true; break; } default: { @@ -252,10 +409,17 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww) typedef struct { const struct SDNA *sdna; - /** Use for file and memory writing (fixed size of #MYWRITE_BUFFER_SIZE). */ - uchar *buf; - /** Number of bytes used in #WriteData.buf (flushed when exceeded). */ - size_t buf_used_len; + struct { + /** Use for file and memory writing (size stored in max_size). */ + uchar *buf; + /** Number of bytes used in #WriteData.buf (flushed when exceeded). */ + size_t used_len; + + /** Maximum size of the buffer. */ + size_t max_size; + /** Threshold above which writes get their own chunk. */ + size_t chunk_size; + } buffer; #ifdef USE_WRITE_DATA_LEN /** Total number of bytes written. */ @@ -271,7 +435,7 @@ typedef struct { bool use_memfile; /** - * Wrap writing, so we can use zlib or + * Wrap writing, so we can use zstd or * other compression types later, see: G_FILE_COMPRESS * Will be NULL for UNDO. */ @@ -291,7 +455,15 @@ static WriteData *writedata_new(WriteWrap *ww) wd->ww = ww; if ((ww == NULL) || (ww->use_buf)) { - wd->buf = MEM_mallocN(MYWRITE_BUFFER_SIZE, "wd->buf"); + if (ww == NULL) { + wd->buffer.max_size = MEM_BUFFER_SIZE; + wd->buffer.chunk_size = MEM_CHUNK_SIZE; + } + else { + wd->buffer.max_size = ZSTD_BUFFER_SIZE; + wd->buffer.chunk_size = ZSTD_CHUNK_SIZE; + } + wd->buffer.buf = MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf"); } return wd; @@ -325,8 +497,8 @@ static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen) static void writedata_free(WriteData *wd) { - if (wd->buf) { - MEM_freeN(wd->buf); + if (wd->buffer.buf) { + MEM_freeN(wd->buffer.buf); } MEM_freeN(wd); } @@ -343,9 +515,9 @@ static void writedata_free(WriteData *wd) */ static void mywrite_flush(WriteData *wd) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } } @@ -369,20 +541,20 @@ static void mywrite(WriteData *wd, const void *adr, size_t len) wd->write_len += len; #endif - if (wd->buf == NULL) { + if (wd->buffer.buf == NULL) { writedata_do_write(wd, adr, len); } else { /* if we have a single big chunk, write existing data in * buffer and write out big chunk in smaller pieces */ - if (len > MYWRITE_MAX_CHUNK) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (len > wd->buffer.chunk_size) { + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } do { - size_t writelen = MIN2(len, MYWRITE_MAX_CHUNK); + size_t writelen = MIN2(len, wd->buffer.chunk_size); writedata_do_write(wd, adr, writelen); adr = (const char *)adr + writelen; len -= writelen; @@ -392,14 +564,14 @@ static void mywrite(WriteData *wd, const void *adr, size_t len) } /* if data would overflow buffer, write out the buffer */ - if (len + wd->buf_used_len > MYWRITE_BUFFER_SIZE - 1) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (len + wd->buffer.used_len > wd->buffer.max_size - 1) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } /* append data at end of buffer */ - memcpy(&wd->buf[wd->buf_used_len], adr, len); - wd->buf_used_len += len; + memcpy(&wd->buffer.buf[wd->buffer.used_len], adr, len); + wd->buffer.used_len += len; } } @@ -430,9 +602,9 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren */ static bool mywrite_end(WriteData *wd) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } if (wd->use_memfile) { @@ -1150,7 +1322,6 @@ bool BLO_write_file(Main *mainvar, ReportList *reports) { char tempname[FILE_MAX + 1]; - eWriteWrapType ww_type; WriteWrap ww; eBLO_WritePathRemap remap_mode = params->remap_mode; @@ -1172,14 +1343,7 @@ bool BLO_write_file(Main *mainvar, /* open temporary file, so we preserve the original in case we crash */ BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath); - if (write_flags & G_FILE_COMPRESS) { - ww_type = WW_WRAP_ZLIB; - } - else { - ww_type = WW_WRAP_NONE; - } - - ww_handle_init(ww_type, &ww); + ww_handle_init((write_flags & G_FILE_COMPRESS) ? WW_WRAP_ZSTD : WW_WRAP_NONE, &ww); if (ww.open(&ww, tempname) == false) { BKE_reportf( diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index ee287c65fe9..8ddcf11602a 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -328,10 +328,14 @@ set(SRC operations/COM_FastGaussianBlurOperation.h operations/COM_GammaCorrectOperation.cc operations/COM_GammaCorrectOperation.h + operations/COM_GaussianAlphaBlurBaseOperation.cc + operations/COM_GaussianAlphaBlurBaseOperation.h operations/COM_GaussianAlphaXBlurOperation.cc operations/COM_GaussianAlphaXBlurOperation.h operations/COM_GaussianAlphaYBlurOperation.cc operations/COM_GaussianAlphaYBlurOperation.h + operations/COM_GaussianBlurBaseOperation.cc + operations/COM_GaussianBlurBaseOperation.h operations/COM_GaussianBokehBlurOperation.cc operations/COM_GaussianBokehBlurOperation.h operations/COM_GaussianXBlurOperation.cc @@ -515,6 +519,8 @@ set(SRC operations/COM_ScaleOperation.h operations/COM_ScreenLensDistortionOperation.cc operations/COM_ScreenLensDistortionOperation.h + operations/COM_TransformOperation.cc + operations/COM_TransformOperation.h operations/COM_TranslateOperation.cc operations/COM_TranslateOperation.h operations/COM_WrapOperation.cc diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 40a1e0da2a8..e270eeb3386 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -33,6 +33,8 @@ enum class eExecutionModel { FullFrame }; +enum class eDimension { X, Y }; + /** * \brief possible data types for sockets * \ingroup Model @@ -119,6 +121,8 @@ constexpr float COM_PREVIEW_SIZE = 140.f; constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f; constexpr float COM_BLUR_BOKEH_PIXELS = 512; +constexpr rcti COM_SINGLE_ELEM_AREA = {0, 1, 0, 1}; + constexpr IndexRange XRange(const rcti &area) { return IndexRange(area.xmin, area.xmax - area.xmin); diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index 403ec62e359..c6e83f93777 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -239,6 +239,12 @@ class CompositorContext { this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices; } + /** Whether it has a view with a specific name and not the default one. */ + bool has_explicit_view() const + { + return m_viewName && m_viewName[0] != '\0'; + } + /** * \brief get the active rendering view */ diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 6b954072a9a..1fbf502fea6 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -245,7 +245,9 @@ void MemoryBuffer::copy_from(const MemoryBuffer *src, void MemoryBuffer::copy_from(const uchar *src, const rcti &area) { - copy_from(src, area, 0, this->get_num_channels(), this->get_num_channels(), 0); + const int elem_stride = this->get_num_channels(); + const int row_stride = elem_stride * getWidth(); + copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0); } void MemoryBuffer::copy_from(const uchar *src, @@ -253,10 +255,18 @@ void MemoryBuffer::copy_from(const uchar *src, const int channel_offset, const int elem_size, const int elem_stride, + const int row_stride, const int to_channel_offset) { - copy_from( - src, area, channel_offset, elem_size, elem_stride, area.xmin, area.ymin, to_channel_offset); + copy_from(src, + area, + channel_offset, + elem_size, + elem_stride, + row_stride, + area.xmin, + area.ymin, + to_channel_offset); } void MemoryBuffer::copy_from(const uchar *src, @@ -264,6 +274,7 @@ void MemoryBuffer::copy_from(const uchar *src, const int channel_offset, const int elem_size, const int elem_stride, + const int row_stride, const int to_x, const int to_y, const int to_channel_offset) @@ -273,10 +284,9 @@ void MemoryBuffer::copy_from(const uchar *src, const int width = BLI_rcti_size_x(&area); const int height = BLI_rcti_size_y(&area); - const int src_row_stride = width * elem_stride; - const uchar *const src_start = src + area.ymin * src_row_stride + channel_offset; + const uchar *const src_start = src + area.ymin * row_stride + channel_offset; for (int y = 0; y < height; y++) { - const uchar *from_elem = src_start + y * src_row_stride; + const uchar *from_elem = src_start + y * row_stride + area.xmin * elem_stride; float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset); const float *row_end = to_elem + width * this->elem_stride; while (to_elem < row_end) { @@ -346,7 +356,16 @@ void MemoryBuffer::copy_from(const ImBuf *src, else if (src->rect) { const uchar *uc_buf = (uchar *)src->rect; const int elem_stride = src->channels; - copy_from(uc_buf, area, channel_offset, elem_size, elem_stride, to_x, to_y, to_channel_offset); + const int row_stride = elem_stride * src->x; + copy_from(uc_buf, + area, + channel_offset, + elem_size, + elem_stride, + row_stride, + to_x, + to_y, + to_channel_offset); if (ensure_linear_space) { colorspace_to_scene_linear(this, area, src->rect_colorspace); } @@ -405,12 +424,48 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4]) } } +static void read_ewa_elem(void *userdata, int x, int y, float result[4]) +{ + const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata); + buffer->read_elem_checked(x, y, result); +} + +void MemoryBuffer::read_elem_filtered( + const float x, const float y, float dx[2], float dy[2], float *out) const +{ + BLI_assert(this->m_datatype == DataType::Color); + + const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; + + float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); + /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, + * but compositor uses pixel space. For now let's just divide the values and + * switch compositor to normalized space for EWA later. + */ + float uv_normal[2] = {get_relative_x(x) * inv_width, get_relative_y(y) * inv_height}; + float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height}; + float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height}; + + BLI_ewa_filter(this->getWidth(), + this->getHeight(), + false, + true, + uv_normal, + du_normal, + dv_normal, + read_ewa_elem, + const_cast<MemoryBuffer *>(this), + out); +} + +/* TODO(manzanilla): to be removed with tiled implementation. */ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4]) { MemoryBuffer *buffer = (MemoryBuffer *)userdata; buffer->read(result, x, y); } +/* TODO(manzanilla): to be removed with tiled implementation. */ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2]) { if (m_is_a_single_elem) { diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 310e87b6a4b..f3e15c2a495 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -191,23 +191,96 @@ class MemoryBuffer { void read_elem(int x, int y, float *out) const { - memcpy(out, get_elem(x, y), m_num_channels * sizeof(float)); + memcpy(out, get_elem(x, y), get_elem_bytes_len()); + } + + void read_elem_checked(int x, int y, float *out) const + { + if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { + clear_elem(out); + } + else { + read_elem(x, y, out); + } + } + + void read_elem_checked(float x, float y, float *out) const + { + if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { + clear_elem(out); + } + else { + read_elem(x, y, out); + } + } + + void read_elem_bilinear(float x, float y, float *out) const + { + /* Only clear past +/-1 borders to be able to smooth edges. */ + if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f || + y >= m_rect.ymax) { + clear_elem(out); + return; + } + + if (m_is_a_single_elem) { + if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin && + y < m_rect.ymax - 1.0f) { + memcpy(out, m_buffer, get_elem_bytes_len()); + return; + } + + /* Do sampling at borders to smooth edges. */ + const float last_x = getWidth() - 1.0f; + const float rel_x = get_relative_x(x); + float single_x = 0.0f; + if (rel_x < 0.0f) { + single_x = rel_x; + } + else if (rel_x > last_x) { + single_x = rel_x - last_x; + } + + const float last_y = getHeight() - 1.0f; + const float rel_y = get_relative_y(y); + float single_y = 0.0f; + if (rel_y < 0.0f) { + single_y = rel_y; + } + else if (rel_y > last_y) { + single_y = rel_y - last_y; + } + + BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y); + return; + } + + BLI_bilinear_interpolation_fl(m_buffer, + out, + getWidth(), + getHeight(), + m_num_channels, + get_relative_x(x), + get_relative_y(y)); } void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const { switch (sampler) { case PixelSampler::Nearest: - this->read_elem(x, y, out); + read_elem_checked(x, y, out); break; case PixelSampler::Bilinear: case PixelSampler::Bicubic: /* No bicubic. Current implementation produces fuzzy results. */ - this->readBilinear(out, x, y); + read_elem_bilinear(x, y, out); break; } } + void read_elem_filtered( + const float x, const float y, float dx[2], float dy[2], float *out) const; + /** * Get channel value at given coordinates. */ @@ -403,6 +476,8 @@ class MemoryBuffer { y = y + m_rect.ymin; } + /* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend + * use #wrap_pixel. */ inline void read(float *result, int x, int y, @@ -425,6 +500,7 @@ class MemoryBuffer { } } + /* TODO(manzanilla): to be removed with tiled implementation. */ inline void readNoCheck(float *result, int x, int y, @@ -507,12 +583,14 @@ class MemoryBuffer { int channel_offset, int elem_size, int elem_stride, + int row_stride, int to_channel_offset); void copy_from(const uchar *src, const rcti &area, int channel_offset, int elem_size, int elem_stride, + int row_stride, int to_x, int to_y, int to_channel_offset); @@ -582,6 +660,21 @@ class MemoryBuffer { return get_memory_width() * get_memory_height(); } + void clear_elem(float *out) const + { + memset(out, 0, this->m_num_channels * sizeof(float)); + } + + template<typename T> T get_relative_x(T x) const + { + return x - m_rect.xmin; + } + + template<typename T> T get_relative_y(T y) const + { + return y - m_rect.ymin; + } + void copy_single_elem_from(const MemoryBuffer *src, int channel_offset, int elem_size, diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc index 205fbcc0440..1872fbcf656 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.cc +++ b/source/blender/compositor/intern/COM_NodeGraph.cc @@ -248,7 +248,9 @@ void NodeGraph::add_proxies_group_inputs(bNode *b_node, bNode *b_node_io) } } -void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer) +void NodeGraph::add_proxies_group_outputs(const CompositorContext &context, + bNode *b_node, + bNode *b_node_io) { bNodeTree *b_group_tree = (bNodeTree *)b_node->id; BLI_assert(b_group_tree); /* should have been checked in advance */ @@ -261,7 +263,8 @@ void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool b_sock_io = b_sock_io->next) { bNodeSocket *b_sock_group = find_b_node_output(b_node, b_sock_io->identifier); if (b_sock_group) { - if (use_buffer) { + if (context.isGroupnodeBufferEnabled() && + context.get_execution_model() == eExecutionModel::Tiled) { SocketBufferNode *buffer = new SocketBufferNode(b_node_io, b_sock_io, b_sock_group); add_node(buffer, b_group_tree, key, is_active_group); } @@ -297,7 +300,7 @@ void NodeGraph::add_proxies_group(const CompositorContext &context, } if (b_node_io->type == NODE_GROUP_OUTPUT && (b_node_io->flag & NODE_DO_OUTPUT)) { - add_proxies_group_outputs(b_node, b_node_io, context.isGroupnodeBufferEnabled()); + add_proxies_group_outputs(context, b_node, b_node_io); } } diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h index 7fa01593e1e..dfcc6c2fcf9 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.h +++ b/source/blender/compositor/intern/COM_NodeGraph.h @@ -107,7 +107,9 @@ class NodeGraph { bool is_active_group); void add_proxies_group_inputs(bNode *b_node, bNode *b_node_io); - void add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer); + void add_proxies_group_outputs(const CompositorContext &context, + bNode *b_node, + bNode *b_node_io); void add_proxies_group(const CompositorContext &context, bNode *b_node, bNodeInstanceKey key); void add_proxies_reroute(bNodeTree *b_ntree, diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 575e8446abe..1b87cdf72fb 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -82,8 +82,12 @@ void NodeOperation::determineResolution(unsigned int resolution[2], input.determineResolution(resolution, preferredResolution); used_resolution_index = m_resolutionInputSocketIndex; } - unsigned int temp2[2] = {resolution[0], resolution[1]}; + if (modify_determined_resolution_fn_) { + modify_determined_resolution_fn_(resolution); + } + + unsigned int temp2[2] = {resolution[0], resolution[1]}; unsigned int temp[2]; for (unsigned int index = 0; index < m_inputs.size(); index++) { if (index == used_resolution_index) { diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 934007d25ce..b402dc7f174 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -287,6 +287,8 @@ class NodeOperation { */ unsigned int m_resolutionInputSocketIndex; + std::function<void(unsigned int resolution[2])> modify_determined_resolution_fn_; + /** * \brief mutex reference for very special node initializations * \note only use when you really know what you are doing. @@ -518,6 +520,15 @@ class NodeOperation { void setResolutionInputSocketIndex(unsigned int index); /** + * Set a custom function to modify determined resolution from main input just before setting it + * as preferred resolution for the other inputs. + */ + void set_determined_resolution_modifier(std::function<void(unsigned int resolution[2])> fn) + { + modify_determined_resolution_fn_ = fn; + } + + /** * \brief get the render priority of this node. * \note only applicable for output operations like ViewerOperation * \return eCompositorPriority diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index 4032a655633..5835f051ce3 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -124,6 +124,10 @@ void CryptomatteNode::input_operations_from_render_source( RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) { + if (context.has_explicit_view() && !STREQ(render_pass->view, context.getViewName())) { + continue; + } + const std::string combined_name = combined_layer_pass_name(render_layer, render_pass); if (blender::StringRef(combined_name).startswith(prefix)) { RenderLayersProg *op = new RenderLayersProg( diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc index af5baa733dc..c2fd8ed5594 100644 --- a/source/blender/compositor/nodes/COM_RotateNode.cc +++ b/source/blender/compositor/nodes/COM_RotateNode.cc @@ -30,20 +30,31 @@ RotateNode::RotateNode(bNode *editorNode) : Node(editorNode) } void RotateNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const + const CompositorContext &context) const { NodeInput *inputSocket = this->getInputSocket(0); NodeInput *inputDegreeSocket = this->getInputSocket(1); NodeOutput *outputSocket = this->getOutputSocket(0); RotateOperation *operation = new RotateOperation(); - SetSamplerOperation *sampler = new SetSamplerOperation(); - sampler->setSampler((PixelSampler)this->getbNode()->custom1); - - converter.addOperation(sampler); converter.addOperation(operation); - converter.addLink(sampler->getOutputSocket(), operation->getInputSocket(0)); - converter.mapInputSocket(inputSocket, sampler->getInputSocket(0)); + PixelSampler sampler = (PixelSampler)this->getbNode()->custom1; + switch (context.get_execution_model()) { + case eExecutionModel::Tiled: { + SetSamplerOperation *sampler_op = new SetSamplerOperation(); + sampler_op->setSampler(sampler); + converter.addOperation(sampler_op); + converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0)); + converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0)); + break; + } + case eExecutionModel::FullFrame: { + operation->set_sampler(sampler); + converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); + break; + } + } + converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1)); converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); } diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc index 0262f653d1a..7b2388bebca 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc @@ -22,6 +22,7 @@ #include "COM_RotateOperation.h" #include "COM_ScaleOperation.h" #include "COM_SetSamplerOperation.h" +#include "COM_TransformOperation.h" #include "COM_TranslateOperation.h" #include "BKE_tracking.h" @@ -42,18 +43,12 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter, NodeInput *imageInput = this->getInputSocket(0); MovieClip *clip = (MovieClip *)editorNode->id; bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0; + const PixelSampler sampler = (PixelSampler)editorNode->custom1; - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - scaleOperation->setSampler((PixelSampler)editorNode->custom1); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - TranslateOperation *translateOperation = new TranslateOperation(); MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation(); MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation(); MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation(); MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation(); - SetSamplerOperation *psoperation = new SetSamplerOperation(); - psoperation->setSampler((PixelSampler)editorNode->custom1); scaleAttribute->setAttribute(MCA_SCALE); scaleAttribute->setFramenumber(context.getFramenumber()); @@ -79,38 +74,67 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter, converter.addOperation(angleAttribute); converter.addOperation(xAttribute); converter.addOperation(yAttribute); - converter.addOperation(scaleOperation); - converter.addOperation(translateOperation); - converter.addOperation(rotateOperation); - converter.addOperation(psoperation); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1)); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2)); - - converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1)); - - converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1)); - converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2)); - - converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket()); - - if (invert) { - // Translate -> Rotate -> Scale. - converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0)); - - converter.addLink(translateOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0)); - - converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0)); - } - else { - // Scale -> Rotate -> Translate. - converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); - - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0)); - - converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0)); + switch (context.get_execution_model()) { + case eExecutionModel::Tiled: { + ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); + scaleOperation->setSampler(sampler); + RotateOperation *rotateOperation = new RotateOperation(); + rotateOperation->setDoDegree2RadConversion(false); + TranslateOperation *translateOperation = new TranslateOperation(); + SetSamplerOperation *psoperation = new SetSamplerOperation(); + psoperation->setSampler(sampler); + + converter.addOperation(scaleOperation); + converter.addOperation(translateOperation); + converter.addOperation(rotateOperation); + converter.addOperation(psoperation); + + converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1)); + converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2)); + + converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1)); + + converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1)); + converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2)); + + converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket()); + + if (invert) { + // Translate -> Rotate -> Scale. + converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0)); + + converter.addLink(translateOperation->getOutputSocket(), + rotateOperation->getInputSocket(0)); + converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0)); + + converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0)); + } + else { + // Scale -> Rotate -> Translate. + converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); + + converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); + converter.addLink(rotateOperation->getOutputSocket(), + translateOperation->getInputSocket(0)); + + converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0)); + } + break; + } + case eExecutionModel::FullFrame: { + TransformOperation *transform_op = new TransformOperation(); + transform_op->set_sampler(sampler); + transform_op->set_convert_rotate_degree_to_rad(false); + transform_op->set_invert(invert); + converter.addOperation(transform_op); + converter.mapInputSocket(imageInput, transform_op->getInputSocket(0)); + converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1)); + converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2)); + converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3)); + converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4)); + converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket()); + } } } diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc index e1deaf616a4..d2fb7b54633 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.cc +++ b/source/blender/compositor/nodes/COM_TransformNode.cc @@ -22,6 +22,7 @@ #include "COM_ScaleOperation.h" #include "COM_SetSamplerOperation.h" #include "COM_SetValueOperation.h" +#include "COM_TransformOperation.h" #include "COM_TranslateOperation.h" namespace blender::compositor { @@ -32,7 +33,7 @@ TransformNode::TransformNode(bNode *editorNode) : Node(editorNode) } void TransformNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const + const CompositorContext &context) const { NodeInput *imageInput = this->getInputSocket(0); NodeInput *xInput = this->getInputSocket(1); @@ -40,33 +41,51 @@ void TransformNode::convertToOperations(NodeConverter &converter, NodeInput *angleInput = this->getInputSocket(3); NodeInput *scaleInput = this->getInputSocket(4); - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - converter.addOperation(scaleOperation); + switch (context.get_execution_model()) { + case eExecutionModel::Tiled: { + ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); + converter.addOperation(scaleOperation); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - converter.addOperation(rotateOperation); + RotateOperation *rotateOperation = new RotateOperation(); + rotateOperation->setDoDegree2RadConversion(false); + converter.addOperation(rotateOperation); - TranslateOperation *translateOperation = new TranslateOperation(); - converter.addOperation(translateOperation); + TranslateOperation *translateOperation = new TranslateOperation(); + converter.addOperation(translateOperation); - SetSamplerOperation *sampler = new SetSamplerOperation(); - sampler->setSampler((PixelSampler)this->getbNode()->custom1); - converter.addOperation(sampler); + SetSamplerOperation *sampler = new SetSamplerOperation(); + sampler->setSampler((PixelSampler)this->getbNode()->custom1); + converter.addOperation(sampler); - converter.mapInputSocket(imageInput, sampler->getInputSocket(0)); - converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale + converter.mapInputSocket(imageInput, sampler->getInputSocket(0)); + converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0)); + converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1)); + converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1)); + converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); + converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1)); - converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0)); - converter.mapInputSocket(xInput, translateOperation->getInputSocket(1)); - converter.mapInputSocket(yInput, translateOperation->getInputSocket(2)); + converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0)); + converter.mapInputSocket(xInput, translateOperation->getInputSocket(1)); + converter.mapInputSocket(yInput, translateOperation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); + converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); + break; + } + case eExecutionModel::FullFrame: { + TransformOperation *op = new TransformOperation(); + op->set_sampler((PixelSampler)this->getbNode()->custom1); + converter.addOperation(op); + + converter.mapInputSocket(imageInput, op->getInputSocket(0)); + converter.mapInputSocket(xInput, op->getInputSocket(1)); + converter.mapInputSocket(yInput, op->getInputSocket(2)); + converter.mapInputSocket(angleInput, op->getInputSocket(3)); + converter.mapInputSocket(scaleInput, op->getInputSocket(4)); + converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket()); + break; + } + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index 8b73624ca79..280a6026ebe 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_BlurBaseOperation.h" +#include "COM_ConstantOperation.h" + #include "BLI_math.h" #include "MEM_guardedalloc.h" @@ -36,11 +38,15 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type) this->m_size = 1.0f; this->m_sizeavailable = false; this->m_extend_bounds = false; + use_variable_size_ = false; } -void BlurBaseOperation::initExecution() + +void BlurBaseOperation::init_data() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputSize = this->getInputSocketReader(1); + if (execution_model_ == eExecutionModel::FullFrame) { + updateSize(); + } + this->m_data.image_in_width = this->getWidth(); this->m_data.image_in_height = this->getHeight(); if (this->m_data.relative) { @@ -61,6 +67,12 @@ void BlurBaseOperation::initExecution() this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex); this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey); } +} + +void BlurBaseOperation::initExecution() +{ + this->m_inputProgram = this->getInputSocketReader(0); + this->m_inputSize = this->getInputSocketReader(1); QualityStepHelper::initExecution(COM_QH_MULTIPLY); } @@ -165,23 +177,82 @@ void BlurBaseOperation::setData(const NodeBlurData *data) memcpy(&m_data, data, sizeof(NodeBlurData)); } +int BlurBaseOperation::get_blur_size(eDimension dim) const +{ + switch (dim) { + case eDimension::X: + return m_data.sizex; + case eDimension::Y: + return m_data.sizey; + } + return -1; +} + void BlurBaseOperation::updateSize() { - if (!this->m_sizeavailable) { - float result[4]; - this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); - this->m_size = result[0]; - this->m_sizeavailable = true; + if (this->m_sizeavailable || use_variable_size_) { + return; } + + switch (execution_model_) { + case eExecutionModel::Tiled: { + float result[4]; + this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); + this->m_size = result[0]; + break; + } + case eExecutionModel::FullFrame: { + NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX); + if (size_input->get_flags().is_constant_operation) { + m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem(); + } /* Else use default. */ + break; + } + } + this->m_sizeavailable = true; } void BlurBaseOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { - NodeOperation::determineResolution(resolution, preferredResolution); - if (this->m_extend_bounds) { - resolution[0] += 2 * this->m_size * m_data.sizex; - resolution[1] += 2 * this->m_size * m_data.sizey; + if (!m_extend_bounds) { + NodeOperation::determineResolution(resolution, preferredResolution); + return; + } + + switch (execution_model_) { + case eExecutionModel::Tiled: { + NodeOperation::determineResolution(resolution, preferredResolution); + resolution[0] += 2 * m_size * m_data.sizex; + resolution[1] += 2 * m_size * m_data.sizey; + break; + } + case eExecutionModel::FullFrame: { + /* Setting a modifier ensures all non main inputs have extended bounds as preferred + * resolution, avoiding unnecessary resolution convertions that would hide constant + * operations. */ + set_determined_resolution_modifier([=](unsigned int res[2]) { + /* Rounding to even prevents jiggling in backdrop while switching size values. */ + res[0] += round_to_even(2 * m_size * m_data.sizex); + res[1] += round_to_even(2 * m_size * m_data.sizey); + }); + NodeOperation::determineResolution(resolution, preferredResolution); + break; + } + } +} + +void BlurBaseOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case 0: + r_input_area = output_area; + break; + case 1: + r_input_area = use_variable_size_ ? output_area : COM_SINGLE_ELEM_AREA; + break; } } diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h index 7937ebd69dc..78b1e919aa6 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" #define MAX_GAUSSTAB_RADIUS 30000 @@ -27,10 +27,16 @@ namespace blender::compositor { -class BlurBaseOperation : public NodeOperation, public QualityStepHelper { +class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelper { private: + bool m_extend_bounds; + + protected: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int SIZE_INPUT_INDEX = 1; + protected: - BlurBaseOperation(DataType data_type); + BlurBaseOperation(DataType data_type8); float *make_gausstab(float rad, int size); #ifdef BLI_HAVE_SSE2 __m128 *convert_gausstab_sse(const float *gausstab, int size); @@ -49,9 +55,11 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper { float m_size; bool m_sizeavailable; - bool m_extend_bounds; + /* Flags for inheriting classes. */ + bool use_variable_size_; public: + virtual void init_data() override; /** * Initialize the execution */ @@ -75,8 +83,14 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper { this->m_extend_bounds = extend_bounds; } + int get_blur_size(eDimension dim) const; + void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + + virtual void get_area_of_interest(int input_idx, + const rcti &output_area, + rcti &r_input_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index 3f98732b403..f2a43b7dbca 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_BokehBlurOperation.h" +#include "COM_ConstantOperation.h" + #include "BLI_math.h" #include "COM_OpenCLDevice.h" @@ -24,6 +26,11 @@ namespace blender::compositor { +constexpr int IMAGE_INPUT_INDEX = 0; +constexpr int BOKEH_INPUT_INDEX = 1; +constexpr int BOUNDING_BOX_INPUT_INDEX = 2; +constexpr int SIZE_INPUT_INDEX = 3; + BokehBlurOperation::BokehBlurOperation() { this->addInputSocket(DataType::Color); @@ -44,6 +51,23 @@ BokehBlurOperation::BokehBlurOperation() this->m_extend_bounds = false; } +void BokehBlurOperation::init_data() +{ + if (execution_model_ == eExecutionModel::FullFrame) { + updateSize(); + } + + NodeOperation *bokeh = get_input_operation(BOKEH_INPUT_INDEX); + const int width = bokeh->getWidth(); + const int height = bokeh->getHeight(); + + const float dimension = MIN2(width, height); + + m_bokehMidX = width / 2.0f; + m_bokehMidY = height / 2.0f; + m_bokehDimension = dimension / 2.0f; +} + void *BokehBlurOperation::initializeTileData(rcti * /*rect*/) { lockMutex(); @@ -58,18 +82,11 @@ void *BokehBlurOperation::initializeTileData(rcti * /*rect*/) void BokehBlurOperation::initExecution() { initMutex(); + this->m_inputProgram = getInputSocketReader(0); this->m_inputBokehProgram = getInputSocketReader(1); this->m_inputBoundingBoxReader = getInputSocketReader(2); - int width = this->m_inputBokehProgram->getWidth(); - int height = this->m_inputBokehProgram->getHeight(); - - float dimension = MIN2(width, height); - - this->m_bokehMidX = width / 2.0f; - this->m_bokehMidY = height / 2.0f; - this->m_bokehDimension = dimension / 2.0f; QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -225,23 +242,146 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device, void BokehBlurOperation::updateSize() { - if (!this->m_sizeavailable) { - float result[4]; - this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest); - this->m_size = result[0]; - CLAMP(this->m_size, 0.0f, 10.0f); - this->m_sizeavailable = true; + if (this->m_sizeavailable) { + return; } + + switch (execution_model_) { + case eExecutionModel::Tiled: { + float result[4]; + this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest); + this->m_size = result[0]; + CLAMP(this->m_size, 0.0f, 10.0f); + break; + } + case eExecutionModel::FullFrame: { + NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX); + if (size_input->get_flags().is_constant_operation) { + m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem(); + CLAMP(m_size, 0.0f, 10.0f); + } /* Else use default. */ + break; + } + } + this->m_sizeavailable = true; } void BokehBlurOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { - NodeOperation::determineResolution(resolution, preferredResolution); - if (this->m_extend_bounds) { - const float max_dim = MAX2(resolution[0], resolution[1]); - resolution[0] += 2 * this->m_size * max_dim / 100.0f; - resolution[1] += 2 * this->m_size * max_dim / 100.0f; + if (!m_extend_bounds) { + NodeOperation::determineResolution(resolution, preferredResolution); + return; + } + + switch (execution_model_) { + case eExecutionModel::Tiled: { + NodeOperation::determineResolution(resolution, preferredResolution); + const float max_dim = MAX2(resolution[0], resolution[1]); + resolution[0] += 2 * this->m_size * max_dim / 100.0f; + resolution[1] += 2 * this->m_size * max_dim / 100.0f; + break; + } + case eExecutionModel::FullFrame: { + set_determined_resolution_modifier([=](unsigned int res[2]) { + const float max_dim = MAX2(res[0], res[1]); + /* Rounding to even prevents image jiggling in backdrop while switching size values. */ + float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f); + res[0] += add_size; + res[1] += add_size; + }); + NodeOperation::determineResolution(resolution, preferredResolution); + break; + } + } +} + +void BokehBlurOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: { + const float max_dim = MAX2(this->getWidth(), this->getHeight()); + const float add_size = m_size * max_dim / 100.0f; + r_input_area.xmin = output_area.xmin - add_size; + r_input_area.xmax = output_area.xmax + add_size; + r_input_area.ymin = output_area.ymin - add_size; + r_input_area.ymax = output_area.ymax + add_size; + break; + } + case BOKEH_INPUT_INDEX: { + NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX); + r_input_area.xmin = 0; + r_input_area.xmax = bokeh_input->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = bokeh_input->getHeight(); + break; + } + case BOUNDING_BOX_INPUT_INDEX: + r_input_area = output_area; + break; + case SIZE_INPUT_INDEX: { + r_input_area = COM_SINGLE_ELEM_AREA; + break; + } + } +} + +void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const float max_dim = MAX2(this->getWidth(), this->getHeight()); + const int pixel_size = m_size * max_dim / 100.0f; + const float m = m_bokehDimension / pixel_size; + + const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX]; + const MemoryBuffer *bokeh_input = inputs[BOKEH_INPUT_INDEX]; + MemoryBuffer *bounding_input = inputs[BOUNDING_BOX_INPUT_INDEX]; + BuffersIterator<float> it = output->iterate_with({bounding_input}, area); + const rcti &image_rect = image_input->get_rect(); + for (; !it.is_end(); ++it) { + const int x = it.x; + const int y = it.y; + const float bounding_box = *it.in(0); + if (bounding_box <= 0.0f) { + image_input->read_elem(x, y, it.out); + continue; + } + + float color_accum[4] = {0}; + float multiplier_accum[4] = {0}; + if (pixel_size < 2) { + image_input->read_elem(x, y, color_accum); + multiplier_accum[0] = 1.0f; + multiplier_accum[1] = 1.0f; + multiplier_accum[2] = 1.0f; + multiplier_accum[3] = 1.0f; + } + const int miny = MAX2(y - pixel_size, image_rect.ymin); + const int maxy = MIN2(y + pixel_size, image_rect.ymax); + const int minx = MAX2(x - pixel_size, image_rect.xmin); + const int maxx = MIN2(x + pixel_size, image_rect.xmax); + const int step = getStep(); + const int elem_stride = image_input->elem_stride * step; + const int row_stride = image_input->row_stride * step; + const float *row_color = image_input->get_elem(minx, miny); + for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) { + const float *color = row_color; + const float v = m_bokehMidY - (ny - y) * m; + for (int nx = minx; nx < maxx; nx += step, color += elem_stride) { + const float u = m_bokehMidX - (nx - x) * m; + float bokeh[4]; + bokeh_input->read_elem_checked(u, v, bokeh); + madd_v4_v4v4(color_accum, bokeh, color); + add_v4_v4(multiplier_accum, bokeh); + } + } + it.out[0] = color_accum[0] * (1.0f / multiplier_accum[0]); + it.out[1] = color_accum[1] * (1.0f / multiplier_accum[1]); + it.out[2] = color_accum[2] * (1.0f / multiplier_accum[2]); + it.out[3] = color_accum[3] * (1.0f / multiplier_accum[3]); } } diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h index 3ce06adb5d6..59c14305393 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" namespace blender::compositor { -class BokehBlurOperation : public NodeOperation, public QualityStepHelper { +class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: SocketReader *m_inputProgram; SocketReader *m_inputBokehProgram; @@ -31,6 +31,7 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper { void updateSize(); float m_size; bool m_sizeavailable; + float m_bokehMidX; float m_bokehMidY; float m_bokehDimension; @@ -39,6 +40,8 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper { public: BokehBlurOperation(); + void init_data() override; + void *initializeTileData(rcti *rect) override; /** * The inner loop of this operation. @@ -79,6 +82,11 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc index ec4331dc231..65742d0cfcc 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc @@ -27,6 +27,7 @@ ChannelMatteOperation::ChannelMatteOperation() addOutputSocket(DataType::Value); this->m_inputImageProgram = nullptr; + flags.can_be_constant = true; } void ChannelMatteOperation::initExecution() @@ -121,4 +122,37 @@ void ChannelMatteOperation::executePixelSampled(float output[4], output[0] = MIN2(alpha, inColor[3]); } +void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + + /* Matte operation. */ + float alpha = color[this->m_ids[0]] - MAX2(color[this->m_ids[1]], color[this->m_ids[2]]); + + /* Flip because 0.0 is transparent, not 1.0. */ + alpha = 1.0f - alpha; + + /* Test range. */ + if (alpha > m_limit_max) { + alpha = color[3]; /* Whatever it was prior. */ + } + else if (alpha < m_limit_min) { + alpha = 0.0f; + } + else { /* Blend. */ + alpha = (alpha - m_limit_min) / m_limit_range; + } + + /* Store matte(alpha) value in [0] to go with + * COM_SetAlphaMultiplyOperation and the Value output. + */ + + /* Don't make something that was more transparent less transparent. */ + *it.out = MIN2(alpha, color[3]); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h index 6e9dcccd36e..ba50105dd3b 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ChannelMatteOperation : public NodeOperation { +class ChannelMatteOperation : public MultiThreadedOperation { private: SocketReader *m_inputImageProgram; @@ -71,6 +71,10 @@ class ChannelMatteOperation : public NodeOperation { this->m_limit_channel = nodeChroma->channel; this->m_matte_channel = custom2; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc index b7fec5f07e5..0784f266b19 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc @@ -29,6 +29,7 @@ ChromaMatteOperation::ChromaMatteOperation() this->m_inputImageProgram = nullptr; this->m_inputKeyProgram = nullptr; + flags.can_be_constant = true; } void ChromaMatteOperation::initExecution() @@ -110,4 +111,58 @@ void ChromaMatteOperation::executePixelSampled(float output[4], } } +void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const float acceptance = this->m_settings->t1; /* In radians. */ + const float cutoff = this->m_settings->t2; /* In radians. */ + const float gain = this->m_settings->fstrength; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *in_image = it.in(0); + const float *in_key = it.in(1); + + /* Store matte(alpha) value in [0] to go with + * #COM_SetAlphaMultiplyOperation and the Value output. */ + + /* Algorithm from book "Video Demystified", does not include the spill reduction part. */ + /* Find theta, the angle that the color space should be rotated based on key. */ + + /* Rescale to `-1.0..1.0`. */ + // const float image_Y = (in_image[0] * 2.0f) - 1.0f; // UNUSED + const float image_cb = (in_image[1] * 2.0f) - 1.0f; + const float image_cr = (in_image[2] * 2.0f) - 1.0f; + + // const float key_Y = (in_key[0] * 2.0f) - 1.0f; // UNUSED + const float key_cb = (in_key[1] * 2.0f) - 1.0f; + const float key_cr = (in_key[2] * 2.0f) - 1.0f; + + const float theta = atan2(key_cr, key_cb); + + /* Rotate the cb and cr into x/z space. */ + const float x_angle = image_cb * cosf(theta) + image_cr * sinf(theta); + const float z_angle = image_cr * cosf(theta) - image_cb * sinf(theta); + + /* If within the acceptance angle. */ + /* If kfg is <0 then the pixel is outside of the key color. */ + const float kfg = x_angle - (fabsf(z_angle) / tanf(acceptance / 2.0f)); + + if (kfg > 0.0f) { /* Found a pixel that is within key color. */ + const float beta = atan2(z_angle, x_angle); + float alpha = 1.0f - (kfg / gain); + + /* Ff beta is within the cutoff angle. */ + if (fabsf(beta) < (cutoff / 2.0f)) { + alpha = 0.0f; + } + + /* Don't make something that was more transparent less transparent. */ + it.out[0] = alpha < in_image[3] ? alpha : in_image[3]; + } + else { /* Pixel is outside key color. */ + it.out[0] = in_image[3]; /* Make pixel just as transparent as it was before. */ + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h index 48c3a785011..065349910a7 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ChromaMatteOperation : public NodeOperation { +class ChromaMatteOperation : public MultiThreadedOperation { private: NodeChroma *m_settings; SocketReader *m_inputImageProgram; @@ -50,6 +50,10 @@ class ChromaMatteOperation : public NodeOperation { { this->m_settings = nodeChroma; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc index ddfbf415d9c..dec6571f217 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc @@ -29,6 +29,7 @@ ColorMatteOperation::ColorMatteOperation() this->m_inputImageProgram = nullptr; this->m_inputKeyProgram = nullptr; + flags.can_be_constant = true; } void ColorMatteOperation::initExecution() @@ -82,4 +83,40 @@ void ColorMatteOperation::executePixelSampled(float output[4], } } +void ColorMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const float hue = m_settings->t1; + const float sat = m_settings->t2; + const float val = m_settings->t3; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *in_color = it.in(0); + const float *in_key = it.in(1); + + /* Store matte(alpha) value in [0] to go with + * COM_SetAlphaMultiplyOperation and the Value output. + */ + + float h_wrap; + if ( + /* Do hue last because it needs to wrap, and does some more checks. */ + + /* #sat */ (fabsf(in_color[1] - in_key[1]) < sat) && + /* #val */ (fabsf(in_color[2] - in_key[2]) < val) && + + /* Multiply by 2 because it wraps on both sides of the hue, + * otherwise 0.5 would key all hue's. */ + + /* #hue */ + ((h_wrap = 2.0f * fabsf(in_color[0] - in_key[0])) < hue || (2.0f - h_wrap) < hue)) { + it.out[0] = 0.0f; /* Make transparent. */ + } + + else { /* Pixel is outside key color. */ + it.out[0] = in_color[3]; /* Make pixel just as transparent as it was before. */ + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h index 439a3b0741d..49d06e62e65 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.h +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ColorMatteOperation : public NodeOperation { +class ColorMatteOperation : public MultiThreadedOperation { private: NodeChroma *m_settings; SocketReader *m_inputImageProgram; @@ -50,6 +50,10 @@ class ColorMatteOperation : public NodeOperation { { this->m_settings = nodeChroma; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc index 0ee65a6529e..6c1b23ea731 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.cc +++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc @@ -29,6 +29,7 @@ ColorRampOperation::ColorRampOperation() this->m_inputProgram = nullptr; this->m_colorBand = nullptr; + this->flags.can_be_constant = true; } void ColorRampOperation::initExecution() { @@ -51,4 +52,13 @@ void ColorRampOperation::deinitExecution() this->m_inputProgram = nullptr; } +void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + BKE_colorband_evaluate(m_colorBand, *it.in(0), it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h index d32af9bea24..ab64b9928fd 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.h +++ b/source/blender/compositor/operations/COM_ColorRampOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_texture_types.h" namespace blender::compositor { -class ColorRampOperation : public NodeOperation { +class ColorRampOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -53,6 +53,10 @@ class ColorRampOperation : public NodeOperation { { this->m_colorBand = colorBand; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc index 7dc7e2775fc..4b0e0520b75 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc @@ -32,6 +32,7 @@ ColorSpillOperation::ColorSpillOperation() this->m_inputFacReader = nullptr; this->m_spillChannel = 1; // GREEN this->m_spillMethod = 0; + flags.can_be_constant = true; } void ColorSpillOperation::initExecution() @@ -118,4 +119,36 @@ void ColorSpillOperation::executePixelSampled(float output[4], } } +void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + const float factor = MIN2(1.0f, *it.in(1)); + + float map; + switch (m_spillMethod) { + case 0: /* simple */ + map = factor * + (color[m_spillChannel] - (m_settings->limscale * color[m_settings->limchan])); + break; + default: /* average */ + map = factor * (color[m_spillChannel] - + (m_settings->limscale * AVG(color[m_channel2], color[m_channel3]))); + break; + } + + if (map > 0.0f) { + it.out[0] = color[0] + m_rmut * (m_settings->uspillr * map); + it.out[1] = color[1] + m_gmut * (m_settings->uspillg * map); + it.out[2] = color[2] + m_bmut * (m_settings->uspillb * map); + it.out[3] = color[3]; + } + else { + copy_v4_v4(it.out, color); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h index 9b82e720527..6a5e688c160 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.h +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ColorSpillOperation : public NodeOperation { +class ColorSpillOperation : public MultiThreadedOperation { protected: NodeColorspill *m_settings; SocketReader *m_inputImageReader; @@ -65,6 +65,10 @@ class ColorSpillOperation : public NodeOperation { } float calculateMapValue(float fac, float *input); + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 384936533c7..d377903efea 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -27,6 +27,7 @@ namespace blender::compositor { ConvertBaseOperation::ConvertBaseOperation() { this->m_inputOperation = nullptr; + this->flags.can_be_constant = true; } void ConvertBaseOperation::initExecution() @@ -39,6 +40,14 @@ void ConvertBaseOperation::deinitExecution() this->m_inputOperation = nullptr; } +void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + BuffersIterator<float> it = output->iterate_with(inputs, area); + update_memory_buffer_partial(it); +} + /* ******** Value to Color ******** */ ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperation() @@ -58,6 +67,14 @@ void ConvertValueToColorOperation::executePixelSampled(float output[4], output[3] = 1.0f; } +void ConvertValueToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + it.out[0] = it.out[1] = it.out[2] = *it.in(0); + it.out[3] = 1.0f; + } +} + /* ******** Color to Value ******** */ ConvertColorToValueOperation::ConvertColorToValueOperation() : ConvertBaseOperation() @@ -76,6 +93,14 @@ void ConvertColorToValueOperation::executePixelSampled(float output[4], output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f; } +void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + it.out[0] = (in[0] + in[1] + in[2]) / 3.0f; + } +} + /* ******** Color to BW ******** */ ConvertColorToBWOperation::ConvertColorToBWOperation() : ConvertBaseOperation() @@ -94,6 +119,13 @@ void ConvertColorToBWOperation::executePixelSampled(float output[4], output[0] = IMB_colormanagement_get_luminance(inputColor); } +void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + it.out[0] = IMB_colormanagement_get_luminance(it.in(0)); + } +} + /* ******** Color to Vector ******** */ ConvertColorToVectorOperation::ConvertColorToVectorOperation() : ConvertBaseOperation() @@ -112,6 +144,13 @@ void ConvertColorToVectorOperation::executePixelSampled(float output[4], copy_v3_v3(output, color); } +void ConvertColorToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + copy_v3_v3(it.out, it.in(0)); + } +} + /* ******** Value to Vector ******** */ ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOperation() @@ -130,6 +169,13 @@ void ConvertValueToVectorOperation::executePixelSampled(float output[4], output[0] = output[1] = output[2] = value; } +void ConvertValueToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + it.out[0] = it.out[1] = it.out[2] = *it.in(0); + } +} + /* ******** Vector to Color ******** */ ConvertVectorToColorOperation::ConvertVectorToColorOperation() : ConvertBaseOperation() @@ -147,6 +193,14 @@ void ConvertVectorToColorOperation::executePixelSampled(float output[4], output[3] = 1.0f; } +void ConvertVectorToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + copy_v3_v3(it.out, it.in(0)); + it.out[3] = 1.0f; + } +} + /* ******** Vector to Value ******** */ ConvertVectorToValueOperation::ConvertVectorToValueOperation() : ConvertBaseOperation() @@ -165,6 +219,14 @@ void ConvertVectorToValueOperation::executePixelSampled(float output[4], output[0] = (input[0] + input[1] + input[2]) / 3.0f; } +void ConvertVectorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + it.out[0] = (in[0] + in[1] + in[2]) / 3.0f; + } +} + /* ******** RGB to YCC ******** */ ConvertRGBToYCCOperation::ConvertRGBToYCCOperation() : ConvertBaseOperation() @@ -207,6 +269,18 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], this->m_mode); + + /* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */ + mul_v3_fl(it.out, 1.0f / 255.0f); + it.out[3] = in[3]; + } +} + /* ******** YCC to RGB ******** */ ConvertYCCToRGBOperation::ConvertYCCToRGBOperation() : ConvertBaseOperation() @@ -253,6 +327,22 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + /* Multiply by 255 to un-normalize (#ycc_to_rgb needs input values in 0-255 range). */ + ycc_to_rgb(in[0] * 255.0f, + in[1] * 255.0f, + in[2] * 255.0f, + &it.out[0], + &it.out[1], + &it.out[2], + this->m_mode); + it.out[3] = in[3]; + } +} + /* ******** RGB to YUV ******** */ ConvertRGBToYUVOperation::ConvertRGBToYUVOperation() : ConvertBaseOperation() @@ -278,6 +368,15 @@ void ConvertRGBToYUVOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + rgb_to_yuv(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709); + it.out[3] = in[3]; + } +} + /* ******** YUV to RGB ******** */ ConvertYUVToRGBOperation::ConvertYUVToRGBOperation() : ConvertBaseOperation() @@ -303,6 +402,15 @@ void ConvertYUVToRGBOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + yuv_to_rgb(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709); + it.out[3] = in[3]; + } +} + /* ******** RGB to HSV ******** */ ConvertRGBToHSVOperation::ConvertRGBToHSVOperation() : ConvertBaseOperation() @@ -322,6 +430,15 @@ void ConvertRGBToHSVOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + rgb_to_hsv_v(in, it.out); + it.out[3] = in[3]; + } +} + /* ******** HSV to RGB ******** */ ConvertHSVToRGBOperation::ConvertHSVToRGBOperation() : ConvertBaseOperation() @@ -344,6 +461,18 @@ void ConvertHSVToRGBOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + hsv_to_rgb_v(in, it.out); + it.out[0] = max_ff(it.out[0], 0.0f); + it.out[1] = max_ff(it.out[1], 0.0f); + it.out[2] = max_ff(it.out[2], 0.0f); + it.out[3] = in[3]; + } +} + /* ******** Premul to Straight ******** */ ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation() @@ -363,6 +492,13 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4], copy_v4_v4(output, converted); } +void ConvertPremulToStraightOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Premultiplied>(it.in(0)).unpremultiply_alpha()); + } +} + /* ******** Straight to Premul ******** */ ConvertStraightToPremulOperation::ConvertStraightToPremulOperation() : ConvertBaseOperation() @@ -382,6 +518,13 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4], copy_v4_v4(output, converted); } +void ConvertStraightToPremulOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Straight>(it.in(0)).premultiply_alpha()); + } +} + /* ******** Separate Channels ******** */ SeparateChannelOperation::SeparateChannelOperation() @@ -410,6 +553,15 @@ void SeparateChannelOperation::executePixelSampled(float output[4], output[0] = input[this->m_channel]; } +void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + it.out[0] = it.in(0)[this->m_channel]; + } +} + /* ******** Combine Channels ******** */ CombineChannelsOperation::CombineChannelsOperation() @@ -466,4 +618,16 @@ void CombineChannelsOperation::executePixelSampled(float output[4], } } +void CombineChannelsOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + it.out[0] = *it.in(0); + it.out[1] = *it.in(1); + it.out[2] = *it.in(2); + it.out[3] = *it.in(3); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index 7a726e35c7c..0334959ae7e 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class ConvertBaseOperation : public NodeOperation { +class ConvertBaseOperation : public MultiThreadedOperation { protected: SocketReader *m_inputOperation; @@ -31,6 +31,13 @@ class ConvertBaseOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) final; + + protected: + virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0; }; class ConvertValueToColorOperation : public ConvertBaseOperation { @@ -38,6 +45,9 @@ class ConvertValueToColorOperation : public ConvertBaseOperation { ConvertValueToColorOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertColorToValueOperation : public ConvertBaseOperation { @@ -45,6 +55,9 @@ class ConvertColorToValueOperation : public ConvertBaseOperation { ConvertColorToValueOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertColorToBWOperation : public ConvertBaseOperation { @@ -52,6 +65,9 @@ class ConvertColorToBWOperation : public ConvertBaseOperation { ConvertColorToBWOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertColorToVectorOperation : public ConvertBaseOperation { @@ -59,6 +75,9 @@ class ConvertColorToVectorOperation : public ConvertBaseOperation { ConvertColorToVectorOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertValueToVectorOperation : public ConvertBaseOperation { @@ -66,6 +85,9 @@ class ConvertValueToVectorOperation : public ConvertBaseOperation { ConvertValueToVectorOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertVectorToColorOperation : public ConvertBaseOperation { @@ -73,6 +95,9 @@ class ConvertVectorToColorOperation : public ConvertBaseOperation { ConvertVectorToColorOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertVectorToValueOperation : public ConvertBaseOperation { @@ -80,6 +105,9 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation { ConvertVectorToValueOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertRGBToYCCOperation : public ConvertBaseOperation { @@ -94,6 +122,9 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation { /** Set the YCC mode */ void setMode(int mode); + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertYCCToRGBOperation : public ConvertBaseOperation { @@ -108,6 +139,9 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation { /** Set the YCC mode */ void setMode(int mode); + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertRGBToYUVOperation : public ConvertBaseOperation { @@ -115,6 +149,9 @@ class ConvertRGBToYUVOperation : public ConvertBaseOperation { ConvertRGBToYUVOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertYUVToRGBOperation : public ConvertBaseOperation { @@ -122,6 +159,9 @@ class ConvertYUVToRGBOperation : public ConvertBaseOperation { ConvertYUVToRGBOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertRGBToHSVOperation : public ConvertBaseOperation { @@ -129,6 +169,9 @@ class ConvertRGBToHSVOperation : public ConvertBaseOperation { ConvertRGBToHSVOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertHSVToRGBOperation : public ConvertBaseOperation { @@ -136,6 +179,9 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation { ConvertHSVToRGBOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertPremulToStraightOperation : public ConvertBaseOperation { @@ -143,6 +189,9 @@ class ConvertPremulToStraightOperation : public ConvertBaseOperation { ConvertPremulToStraightOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class ConvertStraightToPremulOperation : public ConvertBaseOperation { @@ -150,9 +199,12 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation { ConvertStraightToPremulOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; -class SeparateChannelOperation : public NodeOperation { +class SeparateChannelOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; int m_channel; @@ -168,9 +220,13 @@ class SeparateChannelOperation : public NodeOperation { { this->m_channel = channel; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; -class CombineChannelsOperation : public NodeOperation { +class CombineChannelsOperation : public MultiThreadedOperation { private: SocketReader *m_inputChannel1Operation; SocketReader *m_inputChannel2Operation; @@ -183,6 +239,10 @@ class CombineChannelsOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index f12d93bc8d3..12833660fcb 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -95,6 +95,22 @@ void CropOperation::executePixelSampled(float output[4], float x, float y, Pixel } } +void CropOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + rcti crop_area; + BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax); + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) { + copy_v4_v4(it.out, it.in(0)); + } + else { + zero_v4(it.out); + } + } +} + CropImageOperation::CropImageOperation() : CropBaseOperation() { /* pass */ @@ -114,6 +130,18 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void CropImageOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmax = output_area.xmax + this->m_xmin; + r_input_area.xmin = output_area.xmin + this->m_xmin; + r_input_area.ymax = output_area.ymax + this->m_ymin; + r_input_area.ymin = output_area.ymin + this->m_ymin; +} + void CropImageOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { @@ -136,4 +164,21 @@ void CropImageOperation::executePixelSampled(float output[4], } } +void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + rcti op_area; + BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight()); + const MemoryBuffer *input = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) { + input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out); + } + else { + zero_v4(it.out); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h index acdff79a77c..57caa4e5834 100644 --- a/source/blender/compositor/operations/COM_CropOperation.h +++ b/source/blender/compositor/operations/COM_CropOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class CropBaseOperation : public NodeOperation { +class CropBaseOperation : public MultiThreadedOperation { protected: SocketReader *m_inputOperation; NodeTwoXYs *m_settings; @@ -53,6 +53,10 @@ class CropOperation : public CropBaseOperation { public: CropOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class CropImageOperation : public CropBaseOperation { @@ -65,6 +69,11 @@ class CropImageOperation : public CropBaseOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index 1a86fadad76..02e7c5607d8 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -71,4 +71,34 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat } } +void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + zero_v4(it.out); + for (int i = 0; i < it.get_num_inputs(); i++) { + const float *input = it.in(i); + if (i == 0) { + /* Write the front-most object as false color for picking. */ + it.out[0] = input[0]; + uint32_t m3hash; + ::memcpy(&m3hash, &input[0], sizeof(uint32_t)); + /* Since the red channel is likely to be out of display range, + * setting green and blue gives more meaningful images. */ + it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); + it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); + } + for (const float hash : m_objectIndex) { + if (input[0] == hash) { + it.out[3] += input[1]; + } + if (input[2] == hash) { + it.out[3] += input[3]; + } + } + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h index 1b91358d228..f1bf4cdf624 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.h +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class CryptomatteOperation : public NodeOperation { +class CryptomatteOperation : public MultiThreadedOperation { private: Vector<float> m_objectIndex; @@ -35,6 +35,10 @@ class CryptomatteOperation : public NodeOperation { void executePixel(float output[4], int x, int y, void *data) override; void addObjectIndex(float objectIndex); + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc index 0acdfc1651f..31714b03b06 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc @@ -29,6 +29,7 @@ DifferenceMatteOperation::DifferenceMatteOperation() this->m_inputImage1Program = nullptr; this->m_inputImage2Program = nullptr; + flags.can_be_constant = true; } void DifferenceMatteOperation::initExecution() @@ -86,4 +87,44 @@ void DifferenceMatteOperation::executePixelSampled(float output[4], } } +void DifferenceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color1 = it.in(0); + const float *color2 = it.in(1); + + float difference = (fabsf(color2[0] - color1[0]) + fabsf(color2[1] - color1[1]) + + fabsf(color2[2] - color1[2])); + + /* Average together the distances. */ + difference = difference / 3.0f; + + const float tolerance = m_settings->t1; + const float falloff = m_settings->t2; + + /* Make 100% transparent. */ + if (difference <= tolerance) { + it.out[0] = 0.0f; + } + /* In the falloff region, make partially transparent. */ + else if (difference <= falloff + tolerance) { + difference = difference - tolerance; + const float alpha = difference / falloff; + /* Only change if more transparent than before. */ + if (alpha < color1[3]) { + it.out[0] = alpha; + } + else { /* Leave as before. */ + it.out[0] = color1[3]; + } + } + else { + /* Foreground object. */ + it.out[0] = color1[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h index d3963fee1c1..0a86535d946 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class DifferenceMatteOperation : public NodeOperation { +class DifferenceMatteOperation : public MultiThreadedOperation { private: NodeChroma *m_settings; SocketReader *m_inputImage1Program; @@ -50,6 +50,10 @@ class DifferenceMatteOperation : public NodeOperation { { this->m_settings = nodeChroma; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index 9f3f5cfe489..a4c01fda7ca 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -32,20 +32,30 @@ DisplaceOperation::DisplaceOperation() this->flags.complex = true; this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; } void DisplaceOperation::initExecution() { this->m_inputColorProgram = this->getInputSocketReader(0); - this->m_inputVectorProgram = this->getInputSocketReader(1); - this->m_inputScaleXProgram = this->getInputSocketReader(2); - this->m_inputScaleYProgram = this->getInputSocketReader(3); + NodeOperation *vector = this->getInputSocketReader(1); + NodeOperation *scale_x = this->getInputSocketReader(2); + NodeOperation *scale_y = this->getInputSocketReader(3); + if (execution_model_ == eExecutionModel::Tiled) { + vector_read_fn_ = [=](float x, float y, float *out) { + vector->readSampled(out, x, y, PixelSampler::Bilinear); + }; + scale_x_read_fn_ = [=](float x, float y, float *out) { + scale_x->readSampled(out, x, y, PixelSampler::Nearest); + }; + scale_y_read_fn_ = [=](float x, float y, float *out) { + scale_y->readSampled(out, x, y, PixelSampler::Nearest); + }; + } this->m_width_x4 = this->getWidth() * 4; this->m_height_x4 = this->getHeight() * 4; + input_vector_width_ = vector->getWidth(); + input_vector_height_ = vector->getHeight(); } void DisplaceOperation::executePixelSampled(float output[4], @@ -69,8 +79,8 @@ void DisplaceOperation::executePixelSampled(float output[4], bool DisplaceOperation::read_displacement( float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v) { - float width = m_inputVectorProgram->getWidth(); - float height = m_inputVectorProgram->getHeight(); + float width = input_vector_width_; + float height = input_vector_height_; if (x < 0.0f || x >= width || y < 0.0f || y >= height) { r_u = 0.0f; r_v = 0.0f; @@ -78,7 +88,7 @@ bool DisplaceOperation::read_displacement( } float col[4]; - m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear); + vector_read_fn_(x, y, col); r_u = origin[0] - col[0] * xscale; r_v = origin[1] - col[1] * yscale; return true; @@ -90,9 +100,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r float uv[2]; /* temporary variables for derivative estimation */ int num; - m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest); + scale_x_read_fn_(xy[0], xy[1], col); float xs = col[0]; - m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest); + scale_y_read_fn_(xy[0], xy[1], col); float ys = col[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ @@ -146,9 +156,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r void DisplaceOperation::deinitExecution() { this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; + vector_read_fn_ = nullptr; + scale_x_read_fn_ = nullptr; + scale_y_read_fn_ = nullptr; } bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input, @@ -195,4 +205,61 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input, return false; } +void DisplaceOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case 0: { + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = getInputOperation(input_idx)->getWidth(); + r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + break; + } + case 1: { + r_input_area = output_area; + expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); + break; + } + default: { + r_input_area = output_area; + break; + } + } +} + +void DisplaceOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *vector = inputs[1]; + MemoryBuffer *scale_x = inputs[2]; + MemoryBuffer *scale_y = inputs[3]; + vector_read_fn_ = [=](float x, float y, float *out) { vector->read_elem_bilinear(x, y, out); }; + scale_x_read_fn_ = [=](float x, float y, float *out) { scale_x->read_elem_checked(x, y, out); }; + scale_y_read_fn_ = [=](float x, float y, float *out) { scale_y->read_elem_checked(x, y, out); }; +} + +void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_color = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const float xy[2] = {(float)it.x, (float)it.y}; + float uv[2]; + float deriv[2][2]; + + pixelTransform(xy, uv, deriv); + if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { + input_color->read_elem_bilinear(uv[0], uv[1], it.out); + } + else { + /* EWA filtering (without nearest it gets blurry with NO distortion). */ + input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h index fd82692f687..5be914ab672 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceOperation.h @@ -18,23 +18,27 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DisplaceOperation : public NodeOperation { +class DisplaceOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram */ SocketReader *m_inputColorProgram; - SocketReader *m_inputVectorProgram; - SocketReader *m_inputScaleXProgram; - SocketReader *m_inputScaleYProgram; float m_width_x4; float m_height_x4; + int input_vector_width_; + int input_vector_height_; + + std::function<void(float x, float y, float *out)> vector_read_fn_; + std::function<void(float x, float y, float *out)> scale_x_read_fn_; + std::function<void(float x, float y, float *out)> scale_y_read_fn_; + public: DisplaceOperation(); @@ -62,6 +66,14 @@ class DisplaceOperation : public NodeOperation { */ void deinitExecution() override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: bool read_displacement( float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v); diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index f4b77f5d32c..e1c531bd49e 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -132,4 +132,56 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input, return false; } +void DisplaceSimpleOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case 0: { + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = getInputOperation(input_idx)->getWidth(); + r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + break; + } + default: { + r_input_area = output_area; + break; + } + } +} + +void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const float width = this->getWidth(); + const float height = this->getHeight(); + const MemoryBuffer *input_color = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with(inputs.drop_front(1), area); !it.is_end(); + ++it) { + float scale_x = *it.in(1); + float scale_y = *it.in(2); + + /* Clamp x and y displacement to triple image resolution - + * to prevent hangs from huge values mistakenly plugged in eg. z buffers. */ + CLAMP(scale_x, -m_width_x4, m_width_x4); + CLAMP(scale_y, -m_height_x4, m_height_x4); + + /* Main displacement in pixel space. */ + const float *vector = it.in(0); + const float p_dx = vector[0] * scale_x; + const float p_dy = vector[1] * scale_y; + + /* Displaced pixel in uv coords, for image sampling. */ + /* Clamp nodes to avoid glitches. */ + float u = it.x - p_dx + 0.5f; + float v = it.y - p_dy + 0.5f; + CLAMP(u, 0.0f, width - 1.0f); + CLAMP(v, 0.0f, height - 1.0f); + + input_color->read_elem_checked(u, v, it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h index 15e6fcd0523..99f52155466 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DisplaceSimpleOperation : public NodeOperation { +class DisplaceSimpleOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -59,6 +59,11 @@ class DisplaceSimpleOperation : public NodeOperation { * Deinitialize the execution */ void deinitExecution() override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc index 1b3403cbb29..8155ff769a0 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc @@ -29,6 +29,7 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation() this->m_inputImageProgram = nullptr; this->m_inputKeyProgram = nullptr; + flags.can_be_constant = true; } void DistanceRGBMatteOperation::initExecution() @@ -43,7 +44,7 @@ void DistanceRGBMatteOperation::deinitExecution() this->m_inputKeyProgram = nullptr; } -float DistanceRGBMatteOperation::calculateDistance(float key[4], float image[4]) +float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4]) { return len_v3v3(key, image); } @@ -93,4 +94,43 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4], } } +void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *in_image = it.in(0); + const float *in_key = it.in(1); + + float distance = this->calculateDistance(in_key, in_image); + const float tolerance = this->m_settings->t1; + const float falloff = this->m_settings->t2; + + /* Store matte(alpha) value in [0] to go with + * COM_SetAlphaMultiplyOperation and the Value output. + */ + + /* Make 100% transparent. */ + if (distance < tolerance) { + it.out[0] = 0.0f; + } + /* In the falloff region, make partially transparent. */ + else if (distance < falloff + tolerance) { + distance = distance - tolerance; + const float alpha = distance / falloff; + /* Only change if more transparent than before. */ + if (alpha < in_image[3]) { + it.out[0] = alpha; + } + else { /* Leave as before. */ + it.out[0] = in_image[3]; + } + } + else { + /* Leave as before. */ + it.out[0] = in_image[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h index 6fe603233b7..ba6682214ae 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,13 +26,13 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class DistanceRGBMatteOperation : public NodeOperation { +class DistanceRGBMatteOperation : public MultiThreadedOperation { protected: NodeChroma *m_settings; SocketReader *m_inputImageProgram; SocketReader *m_inputKeyProgram; - virtual float calculateDistance(float key[4], float image[4]); + virtual float calculateDistance(const float key[4], const float image[4]); public: /** @@ -52,6 +52,10 @@ class DistanceRGBMatteOperation : public NodeOperation { { this->m_settings = nodeChroma; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc index 597545dd706..50e473ea5b3 100644 --- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc @@ -21,7 +21,7 @@ namespace blender::compositor { -float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4]) +float DistanceYCCMatteOperation::calculateDistance(const float key[4], const float image[4]) { /* only measure the second 2 values */ return len_v2v2(key + 1, image + 1); diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h index a87e885e5d8..0e178fddc39 100644 --- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h @@ -29,7 +29,7 @@ namespace blender::compositor { */ class DistanceYCCMatteOperation : public DistanceRGBMatteOperation { protected: - float calculateDistance(float key[4], float image[4]) override; + float calculateDistance(const float key[4], const float image[4]) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index 3804e6ec646..e0fc45811cb 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -62,6 +62,13 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void FastGaussianBlurOperation::init_data() +{ + BlurBaseOperation::init_data(); + this->m_sx = this->m_data.sizex * this->m_size / 2.0f; + this->m_sy = this->m_data.sizey * this->m_size / 2.0f; +} + void FastGaussianBlurOperation::initExecution() { BlurBaseOperation::initExecution(); @@ -117,6 +124,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, unsigned int chan, unsigned int xy) { + BLI_assert(!src->is_a_single_elem()); double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3]; double *X, *Y, *W; const unsigned int src_width = src->getWidth(); @@ -257,6 +265,64 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, #undef YVV } +void FastGaussianBlurOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: + r_input_area.xmin = 0; + r_input_area.xmax = getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = getHeight(); + break; + default: + BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area); + return; + } +} + +void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + /* TODO(manzanilla): Add a render test and make #IIR_gauss multi-threaded with support for + * an output buffer. */ + const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + MemoryBuffer *image = nullptr; + const bool is_full_output = BLI_rcti_compare(&output->get_rect(), &area); + if (is_full_output) { + image = output; + } + else { + image = new MemoryBuffer(getOutputSocket()->getDataType(), area); + } + image->copy_from(input, area); + + if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) { + for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { + IIR_gauss(image, this->m_sx, c, 3); + } + } + else { + if (this->m_sx > 0.0f) { + for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { + IIR_gauss(image, this->m_sx, c, 1); + } + } + if (this->m_sy > 0.0f) { + for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { + IIR_gauss(image, this->m_sy, c, 2); + } + } + } + + if (!is_full_output) { + output->copy_from(image, area); + delete image; + } +} + FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() { this->addInputSocket(DataType::Value); @@ -341,4 +407,44 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) return this->m_iirgaus; } +void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area.xmin = 0; + r_input_area.xmax = getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = getHeight(); +} + +void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + if (m_iirgaus == nullptr) { + const MemoryBuffer *image = inputs[0]; + MemoryBuffer *gauss = new MemoryBuffer(*image); + FastGaussianBlurOperation::IIR_gauss(gauss, m_sigma, 0, 3); + m_iirgaus = gauss; + } +} + +void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *image = inputs[0]; + BuffersIterator<float> it = output->iterate_with({image, m_iirgaus}, area); + if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) { + for (; !it.is_end(); ++it) { + *it.out = MIN2(*it.in(0), *it.in(1)); + } + } + else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) { + for (; !it.is_end(); ++it) { + *it.out = MAX2(*it.in(0), *it.in(1)); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h index c25afe6c4a4..f42fc76a119 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h @@ -38,8 +38,19 @@ class FastGaussianBlurOperation : public BlurBaseOperation { static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy); void *initializeTileData(rcti *rect) override; + void init_data() override; void deinitExecution() override; void initExecution() override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> UNUSED(inputs)) override + { + } }; enum { @@ -48,7 +59,7 @@ enum { FAST_GAUSS_OVERLAY_MAX = 1, }; -class FastGaussianBlurValueOperation : public NodeOperation { +class FastGaussianBlurValueOperation : public MultiThreadedOperation { private: float m_sigma; MemoryBuffer *m_iirgaus; @@ -80,6 +91,14 @@ class FastGaussianBlurValueOperation : public NodeOperation { { this->m_overlay = overlay; } + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index 8afbec4ddbe..d0dc6c0b570 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -75,4 +75,42 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void FlipOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + if (this->m_flipX) { + const int w = (int)this->getWidth() - 1; + r_input_area.xmax = (w - output_area.xmin) + 1; + r_input_area.xmin = (w - output_area.xmax) - 1; + } + else { + r_input_area.xmin = output_area.xmin; + r_input_area.xmax = output_area.xmax; + } + if (this->m_flipY) { + const int h = (int)this->getHeight() - 1; + r_input_area.ymax = (h - output_area.ymin) + 1; + r_input_area.ymin = (h - output_area.ymax) - 1; + } + else { + r_input_area.ymin = output_area.ymin; + r_input_area.ymax = output_area.ymax; + } +} + +void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x; + const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y; + input_img->read_elem(nx, ny, it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h index f26d587fde6..dba7f82c341 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.h +++ b/source/blender/compositor/operations/COM_FlipOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class FlipOperation : public NodeOperation { +class FlipOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; bool m_flipX; @@ -45,6 +45,11 @@ class FlipOperation : public NodeOperation { { this->m_flipY = flipY; } + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc index 16b79fddd06..1bff3b965c6 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc @@ -26,6 +26,7 @@ GammaCorrectOperation::GammaCorrectOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->m_inputProgram = nullptr; + flags.can_be_constant = true; } void GammaCorrectOperation::initExecution() { @@ -58,6 +59,34 @@ void GammaCorrectOperation::executePixelSampled(float output[4], } } +void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float color[4]; + input->read_elem(it.x, it.y, color); + if (color[3] > 0.0f) { + color[0] /= color[3]; + color[1] /= color[3]; + color[2] /= color[3]; + } + + /* Check for negative to avoid nan's. */ + it.out[0] = color[0] > 0.0f ? color[0] * color[0] : 0.0f; + it.out[1] = color[1] > 0.0f ? color[1] * color[1] : 0.0f; + it.out[2] = color[2] > 0.0f ? color[2] * color[2] : 0.0f; + it.out[3] = color[3]; + + if (color[3] > 0.0f) { + it.out[0] *= color[3]; + it.out[1] *= color[3]; + it.out[2] *= color[3]; + } + } +} + void GammaCorrectOperation::deinitExecution() { this->m_inputProgram = nullptr; @@ -68,6 +97,7 @@ GammaUncorrectOperation::GammaUncorrectOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->m_inputProgram = nullptr; + flags.can_be_constant = true; } void GammaUncorrectOperation::initExecution() { @@ -100,6 +130,33 @@ void GammaUncorrectOperation::executePixelSampled(float output[4], } } +void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float color[4]; + input->read_elem(it.x, it.y, color); + if (color[3] > 0.0f) { + color[0] /= color[3]; + color[1] /= color[3]; + color[2] /= color[3]; + } + + it.out[0] = color[0] > 0.0f ? sqrtf(color[0]) : 0.0f; + it.out[1] = color[1] > 0.0f ? sqrtf(color[1]) : 0.0f; + it.out[2] = color[2] > 0.0f ? sqrtf(color[2]) : 0.0f; + it.out[3] = color[3]; + + if (color[3] > 0.0f) { + it.out[0] *= color[3]; + it.out[1] *= color[3]; + it.out[2] *= color[3]; + } + } +} + void GammaUncorrectOperation::deinitExecution() { this->m_inputProgram = nullptr; diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.h b/source/blender/compositor/operations/COM_GammaCorrectOperation.h index ac3d45b94b1..2a9fde70e87 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.h +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class GammaCorrectOperation : public NodeOperation { +class GammaCorrectOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -46,9 +46,13 @@ class GammaCorrectOperation : public NodeOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; -class GammaUncorrectOperation : public NodeOperation { +class GammaUncorrectOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -72,6 +76,10 @@ class GammaUncorrectOperation : public NodeOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc new file mode 100644 index 00000000000..9bdc652b466 --- /dev/null +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc @@ -0,0 +1,168 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_GaussianAlphaBlurBaseOperation.h" + +namespace blender::compositor { + +GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim) + : BlurBaseOperation(DataType::Value) +{ + this->m_gausstab = nullptr; + this->m_filtersize = 0; + this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */ + dimension_ = dim; +} + +void GaussianAlphaBlurBaseOperation::init_data() +{ + BlurBaseOperation::init_data(); + if (execution_model_ == eExecutionModel::FullFrame) { + rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f); + rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS); + m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); + } +} + +void GaussianAlphaBlurBaseOperation::initExecution() +{ + BlurBaseOperation::initExecution(); + if (execution_model_ == eExecutionModel::FullFrame) { + m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize); + m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff); + } +} + +void GaussianAlphaBlurBaseOperation::deinitExecution() +{ + BlurBaseOperation::deinitExecution(); + + if (this->m_gausstab) { + MEM_freeN(this->m_gausstab); + this->m_gausstab = nullptr; + } + + if (this->m_distbuf_inv) { + MEM_freeN(this->m_distbuf_inv); + this->m_distbuf_inv = nullptr; + } +} + +void GaussianAlphaBlurBaseOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx != IMAGE_INPUT_INDEX) { + BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area); + return; + } + + r_input_area = output_area; + switch (dimension_) { + case eDimension::X: + r_input_area.xmin = output_area.xmin - m_filtersize - 1; + r_input_area.xmax = output_area.xmax + m_filtersize + 1; + break; + case eDimension::Y: + r_input_area.ymin = output_area.ymin - m_filtersize - 1; + r_input_area.ymax = output_area.ymax + m_filtersize + 1; + break; + } +} + +BLI_INLINE float finv_test(const float f, const bool test) +{ + return (LIKELY(test == false)) ? f : 1.0f - f; +} + +void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + const rcti &input_rect = input->get_rect(); + BuffersIterator<float> it = output->iterate_with({input}, area); + + int min_input_coord = -1; + int max_input_coord = -1; + int elem_stride = -1; + std::function<int()> get_current_coord; + switch (dimension_) { + case eDimension::X: + min_input_coord = input_rect.xmin; + max_input_coord = input_rect.xmax; + get_current_coord = [&] { return it.x; }; + elem_stride = input->elem_stride; + break; + case eDimension::Y: + min_input_coord = input_rect.ymin; + max_input_coord = input_rect.ymax; + get_current_coord = [&] { return it.y; }; + elem_stride = input->row_stride; + break; + } + + for (; !it.is_end(); ++it) { + const int coord = get_current_coord(); + const int coord_min = max_ii(coord - m_filtersize, min_input_coord); + const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord); + + /* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */ + /* Gauss. */ + float alpha_accum = 0.0f; + float multiplier_accum = 0.0f; + + /* Dilate. */ + const bool do_invert = m_do_subtract; + /* Init with the current color to avoid unneeded lookups. */ + float value_max = finv_test(*it.in(0), do_invert); + float distfacinv_max = 1.0f; /* 0 to 1 */ + + const int step = QualityStepHelper::getStep(); + const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; + const int in_stride = elem_stride * step; + int index = (coord_min - coord) + m_filtersize; + const int index_end = index + (coord_max - coord_min); + for (; index < index_end; in += in_stride, index += step) { + float value = finv_test(*in, do_invert); + + /* Gauss. */ + float multiplier = m_gausstab[index]; + alpha_accum += value * multiplier; + multiplier_accum += multiplier; + + /* Dilate - find most extreme color. */ + if (value > value_max) { + multiplier = m_distbuf_inv[index]; + value *= multiplier; + if (value > value_max) { + value_max = value; + distfacinv_max = multiplier; + } + } + } + + /* Blend between the max value and gauss blue - gives nice feather. */ + const float value_blur = alpha_accum / multiplier_accum; + const float value_final = (value_max * distfacinv_max) + + (value_blur * (1.0f - distfacinv_max)); + *it.out = finv_test(value_final, do_invert); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h new file mode 100644 index 00000000000..d7ca975ca0a --- /dev/null +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_BlurBaseOperation.h" + +namespace blender::compositor { + +class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { + protected: + float *m_gausstab; + float *m_distbuf_inv; + int m_falloff; /* Falloff for #distbuf_inv. */ + bool m_do_subtract; + int m_filtersize; + float rad_; + eDimension dimension_; + + public: + GaussianAlphaBlurBaseOperation(eDimension dim); + + virtual void init_data() override; + virtual void initExecution() override; + virtual void deinitExecution() override; + + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) final; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) final; + + /** + * Set subtract for Dilate/Erode functionality + */ + void setSubtract(bool subtract) + { + this->m_do_subtract = subtract; + } + void setFalloff(int falloff) + { + this->m_falloff = falloff; + } +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc index 7ca5dc4ca76..6710ed3cf5b 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc @@ -24,11 +24,9 @@ namespace blender::compositor { -GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() : BlurBaseOperation(DataType::Value) +GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() + : GaussianAlphaBlurBaseOperation(eDimension::X) { - this->m_gausstab = nullptr; - this->m_filtersize = 0; - this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */ } void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/) @@ -44,12 +42,11 @@ void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/) void GaussianAlphaXBlurOperation::initExecution() { - /* Until we support size input - comment this. */ - // BlurBaseOperation::initExecution(); + GaussianAlphaBlurBaseOperation::initExecution(); initMutex(); - if (this->m_sizeavailable) { + if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizex, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -144,7 +141,7 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo void GaussianAlphaXBlurOperation::deinitExecution() { - BlurBaseOperation::deinitExecution(); + GaussianAlphaBlurBaseOperation::deinitExecution(); if (this->m_gausstab) { MEM_freeN(this->m_gausstab); diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h index 949956fae04..2a44c639665 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h @@ -18,18 +18,13 @@ #pragma once -#include "COM_BlurBaseOperation.h" -#include "COM_NodeOperation.h" +#include "COM_GaussianAlphaBlurBaseOperation.h" namespace blender::compositor { -class GaussianAlphaXBlurOperation : public BlurBaseOperation { +/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ +class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation { private: - float *m_gausstab; - float *m_distbuf_inv; - int m_falloff; /* falloff for distbuf_inv */ - bool m_do_subtract; - int m_filtersize; void updateGauss(); public: @@ -54,18 +49,6 @@ class GaussianAlphaXBlurOperation : public BlurBaseOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - - /** - * Set subtract for Dilate/Erode functionality - */ - void setSubtract(bool subtract) - { - this->m_do_subtract = subtract; - } - void setFalloff(int falloff) - { - this->m_falloff = falloff; - } }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc index d2385a972dd..09aeddb6573 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc @@ -24,11 +24,9 @@ namespace blender::compositor { -GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() : BlurBaseOperation(DataType::Value) +GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() + : GaussianAlphaBlurBaseOperation(eDimension::Y) { - this->m_gausstab = nullptr; - this->m_filtersize = 0; - this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */ } void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/) @@ -42,14 +40,14 @@ void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/) return buffer; } +/* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianAlphaYBlurOperation::initExecution() { - /* Until we support size input - comment this. */ - // BlurBaseOperation::initExecution(); + GaussianAlphaBlurBaseOperation::initExecution(); initMutex(); - if (this->m_sizeavailable) { + if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizey, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -58,6 +56,7 @@ void GaussianAlphaYBlurOperation::initExecution() } } +/* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianAlphaYBlurOperation::updateGauss() { if (this->m_gausstab == nullptr) { @@ -143,7 +142,7 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo void GaussianAlphaYBlurOperation::deinitExecution() { - BlurBaseOperation::deinitExecution(); + GaussianAlphaBlurBaseOperation::deinitExecution(); if (this->m_gausstab) { MEM_freeN(this->m_gausstab); diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h index d25770386c4..ef01f7e0f92 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h @@ -18,18 +18,13 @@ #pragma once -#include "COM_BlurBaseOperation.h" -#include "COM_NodeOperation.h" +#include "COM_GaussianAlphaBlurBaseOperation.h" namespace blender::compositor { -class GaussianAlphaYBlurOperation : public BlurBaseOperation { +/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ +class GaussianAlphaYBlurOperation : public GaussianAlphaBlurBaseOperation { private: - float *m_gausstab; - float *m_distbuf_inv; - bool m_do_subtract; - int m_falloff; - int m_filtersize; void updateGauss(); public: @@ -54,18 +49,6 @@ class GaussianAlphaYBlurOperation : public BlurBaseOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - - /** - * Set subtract for Dilate/Erode functionality - */ - void setSubtract(bool subtract) - { - this->m_do_subtract = subtract; - } - void setFalloff(int falloff) - { - this->m_falloff = falloff; - } }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc new file mode 100644 index 00000000000..959f599fab4 --- /dev/null +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc @@ -0,0 +1,154 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_GaussianBlurBaseOperation.h" + +namespace blender::compositor { + +GaussianBlurBaseOperation::GaussianBlurBaseOperation(eDimension dim) + : BlurBaseOperation(DataType::Color) +{ + m_gausstab = nullptr; +#ifdef BLI_HAVE_SSE2 + m_gausstab_sse = nullptr; +#endif + m_filtersize = 0; + rad_ = 0.0f; + dimension_ = dim; +} + +void GaussianBlurBaseOperation::init_data() +{ + BlurBaseOperation::init_data(); + if (execution_model_ == eExecutionModel::FullFrame) { + rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f); + rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS); + m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); + } +} + +void GaussianBlurBaseOperation::initExecution() +{ + BlurBaseOperation::initExecution(); + if (execution_model_ == eExecutionModel::FullFrame) { + m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize); +#ifdef BLI_HAVE_SSE2 + m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); +#endif + } +} + +void GaussianBlurBaseOperation::deinitExecution() +{ + BlurBaseOperation::deinitExecution(); + + if (m_gausstab) { + MEM_freeN(m_gausstab); + m_gausstab = nullptr; + } +#ifdef BLI_HAVE_SSE2 + if (m_gausstab_sse) { + MEM_freeN(m_gausstab_sse); + m_gausstab_sse = nullptr; + } +#endif +} + +void GaussianBlurBaseOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx != IMAGE_INPUT_INDEX) { + BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area); + return; + } + + r_input_area = output_area; + switch (dimension_) { + case eDimension::X: + r_input_area.xmin = output_area.xmin - m_filtersize - 1; + r_input_area.xmax = output_area.xmax + m_filtersize + 1; + break; + case eDimension::Y: + r_input_area.ymin = output_area.ymin - m_filtersize - 1; + r_input_area.ymax = output_area.ymax + m_filtersize + 1; + break; + } +} + +void GaussianBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + const rcti &input_rect = input->get_rect(); + BuffersIterator<float> it = output->iterate_with({input}, area); + + int min_input_coord = -1; + int max_input_coord = -1; + int elem_stride = -1; + std::function<int()> get_current_coord; + switch (dimension_) { + case eDimension::X: + min_input_coord = input_rect.xmin; + max_input_coord = input_rect.xmax; + elem_stride = input->elem_stride; + get_current_coord = [&] { return it.x; }; + break; + case eDimension::Y: + min_input_coord = input_rect.ymin; + max_input_coord = input_rect.ymax; + elem_stride = input->row_stride; + get_current_coord = [&] { return it.y; }; + break; + } + + for (; !it.is_end(); ++it) { + const int coord = get_current_coord(); + const int coord_min = max_ii(coord - m_filtersize, min_input_coord); + const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord); + + float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float multiplier_accum = 0.0f; + + const int step = QualityStepHelper::getStep(); + const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; + const int in_stride = elem_stride * step; + int gauss_idx = (coord_min - coord) + m_filtersize; + const int gauss_end = gauss_idx + (coord_max - coord_min); +#ifdef BLI_HAVE_SSE2 + __m128 accum_r = _mm_load_ps(color_accum); + for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) { + __m128 reg_a = _mm_load_ps(in); + reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[gauss_idx]); + accum_r = _mm_add_ps(accum_r, reg_a); + multiplier_accum += m_gausstab[gauss_idx]; + } + _mm_store_ps(color_accum, accum_r); +#else + for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) { + const float multiplier = m_gausstab[gauss_idx]; + madd_v4_v4fl(color_accum, in, multiplier); + multiplier_accum += multiplier; + } +#endif + mul_v4_v4fl(it.out, color_accum, 1.0f / multiplier_accum); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h new file mode 100644 index 00000000000..c0b27078a24 --- /dev/null +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_BlurBaseOperation.h" + +namespace blender::compositor { + +class GaussianBlurBaseOperation : public BlurBaseOperation { + protected: + float *m_gausstab; +#ifdef BLI_HAVE_SSE2 + __m128 *m_gausstab_sse; +#endif + int m_filtersize; + float rad_; + eDimension dimension_; + + public: + GaussianBlurBaseOperation(eDimension dim); + + virtual void init_data() override; + virtual void initExecution() override; + virtual void deinitExecution() override; + + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index b2c65ff2c96..aafc269abac 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -40,6 +40,27 @@ void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/) return buffer; } +void GaussianBokehBlurOperation::init_data() +{ + BlurBaseOperation::init_data(); + const float width = this->getWidth(); + const float height = this->getHeight(); + + if (!this->m_sizeavailable) { + updateSize(); + } + + radxf_ = this->m_size * (float)this->m_data.sizex; + CLAMP(radxf_, 0.0f, width / 2.0f); + + /* Vertical. */ + radyf_ = this->m_size * (float)this->m_data.sizey; + CLAMP(radyf_, 0.0f, height / 2.0f); + + this->m_radx = ceil(radxf_); + this->m_rady = ceil(radyf_); +} + void GaussianBokehBlurOperation::initExecution() { BlurBaseOperation::initExecution(); @@ -54,39 +75,17 @@ void GaussianBokehBlurOperation::initExecution() void GaussianBokehBlurOperation::updateGauss() { if (this->m_gausstab == nullptr) { - float radxf; - float radyf; - int n; - float *dgauss; - float *ddgauss; - int j, i; - const float width = this->getWidth(); - const float height = this->getHeight(); - if (!this->m_sizeavailable) { - updateSize(); - } - radxf = this->m_size * (float)this->m_data.sizex; - CLAMP(radxf, 0.0f, width / 2.0f); - - /* vertical */ - radyf = this->m_size * (float)this->m_data.sizey; - CLAMP(radyf, 0.0f, height / 2.0f); - - this->m_radx = ceil(radxf); - this->m_rady = ceil(radyf); - int ddwidth = 2 * this->m_radx + 1; int ddheight = 2 * this->m_rady + 1; - n = ddwidth * ddheight; - + int n = ddwidth * ddheight; /* create a full filter image */ - ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__); - dgauss = ddgauss; + float *ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__); + float *dgauss = ddgauss; float sum = 0.0f; - float facx = (radxf > 0.0f ? 1.0f / radxf : 0.0f); - float facy = (radyf > 0.0f ? 1.0f / radyf : 0.0f); - for (j = -this->m_rady; j <= this->m_rady; j++) { - for (i = -this->m_radx; i <= this->m_radx; i++, dgauss++) { + float facx = (radxf_ > 0.0f ? 1.0f / radxf_ : 0.0f); + float facy = (radyf_ > 0.0f ? 1.0f / radyf_ : 0.0f); + for (int j = -this->m_rady; j <= this->m_rady; j++) { + for (int i = -this->m_radx; i <= this->m_radx; i++, dgauss++) { float fj = (float)j * facy; float fi = (float)i * facx; float dist = sqrt(fj * fj + fi * fi); @@ -99,7 +98,7 @@ void GaussianBokehBlurOperation::updateGauss() if (sum > 0.0f) { /* normalize */ float norm = 1.0f / sum; - for (j = n - 1; j >= 0; j--) { + for (int j = n - 1; j >= 0; j--) { ddgauss[j] *= norm; } } @@ -196,23 +195,69 @@ bool GaussianBokehBlurOperation::determineDependingAreaOfInterest( return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void GaussianBokehBlurOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx != IMAGE_INPUT_INDEX) { + BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area); + return; + } + + r_input_area.xmax = output_area.xmax + m_radx; + r_input_area.xmin = output_area.xmin - m_radx; + r_input_area.ymax = output_area.ymax + m_rady; + r_input_area.ymin = output_area.ymin - m_rady; +} + +void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + BuffersIterator<float> it = output->iterate_with({}, area); + const rcti &input_rect = input->get_rect(); + for (; !it.is_end(); ++it) { + const int x = it.x; + const int y = it.y; + + const int ymin = max_ii(y - this->m_rady, input_rect.ymin); + const int ymax = min_ii(y + this->m_rady + 1, input_rect.ymax); + const int xmin = max_ii(x - this->m_radx, input_rect.xmin); + const int xmax = min_ii(x + this->m_radx + 1, input_rect.xmax); + + float tempColor[4] = {0}; + float multiplier_accum = 0; + const int step = QualityStepHelper::getStep(); + const int elem_step = step * input->elem_stride; + const int add_const = (xmin - x + this->m_radx); + const int mul_const = (this->m_radx * 2 + 1); + for (int ny = ymin; ny < ymax; ny += step) { + const float *color = input->get_elem(xmin, ny); + int gauss_index = ((ny - y) + this->m_rady) * mul_const + add_const; + const int gauss_end = gauss_index + (xmax - xmin); + for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) { + const float multiplier = this->m_gausstab[gauss_index]; + madd_v4_v4fl(tempColor, color, multiplier); + multiplier_accum += multiplier; + } + } + + mul_v4_v4fl(it.out, tempColor, 1.0f / multiplier_accum); + } +} + // reference image GaussianBlurReferenceOperation::GaussianBlurReferenceOperation() : BlurBaseOperation(DataType::Color) { this->m_maintabs = nullptr; + use_variable_size_ = true; } -void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/) -{ - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - return buffer; -} - -void GaussianBlurReferenceOperation::initExecution() +void GaussianBlurReferenceOperation::init_data() { - BlurBaseOperation::initExecution(); - // setup gaustab + /* Setup variables for gausstab and area of interest. */ this->m_data.image_in_width = this->getWidth(); this->m_data.image_in_height = this->getHeight(); if (this->m_data.relative) { @@ -232,7 +277,7 @@ void GaussianBlurReferenceOperation::initExecution() } } - /* horizontal */ + /* Horizontal. */ m_filtersizex = (float)this->m_data.sizex; int imgx = getWidth() / 2; if (m_filtersizex > imgx) { @@ -243,7 +288,7 @@ void GaussianBlurReferenceOperation::initExecution() } m_radx = (float)m_filtersizex; - /* vertical */ + /* Vertical. */ m_filtersizey = (float)this->m_data.sizey; int imgy = getHeight() / 2; if (m_filtersizey > imgy) { @@ -253,6 +298,18 @@ void GaussianBlurReferenceOperation::initExecution() m_filtersizey = 1; } m_rady = (float)m_filtersizey; +} + +void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/) +{ + void *buffer = getInputOperation(0)->initializeTileData(nullptr); + return buffer; +} + +void GaussianBlurReferenceOperation::initExecution() +{ + BlurBaseOperation::initExecution(); + updateGauss(); } @@ -363,4 +420,78 @@ bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx != IMAGE_INPUT_INDEX) { + BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area); + return; + } + + const int add_x = this->m_data.sizex + 2; + const int add_y = this->m_data.sizey + 2; + r_input_area.xmax = output_area.xmax + add_x; + r_input_area.xmin = output_area.xmin - add_x; + r_input_area.ymax = output_area.ymax + add_y; + r_input_area.ymin = output_area.ymin - add_y; +} + +void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX]; + MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX]; + for (BuffersIterator<float> it = output->iterate_with({size_input}, area); !it.is_end(); ++it) { + const float ref_size = *it.in(0); + int ref_radx = (int)(ref_size * m_radx); + int ref_rady = (int)(ref_size * m_rady); + if (ref_radx > m_filtersizex) { + ref_radx = m_filtersizex; + } + else if (ref_radx < 1) { + ref_radx = 1; + } + if (ref_rady > m_filtersizey) { + ref_rady = m_filtersizey; + } + else if (ref_rady < 1) { + ref_rady = 1; + } + + const int x = it.x; + const int y = it.y; + if (ref_radx == 1 && ref_rady == 1) { + image_input->read_elem(x, y, it.out); + continue; + } + + const int w = getWidth(); + const int height = getHeight(); + const int minxr = x - ref_radx < 0 ? -x : -ref_radx; + const int maxxr = x + ref_radx > w ? w - x : ref_radx; + const int minyr = y - ref_rady < 0 ? -y : -ref_rady; + const int maxyr = y + ref_rady > height ? height - y : ref_rady; + + const float *gausstabx = m_maintabs[ref_radx - 1]; + const float *gausstabcentx = gausstabx + ref_radx; + const float *gausstaby = m_maintabs[ref_rady - 1]; + const float *gausstabcenty = gausstaby + ref_rady; + + float gauss_sum = 0.0f; + float color_sum[4] = {0}; + const float *row_color = image_input->get_elem(x + minxr, y + minyr); + for (int i = minyr; i < maxyr; i++, row_color += image_input->row_stride) { + const float *color = row_color; + for (int j = minxr; j < maxxr; j++, color += image_input->elem_stride) { + const float val = gausstabcenty[i] * gausstabcentx[j]; + gauss_sum += val; + madd_v4_v4fl(color_sum, color, val); + } + } + mul_v4_v4fl(it.out, color_sum, 1.0f / gauss_sum); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h index 59ba3d06619..a64b5b327b0 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h @@ -28,10 +28,13 @@ class GaussianBokehBlurOperation : public BlurBaseOperation { private: float *m_gausstab; int m_radx, m_rady; + float radxf_; + float radyf_; void updateGauss(); public: GaussianBokehBlurOperation(); + void init_data() override; void initExecution() override; void *initializeTileData(rcti *rect) override; /** @@ -47,6 +50,13 @@ class GaussianBokehBlurOperation : public BlurBaseOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class GaussianBlurReferenceOperation : public BlurBaseOperation { @@ -61,6 +71,7 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation { public: GaussianBlurReferenceOperation(); + void init_data() override; void initExecution() override; void *initializeTileData(rcti *rect) override; /** @@ -76,6 +87,13 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc index 4b46cfc8776..8d686265231 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc @@ -25,13 +25,8 @@ namespace blender::compositor { -GaussianXBlurOperation::GaussianXBlurOperation() : BlurBaseOperation(DataType::Color) +GaussianXBlurOperation::GaussianXBlurOperation() : GaussianBlurBaseOperation(eDimension::X) { - this->m_gausstab = nullptr; -#ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = nullptr; -#endif - this->m_filtersize = 0; } void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/) @@ -45,13 +40,14 @@ void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/) return buffer; } +/* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianXBlurOperation::initExecution() { - BlurBaseOperation::initExecution(); + GaussianBlurBaseOperation::initExecution(); initMutex(); - if (this->m_sizeavailable) { + if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizex, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -63,6 +59,7 @@ void GaussianXBlurOperation::initExecution() } } +/* TODO(manzanilla): to be removed with tiled implementation. */ void GaussianXBlurOperation::updateGauss() { if (this->m_gausstab == nullptr) { @@ -158,7 +155,7 @@ void GaussianXBlurOperation::executeOpenCL(OpenCLDevice *device, void GaussianXBlurOperation::deinitExecution() { - BlurBaseOperation::deinitExecution(); + GaussianBlurBaseOperation::deinitExecution(); if (this->m_gausstab) { MEM_freeN(this->m_gausstab); diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h index 15277f0a42d..e09e57bad67 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h @@ -18,18 +18,13 @@ #pragma once -#include "COM_BlurBaseOperation.h" -#include "COM_NodeOperation.h" +#include "COM_GaussianBlurBaseOperation.h" namespace blender::compositor { -class GaussianXBlurOperation : public BlurBaseOperation { +/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ +class GaussianXBlurOperation : public GaussianBlurBaseOperation { private: - float *m_gausstab; -#ifdef BLI_HAVE_SSE2 - __m128 *m_gausstab_sse; -#endif - int m_filtersize; void updateGauss(); public: diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc index 590ac5faa6a..32d469a0ae4 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc @@ -25,13 +25,8 @@ namespace blender::compositor { -GaussianYBlurOperation::GaussianYBlurOperation() : BlurBaseOperation(DataType::Color) +GaussianYBlurOperation::GaussianYBlurOperation() : GaussianBlurBaseOperation(eDimension::Y) { - this->m_gausstab = nullptr; -#ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = nullptr; -#endif - this->m_filtersize = 0; } void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/) @@ -47,11 +42,11 @@ void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/) void GaussianYBlurOperation::initExecution() { - BlurBaseOperation::initExecution(); + GaussianBlurBaseOperation::initExecution(); initMutex(); - if (this->m_sizeavailable) { + if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { float rad = max_ff(m_size * m_data.sizey, 0.0f); m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); @@ -158,7 +153,7 @@ void GaussianYBlurOperation::executeOpenCL(OpenCLDevice *device, void GaussianYBlurOperation::deinitExecution() { - BlurBaseOperation::deinitExecution(); + GaussianBlurBaseOperation::deinitExecution(); if (this->m_gausstab) { MEM_freeN(this->m_gausstab); diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h index 56d40849ba4..bb33f8b74cb 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h @@ -18,18 +18,13 @@ #pragma once -#include "COM_BlurBaseOperation.h" -#include "COM_NodeOperation.h" +#include "COM_GaussianBlurBaseOperation.h" namespace blender::compositor { -class GaussianYBlurOperation : public BlurBaseOperation { +/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ +class GaussianYBlurOperation : public GaussianBlurBaseOperation { private: - float *m_gausstab; -#ifdef BLI_HAVE_SSE2 - __m128 *m_gausstab_sse; -#endif - int m_filtersize; void updateGauss(); public: diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc index 994b00cd3f4..d5ebd5e9df7 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc @@ -96,4 +96,67 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void KeyingBlurOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + switch (m_axis) { + case BLUR_AXIS_X: + r_input_area.xmin = output_area.xmin - m_size; + r_input_area.ymin = output_area.ymin; + r_input_area.xmax = output_area.xmax + m_size; + r_input_area.ymax = output_area.ymax; + break; + case BLUR_AXIS_Y: + r_input_area.xmin = output_area.xmin; + r_input_area.ymin = output_area.ymin - m_size; + r_input_area.xmax = output_area.xmax; + r_input_area.ymax = output_area.ymax + m_size; + break; + default: + BLI_assert_msg(0, "Unknown axis"); + break; + } +} + +void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + BuffersIterator<float> it = output->iterate_with(inputs, area); + + int coord_max; + int elem_stride; + std::function<int()> get_current_coord; + switch (m_axis) { + case BLUR_AXIS_X: + get_current_coord = [&] { return it.x; }; + coord_max = this->getWidth(); + elem_stride = input->elem_stride; + break; + case BLUR_AXIS_Y: + get_current_coord = [&] { return it.y; }; + coord_max = this->getHeight(); + elem_stride = input->row_stride; + break; + } + + for (; !it.is_end(); ++it) { + const int coord = get_current_coord(); + const int start_coord = MAX2(0, coord - m_size + 1); + const int end_coord = MIN2(coord_max, coord + m_size); + const int count = end_coord - start_coord; + + float sum = 0.0f; + const float *start = it.in(0) + (start_coord - coord) * elem_stride; + const float *end = start + count * elem_stride; + for (const float *elem = start; elem < end; elem += elem_stride) { + sum += *elem; + } + + *it.out = sum / count; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h index b055d7713f1..b290b905e63 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h @@ -18,14 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { /** * Class with implementation of blurring for keying node */ -class KeyingBlurOperation : public NodeOperation { +class KeyingBlurOperation : public MultiThreadedOperation { protected: int m_size; int m_axis; @@ -54,6 +54,13 @@ class KeyingBlurOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc index 4029be4e077..817c920ed91 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc @@ -130,4 +130,89 @@ bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void KeyingClipOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_kernelRadius; + r_input_area.xmax = output_area.xmax + m_kernelRadius; + r_input_area.ymin = output_area.ymin - m_kernelRadius; + r_input_area.ymax = output_area.ymax + m_kernelRadius; +} + +void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input = inputs[0]; + BuffersIterator<float> it = output->iterate_with(inputs, area); + + const int delta = m_kernelRadius; + const float tolerance = m_kernelTolerance; + const int width = this->getWidth(); + const int height = this->getHeight(); + const int row_stride = input->row_stride; + const int elem_stride = input->elem_stride; + for (; !it.is_end(); ++it) { + const int x = it.x; + const int y = it.y; + + const int start_x = MAX2(0, x - delta + 1); + const int start_y = MAX2(0, y - delta + 1); + const int end_x = MIN2(x + delta, width); + const int end_y = MIN2(y + delta, height); + const int x_len = end_x - start_x; + const int y_len = end_y - start_y; + + const int total_count = x_len * y_len - 1; + const int threshold_count = ceil((float)total_count * 0.9f); + bool ok = false; + if (delta == 0) { + ok = true; + } + + const float *main_elem = it.in(0); + const float value = *main_elem; + const float *row = input->get_elem(start_x, start_y); + const float *end_row = row + y_len * row_stride; + int count = 0; + for (; ok == false && row < end_row; row += row_stride) { + const float *end_elem = row + x_len * elem_stride; + for (const float *elem = row; ok == false && elem < end_elem; elem += elem_stride) { + if (UNLIKELY(elem == main_elem)) { + continue; + } + + const float current_value = *elem; + if (fabsf(current_value - value) < tolerance) { + count++; + if (count >= threshold_count) { + ok = true; + } + } + } + } + + if (m_isEdgeMatte) { + *it.out = ok ? 0.0f : 1.0f; + } + else { + if (!ok) { + *it.out = value; + } + else if (value < m_clipBlack) { + *it.out = 0.0f; + } + else if (value >= m_clipWhite) { + *it.out = 1.0f; + } + else { + *it.out = (value - m_clipBlack) / (m_clipWhite - m_clipBlack); + } + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h index 0a21fb48c99..1a17d591781 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.h +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h @@ -18,14 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { /** * Class with implementation of black/white clipping for keying node */ -class KeyingClipOperation : public NodeOperation { +class KeyingClipOperation : public MultiThreadedOperation { protected: float m_clipBlack; float m_clipWhite; @@ -68,6 +68,13 @@ class KeyingClipOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc index d31a88cb91e..620b767e584 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc @@ -36,6 +36,7 @@ KeyingDespillOperation::KeyingDespillOperation() this->m_pixelReader = nullptr; this->m_screenReader = nullptr; + flags.can_be_constant = true; } void KeyingDespillOperation::initExecution() @@ -82,4 +83,32 @@ void KeyingDespillOperation::executePixelSampled(float output[4], } } +void KeyingDespillOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *pixel_color = it.in(0); + const float *screen_color = it.in(1); + + const int screen_primary_channel = max_axis_v3(screen_color); + const int other_1 = (screen_primary_channel + 1) % 3; + const int other_2 = (screen_primary_channel + 2) % 3; + + const int min_channel = MIN2(other_1, other_2); + const int max_channel = MAX2(other_1, other_2); + + const float average_value = m_colorBalance * pixel_color[min_channel] + + (1.0f - m_colorBalance) * pixel_color[max_channel]; + const float amount = (pixel_color[screen_primary_channel] - average_value); + + copy_v4_v4(it.out, pixel_color); + + const float amount_despill = m_despillFactor * amount; + if (amount_despill > 0.0f) { + it.out[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h index 279ac60e6e9..16bed651d3a 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h @@ -18,14 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { /** * Class with implementation of keying despill node */ -class KeyingDespillOperation : public NodeOperation { +class KeyingDespillOperation : public MultiThreadedOperation { protected: SocketReader *m_pixelReader; SocketReader *m_screenReader; @@ -48,6 +48,10 @@ class KeyingDespillOperation : public NodeOperation { } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc index e786e4b8219..3edb5a5d34e 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingOperation.cc @@ -110,4 +110,49 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix } } +void KeyingOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *pixel_color = it.in(0); + const float *screen_color = it.in(1); + + const int primary_channel = max_axis_v3(screen_color); + const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]); + + if (min_pixel_color > 1.0f) { + /* Overexposure doesn't happen on screen itself and usually happens + * on light sources in the shot, this need to be checked separately + * because saturation and falloff calculation is based on the fact + * that pixels are not overexposed. + */ + it.out[0] = 1.0f; + } + else { + const float saturation = get_pixel_saturation(pixel_color, m_screenBalance, primary_channel); + const float screen_saturation = get_pixel_saturation( + screen_color, m_screenBalance, primary_channel); + + if (saturation < 0) { + /* Means main channel of pixel is different from screen, + * assume this is completely a foreground. + */ + it.out[0] = 1.0f; + } + else if (saturation >= screen_saturation) { + /* Matched main channels and higher saturation on pixel + * is treated as completely background. + */ + it.out[0] = 0.0f; + } + else { + /* Nice alpha falloff on edges. */ + const float distance = 1.0f - saturation / screen_saturation; + it.out[0] = distance; + } + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h index 3d41ecaa0f6..e134ad54896 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.h +++ b/source/blender/compositor/operations/COM_KeyingOperation.h @@ -20,7 +20,7 @@ #include <string.h> -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "BLI_listbase.h" @@ -29,7 +29,7 @@ namespace blender::compositor { /** * Class with implementation of keying node */ -class KeyingOperation : public NodeOperation { +class KeyingOperation : public MultiThreadedOperation { protected: SocketReader *m_pixelReader; SocketReader *m_screenReader; @@ -48,6 +48,10 @@ class KeyingOperation : public NodeOperation { } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index 17b613246ad..c00aafc19a2 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -39,12 +39,21 @@ KeyingScreenOperation::KeyingScreenOperation() this->m_framenumber = 0; this->m_trackingObject[0] = 0; flags.complex = true; + m_cachedTriangulation = nullptr; } void KeyingScreenOperation::initExecution() { initMutex(); - this->m_cachedTriangulation = nullptr; + if (execution_model_ == eExecutionModel::FullFrame) { + BLI_assert(m_cachedTriangulation == nullptr); + if (m_movieClip) { + m_cachedTriangulation = buildVoronoiTriangulation(); + } + } + else { + this->m_cachedTriangulation = nullptr; + } } void KeyingScreenOperation::deinitExecution() @@ -226,7 +235,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri return triangulation; } -void *KeyingScreenOperation::initializeTileData(rcti *rect) +KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti *rect) { TileData *tile_data; TriangulationData *triangulation; @@ -234,18 +243,6 @@ void *KeyingScreenOperation::initializeTileData(rcti *rect) int chunk_size = 20; int i; - if (this->m_movieClip == nullptr) { - return nullptr; - } - - if (!this->m_cachedTriangulation) { - lockMutex(); - if (this->m_cachedTriangulation == nullptr) { - this->m_cachedTriangulation = buildVoronoiTriangulation(); - } - unlockMutex(); - } - triangulation = this->m_cachedTriangulation; if (!triangulation) { @@ -278,6 +275,23 @@ void *KeyingScreenOperation::initializeTileData(rcti *rect) return tile_data; } +void *KeyingScreenOperation::initializeTileData(rcti *rect) +{ + if (this->m_movieClip == nullptr) { + return nullptr; + } + + if (!this->m_cachedTriangulation) { + lockMutex(); + if (this->m_cachedTriangulation == nullptr) { + this->m_cachedTriangulation = buildVoronoiTriangulation(); + } + unlockMutex(); + } + + return triangulate(rect); +} + void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data) { TileData *tile_data = (TileData *)data; @@ -347,4 +361,57 @@ void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *da } } +void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + if (m_movieClip == nullptr) { + output->fill(area, COM_COLOR_BLACK); + return; + } + + TileData *tri_area = this->triangulate(&area); + BLI_assert(tri_area != nullptr); + + const int *triangles = tri_area->triangles; + const int num_triangles = tri_area->triangles_total; + const TriangulationData *triangulation = m_cachedTriangulation; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(it.out, COM_COLOR_BLACK); + + const float co[2] = {(float)it.x, (float)it.y}; + for (int i = 0; i < num_triangles; i++) { + const int triangle_idx = triangles[i]; + const rcti *rect = &triangulation->triangles_AABB[triangle_idx]; + + if (!BLI_rcti_isect_pt(rect, it.x, it.y)) { + continue; + } + + const int *triangle = triangulation->triangles[triangle_idx]; + const VoronoiTriangulationPoint &a = triangulation->triangulated_points[triangle[0]]; + const VoronoiTriangulationPoint &b = triangulation->triangulated_points[triangle[1]]; + const VoronoiTriangulationPoint &c = triangulation->triangulated_points[triangle[2]]; + + float w[3]; + if (!barycentric_coords_v2(a.co, b.co, c.co, co, w)) { + continue; + } + + if (barycentric_inside_triangle_v2(w)) { + it.out[0] = a.color[0] * w[0] + b.color[0] * w[1] + c.color[0] * w[2]; + it.out[1] = a.color[1] * w[0] + b.color[1] * w[1] + c.color[1] * w[2]; + it.out[2] = a.color[2] * w[0] + b.color[2] * w[1] + c.color[2] * w[2]; + break; + } + } + } + + if (tri_area->triangles) { + MEM_freeN(tri_area->triangles); + } + + MEM_freeN(tri_area); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h index 4118d229be9..0bc47dbea30 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h @@ -20,7 +20,7 @@ #include <string.h> -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_movieclip_types.h" @@ -34,7 +34,7 @@ namespace blender::compositor { /** * Class with implementation of green screen gradient rasterization */ -class KeyingScreenOperation : public NodeOperation { +class KeyingScreenOperation : public MultiThreadedOperation { protected: typedef struct TriangulationData { VoronoiTriangulationPoint *triangulated_points; @@ -43,6 +43,7 @@ class KeyingScreenOperation : public NodeOperation { rcti *triangles_AABB; } TriangulationData; + /* TODO(manzanilla): rename to #TrianguledArea on removing tiled implementation. */ typedef struct TileData { int *triangles; int triangles_total; @@ -84,6 +85,13 @@ class KeyingScreenOperation : public NodeOperation { } void executePixel(float output[4], int x, int y, void *data) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + private: + TileData *triangulate(const rcti *rect); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc index 5ca16e40ce3..c642c60b912 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc @@ -29,6 +29,7 @@ LuminanceMatteOperation::LuminanceMatteOperation() addOutputSocket(DataType::Value); this->m_inputImageProgram = nullptr; + flags.can_be_constant = true; } void LuminanceMatteOperation::initExecution() @@ -78,4 +79,39 @@ void LuminanceMatteOperation::executePixelSampled(float output[4], output[0] = min_ff(alpha, inColor[3]); } +void LuminanceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + const float luminance = IMB_colormanagement_get_luminance(color); + + /* One line thread-friend algorithm: + * `it.out[0] = MIN2(color[3], MIN2(1.0f, MAX2(0.0f, ((luminance - low) / (high - low))));` + */ + + /* Test range. */ + const float high = m_settings->t1; + const float low = m_settings->t2; + float alpha; + if (luminance > high) { + alpha = 1.0f; + } + else if (luminance < low) { + alpha = 0.0f; + } + else { /* Blend. */ + alpha = (luminance - low) / (high - low); + } + + /* Store matte(alpha) value in [0] to go with + * COM_SetAlphaMultiplyOperation and the Value output. + */ + + /* Don't make something that was more transparent less transparent. */ + it.out[0] = MIN2(alpha, color[3]); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h index 035c68b9d59..aedfc715382 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class LuminanceMatteOperation : public NodeOperation { +class LuminanceMatteOperation : public MultiThreadedOperation { private: NodeChroma *m_settings; SocketReader *m_inputImageProgram; @@ -49,6 +49,10 @@ class LuminanceMatteOperation : public NodeOperation { { this->m_settings = nodeChroma; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index 74e3d965d41..ad047c619f8 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -34,10 +34,26 @@ MapUVOperation::MapUVOperation() this->m_inputColorProgram = nullptr; } +void MapUVOperation::init_data() +{ + NodeOperation *image_input = get_input_operation(0); + image_width_ = image_input->getWidth(); + image_height_ = image_input->getHeight(); + + NodeOperation *uv_input = get_input_operation(1); + uv_width_ = uv_input->getWidth(); + uv_height_ = uv_input->getHeight(); +} + void MapUVOperation::initExecution() { this->m_inputColorProgram = this->getInputSocketReader(0); this->m_inputUVProgram = this->getInputSocketReader(1); + if (execution_model_ == eExecutionModel::Tiled) { + uv_input_read_fn_ = [=](float x, float y, float *out) { + this->m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear); + }; + } } void MapUVOperation::executePixelSampled(float output[4], @@ -81,9 +97,7 @@ void MapUVOperation::executePixelSampled(float output[4], bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha) { - float width = m_inputUVProgram->getWidth(); - float height = m_inputUVProgram->getHeight(); - if (x < 0.0f || x >= width || y < 0.0f || y >= height) { + if (x < 0.0f || x >= uv_width_ || y < 0.0f || y >= uv_height_) { r_u = 0.0f; r_v = 0.0f; r_alpha = 0.0f; @@ -91,9 +105,9 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_ } float vector[3]; - m_inputUVProgram->readSampled(vector, x, y, PixelSampler::Bilinear); - r_u = vector[0] * m_inputColorProgram->getWidth(); - r_v = vector[1] * m_inputColorProgram->getHeight(); + uv_input_read_fn_(x, y, vector); + r_u = vector[0] * image_width_; + r_v = vector[1] * image_height_; r_alpha = vector[2]; return true; } @@ -186,4 +200,75 @@ bool MapUVOperation::determineDependingAreaOfInterest(rcti *input, return false; } +void MapUVOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case 0: { + r_input_area.xmin = 0; + r_input_area.xmax = image_width_; + r_input_area.ymin = 0; + r_input_area.ymax = image_height_; + break; + } + case 1: { + r_input_area = output_area; + expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); + break; + } + } +} + +void MapUVOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *uv_input = inputs[1]; + uv_input_read_fn_ = [=](float x, float y, float *out) { + uv_input->read_elem_bilinear(x, y, out); + }; +} + +void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float xy[2] = {(float)it.x, (float)it.y}; + float uv[2]; + float deriv[2][2]; + float alpha; + pixelTransform(xy, uv, deriv, alpha); + if (alpha == 0.0f) { + zero_v4(it.out); + continue; + } + + /* EWA filtering. */ + input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); + + /* UV to alpha threshold. */ + const float threshold = this->m_alpha * 0.05f; + /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. + * this calculation is not very well defined, should be looked into if it becomes a problem ... + */ + const float du = len_v2(deriv[0]); + const float dv = len_v2(deriv[1]); + const float factor = 1.0f - threshold * (du / image_width_ + dv / image_height_); + if (factor < 0.0f) { + alpha = 0.0f; + } + else { + alpha *= factor; + } + + /* "premul" */ + if (alpha < 1.0f) { + mul_v4_fl(it.out, alpha); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h index eb5f7d49122..65fbcb461c9 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.h +++ b/source/blender/compositor/operations/COM_MapUVOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class MapUVOperation : public NodeOperation { +class MapUVOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -30,8 +30,15 @@ class MapUVOperation : public NodeOperation { SocketReader *m_inputUVProgram; SocketReader *m_inputColorProgram; + int uv_width_; + int uv_height_; + int image_width_; + int image_height_; + float m_alpha; + std::function<void(float x, float y, float *out)> uv_input_read_fn_; + public: MapUVOperation(); @@ -49,6 +56,8 @@ class MapUVOperation : public NodeOperation { void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha); + void init_data() override; + /** * Initialize the execution */ @@ -64,6 +73,14 @@ class MapUVOperation : public NodeOperation { this->m_alpha = alpha; } + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: bool read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha); }; diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index a94c14347fb..2256dce011b 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -24,6 +24,8 @@ namespace blender::compositor { MathBaseOperation::MathBaseOperation() { + /* TODO(manzanilla): after removing tiled implementation, template this class to only add needed + * number of inputs. */ this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); @@ -32,6 +34,7 @@ MathBaseOperation::MathBaseOperation() this->m_inputValue2Operation = nullptr; this->m_inputValue3Operation = nullptr; this->m_useClamp = false; + this->flags.can_be_constant = true; } void MathBaseOperation::initExecution() @@ -73,6 +76,14 @@ void MathBaseOperation::clampIfNeeded(float *color) } } +void MathBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + BuffersIterator<float> it = output->iterate_with(inputs, area); + update_memory_buffer_partial(it); +} + void MathAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float inputValue1[4]; @@ -139,6 +150,14 @@ void MathDivideOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathDivideOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float divisor = *it.in(1); + *it.out = clamp_when_enabled((divisor == 0) ? 0 : *it.in(0) / divisor); + } +} + void MathSineOperation::executePixelSampled(float output[4], float x, float y, @@ -155,6 +174,14 @@ void MathSineOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathSineOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = sin(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathCosineOperation::executePixelSampled(float output[4], float x, float y, @@ -171,6 +198,14 @@ void MathCosineOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathCosineOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = cos(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathTangentOperation::executePixelSampled(float output[4], float x, float y, @@ -187,6 +222,14 @@ void MathTangentOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathTangentOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = tan(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathHyperbolicSineOperation::executePixelSampled(float output[4], float x, float y, @@ -203,6 +246,14 @@ void MathHyperbolicSineOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathHyperbolicSineOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = sinh(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathHyperbolicCosineOperation::executePixelSampled(float output[4], float x, float y, @@ -219,6 +270,14 @@ void MathHyperbolicCosineOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathHyperbolicCosineOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = cosh(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathHyperbolicTangentOperation::executePixelSampled(float output[4], float x, float y, @@ -235,6 +294,14 @@ void MathHyperbolicTangentOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathHyperbolicTangentOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = tanh(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathArcSineOperation::executePixelSampled(float output[4], float x, float y, @@ -256,6 +323,14 @@ void MathArcSineOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathArcSineOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + float value1 = *it.in(0); + *it.out = clamp_when_enabled((value1 <= 1 && value1 >= -1) ? asin(value1) : 0.0f); + } +} + void MathArcCosineOperation::executePixelSampled(float output[4], float x, float y, @@ -277,6 +352,14 @@ void MathArcCosineOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathArcCosineOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + float value1 = *it.in(0); + *it.out = clamp_when_enabled((value1 <= 1 && value1 >= -1) ? acos(value1) : 0.0f); + } +} + void MathArcTangentOperation::executePixelSampled(float output[4], float x, float y, @@ -293,6 +376,14 @@ void MathArcTangentOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathArcTangentOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = atan(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathPowerOperation::executePixelSampled(float output[4], float x, float y, @@ -321,6 +412,29 @@ void MathPowerOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathPowerOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value1 = *it.in(0); + const float value2 = *it.in(1); + if (value1 >= 0) { + *it.out = pow(value1, value2); + } + else { + const float y_mod_1 = fmod(value2, 1); + /* If input value is not nearly an integer, fall back to zero, nicer than straight rounding. + */ + if (y_mod_1 > 0.999f || y_mod_1 < 0.001f) { + *it.out = pow(value1, floorf(value2 + 0.5f)); + } + else { + *it.out = 0.0f; + } + } + clamp_when_enabled(it.out); + } +} + void MathLogarithmOperation::executePixelSampled(float output[4], float x, float y, @@ -342,6 +456,21 @@ void MathLogarithmOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathLogarithmOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value1 = *it.in(0); + const float value2 = *it.in(1); + if (value1 > 0 && value2 > 0) { + *it.out = log(value1) / log(value2); + } + else { + *it.out = 0.0; + } + clamp_when_enabled(it.out); + } +} + void MathMinimumOperation::executePixelSampled(float output[4], float x, float y, @@ -358,6 +487,14 @@ void MathMinimumOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathMinimumOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = MIN2(*it.in(0), *it.in(1)); + clamp_when_enabled(it.out); + } +} + void MathMaximumOperation::executePixelSampled(float output[4], float x, float y, @@ -374,6 +511,14 @@ void MathMaximumOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathMaximumOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = MAX2(*it.in(0), *it.in(1)); + clamp_when_enabled(it.out); + } +} + void MathRoundOperation::executePixelSampled(float output[4], float x, float y, @@ -390,6 +535,14 @@ void MathRoundOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathRoundOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = round(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathLessThanOperation::executePixelSampled(float output[4], float x, float y, @@ -443,6 +596,15 @@ void MathModuloOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathModuloOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value2 = *it.in(1); + *it.out = (value2 == 0) ? 0 : fmod(*it.in(0), value2); + clamp_when_enabled(it.out); + } +} + void MathAbsoluteOperation::executePixelSampled(float output[4], float x, float y, @@ -457,6 +619,14 @@ void MathAbsoluteOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathAbsoluteOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = fabs(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathRadiansOperation::executePixelSampled(float output[4], float x, float y, @@ -471,6 +641,14 @@ void MathRadiansOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathRadiansOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = DEG2RADF(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathDegreesOperation::executePixelSampled(float output[4], float x, float y, @@ -485,6 +663,14 @@ void MathDegreesOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathDegreesOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = RAD2DEGF(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathArcTan2Operation::executePixelSampled(float output[4], float x, float y, @@ -501,6 +687,14 @@ void MathArcTan2Operation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathArcTan2Operation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = atan2(*it.in(0), *it.in(1)); + clamp_when_enabled(it.out); + } +} + void MathFloorOperation::executePixelSampled(float output[4], float x, float y, @@ -515,6 +709,14 @@ void MathFloorOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathFloorOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = floor(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathCeilOperation::executePixelSampled(float output[4], float x, float y, @@ -529,6 +731,14 @@ void MathCeilOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathCeilOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = ceil(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathFractOperation::executePixelSampled(float output[4], float x, float y, @@ -543,6 +753,14 @@ void MathFractOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathFractOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value = *it.in(0); + *it.out = clamp_when_enabled(value - floor(value)); + } +} + void MathSqrtOperation::executePixelSampled(float output[4], float x, float y, @@ -562,6 +780,14 @@ void MathSqrtOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathSqrtOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value = *it.in(0); + *it.out = clamp_when_enabled(value > 0 ? sqrt(value) : 0.0f); + } +} + void MathInverseSqrtOperation::executePixelSampled(float output[4], float x, float y, @@ -581,6 +807,14 @@ void MathInverseSqrtOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathInverseSqrtOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value = *it.in(0); + *it.out = clamp_when_enabled(value > 0 ? 1.0f / sqrt(value) : 0.0f); + } +} + void MathSignOperation::executePixelSampled(float output[4], float x, float y, @@ -595,6 +829,14 @@ void MathSignOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathSignOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = compatible_signf(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathExponentOperation::executePixelSampled(float output[4], float x, float y, @@ -609,6 +851,14 @@ void MathExponentOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathExponentOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = expf(*it.in(0)); + clamp_when_enabled(it.out); + } +} + void MathTruncOperation::executePixelSampled(float output[4], float x, float y, @@ -623,6 +873,15 @@ void MathTruncOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathTruncOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value = *it.in(0); + *it.out = (value >= 0.0f) ? floor(value) : ceil(value); + clamp_when_enabled(it.out); + } +} + void MathSnapOperation::executePixelSampled(float output[4], float x, float y, @@ -644,6 +903,21 @@ void MathSnapOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathSnapOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float value1 = *it.in(0); + const float value2 = *it.in(1); + if (value1 == 0 || value2 == 0) { /* Avoid dividing by zero. */ + *it.out = 0.0f; + } + else { + *it.out = floorf(value1 / value2) * value2; + } + clamp_when_enabled(it.out); + } +} + void MathWrapOperation::executePixelSampled(float output[4], float x, float y, @@ -662,6 +936,14 @@ void MathWrapOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathWrapOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = wrapf(*it.in(0), *it.in(1), *it.in(2)); + clamp_when_enabled(it.out); + } +} + void MathPingpongOperation::executePixelSampled(float output[4], float x, float y, @@ -678,6 +960,14 @@ void MathPingpongOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathPingpongOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = pingpongf(*it.in(0), *it.in(1)); + clamp_when_enabled(it.out); + } +} + void MathCompareOperation::executePixelSampled(float output[4], float x, float y, @@ -697,6 +987,14 @@ void MathCompareOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathCompareOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = (fabsf(*it.in(0) - *it.in(1)) <= MAX2(*it.in(2), 1e-5f)) ? 1.0f : 0.0f; + clamp_when_enabled(it.out); + } +} + void MathMultiplyAddOperation::executePixelSampled(float output[4], float x, float y, @@ -715,6 +1013,14 @@ void MathMultiplyAddOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathMultiplyAddOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = it.in(0)[0] * it.in(1)[0] + it.in(2)[0]; + clamp_when_enabled(it.out); + } +} + void MathSmoothMinOperation::executePixelSampled(float output[4], float x, float y, @@ -733,6 +1039,14 @@ void MathSmoothMinOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathSmoothMinOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = smoothminf(*it.in(0), *it.in(1), *it.in(2)); + clamp_when_enabled(it.out); + } +} + void MathSmoothMaxOperation::executePixelSampled(float output[4], float x, float y, @@ -751,4 +1065,12 @@ void MathSmoothMaxOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MathSmoothMaxOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + *it.out = -smoothminf(-it.in(0)[0], -it.in(1)[0], it.in(2)[0]); + clamp_when_enabled(it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h index 08794c8db22..d2da05db68e 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.h +++ b/source/blender/compositor/operations/COM_MathBaseOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class MathBaseOperation : public NodeOperation { +class MathBaseOperation : public MultiThreadedOperation { protected: /** * Prefetched reference to the inputProgram @@ -43,8 +43,24 @@ class MathBaseOperation : public NodeOperation { */ MathBaseOperation(); + /* TODO(manzanilla): to be removed with tiled implementation. */ void clampIfNeeded(float color[4]); + float clamp_when_enabled(float value) + { + if (this->m_useClamp) { + return CLAMPIS(value, 0.0f, 1.0f); + } + return value; + } + + void clamp_when_enabled(float *out) + { + if (this->m_useClamp) { + CLAMP(*out, 0.0f, 1.0f); + } + } + public: /** * Initialize the execution @@ -66,87 +82,151 @@ class MathBaseOperation : public NodeOperation { { this->m_useClamp = value; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) final; + + protected: + virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0; }; -class MathAddOperation : public MathBaseOperation { +template<template<typename> typename TFunctor> +class MathFunctor2Operation : public MathBaseOperation { + void update_memory_buffer_partial(BuffersIterator<float> &it) final + { + TFunctor functor; + for (; !it.is_end(); ++it) { + *it.out = functor(*it.in(0), *it.in(1)); + clamp_when_enabled(it.out); + } + } +}; + +class MathAddOperation : public MathFunctor2Operation<std::plus> { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; -class MathSubtractOperation : public MathBaseOperation { +class MathSubtractOperation : public MathFunctor2Operation<std::minus> { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; -class MathMultiplyOperation : public MathBaseOperation { +class MathMultiplyOperation : public MathFunctor2Operation<std::multiplies> { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathDivideOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathSineOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathCosineOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathTangentOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathHyperbolicSineOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathHyperbolicCosineOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathHyperbolicTangentOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathArcSineOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathArcCosineOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathArcTangentOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathPowerOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathLogarithmOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathMinimumOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathMaximumOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathRoundOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; -class MathLessThanOperation : public MathBaseOperation { +class MathLessThanOperation : public MathFunctor2Operation<std::less> { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; -class MathGreaterThanOperation : public MathBaseOperation { +class MathGreaterThanOperation : public MathFunctor2Operation<std::greater> { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; @@ -154,101 +234,161 @@ class MathGreaterThanOperation : public MathBaseOperation { class MathModuloOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathAbsoluteOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathRadiansOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathDegreesOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathArcTan2Operation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathFloorOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathCeilOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathFractOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathSqrtOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathInverseSqrtOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathSignOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathExponentOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathTruncOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathSnapOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathWrapOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathPingpongOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathCompareOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathMultiplyAddOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathSmoothMinOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; class MathSmoothMaxOperation : public MathBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index c8e045ea117..d3424959061 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -128,4 +128,51 @@ bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void MovieDistortionOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_margin[0]; + r_input_area.ymin = output_area.ymin - m_margin[1]; + r_input_area.xmax = output_area.xmax + m_margin[0]; + r_input_area.ymax = output_area.ymax + m_margin[1]; +} + +void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + if (this->m_distortion == nullptr) { + output->copy_from(input_img, area); + return; + } + + /* `float overscan = 0.0f;` */ + const float pixel_aspect = this->m_pixel_aspect; + const float w = (float)this->m_width /* `/ (1 + overscan)` */; + const float h = (float)this->m_height /* `/ (1 + overscan)` */; + const float aspx = w / (float)this->m_calibration_width; + const float aspy = h / (float)this->m_calibration_height; + float xy[2]; + float distorted_xy[2]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx; + xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect; + + if (this->m_apply) { + BKE_tracking_distortion_undistort_v2(this->m_distortion, xy, distorted_xy); + } + else { + BKE_tracking_distortion_distort_v2(this->m_distortion, xy, distorted_xy); + } + + const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */; + const float v = (distorted_xy[1] * aspy /* `+ 0.5 * overscan * h` */) * pixel_aspect; + input_img->read_elem_bilinear(u, v, it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h index 631a62f7ebf..69c2f9c269c 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_movieclip_types.h" #include "MEM_guardedalloc.h" @@ -26,7 +26,7 @@ namespace blender::compositor { -class MovieDistortionOperation : public NodeOperation { +class MovieDistortionOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; MovieClip *m_movieClip; @@ -58,6 +58,11 @@ class MovieDistortionOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index 3577860b93d..d2a06ddd7c4 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -16,6 +16,7 @@ */ #include "COM_PlaneCornerPinOperation.h" +#include "COM_ConstantOperation.h" #include "COM_ReadBufferOperation.h" #include "MEM_guardedalloc.h" @@ -28,6 +29,11 @@ namespace blender::compositor { +constexpr int LOWER_LEFT_CORNER_INDEX = 0; +constexpr int LOWER_RIGHT_CORNER_INDEX = 1; +constexpr int UPPER_RIGHT_CORNER_INDEX = 2; +constexpr int UPPER_LEFT_CORNER_INDEX = 3; + static bool check_corners(float corners[4][2]) { int i, next, prev; @@ -58,6 +64,7 @@ static bool check_corners(float corners[4][2]) return true; } +/* TODO(manzanilla): to be removed with tiled implementation. */ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float corners[4][2]) { for (int i = 0; i < 4; i++) { @@ -87,6 +94,53 @@ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float c } } +static void set_default_corner(const int corner_idx, float r_corner[2]) +{ + BLI_assert(corner_idx >= 0 && corner_idx < 4); + switch (corner_idx) { + case LOWER_LEFT_CORNER_INDEX: + r_corner[0] = 0.0f; + r_corner[1] = 0.0f; + break; + case LOWER_RIGHT_CORNER_INDEX: + r_corner[0] = 1.0f; + r_corner[1] = 0.0f; + break; + case UPPER_RIGHT_CORNER_INDEX: + r_corner[0] = 1.0f; + r_corner[1] = 1.0f; + break; + case UPPER_LEFT_CORNER_INDEX: + r_corner[0] = 0.0f; + r_corner[1] = 1.0f; + break; + } +} + +static void read_input_corners(NodeOperation *op, const int first_input_idx, float r_corners[4][2]) +{ + for (const int i : IndexRange(4)) { + NodeOperation *input = op->get_input_operation(i + first_input_idx); + if (input->get_flags().is_constant_operation) { + ConstantOperation *corner_input = static_cast<ConstantOperation *>(input); + copy_v2_v2(r_corners[i], corner_input->get_constant_elem()); + } + else { + set_default_corner(i, r_corners[i]); + } + } + + /* Convexity check: concave corners need to be prevented, otherwise + * #BKE_tracking_homography_between_two_quads will freeze. */ + if (!check_corners(r_corners)) { + /* Revert to default corners. There could be a more elegant solution, + * this prevents freezing at least. */ + for (const int i : IndexRange(4)) { + set_default_corner(i, r_corners[i]); + } + } +} + /* ******** PlaneCornerPinMaskOperation ******** */ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(false) @@ -103,6 +157,17 @@ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(fal flags.complex = true; } +void PlaneCornerPinMaskOperation::init_data() +{ + if (execution_model_ == eExecutionModel::FullFrame) { + float corners[4][2]; + read_input_corners(this, 0, corners); + calculateCorners(corners, true, 0); + } +} + +/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinitExecution and do the + * same on #PlaneCornerPinWarpImageOperation. */ void PlaneCornerPinMaskOperation::initExecution() { PlaneDistortMaskOperation::initExecution(); @@ -147,10 +212,22 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) void PlaneCornerPinMaskOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { + if (execution_model_ == eExecutionModel::FullFrame) { + /* Determine inputs resolution. */ + PlaneDistortMaskOperation::determineResolution(resolution, preferredResolution); + } resolution[0] = preferredResolution[0]; resolution[1] = preferredResolution[1]; } +void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + /* All corner inputs are used as constants. */ + r_input_area = COM_SINGLE_ELEM_AREA; +} + /* ******** PlaneCornerPinWarpImageOperation ******** */ PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners_ready(false) @@ -161,6 +238,15 @@ PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners addInputSocket(DataType::Vector); } +void PlaneCornerPinWarpImageOperation::init_data() +{ + if (execution_model_ == eExecutionModel::FullFrame) { + float corners[4][2]; + read_input_corners(this, 1, corners); + calculateCorners(corners, true, 0); + } +} + void PlaneCornerPinWarpImageOperation::initExecution() { PlaneDistortWarpImageOperation::initExecution(); @@ -227,4 +313,17 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest( #endif } +void PlaneCornerPinWarpImageOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx == 0) { + PlaneDistortWarpImageOperation::get_area_of_interest(input_idx, output_area, r_input_area); + } + else { + /* Corner inputs are used as constants. */ + r_input_area = COM_SINGLE_ELEM_AREA; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h index 91c0cd9e16b..2831e937147 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h @@ -31,11 +31,13 @@ namespace blender::compositor { class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { private: + /* TODO(manzanilla): to be removed with tiled implementation. */ bool m_corners_ready; public: PlaneCornerPinMaskOperation(); + void init_data() override; void initExecution() override; void deinitExecution() override; @@ -43,6 +45,8 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { @@ -52,6 +56,7 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { public: PlaneCornerPinWarpImageOperation(); + void init_data() override; void initExecution() override; void deinitExecution() override; @@ -60,6 +65,8 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 4edcc206f5b..a80cbbe942a 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -85,8 +85,9 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], { PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample); - const int width = this->m_pixelReader->getWidth(); - const int height = this->m_pixelReader->getHeight(); + const NodeOperation *image = get_input_operation(0); + const int width = image->getWidth(); + const int height = image->getHeight(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; MotionSample *sample_data = &this->m_samples[sample]; @@ -127,6 +128,34 @@ void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], } } +void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + float uv[2]; + float deriv[2][2]; + BuffersIterator<float> it = output->iterate_with({}, area); + if (this->m_motion_blur_samples == 1) { + for (; !it.is_end(); ++it) { + warpCoord(it.x, it.y, this->m_samples[0].perspectiveMatrix, uv, deriv); + input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); + } + } + else { + for (; !it.is_end(); ++it) { + zero_v4(it.out); + for (const int sample : IndexRange(this->m_motion_blur_samples)) { + float color[4]; + warpCoord(it.x, it.y, this->m_samples[sample].perspectiveMatrix, uv, deriv); + input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color); + add_v4_v4(it.out, color); + } + mul_v4_fl(it.out, 1.0f / (float)this->m_motion_blur_samples); + } + } +} + bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( rcti *input, ReadBufferOperation *readOperation, rcti *output) { @@ -157,6 +186,51 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx != 0) { + r_input_area = output_area; + return; + } + + /* TODO: figure out the area needed for warping and EWA filtering. */ + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = get_input_operation(0)->getWidth(); + r_input_area.ymax = get_input_operation(0)->getHeight(); + +/* Old implemention but resulting coordinates are way out of input operation bounds and in some + * cases the area result may incorrectly cause cropping. */ +#if 0 + float min[2], max[2]; + INIT_MINMAX2(min, max); + for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + float UVs[4][2]; + float deriv[2][2]; + MotionSample *sample_data = &this->m_samples[sample]; + /* TODO(sergey): figure out proper way to do this. */ + warpCoord( + output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); + warpCoord( + output_area.xmax + 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); + warpCoord( + output_area.xmax + 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv); + warpCoord( + output_area.xmin - 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv); + for (int i = 0; i < 4; i++) { + minmax_v2v2_v2(min, max, UVs[i]); + } + } + + r_input_area.xmin = min[0] - 1; + r_input_area.ymin = min[1] - 1; + r_input_area.xmax = max[0] + 1; + r_input_area.ymax = max[1] + 1; +#endif +} + /* ******** PlaneDistort Mask ******** */ PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation() @@ -219,4 +293,41 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], } } +void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + int inside_count = 0; + for (const int motion_sample : IndexRange(this->m_motion_blur_samples)) { + MotionSample &sample = this->m_samples[motion_sample]; + inside_count += get_jitter_samples_inside_count(it.x, it.y, sample); + } + *it.out = (float)inside_count / (this->m_osa * this->m_motion_blur_samples); + } +} + +int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x, + int y, + MotionSample &sample_data) +{ + float point[2]; + int inside_count = 0; + for (int sample = 0; sample < this->m_osa; sample++) { + point[0] = x + this->m_jitter[sample][0]; + point[1] = y + this->m_jitter[sample][1]; + if (isect_point_tri_v2(point, + sample_data.frameSpaceCorners[0], + sample_data.frameSpaceCorners[1], + sample_data.frameSpaceCorners[2]) || + isect_point_tri_v2(point, + sample_data.frameSpaceCorners[0], + sample_data.frameSpaceCorners[2], + sample_data.frameSpaceCorners[3])) { + inside_count++; + } + } + return inside_count; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index cc6e4d00d71..3ef9c1dfab8 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -20,7 +20,7 @@ #include <string.h> -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_movieclip_types.h" #include "DNA_tracking_types.h" @@ -32,7 +32,7 @@ namespace blender::compositor { #define PLANE_DISTORT_MAX_SAMPLES 64 -class PlaneDistortBaseOperation : public NodeOperation { +class PlaneDistortBaseOperation : public MultiThreadedOperation { protected: struct MotionSample { float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ @@ -78,6 +78,11 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { @@ -91,6 +96,13 @@ class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { void initExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + private: + int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 0884f2ad979..bf24f843ca2 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -101,18 +101,40 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2], /* ******** PlaneTrackMaskOperation ******** */ +void PlaneTrackMaskOperation::init_data() +{ + PlaneDistortMaskOperation::init_data(); + if (execution_model_ == eExecutionModel::FullFrame) { + PlaneTrackCommon::read_and_calculate_corners(this); + } +} + +/* TODO(manzanilla): to be removed with tiled implementation. */ void PlaneTrackMaskOperation::initExecution() { PlaneDistortMaskOperation::initExecution(); - PlaneTrackCommon::read_and_calculate_corners(this); + if (execution_model_ == eExecutionModel::Tiled) { + PlaneTrackCommon::read_and_calculate_corners(this); + } } /* ******** PlaneTrackWarpImageOperation ******** */ +void PlaneTrackWarpImageOperation::init_data() +{ + PlaneDistortWarpImageOperation::init_data(); + if (execution_model_ == eExecutionModel::FullFrame) { + PlaneTrackCommon::read_and_calculate_corners(this); + } +} + +/* TODO(manzanilla): to be removed with tiled implementation. */ void PlaneTrackWarpImageOperation::initExecution() { PlaneDistortWarpImageOperation::initExecution(); - PlaneTrackCommon::read_and_calculate_corners(this); + if (execution_model_ == eExecutionModel::Tiled) { + PlaneTrackCommon::read_and_calculate_corners(this); + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index 3bae230aa06..d2027755162 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -73,6 +73,8 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr { } + void init_data() override; + void initExecution() override; void determineResolution(unsigned int resolution[2], @@ -92,6 +94,8 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation, { } + void init_data() override; + void initExecution() override; void determineResolution(unsigned int resolution[2], diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index 93702d3f0cf..fcab5dd5751 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_ProjectorLensDistortionOperation.h" +#include "COM_ConstantOperation.h" + #include "BLI_math.h" #include "BLI_utildefines.h" @@ -32,6 +34,20 @@ ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() this->m_dispersionAvailable = false; this->m_dispersion = 0.0f; } + +void ProjectorLensDistortionOperation::init_data() +{ + if (execution_model_ == eExecutionModel::FullFrame) { + NodeOperation *dispersion_input = get_input_operation(1); + if (dispersion_input->get_flags().is_constant_operation) { + this->m_dispersion = + static_cast<ConstantOperation *>(dispersion_input)->get_constant_elem()[0]; + } + this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f); + this->m_kr2 = this->m_kr * 20; + } +} + void ProjectorLensDistortionOperation::initExecution() { this->initMutex(); @@ -97,6 +113,7 @@ bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( return false; } +/* TODO(manzanilla): to be removed with tiled implementation. */ void ProjectorLensDistortionOperation::updateDispersion() { if (this->m_dispersionAvailable) { @@ -114,4 +131,41 @@ void ProjectorLensDistortionOperation::updateDispersion() this->unlockMutex(); } +void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx == 1) { + /* Dispersion input is used as constant only. */ + r_input_area = COM_SINGLE_ELEM_AREA; + return; + } + + r_input_area.ymax = output_area.ymax; + r_input_area.ymin = output_area.ymin; + r_input_area.xmin = output_area.xmin - this->m_kr2 - 2; + r_input_area.xmax = output_area.xmax + this->m_kr2 + 2; +} + +void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + const float height = this->getHeight(); + const float width = this->getWidth(); + float color[4]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const float v = (it.y + 0.5f) / height; + const float u = (it.x + 0.5f) / width; + input_image->read_elem_bilinear((u * width + this->m_kr2) - 0.5f, v * height - 0.5f, color); + it.out[0] = color[0]; + input_image->read_elem(it.x, it.y, color); + it.out[1] = color[1]; + input_image->read_elem_bilinear((u * width - this->m_kr2) - 0.5f, v * height - 0.5f, color); + it.out[2] = color[2]; + it.out[3] = 1.0f; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h index bce61d3de15..7c7626bf271 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { -class ProjectorLensDistortionOperation : public NodeOperation { +class ProjectorLensDistortionOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -31,6 +31,7 @@ class ProjectorLensDistortionOperation : public NodeOperation { SocketReader *m_inputProgram; float m_dispersion; + /* TODO(manzanilla): to be removed with tiled implementation. */ bool m_dispersionAvailable; float m_kr, m_kr2; @@ -43,6 +44,7 @@ class ProjectorLensDistortionOperation : public NodeOperation { */ void executePixel(float output[4], int x, int y, void *data) override; + void init_data() override; /** * Initialize the execution */ @@ -59,6 +61,11 @@ class ProjectorLensDistortionOperation : public NodeOperation { rcti *output) override; void updateDispersion(); + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index 4fb3d324992..e3c482c15cb 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_RotateOperation.h" +#include "COM_ConstantOperation.h" + #include "BLI_math.h" namespace blender::compositor { @@ -31,13 +33,50 @@ RotateOperation::RotateOperation() this->m_degreeSocket = nullptr; this->m_doDegree2RadConversion = false; this->m_isDegreeSet = false; + sampler_ = PixelSampler::Bilinear; +} + +void RotateOperation::get_area_rotation_bounds(const rcti &area, + const float center_x, + const float center_y, + const float sine, + const float cosine, + rcti &r_bounds) +{ + const float dxmin = area.xmin - center_x; + const float dymin = area.ymin - center_y; + const float dxmax = area.xmax - center_x; + const float dymax = area.ymax - center_y; + + const float x1 = center_x + (cosine * dxmin + sine * dymin); + const float x2 = center_x + (cosine * dxmax + sine * dymin); + const float x3 = center_x + (cosine * dxmin + sine * dymax); + const float x4 = center_x + (cosine * dxmax + sine * dymax); + const float y1 = center_y + (-sine * dxmin + cosine * dymin); + const float y2 = center_y + (-sine * dxmax + cosine * dymin); + const float y3 = center_y + (-sine * dxmin + cosine * dymax); + const float y4 = center_y + (-sine * dxmax + cosine * dymax); + const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); + const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); + const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); + const float maxy = MAX2(y1, MAX2(y2, MAX2(y3, y4))); + + r_bounds.xmin = floor(minx); + r_bounds.xmax = ceil(maxx); + r_bounds.ymin = floor(miny); + r_bounds.ymax = ceil(maxy); +} + +void RotateOperation::init_data() +{ + this->m_centerX = (getWidth() - 1) / 2.0; + this->m_centerY = (getHeight() - 1) / 2.0; } + void RotateOperation::initExecution() { this->m_imageSocket = this->getInputSocketReader(0); this->m_degreeSocket = this->getInputSocketReader(1); - this->m_centerX = (getWidth() - 1) / 2.0; - this->m_centerY = (getHeight() - 1) / 2.0; } void RotateOperation::deinitExecution() @@ -50,7 +89,19 @@ inline void RotateOperation::ensureDegree() { if (!this->m_isDegreeSet) { float degree[4]; - this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); + switch (execution_model_) { + case eExecutionModel::Tiled: + this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); + break; + case eExecutionModel::FullFrame: + NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX); + const bool is_constant_degree = degree_op->get_flags().is_constant_operation; + degree[0] = is_constant_degree ? + static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] : + 0.0f; + break; + } + double rad; if (this->m_doDegree2RadConversion) { rad = DEG2RAD((double)degree[0]); @@ -108,4 +159,33 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void RotateOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx == DEGREE_INPUT_INDEX) { + /* Degrees input is always used as constant. */ + r_input_area = COM_SINGLE_ELEM_AREA; + return; + } + + ensureDegree(); + get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area); + expand_area_for_sampler(r_input_area, sampler_); +} + +void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + ensureDegree(); + const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float x = it.x; + float y = it.y; + rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine); + input_img->read_elem_sampled(x, y, sampler_, it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h index d76507f9816..f0de699f9ce 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.h +++ b/source/blender/compositor/operations/COM_RotateOperation.h @@ -18,12 +18,15 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class RotateOperation : public NodeOperation { +class RotateOperation : public MultiThreadedOperation { private: + constexpr static int IMAGE_INPUT_INDEX = 0; + constexpr static int DEGREE_INPUT_INDEX = 1; + SocketReader *m_imageSocket; SocketReader *m_degreeSocket; float m_centerX; @@ -32,21 +35,51 @@ class RotateOperation : public NodeOperation { float m_sine; bool m_doDegree2RadConversion; bool m_isDegreeSet; + PixelSampler sampler_; public: RotateOperation(); + + static void rotate_coords( + float &x, float &y, float center_x, float center_y, float sine, float cosine) + { + const float dx = x - center_x; + const float dy = y - center_y; + x = center_x + (cosine * dx + sine * dy); + y = center_y + (-sine * dx + cosine * dy); + } + + static void get_area_rotation_bounds(const rcti &area, + const float center_x, + const float center_y, + const float sine, + const float cosine, + rcti &r_bounds); + bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_data() override; void initExecution() override; void deinitExecution() override; + void setDoDegree2RadConversion(bool abool) { this->m_doDegree2RadConversion = abool; } + void set_sampler(PixelSampler sampler) + { + sampler_ = sampler; + } + void ensureDegree(); + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 5410b2c832a..ef34bc7bee6 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -74,17 +74,18 @@ float ScaleOperation::get_constant_scale_y() return get_constant_scale(2, get_relative_scale_y_factor()); } -BLI_INLINE float scale_coord(const int coord, const float center, const float relative_scale) +void ScaleOperation::scale_area( + rcti &rect, float center_x, float center_y, float scale_x, float scale_y) { - return center + (coord - center) / relative_scale; + rect.xmin = scale_coord(rect.xmin, center_x, scale_x); + rect.xmax = scale_coord(rect.xmax, center_x, scale_x); + rect.ymin = scale_coord(rect.ymin, center_y, scale_y); + rect.ymax = scale_coord(rect.ymax, center_y, scale_y); } void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y) { - rect.xmin = scale_coord(rect.xmin, m_centerX, scale_x); - rect.xmax = scale_coord(rect.xmax, m_centerX, scale_x); - rect.ymin = scale_coord(rect.ymin, m_centerY, scale_y); - rect.ymax = scale_coord(rect.ymax, m_centerY, scale_y); + scale_area(rect, m_centerX, m_centerY, scale_x, scale_y); } void ScaleOperation::init_data() diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 62a2cabc8e6..65762d1ce62 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -46,6 +46,9 @@ class BaseScaleOperation : public MultiThreadedOperation { }; class ScaleOperation : public BaseScaleOperation { + public: + static constexpr float MIN_SCALE = 0.0001f; + protected: SocketReader *m_inputOperation; SocketReader *m_inputXOperation; @@ -57,6 +60,12 @@ class ScaleOperation : public BaseScaleOperation { ScaleOperation(); ScaleOperation(DataType data_type); + static float scale_coord(const float coord, const float center, const float relative_scale) + { + return center + (coord - center) / MAX2(relative_scale, MIN_SCALE); + } + static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y); + void init_data() override; void initExecution() override; void deinitExecution() override; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 634fe66b0dd..f9ba2ef69ad 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -17,6 +17,7 @@ */ #include "COM_ScreenLensDistortionOperation.h" +#include "COM_ConstantOperation.h" #include "BLI_math.h" #include "BLI_rand.h" @@ -53,6 +54,35 @@ void ScreenLensDistortionOperation::setDispersion(float dispersion) m_dispersion_const = true; } +void ScreenLensDistortionOperation::init_data() +{ + this->m_cx = 0.5f * (float)getWidth(); + this->m_cy = 0.5f * (float)getHeight(); + + switch (execution_model_) { + case eExecutionModel::FullFrame: { + NodeOperation *distortion_op = get_input_operation(1); + NodeOperation *dispersion_op = get_input_operation(2); + if (!m_distortion_const && distortion_op->get_flags().is_constant_operation) { + m_distortion = static_cast<ConstantOperation *>(distortion_op)->get_constant_elem()[0]; + } + if (!m_dispersion_const && distortion_op->get_flags().is_constant_operation) { + m_dispersion = static_cast<ConstantOperation *>(dispersion_op)->get_constant_elem()[0]; + } + updateVariables(m_distortion, m_dispersion); + break; + } + case eExecutionModel::Tiled: { + /* If both are constant, init variables once. */ + if (m_distortion_const && m_dispersion_const) { + updateVariables(m_distortion, m_dispersion); + m_variables_ready = true; + } + break; + } + } +} + void ScreenLensDistortionOperation::initExecution() { this->m_inputProgram = this->getInputSocketReader(0); @@ -61,15 +91,6 @@ void ScreenLensDistortionOperation::initExecution() uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); rng_seed ^= (uint)POINTER_AS_INT(m_inputProgram); this->m_rng = BLI_rng_new(rng_seed); - - this->m_cx = 0.5f * (float)getWidth(); - this->m_cy = 0.5f * (float)getHeight(); - - /* if both are constant, init variables once */ - if (m_distortion_const && m_dispersion_const) { - updateVariables(m_distortion, m_dispersion); - m_variables_ready = true; - } } void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) @@ -130,7 +151,7 @@ bool ScreenLensDistortionOperation::get_delta(float r_sq, return false; } -void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer, +void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer, int a, int b, float r_sq, @@ -154,7 +175,14 @@ void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer, float xy[2]; distort_uv(uv, t, xy); - buffer->readBilinear(color, xy[0], xy[1]); + switch (execution_model_) { + case eExecutionModel::Tiled: + buffer->readBilinear(color, xy[0], xy[1]); + break; + case eExecutionModel::FullFrame: + buffer->read_elem_bilinear(xy[0], xy[1], color); + break; + } sum[a] += (1.0f - tz) * color[a]; sum[b] += (tz)*color[b]; @@ -354,4 +382,143 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp mul_v3_v3fl(m_k4, m_k, 4.0f); } +void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + if (input_idx != 0) { + /* Dispersion and distorsion inputs are used as constants only. */ + r_input_area = COM_SINGLE_ELEM_AREA; + } + + /* XXX the original method of estimating the area-of-interest does not work + * it assumes a linear increase/decrease of mapped coordinates, which does not + * yield correct results for the area and leaves uninitialized buffer areas. + * So now just use the full image area, which may not be as efficient but works at least ... + */ +#if 1 + NodeOperation *image = getInputOperation(0); + r_input_area.xmax = image->getWidth(); + r_input_area.xmin = 0; + r_input_area.ymax = image->getHeight(); + r_input_area.ymin = 0; + +#else /* Original method in tiled implementation. */ + rcti newInput; + const float margin = 2; + + BLI_rcti_init_minmax(&newInput); + + if (m_dispersion_const && m_distortion_const) { + /* update from fixed distortion/dispersion */ +# define UPDATE_INPUT(x, y) \ + { \ + float coords[6]; \ + determineUV(coords, x, y); \ + newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ + newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ + newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ + newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + } \ + (void)0 + + UPDATE_INPUT(input->xmin, input->xmax); + UPDATE_INPUT(input->xmin, input->ymax); + UPDATE_INPUT(input->xmax, input->ymax); + UPDATE_INPUT(input->xmax, input->ymin); + +# undef UPDATE_INPUT + } + else { + /* use maximum dispersion 1.0 if not const */ + float dispersion = m_dispersion_const ? m_dispersion : 1.0f; + +# define UPDATE_INPUT(x, y, distortion) \ + { \ + float coords[6]; \ + updateVariables(distortion, dispersion); \ + determineUV(coords, x, y); \ + newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ + newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ + newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ + newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + } \ + (void)0 + + if (m_distortion_const) { + /* update from fixed distortion */ + UPDATE_INPUT(input->xmin, input->xmax, m_distortion); + UPDATE_INPUT(input->xmin, input->ymax, m_distortion); + UPDATE_INPUT(input->xmax, input->ymax, m_distortion); + UPDATE_INPUT(input->xmax, input->ymin, m_distortion); + } + else { + /* update from min/max distortion (-1..1) */ + UPDATE_INPUT(input->xmin, input->xmax, -1.0f); + UPDATE_INPUT(input->xmin, input->ymax, -1.0f); + UPDATE_INPUT(input->xmax, input->ymax, -1.0f); + UPDATE_INPUT(input->xmax, input->ymin, -1.0f); + + UPDATE_INPUT(input->xmin, input->xmax, 1.0f); + UPDATE_INPUT(input->xmin, input->ymax, 1.0f); + UPDATE_INPUT(input->xmax, input->ymax, 1.0f); + UPDATE_INPUT(input->xmax, input->ymin, 1.0f); + +# undef UPDATE_INPUT + } + } + + newInput.xmin -= margin; + newInput.ymin -= margin; + newInput.xmax += margin; + newInput.ymax += margin; + + operation = getInputOperation(0); + if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + return true; + } + return false; +#endif +} + +void ScreenLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float xy[2] = {(float)it.x, (float)it.y}; + float uv[2]; + get_uv(xy, uv); + const float uv_dot = len_squared_v2(uv); + + float delta[3][2]; + const bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]); + const bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]); + const bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]); + if (!(valid_r && valid_g && valid_b)) { + zero_v4(it.out); + continue; + } + + int count[3] = {0, 0, 0}; + float sum[4] = {0, 0, 0, 0}; + accumulate(input_image, 0, 1, uv_dot, uv, delta, sum, count); + accumulate(input_image, 1, 2, uv_dot, uv, delta, sum, count); + + if (count[0]) { + it.out[0] = 2.0f * sum[0] / (float)count[0]; + } + if (count[1]) { + it.out[1] = 2.0f * sum[1] / (float)count[1]; + } + if (count[2]) { + it.out[2] = 2.0f * sum[2] / (float)count[2]; + } + + /* Set alpha. */ + it.out[3] = 1.0f; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h index 98872bfe142..616fc8883b0 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h @@ -18,14 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" struct RNG; namespace blender::compositor { -class ScreenLensDistortionOperation : public NodeOperation { +class ScreenLensDistortionOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -50,6 +50,8 @@ class ScreenLensDistortionOperation : public NodeOperation { public: ScreenLensDistortionOperation(); + void init_data() override; + /** * The inner loop of this operation. */ @@ -84,6 +86,11 @@ class ScreenLensDistortionOperation : public NodeOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: void determineUV(float result[6], float x, float y) const; void updateVariables(float distortion, float dispersion); @@ -91,7 +98,7 @@ class ScreenLensDistortionOperation : public NodeOperation { void get_uv(const float xy[2], float uv[2]) const; void distort_uv(const float uv[2], float t, float xy[2]) const; bool get_delta(float r_sq, float k4, const float uv[2], float delta[2]) const; - void accumulate(MemoryBuffer *buffer, + void accumulate(const MemoryBuffer *buffer, int a, int b, float r_sq, diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc index 24edbc61d40..e4686ffa76d 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc @@ -28,6 +28,7 @@ SetAlphaMultiplyOperation::SetAlphaMultiplyOperation() this->m_inputColor = nullptr; this->m_inputAlpha = nullptr; + this->flags.can_be_constant = true; } void SetAlphaMultiplyOperation::initExecution() @@ -56,4 +57,15 @@ void SetAlphaMultiplyOperation::deinitExecution() this->m_inputAlpha = nullptr; } +void SetAlphaMultiplyOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + const float alpha = *it.in(1); + mul_v4_v4fl(it.out, color, alpha); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h index b4eea659fa2..44885318901 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -27,7 +27,7 @@ namespace blender::compositor { * * `output color.rgba = input color.rgba * input alpha` */ -class SetAlphaMultiplyOperation : public NodeOperation { +class SetAlphaMultiplyOperation : public MultiThreadedOperation { private: SocketReader *m_inputColor; SocketReader *m_inputAlpha; @@ -39,6 +39,10 @@ class SetAlphaMultiplyOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc index 90bfc814b09..434f5d9b63c 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc @@ -28,6 +28,7 @@ SetAlphaReplaceOperation::SetAlphaReplaceOperation() this->m_inputColor = nullptr; this->m_inputAlpha = nullptr; + this->flags.can_be_constant = true; } void SetAlphaReplaceOperation::initExecution() @@ -54,4 +55,16 @@ void SetAlphaReplaceOperation::deinitExecution() this->m_inputAlpha = nullptr; } +void SetAlphaReplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + const float alpha = *it.in(1); + copy_v3_v3(it.out, color); + it.out[3] = alpha; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h index c84299b6d82..2c2d4cddf5b 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class SetAlphaReplaceOperation : public NodeOperation { +class SetAlphaReplaceOperation : public MultiThreadedOperation { private: SocketReader *m_inputColor; SocketReader *m_inputAlpha; @@ -44,6 +44,10 @@ class SetAlphaReplaceOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc new file mode 100644 index 00000000000..2feaa0ae16d --- /dev/null +++ b/source/blender/compositor/operations/COM_TransformOperation.cc @@ -0,0 +1,156 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_TransformOperation.h" +#include "COM_ConstantOperation.h" +#include "COM_RotateOperation.h" +#include "COM_ScaleOperation.h" + +#include "BLI_math.h" + +namespace blender::compositor { + +TransformOperation::TransformOperation() +{ + addInputSocket(DataType::Color); + addInputSocket(DataType::Value); + addInputSocket(DataType::Value); + addInputSocket(DataType::Value); + addInputSocket(DataType::Value); + addOutputSocket(DataType::Color); + translate_factor_x_ = 1.0f; + translate_factor_y_ = 1.0f; + convert_degree_to_rad_ = false; + sampler_ = PixelSampler::Bilinear; + invert_ = false; +} + +void TransformOperation::init_data() +{ + /* Translation. */ + translate_x_ = 0; + NodeOperation *x_op = getInputOperation(X_INPUT_INDEX); + if (x_op->get_flags().is_constant_operation) { + translate_x_ = static_cast<ConstantOperation *>(x_op)->get_constant_elem()[0] * + translate_factor_x_; + } + translate_y_ = 0; + NodeOperation *y_op = getInputOperation(Y_INPUT_INDEX); + if (y_op->get_flags().is_constant_operation) { + translate_y_ = static_cast<ConstantOperation *>(y_op)->get_constant_elem()[0] * + translate_factor_y_; + } + + /* Scaling. */ + scale_center_x_ = getWidth() / 2.0; + scale_center_y_ = getHeight() / 2.0; + constant_scale_ = 1.0f; + NodeOperation *scale_op = getInputOperation(SCALE_INPUT_INDEX); + if (scale_op->get_flags().is_constant_operation) { + constant_scale_ = static_cast<ConstantOperation *>(scale_op)->get_constant_elem()[0]; + } + + /* Rotation. */ + rotate_center_x_ = (getWidth() - 1.0) / 2.0; + rotate_center_y_ = (getHeight() - 1.0) / 2.0; + NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX); + const bool is_constant_degree = degree_op->get_flags().is_constant_operation; + const float degree = is_constant_degree ? + static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] : + 0.0f; + const double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree; + rotate_cosine_ = cos(rad); + rotate_sine_ = sin(rad); +} + +void TransformOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: { + BLI_rcti_translate(&r_input_area, translate_x_, translate_y_); + ScaleOperation::scale_area( + r_input_area, scale_center_x_, scale_center_y_, constant_scale_, constant_scale_); + RotateOperation::get_area_rotation_bounds(r_input_area, + rotate_center_x_, + rotate_center_y_, + rotate_sine_, + rotate_cosine_, + r_input_area); + expand_area_for_sampler(r_input_area, sampler_); + break; + } + case X_INPUT_INDEX: + case Y_INPUT_INDEX: + case DEGREE_INPUT_INDEX: { + r_input_area = COM_SINGLE_ELEM_AREA; + break; + } + case SCALE_INPUT_INDEX: { + r_input_area = output_area; + break; + } + } +} + +void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; + MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX]; + BuffersIterator<float> it = output->iterate_with({input_scale}, area); + if (invert_) { + transform_inverted(it, input_img); + } + else { + transform(it, input_img); + } +} + +void TransformOperation::transform(BuffersIterator<float> &it, const MemoryBuffer *input_img) +{ + for (; !it.is_end(); ++it) { + const float scale = *it.in(0); + float x = it.x - translate_x_; + float y = it.y - translate_y_; + RotateOperation::rotate_coords( + x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_); + x = ScaleOperation::scale_coord(x, scale_center_x_, scale); + y = ScaleOperation::scale_coord(y, scale_center_y_, scale); + input_img->read_elem_sampled(x, y, sampler_, it.out); + } +} + +void TransformOperation::transform_inverted(BuffersIterator<float> &it, + const MemoryBuffer *input_img) +{ + for (; !it.is_end(); ++it) { + const float scale = *it.in(0); + float x = ScaleOperation::scale_coord(it.x, scale_center_x_, scale); + float y = ScaleOperation::scale_coord(it.y, scale_center_y_, scale); + RotateOperation::rotate_coords( + x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_); + x -= translate_x_; + y -= translate_y_; + input_img->read_elem_sampled(x, y, sampler_, it.out); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TransformOperation.h b/source/blender/compositor/operations/COM_TransformOperation.h new file mode 100644 index 00000000000..480998a0207 --- /dev/null +++ b/source/blender/compositor/operations/COM_TransformOperation.h @@ -0,0 +1,87 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_MultiThreadedOperation.h" + +namespace blender::compositor { + +class TransformOperation : public MultiThreadedOperation { + private: + constexpr static int IMAGE_INPUT_INDEX = 0; + constexpr static int X_INPUT_INDEX = 1; + constexpr static int Y_INPUT_INDEX = 2; + constexpr static int DEGREE_INPUT_INDEX = 3; + constexpr static int SCALE_INPUT_INDEX = 4; + + float scale_center_x_; + float scale_center_y_; + float rotate_center_x_; + float rotate_center_y_; + float rotate_cosine_; + float rotate_sine_; + float translate_x_; + float translate_y_; + float constant_scale_; + + /* Set variables. */ + PixelSampler sampler_; + bool convert_degree_to_rad_; + float translate_factor_x_; + float translate_factor_y_; + bool invert_; + + public: + TransformOperation(); + + void set_translate_factor_xy(float x, float y) + { + translate_factor_x_ = x; + translate_factor_y_ = y; + } + + void set_convert_rotate_degree_to_rad(bool value) + { + convert_degree_to_rad_ = value; + } + + void set_sampler(PixelSampler sampler) + { + sampler_ = sampler; + } + + void set_invert(bool value) + { + invert_ = value; + } + + void init_data() override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + private: + /** Translate -> Rotate -> Scale. */ + void transform(BuffersIterator<float> &it, const MemoryBuffer *input_img); + /** Scale -> Rotate -> Translate. */ + void transform_inverted(BuffersIterator<float> &it, const MemoryBuffer *input_img); +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index 19cd5a53084..6af6f5a6244 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -18,6 +18,7 @@ #include "COM_VariableSizeBokehBlurOperation.h" #include "BLI_math.h" +#include "COM_ExecutionSystem.h" #include "COM_OpenCLDevice.h" #include "RE_pipeline.h" @@ -276,6 +277,166 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest( return false; } +void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: + case SIZE_INPUT_INDEX: { + const float max_dim = MAX2(getWidth(), getHeight()); + const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const int max_blur_scalar = m_maxBlur * scalar; + r_input_area.xmax = output_area.xmax + max_blur_scalar + 2; + r_input_area.xmin = output_area.xmin - max_blur_scalar - 2; + r_input_area.ymax = output_area.ymax + max_blur_scalar + 2; + r_input_area.ymin = output_area.ymin - max_blur_scalar - 2; + break; + } + case BOKEH_INPUT_INDEX: { + r_input_area.xmax = COM_BLUR_BOKEH_PIXELS; + r_input_area.xmin = 0; + r_input_area.ymax = COM_BLUR_BOKEH_PIXELS; + r_input_area.ymin = 0; + break; + } +#ifdef COM_DEFOCUS_SEARCH + case DEFOCUS_INPUT_INDEX: { + r_input_area.xmax = (output_area.xmax / InverseSearchRadiusOperation::DIVIDER) + 1; + r_input_area.xmin = (output_area.xmin / InverseSearchRadiusOperation::DIVIDER) - 1; + r_input_area.ymax = (output_area.ymax / InverseSearchRadiusOperation::DIVIDER) + 1; + r_input_area.ymin = (output_area.ymin / InverseSearchRadiusOperation::DIVIDER) - 1; + break; + } +#endif + } +} + +struct PixelData { + float multiplier_accum[4]; + float color_accum[4]; + float threshold; + float scalar; + float size_center; + int max_blur_scalar; + int step; + MemoryBuffer *bokeh_input; + MemoryBuffer *size_input; + MemoryBuffer *image_input; + int image_width; + int image_height; +}; + +static void blur_pixel(int x, int y, PixelData &p) +{ + BLI_assert(p.bokeh_input->getWidth() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(p.bokeh_input->getHeight() == COM_BLUR_BOKEH_PIXELS); + +#ifdef COM_DEFOCUS_SEARCH + float search[4]; + inputs[DEFOCUS_INPUT_INDEX]->read_elem_checked(x / InverseSearchRadiusOperation::DIVIDER, + y / InverseSearchRadiusOperation::DIVIDER, + search); + const int minx = search[0]; + const int miny = search[1]; + const int maxx = search[2]; + const int maxy = search[3]; +#else + const int minx = MAX2(x - p.max_blur_scalar, 0); + const int miny = MAX2(y - p.max_blur_scalar, 0); + const int maxx = MIN2(x + p.max_blur_scalar, p.image_width); + const int maxy = MIN2(y + p.max_blur_scalar, p.image_height); +#endif + + const int color_row_stride = p.image_input->row_stride * p.step; + const int color_elem_stride = p.image_input->elem_stride * p.step; + const int size_row_stride = p.size_input->row_stride * p.step; + const int size_elem_stride = p.size_input->elem_stride * p.step; + const float *row_color = p.image_input->get_elem(minx, miny); + const float *row_size = p.size_input->get_elem(minx, miny); + for (int ny = miny; ny < maxy; + ny += p.step, row_size += size_row_stride, row_color += color_row_stride) { + const float dy = ny - y; + const float *size_elem = row_size; + const float *color = row_color; + for (int nx = minx; nx < maxx; + nx += p.step, size_elem += size_elem_stride, color += color_elem_stride) { + if (nx == x && ny == y) { + continue; + } + const float size = MIN2(size_elem[0] * p.scalar, p.size_center); + if (size <= p.threshold) { + continue; + } + const float dx = nx - x; + if (size <= fabsf(dx) || size <= fabsf(dy)) { + continue; + } + + /* XXX: There is no way to ensure bokeh input is an actual bokeh with #COM_BLUR_BOKEH_PIXELS + * size, anything may be connected. Use the real input size and remove asserts? */ + const float u = (float)(COM_BLUR_BOKEH_PIXELS / 2) + + (dx / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1); + const float v = (float)(COM_BLUR_BOKEH_PIXELS / 2) + + (dy / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1); + float bokeh[4]; + p.bokeh_input->read_elem_checked(u, v, bokeh); + madd_v4_v4v4(p.color_accum, bokeh, color); + add_v4_v4(p.multiplier_accum, bokeh); + } + } +} + +void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + PixelData p; + p.bokeh_input = inputs[BOKEH_INPUT_INDEX]; + p.size_input = inputs[SIZE_INPUT_INDEX]; + p.image_input = inputs[IMAGE_INPUT_INDEX]; + p.step = QualityStepHelper::getStep(); + p.threshold = m_threshold; + p.image_width = this->getWidth(); + p.image_height = this->getHeight(); + + rcti scalar_area; + this->get_area_of_interest(SIZE_INPUT_INDEX, area, scalar_area); + BLI_rcti_isect(&scalar_area, &p.size_input->get_rect(), &scalar_area); + const float max_size = p.size_input->get_max_value(scalar_area); + + const float max_dim = MAX2(this->getWidth(), this->getHeight()); + p.scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + p.max_blur_scalar = static_cast<int>(max_size * p.scalar); + CLAMP(p.max_blur_scalar, 1, m_maxBlur); + + for (BuffersIterator<float> it = output->iterate_with({p.image_input, p.size_input}, area); + !it.is_end(); + ++it) { + const float *color = it.in(0); + const float size = *it.in(1); + copy_v4_v4(p.color_accum, color); + copy_v4_fl(p.multiplier_accum, 1.0f); + p.size_center = size * p.scalar; + + if (p.size_center > p.threshold) { + blur_pixel(it.x, it.y, p); + } + + it.out[0] = p.color_accum[0] / p.multiplier_accum[0]; + it.out[1] = p.color_accum[1] / p.multiplier_accum[1]; + it.out[2] = p.color_accum[2] / p.multiplier_accum[2]; + it.out[3] = p.color_accum[3] / p.multiplier_accum[3]; + + /* Blend in out values over the threshold, otherwise we get sharp, ugly transitions. */ + if ((p.size_center > p.threshold) && (p.size_center < p.threshold * 2.0f)) { + /* Factor from 0-1. */ + const float fac = (p.size_center - p.threshold) / p.threshold; + interp_v4_v4v4(it.out, color, it.out, fac); + } + } +} + #ifdef COM_DEFOCUS_SEARCH // InverseSearchRadiusOperation InverseSearchRadiusOperation::InverseSearchRadiusOperation() diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h index baeab6a646e..d6df9f5b858 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h @@ -18,15 +18,22 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" namespace blender::compositor { //#define COM_DEFOCUS_SEARCH -class VariableSizeBokehBlurOperation : public NodeOperation, public QualityStepHelper { +class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int BOKEH_INPUT_INDEX = 1; + static constexpr int SIZE_INPUT_INDEX = 2; +#ifdef COM_DEFOCUS_SEARCH + static constexpr int DEFOCUS_INPUT_INDEX = 3; +#endif + int m_maxBlur; float m_threshold; bool m_do_size_scale; /* scale size, matching 'BokehBlurNode' */ @@ -84,8 +91,14 @@ class VariableSizeBokehBlurOperation : public NodeOperation, public QualityStepH MemoryBuffer **inputMemoryBuffers, std::list<cl_mem> *clMemToCleanUp, std::list<cl_kernel> *clKernelsToCleanUp) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; +/* Currently unused. If ever used, it needs fullframe implementation. */ #ifdef COM_DEFOCUS_SEARCH class InverseSearchRadiusOperation : public NodeOperation { private: diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index f4dc553e982..879a7b08eba 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -246,7 +246,7 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity); BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state); - /* Do not fadeout when doing probe rendering, only when drawing the background */ + /* Do not fade-out when doing probe rendering, only when drawing the background. */ DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); } else { diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c index 395d50fbc6b..438c95118ef 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.c @@ -111,7 +111,9 @@ static void space_image_gpu_texture_get(Image *image, /* Update multi-index and pass for the current eye. */ BKE_image_multilayer_index(image->rr, &sima->iuser); } - BKE_image_multiview_index(image, &sima->iuser); + else { + BKE_image_multiview_index(image, &sima->iuser); + } if (ibuf) { const int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(ibuf); diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 2a9080eb217..04d90919a20 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -864,7 +864,6 @@ typedef union OVERLAY_CameraInstanceData { static void camera_view3d_reconstruction(OVERLAY_ExtraCallBuffers *cb, Scene *scene, View3D *v3d, - Object *camera_object, Object *ob, const float color[4]) { @@ -943,7 +942,7 @@ static void camera_view3d_reconstruction(OVERLAY_ExtraCallBuffers *cb, } if (is_select) { - DRW_select_load_id(camera_object->runtime.select_id | (track_index << 16)); + DRW_select_load_id(ob->runtime.select_id | (track_index << 16)); track_index++; } @@ -1251,7 +1250,7 @@ void OVERLAY_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) /* Motion Tracking. */ if ((v3d->flag2 & V3D_SHOW_RECONSTRUCTION) != 0) { - camera_view3d_reconstruction(cb, scene, v3d, camera_object, ob, color_p); + camera_view3d_reconstruction(cb, scene, v3d, ob, color_p); } /* Background images. */ diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 7dc468d1a73..db96d6a774f 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -101,7 +101,7 @@ BLI_INLINE int mesh_render_mat_len_get(const Mesh *me) return MAX2(1, me->totcol); } -typedef struct MeshBufferCache { +typedef struct MeshBufferList { /* Every VBO below contains at least enough * data for every loops in the mesh (except fdots and skin roots). * For some VBOs, it extends to (in this exact order) : @@ -152,16 +152,91 @@ typedef struct MeshBufferCache { GPUIndexBuf *edituv_points; GPUIndexBuf *edituv_fdots; } ibo; - /* Index buffer per material. These are subranges of `ibo.tris` */ - GPUIndexBuf **tris_per_mat; -} MeshBufferCache; +} MeshBufferList; + +typedef struct MeshBatchList { + /* Surfaces / Render */ + GPUBatch *surface; + GPUBatch *surface_weights; + /* Edit mode */ + GPUBatch *edit_triangles; + GPUBatch *edit_vertices; + GPUBatch *edit_edges; + GPUBatch *edit_vnor; + GPUBatch *edit_lnor; + GPUBatch *edit_fdots; + GPUBatch *edit_mesh_analysis; + GPUBatch *edit_skin_roots; + /* Edit UVs */ + GPUBatch *edituv_faces_stretch_area; + GPUBatch *edituv_faces_stretch_angle; + GPUBatch *edituv_faces; + GPUBatch *edituv_edges; + GPUBatch *edituv_verts; + GPUBatch *edituv_fdots; + /* Edit selection */ + GPUBatch *edit_selection_verts; + GPUBatch *edit_selection_edges; + GPUBatch *edit_selection_faces; + GPUBatch *edit_selection_fdots; + /* Common display / Other */ + GPUBatch *all_verts; + GPUBatch *all_edges; + GPUBatch *loose_edges; + GPUBatch *edge_detection; + GPUBatch *wire_edges; /* Individual edges with face normals. */ + GPUBatch *wire_loops; /* Loops around faces. no edges between selected faces */ + GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */ + GPUBatch *sculpt_overlays; +} MeshBatchList; + +#define MBC_BATCH_LEN (sizeof(MeshBatchList) / sizeof(void *)) +#define MBC_VBO_LEN (sizeof(((MeshBufferList){0}).vbo) / sizeof(void *)) +#define MBC_IBO_LEN (sizeof(((MeshBufferList){0}).ibo) / sizeof(void *)) + +#define MBC_BATCH_INDEX(batch) (offsetof(MeshBatchList, batch) / sizeof(void *)) + +typedef enum DRWBatchFlag { + MBC_SURFACE = (1u << MBC_BATCH_INDEX(surface)), + MBC_SURFACE_WEIGHTS = (1u << MBC_BATCH_INDEX(surface_weights)), + MBC_EDIT_TRIANGLES = (1u << MBC_BATCH_INDEX(edit_triangles)), + MBC_EDIT_VERTICES = (1u << MBC_BATCH_INDEX(edit_vertices)), + MBC_EDIT_EDGES = (1u << MBC_BATCH_INDEX(edit_edges)), + MBC_EDIT_VNOR = (1u << MBC_BATCH_INDEX(edit_vnor)), + MBC_EDIT_LNOR = (1u << MBC_BATCH_INDEX(edit_lnor)), + MBC_EDIT_FACEDOTS = (1u << MBC_BATCH_INDEX(edit_fdots)), + MBC_EDIT_MESH_ANALYSIS = (1u << MBC_BATCH_INDEX(edit_mesh_analysis)), + MBC_SKIN_ROOTS = (1u << MBC_BATCH_INDEX(edit_skin_roots)), + MBC_EDITUV_FACES_STRETCH_AREA = (1u << MBC_BATCH_INDEX(edituv_faces_stretch_area)), + MBC_EDITUV_FACES_STRETCH_ANGLE = (1u << MBC_BATCH_INDEX(edituv_faces_stretch_angle)), + MBC_EDITUV_FACES = (1u << MBC_BATCH_INDEX(edituv_faces)), + MBC_EDITUV_EDGES = (1u << MBC_BATCH_INDEX(edituv_edges)), + MBC_EDITUV_VERTS = (1u << MBC_BATCH_INDEX(edituv_verts)), + MBC_EDITUV_FACEDOTS = (1u << MBC_BATCH_INDEX(edituv_fdots)), + MBC_EDIT_SELECTION_VERTS = (1u << MBC_BATCH_INDEX(edit_selection_verts)), + MBC_EDIT_SELECTION_EDGES = (1u << MBC_BATCH_INDEX(edit_selection_edges)), + MBC_EDIT_SELECTION_FACES = (1u << MBC_BATCH_INDEX(edit_selection_faces)), + MBC_EDIT_SELECTION_FACEDOTS = (1u << MBC_BATCH_INDEX(edit_selection_fdots)), + MBC_ALL_VERTS = (1u << MBC_BATCH_INDEX(all_verts)), + MBC_ALL_EDGES = (1u << MBC_BATCH_INDEX(all_edges)), + MBC_LOOSE_EDGES = (1u << MBC_BATCH_INDEX(loose_edges)), + MBC_EDGE_DETECTION = (1u << MBC_BATCH_INDEX(edge_detection)), + MBC_WIRE_EDGES = (1u << MBC_BATCH_INDEX(wire_edges)), + MBC_WIRE_LOOPS = (1u << MBC_BATCH_INDEX(wire_loops)), + MBC_WIRE_LOOPS_UVS = (1u << MBC_BATCH_INDEX(wire_loops_uvs)), + MBC_SCULPT_OVERLAYS = (1u << MBC_BATCH_INDEX(sculpt_overlays)), +} DRWBatchFlag; + +BLI_STATIC_ASSERT(MBC_BATCH_LEN < 32, "Number of batches exceeded the limit of bit fields"); /** * Data that are kept around between extractions to reduce rebuilding time. * * - Loose geometry. */ -typedef struct MeshBufferExtractionCache { +typedef struct MeshBufferCache { + MeshBufferList buff; + struct { int edge_len; int vert_len; @@ -174,7 +249,7 @@ typedef struct MeshBufferExtractionCache { int *mat_tri_len; int visible_tri_len; } poly_sorted; -} MeshBufferExtractionCache; +} MeshBufferCache; #define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \ for (MeshBufferCache *mbc = &batch_cache->final; \ @@ -186,50 +261,15 @@ typedef struct MeshBufferExtractionCache { typedef struct MeshBatchCache { MeshBufferCache final, cage, uv_cage; - MeshBufferExtractionCache final_extraction_cache; - MeshBufferExtractionCache cage_extraction_cache; - MeshBufferExtractionCache uv_cage_extraction_cache; + MeshBatchList batch; - struct { - /* Surfaces / Render */ - GPUBatch *surface; - GPUBatch *surface_weights; - /* Edit mode */ - GPUBatch *edit_triangles; - GPUBatch *edit_vertices; - GPUBatch *edit_edges; - GPUBatch *edit_vnor; - GPUBatch *edit_lnor; - GPUBatch *edit_fdots; - GPUBatch *edit_mesh_analysis; - GPUBatch *edit_skin_roots; - /* Edit UVs */ - GPUBatch *edituv_faces_stretch_area; - GPUBatch *edituv_faces_stretch_angle; - GPUBatch *edituv_faces; - GPUBatch *edituv_edges; - GPUBatch *edituv_verts; - GPUBatch *edituv_fdots; - /* Edit selection */ - GPUBatch *edit_selection_verts; - GPUBatch *edit_selection_edges; - GPUBatch *edit_selection_faces; - GPUBatch *edit_selection_fdots; - /* Common display / Other */ - GPUBatch *all_verts; - GPUBatch *all_edges; - GPUBatch *loose_edges; - GPUBatch *edge_detection; - GPUBatch *wire_edges; /* Individual edges with face normals. */ - GPUBatch *wire_loops; /* Loops around faces. no edges between selected faces */ - GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */ - GPUBatch *sculpt_overlays; - } batch; + /* Index buffer per material. These are subranges of `ibo.tris` */ + GPUIndexBuf **tris_per_mat; GPUBatch **surface_per_mat; - uint32_t batch_requested; /* DRWBatchFlag */ - uint32_t batch_ready; /* DRWBatchFlag */ + DRWBatchFlag batch_requested; /* DRWBatchFlag */ + DRWBatchFlag batch_ready; /* DRWBatchFlag */ /* settings to determine if cache is invalid */ int edge_len; @@ -259,55 +299,13 @@ typedef struct MeshBatchCache { bool no_loose_wire; } MeshBatchCache; -#define MBC_BATCH_LEN (sizeof(((MeshBatchCache){0}).batch) / sizeof(void *)) -#define MBC_VBO_LEN (sizeof(((MeshBufferCache){0}).vbo) / sizeof(void *)) -#define MBC_IBO_LEN (sizeof(((MeshBufferCache){0}).ibo) / sizeof(void *)) - -#define MBC_BATCH_INDEX(batch_name) \ - ((offsetof(MeshBatchCache, batch_name) - offsetof(MeshBatchCache, batch)) / sizeof(void *)) - -typedef enum DRWBatchFlag { - MBC_SURFACE = (1u << MBC_BATCH_INDEX(batch.surface)), - MBC_SURFACE_WEIGHTS = (1u << MBC_BATCH_INDEX(batch.surface_weights)), - MBC_EDIT_TRIANGLES = (1u << MBC_BATCH_INDEX(batch.edit_triangles)), - MBC_EDIT_VERTICES = (1u << MBC_BATCH_INDEX(batch.edit_vertices)), - MBC_EDIT_EDGES = (1u << MBC_BATCH_INDEX(batch.edit_edges)), - MBC_EDIT_VNOR = (1u << MBC_BATCH_INDEX(batch.edit_vnor)), - MBC_EDIT_LNOR = (1u << MBC_BATCH_INDEX(batch.edit_lnor)), - MBC_EDIT_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edit_fdots)), - MBC_EDIT_MESH_ANALYSIS = (1u << MBC_BATCH_INDEX(batch.edit_mesh_analysis)), - MBC_SKIN_ROOTS = (1u << MBC_BATCH_INDEX(batch.edit_skin_roots)), - MBC_EDITUV_FACES_STRETCH_AREA = (1u << MBC_BATCH_INDEX(batch.edituv_faces_stretch_area)), - MBC_EDITUV_FACES_STRETCH_ANGLE = (1u << MBC_BATCH_INDEX(batch.edituv_faces_stretch_angle)), - MBC_EDITUV_FACES = (1u << MBC_BATCH_INDEX(batch.edituv_faces)), - MBC_EDITUV_EDGES = (1u << MBC_BATCH_INDEX(batch.edituv_edges)), - MBC_EDITUV_VERTS = (1u << MBC_BATCH_INDEX(batch.edituv_verts)), - MBC_EDITUV_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edituv_fdots)), - MBC_EDIT_SELECTION_VERTS = (1u << MBC_BATCH_INDEX(batch.edit_selection_verts)), - MBC_EDIT_SELECTION_EDGES = (1u << MBC_BATCH_INDEX(batch.edit_selection_edges)), - MBC_EDIT_SELECTION_FACES = (1u << MBC_BATCH_INDEX(batch.edit_selection_faces)), - MBC_EDIT_SELECTION_FACEDOTS = (1u << MBC_BATCH_INDEX(batch.edit_selection_fdots)), - MBC_ALL_VERTS = (1u << MBC_BATCH_INDEX(batch.all_verts)), - MBC_ALL_EDGES = (1u << MBC_BATCH_INDEX(batch.all_edges)), - MBC_LOOSE_EDGES = (1u << MBC_BATCH_INDEX(batch.loose_edges)), - MBC_EDGE_DETECTION = (1u << MBC_BATCH_INDEX(batch.edge_detection)), - MBC_WIRE_EDGES = (1u << MBC_BATCH_INDEX(batch.wire_edges)), - MBC_WIRE_LOOPS = (1u << MBC_BATCH_INDEX(batch.wire_loops)), - MBC_WIRE_LOOPS_UVS = (1u << MBC_BATCH_INDEX(batch.wire_loops_uvs)), - MBC_SCULPT_OVERLAYS = (1u << MBC_BATCH_INDEX(batch.sculpt_overlays)), -} DRWBatchFlag; - -BLI_STATIC_ASSERT(MBC_BATCH_INDEX(surface_per_mat) < 32, - "Number of batches exceeded the limit of bit fields"); - #define MBC_EDITUV \ (MBC_EDITUV_FACES_STRETCH_AREA | MBC_EDITUV_FACES_STRETCH_ANGLE | MBC_EDITUV_FACES | \ MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_WIRE_LOOPS_UVS) void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, - MeshBatchCache *cache, - MeshBufferCache *mbc, - MeshBufferExtractionCache *extraction_cache, + MeshBatchCache *mbc, + MeshBufferCache *extraction_cache, Mesh *me, const bool is_editmode, const bool is_paint_mode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 1bc2b8a9def..06c449fe590 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -162,7 +162,7 @@ struct ExtractTaskData { const MeshRenderData *mr = nullptr; MeshBatchCache *cache = nullptr; ExtractorRunDatas *extractors = nullptr; - MeshBufferCache *mbc = nullptr; + MeshBufferList *mbuflist = nullptr; eMRIterType iter_type; bool use_threading = false; @@ -170,9 +170,13 @@ struct ExtractTaskData { ExtractTaskData(const MeshRenderData *mr, struct MeshBatchCache *cache, ExtractorRunDatas *extractors, - MeshBufferCache *mbc, + MeshBufferList *mbuflist, const bool use_threading) - : mr(mr), cache(cache), extractors(extractors), mbc(mbc), use_threading(use_threading) + : mr(mr), + cache(cache), + extractors(extractors), + mbuflist(mbuflist), + use_threading(use_threading) { iter_type = extractors->iter_types(); }; @@ -204,13 +208,13 @@ static void extract_task_data_free(void *data) BLI_INLINE void extract_init(const MeshRenderData *mr, struct MeshBatchCache *cache, ExtractorRunDatas &extractors, - MeshBufferCache *mbc, + MeshBufferList *mbuflist, void *data_stack) { uint32_t data_offset = 0; for (ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; - run_data.buffer = mesh_extract_buffer_get(extractor, mbc); + run_data.buffer = mesh_extract_buffer_get(extractor, mbuflist); run_data.data_offset = data_offset; extractor->init(mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, data_offset)); data_offset += (uint32_t)extractor->data_size; @@ -445,7 +449,7 @@ static void extract_task_range_run(void *__restrict taskdata) settings.func_reduce = extract_task_reduce; settings.min_iter_per_thread = MIN_RANGE_LEN; - extract_init(data->mr, data->cache, *data->extractors, data->mbc, userdata_chunk); + extract_init(data->mr, data->cache, *data->extractors, data->mbuflist, userdata_chunk); if (iter_type & MR_ITER_LOOPTRI) { extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LOOPTRI, is_mesh, &settings); @@ -474,10 +478,10 @@ static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph, const MeshRenderData *mr, MeshBatchCache *cache, ExtractorRunDatas *extractors, - MeshBufferCache *mbc, + MeshBufferList *mbuflist, const bool use_threading) { - ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, use_threading); + ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbuflist, use_threading); struct TaskNode *task_node = BLI_task_graph_node_create( task_graph, extract_task_range_run, @@ -493,12 +497,12 @@ static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph, * \{ */ struct MeshRenderDataUpdateTaskData { MeshRenderData *mr = nullptr; - MeshBufferExtractionCache *cache = nullptr; + MeshBufferCache *cache = nullptr; eMRIterType iter_type; eMRDataType data_flag; MeshRenderDataUpdateTaskData(MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, eMRIterType iter_type, eMRDataType data_flag) : mr(mr), cache(cache), iter_type(iter_type), data_flag(data_flag) @@ -538,7 +542,7 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data) static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, const eMRIterType iter_type, const eMRDataType data_flag) { @@ -562,7 +566,6 @@ static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *t static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache *mbc, - MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, @@ -613,9 +616,11 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, /* Create an array containing all the extractors that needs to be executed. */ ExtractorRunDatas extractors; + MeshBufferList *mbuflist = &mbc->buff; + #define EXTRACT_ADD_REQUESTED(type, name) \ do { \ - if (DRW_##type##_requested(mbc->type.name)) { \ + if (DRW_##type##_requested(mbuflist->type.name)) { \ const MeshExtract *extractor = mesh_extract_override_get( \ &extract_##name, do_hq_normals, override_single_mat); \ extractors.append(extractor); \ @@ -647,19 +652,19 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(vbo, skin_roots); EXTRACT_ADD_REQUESTED(ibo, tris); - if (DRW_ibo_requested(mbc->ibo.lines_loose)) { + if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { /* `ibo.lines_loose` require the `ibo.lines` buffer. */ - if (mbc->ibo.lines == nullptr) { - DRW_ibo_request(nullptr, &mbc->ibo.lines); + if (mbuflist->ibo.lines == nullptr) { + DRW_ibo_request(nullptr, &mbuflist->ibo.lines); } - const MeshExtract *extractor = DRW_ibo_requested(mbc->ibo.lines) ? + const MeshExtract *extractor = DRW_ibo_requested(mbuflist->ibo.lines) ? &extract_lines_with_lines_loose : &extract_lines_loose_only; extractors.append(extractor); } - else if (DRW_ibo_requested(mbc->ibo.lines)) { + else if (DRW_ibo_requested(mbuflist->ibo.lines)) { const MeshExtract *extractor; - if (mbc->ibo.lines_loose != nullptr) { + if (mbuflist->ibo.lines_loose != nullptr) { /* Update `ibo.lines_loose` as it depends on `ibo.lines`. */ extractor = &extract_lines_with_lines_loose; } @@ -701,7 +706,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, eMRDataType data_flag = extractors.data_types(); struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, extraction_cache, iter_type, data_flag); + task_graph, mr, mbc, iter_type, data_flag); /* Simple heuristic. */ const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIN_RANGE_LEN; @@ -714,7 +719,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); single_threaded_extractors->append(extractor); struct TaskNode *task_node = extract_task_node_create( - task_graph, mr, cache, single_threaded_extractors, mbc, false); + task_graph, mr, cache, single_threaded_extractors, mbuflist, false); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); } @@ -725,7 +730,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, extractors.filter_threaded_extractors_into(*multi_threaded_extractors); if (!multi_threaded_extractors->is_empty()) { struct TaskNode *task_node = extract_task_node_create( - task_graph, mr, cache, multi_threaded_extractors, mbc, true); + task_graph, mr, cache, multi_threaded_extractors, mbuflist, true); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); } @@ -738,7 +743,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, /* Run all requests on the same thread. */ ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); struct TaskNode *task_node = extract_task_node_create( - task_graph, mr, cache, extractors_copy, mbc, false); + task_graph, mr, cache, extractors_copy, mbuflist, false); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); } @@ -776,7 +781,6 @@ extern "C" { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache *mbc, - MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, @@ -793,7 +797,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, blender::draw::mesh_buffer_cache_create_requested(task_graph, cache, mbc, - extraction_cache, me, is_editmode, is_paint_mode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index 27fd6ca9134..abfbeabef6b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -45,17 +45,15 @@ * \{ */ static void mesh_render_data_lverts_bm(const MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, BMesh *bm); static void mesh_render_data_ledges_bm(const MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, BMesh *bm); -static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, - MeshBufferExtractionCache *cache); -static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, - MeshBufferExtractionCache *cache); +static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache); +static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, MeshBufferCache *cache); -static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) +static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferCache *cache) { mr->ledges = cache->loose_geom.edges; mr->lverts = cache->loose_geom.verts; @@ -65,8 +63,7 @@ static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtra mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); } -static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, MeshBufferCache *cache) { /* Early exit: Are loose geometry already available. * Only checking for loose verts as loose edges and verts are calculated at the same time. */ @@ -76,8 +73,7 @@ static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, mesh_render_data_loose_geom_build(mr, cache); } -static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, MeshBufferCache *cache) { cache->loose_geom.vert_len = 0; cache->loose_geom.edge_len = 0; @@ -94,8 +90,7 @@ static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, } } -static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache) { BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); @@ -128,9 +123,7 @@ static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MEM_freeN(lvert_map); } -static void mesh_render_data_lverts_bm(const MeshRenderData *mr, - MeshBufferExtractionCache *cache, - BMesh *bm) +static void mesh_render_data_lverts_bm(const MeshRenderData *mr, MeshBufferCache *cache, BMesh *bm) { int elem_id; BMIter iter; @@ -147,9 +140,7 @@ static void mesh_render_data_lverts_bm(const MeshRenderData *mr, } } -static void mesh_render_data_ledges_bm(const MeshRenderData *mr, - MeshBufferExtractionCache *cache, - BMesh *bm) +static void mesh_render_data_ledges_bm(const MeshRenderData *mr, MeshBufferCache *cache, BMesh *bm) { int elem_id; BMIter iter; @@ -167,7 +158,7 @@ static void mesh_render_data_ledges_bm(const MeshRenderData *mr, } void mesh_render_data_update_loose_geom(MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, const eMRIterType iter_type, const eMRDataType data_flag) { @@ -185,16 +176,13 @@ void mesh_render_data_update_loose_geom(MeshRenderData *mr, * Contains polygon indices sorted based on their material. * * \{ */ -static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, - const MeshBufferExtractionCache *cache); -static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, - MeshBufferExtractionCache *cache); -static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, - MeshBufferExtractionCache *cache); +static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, const MeshBufferCache *cache); +static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, MeshBufferCache *cache); +static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCache *cache); static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr); void mesh_render_data_update_polys_sorted(MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, const eMRDataType data_flag) { if (data_flag & MR_DATA_POLYS_SORTED) { @@ -203,16 +191,14 @@ void mesh_render_data_update_polys_sorted(MeshRenderData *mr, } } -static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, - const MeshBufferExtractionCache *cache) +static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, const MeshBufferCache *cache) { mr->poly_sorted.tri_first_index = cache->poly_sorted.tri_first_index; mr->poly_sorted.mat_tri_len = cache->poly_sorted.mat_tri_len; mr->poly_sorted.visible_tri_len = cache->poly_sorted.visible_tri_len; } -static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, MeshBufferCache *cache) { if (cache->poly_sorted.tri_first_index) { return; @@ -220,8 +206,7 @@ static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, mesh_render_data_polys_sorted_build(mr, cache); } -static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCache *cache) { int *tri_first_index = MEM_mallocN(sizeof(*tri_first_index) * mr->poly_len, __func__); int *mat_tri_len = mesh_render_data_mat_tri_len_build(mr); @@ -584,7 +569,7 @@ void mesh_render_data_free(MeshRenderData *mr) MEM_SAFE_FREE(mr->mlooptri); MEM_SAFE_FREE(mr->loop_normals); - /* Loose geometry are owned by #MeshBufferExtractionCache. */ + /* Loose geometry are owned by #MeshBufferCache. */ mr->ledges = NULL; mr->lverts = NULL; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 52b76733b78..18664498d00 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -79,39 +79,42 @@ /* clang-format off */ -#define _BUFFER_INDEX(buff_name) ((offsetof(MeshBufferCache, buff_name) - offsetof(MeshBufferCache, vbo)) / sizeof(void *)) - -#define _MDEPS_CREATE1(b) (1u << MBC_BATCH_INDEX(b)) -#define _MDEPS_CREATE2(b1, b2) _MDEPS_CREATE1(b1) | _MDEPS_CREATE1(b2) -#define _MDEPS_CREATE3(b1, b2, b3) _MDEPS_CREATE2(b1, b2) | _MDEPS_CREATE1(b3) -#define _MDEPS_CREATE4(b1, b2, b3, b4) _MDEPS_CREATE3(b1, b2, b3) | _MDEPS_CREATE1(b4) -#define _MDEPS_CREATE5(b1, b2, b3, b4, b5) _MDEPS_CREATE4(b1, b2, b3, b4) | _MDEPS_CREATE1(b5) -#define _MDEPS_CREATE6(b1, b2, b3, b4, b5, b6) _MDEPS_CREATE5(b1, b2, b3, b4, b5) | _MDEPS_CREATE1(b6) -#define _MDEPS_CREATE7(b1, b2, b3, b4, b5, b6, b7) _MDEPS_CREATE6(b1, b2, b3, b4, b5, b6) | _MDEPS_CREATE1(b7) -#define _MDEPS_CREATE8(b1, b2, b3, b4, b5, b6, b7, b8) _MDEPS_CREATE7(b1, b2, b3, b4, b5, b6, b7) | _MDEPS_CREATE1(b8) -#define _MDEPS_CREATE9(b1, b2, b3, b4, b5, b6, b7, b8, b9) _MDEPS_CREATE8(b1, b2, b3, b4, b5, b6, b7, b8) | _MDEPS_CREATE1(b9) -#define _MDEPS_CREATE10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) _MDEPS_CREATE9(b1, b2, b3, b4, b5, b6, b7, b8, b9) | _MDEPS_CREATE1(b10) -#define _MDEPS_CREATE19(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19) _MDEPS_CREATE10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) | _MDEPS_CREATE9(b11, b12, b13, b14, b15, b16, b17, b18, b19) - -#define MDEPS_CREATE(buff_name, ...) [_BUFFER_INDEX(buff_name)] = VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE, __VA_ARGS__) - -#define _MDEPS_CREATE_MAP1(a) g_buffer_deps[_BUFFER_INDEX(a)] -#define _MDEPS_CREATE_MAP2(a, b) _MDEPS_CREATE_MAP1(a) | _MDEPS_CREATE_MAP1(b) -#define _MDEPS_CREATE_MAP3(a, b, c) _MDEPS_CREATE_MAP2(a, b) | _MDEPS_CREATE_MAP1(c) -#define _MDEPS_CREATE_MAP4(a, b, c, d) _MDEPS_CREATE_MAP3(a, b, c) | _MDEPS_CREATE_MAP1(d) -#define _MDEPS_CREATE_MAP5(a, b, c, d, e) _MDEPS_CREATE_MAP4(a, b, c, d) | _MDEPS_CREATE_MAP1(e) -#define _MDEPS_CREATE_MAP6(a, b, c, d, e, f) _MDEPS_CREATE_MAP5(a, b, c, d, e) | _MDEPS_CREATE_MAP1(f) -#define _MDEPS_CREATE_MAP7(a, b, c, d, e, f, g) _MDEPS_CREATE_MAP6(a, b, c, d, e, f) | _MDEPS_CREATE_MAP1(g) -#define _MDEPS_CREATE_MAP8(a, b, c, d, e, f, g, h) _MDEPS_CREATE_MAP7(a, b, c, d, e, f, g) | _MDEPS_CREATE_MAP1(h) -#define _MDEPS_CREATE_MAP9(a, b, c, d, e, f, g, h, i) _MDEPS_CREATE_MAP8(a, b, c, d, e, f, g, h) | _MDEPS_CREATE_MAP1(i) -#define _MDEPS_CREATE_MAP10(a, b, c, d, e, f, g, h, i, j) _MDEPS_CREATE_MAP9(a, b, c, d, e, f, g, h, i) | _MDEPS_CREATE_MAP1(j) - -#define MDEPS_CREATE_MAP(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE_MAP, __VA_ARGS__) +#define BUFFER_INDEX(buff_name) ((offsetof(MeshBufferList, buff_name) - offsetof(MeshBufferList, vbo)) / sizeof(void *)) +#define BUFFER_LEN (sizeof(MeshBufferList) / sizeof(void *)) + +#define _BATCH_FLAG1(b) (1u << MBC_BATCH_INDEX(b)) +#define _BATCH_FLAG2(b1, b2) _BATCH_FLAG1(b1) | _BATCH_FLAG1(b2) +#define _BATCH_FLAG3(b1, b2, b3) _BATCH_FLAG2(b1, b2) | _BATCH_FLAG1(b3) +#define _BATCH_FLAG4(b1, b2, b3, b4) _BATCH_FLAG3(b1, b2, b3) | _BATCH_FLAG1(b4) +#define _BATCH_FLAG5(b1, b2, b3, b4, b5) _BATCH_FLAG4(b1, b2, b3, b4) | _BATCH_FLAG1(b5) +#define _BATCH_FLAG6(b1, b2, b3, b4, b5, b6) _BATCH_FLAG5(b1, b2, b3, b4, b5) | _BATCH_FLAG1(b6) +#define _BATCH_FLAG7(b1, b2, b3, b4, b5, b6, b7) _BATCH_FLAG6(b1, b2, b3, b4, b5, b6) | _BATCH_FLAG1(b7) +#define _BATCH_FLAG8(b1, b2, b3, b4, b5, b6, b7, b8) _BATCH_FLAG7(b1, b2, b3, b4, b5, b6, b7) | _BATCH_FLAG1(b8) +#define _BATCH_FLAG9(b1, b2, b3, b4, b5, b6, b7, b8, b9) _BATCH_FLAG8(b1, b2, b3, b4, b5, b6, b7, b8) | _BATCH_FLAG1(b9) +#define _BATCH_FLAG10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) _BATCH_FLAG9(b1, b2, b3, b4, b5, b6, b7, b8, b9) | _BATCH_FLAG1(b10) +#define _BATCH_FLAG18(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18) _BATCH_FLAG10(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10) | _BATCH_FLAG8(b11, b12, b13, b14, b15, b16, b17, b18) + +#define BATCH_FLAG(...) VA_NARGS_CALL_OVERLOAD(_BATCH_FLAG, __VA_ARGS__) + +#define _BATCH_MAP1(a) g_buffer_deps[BUFFER_INDEX(a)] +#define _BATCH_MAP2(a, b) _BATCH_MAP1(a) | _BATCH_MAP1(b) +#define _BATCH_MAP3(a, b, c) _BATCH_MAP2(a, b) | _BATCH_MAP1(c) +#define _BATCH_MAP4(a, b, c, d) _BATCH_MAP3(a, b, c) | _BATCH_MAP1(d) +#define _BATCH_MAP5(a, b, c, d, e) _BATCH_MAP4(a, b, c, d) | _BATCH_MAP1(e) +#define _BATCH_MAP6(a, b, c, d, e, f) _BATCH_MAP5(a, b, c, d, e) | _BATCH_MAP1(f) +#define _BATCH_MAP7(a, b, c, d, e, f, g) _BATCH_MAP6(a, b, c, d, e, f) | _BATCH_MAP1(g) +#define _BATCH_MAP8(a, b, c, d, e, f, g, h) _BATCH_MAP7(a, b, c, d, e, f, g) | _BATCH_MAP1(h) +#define _BATCH_MAP9(a, b, c, d, e, f, g, h, i) _BATCH_MAP8(a, b, c, d, e, f, g, h) | _BATCH_MAP1(i) +#define _BATCH_MAP10(a, b, c, d, e, f, g, h, i, j) _BATCH_MAP9(a, b, c, d, e, f, g, h, i) | _BATCH_MAP1(j) + +#define BATCH_MAP(...) VA_NARGS_CALL_OVERLOAD(_BATCH_MAP, __VA_ARGS__) #ifndef NDEBUG -# define _MDEPS_ASSERT2(b, name) \ - g_buffer_deps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \ - BLI_assert(g_buffer_deps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b)) +# define MDEPS_ASSERT_INDEX(buffer_index, batch_flag) \ + g_buffer_deps_d[buffer_index] |= batch_flag; \ + BLI_assert(g_buffer_deps[buffer_index] & batch_flag) + +# define _MDEPS_ASSERT2(b, n1) MDEPS_ASSERT_INDEX(BUFFER_INDEX(n1), b) # define _MDEPS_ASSERT3(b, n1, n2) _MDEPS_ASSERT2(b, n1); _MDEPS_ASSERT2(b, n2) # define _MDEPS_ASSERT4(b, n1, n2, n3) _MDEPS_ASSERT3(b, n1, n2); _MDEPS_ASSERT2(b, n3) # define _MDEPS_ASSERT5(b, n1, n2, n3, n4) _MDEPS_ASSERT4(b, n1, n2, n3); _MDEPS_ASSERT2(b, n4) @@ -119,99 +122,97 @@ # define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6) # define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) -# define MDEPS_ASSERT(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) -# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_deps_d[_BUFFER_INDEX(name)] == g_buffer_deps[_BUFFER_INDEX(name)]) +# define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) +# define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__) +# define MDEPS_ASSERT_MAP_INDEX(buff_index) BLI_assert(g_buffer_deps_d[buff_index] == g_buffer_deps[buff_index]) +# define MDEPS_ASSERT_MAP(buff_name) MDEPS_ASSERT_MAP_INDEX(BUFFER_INDEX(buff_name)) #else -# define MDEPS_ASSERT(...) -# define MDEPS_ASSERT_MAP(name) +# define MDEPS_ASSERT_INDEX(buffer_index, batch_flag) +# define MDEPS_ASSERT_FLAG(...) +# define MDEPS_ASSERT(batch_name, ...) +# define MDEPS_ASSERT_MAP_INDEX(buff_index) +# define MDEPS_ASSERT_MAP(buff_name) #endif /* clang-format on */ +#define TRIS_PER_MAT_INDEX BUFFER_LEN +#define SURFACE_PER_MAT_FLAG (1u << MBC_BATCH_LEN) + static const DRWBatchFlag g_buffer_deps[] = { - MDEPS_CREATE(vbo.pos_nor, - batch.surface, - batch.surface_weights, - batch.edit_triangles, - batch.edit_vertices, - batch.edit_edges, - batch.edit_vnor, - batch.edit_lnor, - batch.edit_mesh_analysis, - batch.edit_selection_verts, - batch.edit_selection_edges, - batch.edit_selection_faces, - batch.all_verts, - batch.all_edges, - batch.loose_edges, - batch.edge_detection, - batch.wire_edges, - batch.wire_loops, - batch.sculpt_overlays, - surface_per_mat), - MDEPS_CREATE(vbo.lnor, batch.surface, batch.edit_lnor, batch.wire_loops, surface_per_mat), - MDEPS_CREATE(vbo.edge_fac, batch.wire_edges), - MDEPS_CREATE(vbo.weights, batch.surface_weights), - MDEPS_CREATE(vbo.uv, - batch.surface, - batch.edituv_faces_stretch_area, - batch.edituv_faces_stretch_angle, - batch.edituv_faces, - batch.edituv_edges, - batch.edituv_verts, - batch.wire_loops_uvs, - surface_per_mat), - MDEPS_CREATE(vbo.tan, surface_per_mat), - MDEPS_CREATE(vbo.vcol, batch.surface, surface_per_mat), - MDEPS_CREATE(vbo.sculpt_data, batch.sculpt_overlays), - MDEPS_CREATE(vbo.orco, surface_per_mat), - MDEPS_CREATE(vbo.edit_data, batch.edit_triangles, batch.edit_edges, batch.edit_vertices), - MDEPS_CREATE(vbo.edituv_data, - batch.edituv_faces, - batch.edituv_faces_stretch_area, - batch.edituv_faces_stretch_angle, - batch.edituv_edges, - batch.edituv_verts), - MDEPS_CREATE(vbo.edituv_stretch_area, batch.edituv_faces_stretch_area), - MDEPS_CREATE(vbo.edituv_stretch_angle, batch.edituv_faces_stretch_angle), - MDEPS_CREATE(vbo.mesh_analysis, batch.edit_mesh_analysis), - MDEPS_CREATE(vbo.fdots_pos, batch.edit_fdots, batch.edit_selection_fdots), - MDEPS_CREATE(vbo.fdots_nor, batch.edit_fdots), - MDEPS_CREATE(vbo.fdots_uv, batch.edituv_fdots), - MDEPS_CREATE(vbo.fdots_edituv_data, batch.edituv_fdots), - MDEPS_CREATE(vbo.skin_roots, batch.edit_skin_roots), - MDEPS_CREATE(vbo.vert_idx, batch.edit_selection_verts), - MDEPS_CREATE(vbo.edge_idx, batch.edit_selection_edges), - MDEPS_CREATE(vbo.poly_idx, batch.edit_selection_faces), - MDEPS_CREATE(vbo.fdot_idx, batch.edit_selection_fdots), - - MDEPS_CREATE(ibo.tris, - batch.surface, - batch.surface_weights, - batch.edit_triangles, - batch.edit_lnor, - batch.edit_mesh_analysis, - batch.edit_selection_faces, - batch.sculpt_overlays), - MDEPS_CREATE(ibo.lines, - batch.edit_edges, - batch.edit_selection_edges, - batch.all_edges, - batch.wire_edges), - MDEPS_CREATE(ibo.lines_loose, batch.loose_edges), - MDEPS_CREATE(ibo.points, batch.edit_vnor, batch.edit_vertices, batch.edit_selection_verts), - MDEPS_CREATE(ibo.fdots, batch.edit_fdots, batch.edit_selection_fdots), - MDEPS_CREATE(ibo.lines_paint_mask, batch.wire_loops), - MDEPS_CREATE(ibo.lines_adjacency, batch.edge_detection), - MDEPS_CREATE(ibo.edituv_tris, - batch.edituv_faces, - batch.edituv_faces_stretch_area, - batch.edituv_faces_stretch_angle), - MDEPS_CREATE(ibo.edituv_lines, batch.edituv_edges, batch.wire_loops_uvs), - MDEPS_CREATE(ibo.edituv_points, batch.edituv_verts), - MDEPS_CREATE(ibo.edituv_fdots, batch.edituv_fdots), - - MDEPS_CREATE(tris_per_mat, surface_per_mat), + [BUFFER_INDEX(vbo.pos_nor)] = BATCH_FLAG(surface, + surface_weights, + edit_triangles, + edit_vertices, + edit_edges, + edit_vnor, + edit_lnor, + edit_mesh_analysis, + edit_selection_verts, + edit_selection_edges, + edit_selection_faces, + all_verts, + all_edges, + loose_edges, + edge_detection, + wire_edges, + wire_loops, + sculpt_overlays) | + SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.lnor)] = BATCH_FLAG(surface, edit_lnor, wire_loops) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.edge_fac)] = BATCH_FLAG(wire_edges), + [BUFFER_INDEX(vbo.weights)] = BATCH_FLAG(surface_weights), + [BUFFER_INDEX(vbo.uv)] = BATCH_FLAG(surface, + edituv_faces_stretch_area, + edituv_faces_stretch_angle, + edituv_faces, + edituv_edges, + edituv_verts, + wire_loops_uvs) | + SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.tan)] = SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.vcol)] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.sculpt_data)] = BATCH_FLAG(sculpt_overlays), + [BUFFER_INDEX(vbo.orco)] = SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.edit_data)] = BATCH_FLAG(edit_triangles, edit_edges, edit_vertices), + [BUFFER_INDEX(vbo.edituv_data)] = BATCH_FLAG(edituv_faces, + edituv_faces_stretch_area, + edituv_faces_stretch_angle, + edituv_edges, + edituv_verts), + [BUFFER_INDEX(vbo.edituv_stretch_area)] = BATCH_FLAG(edituv_faces_stretch_area), + [BUFFER_INDEX(vbo.edituv_stretch_angle)] = BATCH_FLAG(edituv_faces_stretch_angle), + [BUFFER_INDEX(vbo.mesh_analysis)] = BATCH_FLAG(edit_mesh_analysis), + [BUFFER_INDEX(vbo.fdots_pos)] = BATCH_FLAG(edit_fdots, edit_selection_fdots), + [BUFFER_INDEX(vbo.fdots_nor)] = BATCH_FLAG(edit_fdots), + [BUFFER_INDEX(vbo.fdots_uv)] = BATCH_FLAG(edituv_fdots), + [BUFFER_INDEX(vbo.fdots_edituv_data)] = BATCH_FLAG(edituv_fdots), + [BUFFER_INDEX(vbo.skin_roots)] = BATCH_FLAG(edit_skin_roots), + [BUFFER_INDEX(vbo.vert_idx)] = BATCH_FLAG(edit_selection_verts), + [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges), + [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces), + [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots), + + [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface, + surface_weights, + edit_triangles, + edit_lnor, + edit_mesh_analysis, + edit_selection_faces, + sculpt_overlays), + [BUFFER_INDEX(ibo.lines)] = BATCH_FLAG( + edit_edges, edit_selection_edges, all_edges, wire_edges), + [BUFFER_INDEX(ibo.lines_loose)] = BATCH_FLAG(loose_edges), + [BUFFER_INDEX(ibo.points)] = BATCH_FLAG(edit_vnor, edit_vertices, edit_selection_verts), + [BUFFER_INDEX(ibo.fdots)] = BATCH_FLAG(edit_fdots, edit_selection_fdots), + [BUFFER_INDEX(ibo.lines_paint_mask)] = BATCH_FLAG(wire_loops), + [BUFFER_INDEX(ibo.lines_adjacency)] = BATCH_FLAG(edge_detection), + [BUFFER_INDEX(ibo.edituv_tris)] = BATCH_FLAG( + edituv_faces, edituv_faces_stretch_area, edituv_faces_stretch_angle), + [BUFFER_INDEX(ibo.edituv_lines)] = BATCH_FLAG(edituv_edges, wire_loops_uvs), + [BUFFER_INDEX(ibo.edituv_points)] = BATCH_FLAG(edituv_verts), + [BUFFER_INDEX(ibo.edituv_fdots)] = BATCH_FLAG(edituv_fdots), + [TRIS_PER_MAT_INDEX] = SURFACE_PER_MAT_FLAG, }; #ifndef NDEBUG @@ -231,7 +232,7 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch } } - if (batch_map & (1u << MBC_BATCH_INDEX(surface_per_mat))) { + if (batch_map & SURFACE_PER_MAT_FLAG) { mesh_batch_cache_discard_surface_batches(cache); } } @@ -661,8 +662,7 @@ static void mesh_batch_cache_init(Mesh *me) cache->mat_len = mesh_render_mat_len_get(me); cache->surface_per_mat = MEM_callocN(sizeof(*cache->surface_per_mat) * cache->mat_len, __func__); - cache->final.tris_per_mat = MEM_callocN(sizeof(*cache->final.tris_per_mat) * cache->mat_len, - __func__); + cache->tris_per_mat = MEM_callocN(sizeof(*cache->tris_per_mat) * cache->mat_len, __func__); cache->is_dirty = false; cache->batch_ready = 0; @@ -688,8 +688,8 @@ static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache, const struct DRW_MeshWeightState *wstate) { if (!drw_mesh_weight_state_compare(&cache->weight_state, wstate)) { - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.weights); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.weights); } GPU_BATCH_CLEAR_SAFE(cache->batch.surface_weights); @@ -708,6 +708,9 @@ static void mesh_batch_cache_request_surface_batches(MeshBatchCache *cache) } } +/* Free batches with material-mapped looptris. + * NOTE: The updating of the indices buffers (#tris_per_mat) is handled in the extractors. + * No need to discard they here. */ static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache) { GPU_BATCH_DISCARD_SAFE(cache->batch.surface); @@ -719,41 +722,41 @@ static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache) static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) { - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.tan); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.tan); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.orco); } - DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo.uv, vbo.tan, vbo.vcol, vbo.orco); + DRWBatchFlag batch_map = BATCH_MAP(vbo.uv, vbo.tan, vbo.vcol, vbo.orco); mesh_batch_cache_discard_batch(cache, batch_map); mesh_cd_layers_type_clear(&cache->cd_used); } static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache) { - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_stretch_angle); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_stretch_area); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_uv); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_tris); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_lines); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots); - } - DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo.edituv_stretch_angle, - vbo.edituv_stretch_area, - vbo.uv, - vbo.edituv_data, - vbo.fdots_uv, - vbo.fdots_edituv_data, - ibo.edituv_tris, - ibo.edituv_lines, - ibo.edituv_points, - ibo.edituv_fdots); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_stretch_angle); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_stretch_area); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_uv); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_tris); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_lines); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_points); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_fdots); + } + DRWBatchFlag batch_map = BATCH_MAP(vbo.edituv_stretch_angle, + vbo.edituv_stretch_area, + vbo.uv, + vbo.edituv_data, + vbo.fdots_uv, + vbo.fdots_edituv_data, + ibo.edituv_tris, + ibo.edituv_lines, + ibo.edituv_points, + ibo.edituv_fdots); mesh_batch_cache_discard_batch(cache, batch_map); cache->tot_area = 0.0f; @@ -768,20 +771,20 @@ static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache) static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache) { - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_tris); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_lines); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots); - } - DRWBatchFlag batch_map = MDEPS_CREATE_MAP(vbo.edituv_data, - vbo.fdots_edituv_data, - ibo.edituv_tris, - ibo.edituv_lines, - ibo.edituv_points, - ibo.edituv_fdots); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_tris); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_lines); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_points); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_fdots); + } + DRWBatchFlag batch_map = BATCH_MAP(vbo.edituv_data, + vbo.fdots_edituv_data, + ibo.edituv_tris, + ibo.edituv_lines, + ibo.edituv_points, + ibo.edituv_fdots); mesh_batch_cache_discard_batch(cache, batch_map); } @@ -794,11 +797,11 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) DRWBatchFlag batch_map; switch (mode) { case BKE_MESH_BATCH_DIRTY_SELECT: - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edit_data); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edit_data); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_nor); } - batch_map = MDEPS_CREATE_MAP(vbo.edit_data, vbo.fdots_nor); + batch_map = BATCH_MAP(vbo.edit_data, vbo.fdots_nor); mesh_batch_cache_discard_batch(cache, batch_map); /* Because visible UVs depends on edit mode selection, discard topology. */ @@ -807,12 +810,12 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) case BKE_MESH_BATCH_DIRTY_SELECT_PAINT: /* Paint mode selection flag is packed inside the nor attribute. * Note that it can be slow if auto smooth is enabled. (see T63946) */ - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.lines_paint_mask); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.lines_paint_mask); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.pos_nor); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.lnor); } - batch_map = MDEPS_CREATE_MAP(ibo.lines_paint_mask, vbo.pos_nor, vbo.lnor); + batch_map = BATCH_MAP(ibo.lines_paint_mask, vbo.pos_nor, vbo.lnor); mesh_batch_cache_discard_batch(cache, batch_map); break; case BKE_MESH_BATCH_DIRTY_ALL: @@ -826,11 +829,11 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) mesh_batch_cache_discard_uvedit(cache); break; case BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT: - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data); } - batch_map = MDEPS_CREATE_MAP(vbo.edituv_data, vbo.fdots_edituv_data); + batch_map = BATCH_MAP(vbo.edituv_data, vbo.fdots_edituv_data); mesh_batch_cache_discard_batch(cache, batch_map); break; default: @@ -838,28 +841,30 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) } } -static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache) +static void mesh_buffer_list_clear(MeshBufferList *mbuflist) { - GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; - GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; - for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { + GPUVertBuf **vbos = (GPUVertBuf **)&mbuflist->vbo; + GPUIndexBuf **ibos = (GPUIndexBuf **)&mbuflist->ibo; + for (int i = 0; i < sizeof(mbuflist->vbo) / sizeof(void *); i++) { GPU_VERTBUF_DISCARD_SAFE(vbos[i]); } - for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { + for (int i = 0; i < sizeof(mbuflist->ibo) / sizeof(void *); i++) { GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); } } -static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache) +static void mesh_buffer_cache_clear(MeshBufferCache *mbc) { - MEM_SAFE_FREE(extraction_cache->loose_geom.verts); - MEM_SAFE_FREE(extraction_cache->loose_geom.edges); - extraction_cache->loose_geom.edge_len = 0; - extraction_cache->loose_geom.vert_len = 0; + mesh_buffer_list_clear(&mbc->buff); - MEM_SAFE_FREE(extraction_cache->poly_sorted.tri_first_index); - MEM_SAFE_FREE(extraction_cache->poly_sorted.mat_tri_len); - extraction_cache->poly_sorted.visible_tri_len = 0; + MEM_SAFE_FREE(mbc->loose_geom.verts); + MEM_SAFE_FREE(mbc->loose_geom.edges); + mbc->loose_geom.edge_len = 0; + mbc->loose_geom.vert_len = 0; + + MEM_SAFE_FREE(mbc->poly_sorted.tri_first_index); + MEM_SAFE_FREE(mbc->poly_sorted.mat_tri_len); + mbc->poly_sorted.visible_tri_len = 0; } static void mesh_batch_cache_clear(Mesh *me) @@ -868,18 +873,14 @@ static void mesh_batch_cache_clear(Mesh *me) if (!cache) { return; } - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - mesh_buffer_cache_clear(mbufcache); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + mesh_buffer_cache_clear(mbc); } - mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache); - mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache); - mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache); - for (int i = 0; i < cache->mat_len; i++) { - GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]); + GPU_INDEXBUF_DISCARD_SAFE(cache->tris_per_mat[i]); } - MEM_SAFE_FREE(cache->final.tris_per_mat); + MEM_SAFE_FREE(cache->tris_per_mat); for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) { GPUBatch **batch = (GPUBatch **)&cache->batch; @@ -1083,8 +1084,8 @@ GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(Mesh *me) /* Request surface to trigger the vbo filling. Otherwise it may do nothing. */ mesh_batch_cache_request_surface_batches(cache); - DRW_vbo_request(NULL, &cache->final.vbo.pos_nor); - return cache->final.vbo.pos_nor; + DRW_vbo_request(NULL, &cache->final.buff.vbo.pos_nor); + return cache->final.buff.vbo.pos_nor; } /** \} */ @@ -1316,22 +1317,22 @@ static void drw_mesh_batch_cache_check_available(struct TaskGraph *task_graph, M BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0)); } for (int i = 0; i < MBC_VBO_LEN; i++) { - BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i])); + BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.buff.vbo)[i])); } for (int i = 0; i < MBC_IBO_LEN; i++) { - BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i])); + BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.buff.ibo)[i])); } for (int i = 0; i < MBC_VBO_LEN; i++) { - BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i])); + BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.buff.vbo)[i])); } for (int i = 0; i < MBC_IBO_LEN; i++) { - BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i])); + BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.buff.ibo)[i])); } for (int i = 0; i < MBC_VBO_LEN; i++) { - BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i])); + BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.buff.vbo)[i])); } for (int i = 0; i < MBC_IBO_LEN; i++) { - BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i])); + BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.buff.ibo)[i])); } } #endif @@ -1414,25 +1415,25 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); if (cd_overlap == false) { - FOREACH_MESH_BUFFER_CACHE (cache, mbuffercache) { + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.uv); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); cd_uv_update = true; } if ((cache->cd_used.tan & cache->cd_needed.tan) != cache->cd_needed.tan || cache->cd_used.tan_orco != cache->cd_needed.tan_orco) { - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.tan); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.tan); } if (cache->cd_used.orco != cache->cd_needed.orco) { - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.orco); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.orco); } if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) { - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.sculpt_data); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data); } if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) || ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) != cache->cd_needed.sculpt_vcol)) { - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.vcol); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } } /* We can't discard batches at this point as they have been @@ -1454,14 +1455,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, const bool is_uvsyncsel = ts && (ts->uv_flag & UV_SYNC_SELECTION); if (cd_uv_update || (cache->is_uvsyncsel != is_uvsyncsel)) { cache->is_uvsyncsel = is_uvsyncsel; - FOREACH_MESH_BUFFER_CACHE (cache, mbuffercache) { - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.edituv_data); - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.fdots_uv); - GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.fdots_edituv_data); - GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_tris); - GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_lines); - GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_points); - GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.edituv_fdots); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.edituv_data); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_uv); + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.fdots_edituv_data); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_tris); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_lines); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_points); + GPU_INDEXBUF_DISCARD_SAFE(mbc->buff.ibo.edituv_fdots); } /* We only clear the batches as they may already have been * referenced. */ @@ -1502,173 +1503,174 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, const bool do_uvcage = is_editmode && !me->edit_mesh->mesh_eval_final->runtime.is_original; - MeshBufferCache *mbufcache = &cache->final; + MeshBufferList *mbuflist = &cache->final.buff; /* Initialize batches and request VBO's & IBO's. */ - MDEPS_ASSERT(batch.surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); + MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.surface, &mbufcache->ibo.tris); + DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris); /* Order matters. First ones override latest VBO's attributes. */ - DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.lnor); - DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.lnor); + DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.pos_nor); if (cache->cd_used.uv != 0) { - DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.uv); + DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv); } if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { - DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.vcol); + DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol); } } - MDEPS_ASSERT(batch.all_verts, vbo.pos_nor); + MDEPS_ASSERT(all_verts, vbo.pos_nor); if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache->batch.all_verts, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.all_verts, &mbuflist->vbo.pos_nor); } - MDEPS_ASSERT(batch.sculpt_overlays, ibo.tris, vbo.pos_nor, vbo.sculpt_data); + MDEPS_ASSERT(sculpt_overlays, ibo.tris, vbo.pos_nor, vbo.sculpt_data); if (DRW_batch_requested(cache->batch.sculpt_overlays, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.sculpt_overlays, &mbufcache->ibo.tris); - DRW_vbo_request(cache->batch.sculpt_overlays, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.sculpt_overlays, &mbufcache->vbo.sculpt_data); + DRW_ibo_request(cache->batch.sculpt_overlays, &mbuflist->ibo.tris); + DRW_vbo_request(cache->batch.sculpt_overlays, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.sculpt_overlays, &mbuflist->vbo.sculpt_data); } - MDEPS_ASSERT(batch.all_edges, ibo.lines, vbo.pos_nor); + MDEPS_ASSERT(all_edges, ibo.lines, vbo.pos_nor); if (DRW_batch_requested(cache->batch.all_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.all_edges, &mbufcache->ibo.lines); - DRW_vbo_request(cache->batch.all_edges, &mbufcache->vbo.pos_nor); + DRW_ibo_request(cache->batch.all_edges, &mbuflist->ibo.lines); + DRW_vbo_request(cache->batch.all_edges, &mbuflist->vbo.pos_nor); } - MDEPS_ASSERT(batch.loose_edges, ibo.lines_loose, vbo.pos_nor); + MDEPS_ASSERT(loose_edges, ibo.lines_loose, vbo.pos_nor); if (DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(NULL, &mbufcache->ibo.lines); - DRW_ibo_request(cache->batch.loose_edges, &mbufcache->ibo.lines_loose); - DRW_vbo_request(cache->batch.loose_edges, &mbufcache->vbo.pos_nor); + DRW_ibo_request(NULL, &mbuflist->ibo.lines); + DRW_ibo_request(cache->batch.loose_edges, &mbuflist->ibo.lines_loose); + DRW_vbo_request(cache->batch.loose_edges, &mbuflist->vbo.pos_nor); } - MDEPS_ASSERT(batch.edge_detection, ibo.lines_adjacency, vbo.pos_nor); + MDEPS_ASSERT(edge_detection, ibo.lines_adjacency, vbo.pos_nor); if (DRW_batch_requested(cache->batch.edge_detection, GPU_PRIM_LINES_ADJ)) { - DRW_ibo_request(cache->batch.edge_detection, &mbufcache->ibo.lines_adjacency); - DRW_vbo_request(cache->batch.edge_detection, &mbufcache->vbo.pos_nor); + DRW_ibo_request(cache->batch.edge_detection, &mbuflist->ibo.lines_adjacency); + DRW_vbo_request(cache->batch.edge_detection, &mbuflist->vbo.pos_nor); } - MDEPS_ASSERT(batch.surface_weights, ibo.tris, vbo.pos_nor, vbo.weights); + MDEPS_ASSERT(surface_weights, ibo.tris, vbo.pos_nor, vbo.weights); if (DRW_batch_requested(cache->batch.surface_weights, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.surface_weights, &mbufcache->ibo.tris); - DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.surface_weights, &mbufcache->vbo.weights); + DRW_ibo_request(cache->batch.surface_weights, &mbuflist->ibo.tris); + DRW_vbo_request(cache->batch.surface_weights, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.surface_weights, &mbuflist->vbo.weights); } - MDEPS_ASSERT(batch.wire_loops, ibo.lines_paint_mask, vbo.lnor, vbo.pos_nor); + MDEPS_ASSERT(wire_loops, ibo.lines_paint_mask, vbo.lnor, vbo.pos_nor); if (DRW_batch_requested(cache->batch.wire_loops, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.wire_loops, &mbufcache->ibo.lines_paint_mask); + DRW_ibo_request(cache->batch.wire_loops, &mbuflist->ibo.lines_paint_mask); /* Order matters. First ones override latest VBO's attributes. */ - DRW_vbo_request(cache->batch.wire_loops, &mbufcache->vbo.lnor); - DRW_vbo_request(cache->batch.wire_loops, &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->batch.wire_loops, &mbuflist->vbo.lnor); + DRW_vbo_request(cache->batch.wire_loops, &mbuflist->vbo.pos_nor); } - MDEPS_ASSERT(batch.wire_edges, ibo.lines, vbo.pos_nor, vbo.edge_fac); + MDEPS_ASSERT(wire_edges, ibo.lines, vbo.pos_nor, vbo.edge_fac); if (DRW_batch_requested(cache->batch.wire_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.wire_edges, &mbufcache->ibo.lines); - DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.wire_edges, &mbufcache->vbo.edge_fac); + DRW_ibo_request(cache->batch.wire_edges, &mbuflist->ibo.lines); + DRW_vbo_request(cache->batch.wire_edges, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.wire_edges, &mbuflist->vbo.edge_fac); } - MDEPS_ASSERT(batch.wire_loops_uvs, ibo.edituv_lines, vbo.uv); + MDEPS_ASSERT(wire_loops_uvs, ibo.edituv_lines, vbo.uv); if (DRW_batch_requested(cache->batch.wire_loops_uvs, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.wire_loops_uvs, &mbufcache->ibo.edituv_lines); + DRW_ibo_request(cache->batch.wire_loops_uvs, &mbuflist->ibo.edituv_lines); /* For paint overlay. Active layer should have been queried. */ if (cache->cd_used.uv != 0) { - DRW_vbo_request(cache->batch.wire_loops_uvs, &mbufcache->vbo.uv); + DRW_vbo_request(cache->batch.wire_loops_uvs, &mbuflist->vbo.uv); } } - MDEPS_ASSERT(batch.edit_mesh_analysis, ibo.tris, vbo.pos_nor, vbo.mesh_analysis); + MDEPS_ASSERT(edit_mesh_analysis, ibo.tris, vbo.pos_nor, vbo.mesh_analysis); if (DRW_batch_requested(cache->batch.edit_mesh_analysis, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edit_mesh_analysis, &mbufcache->ibo.tris); - DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbufcache->vbo.mesh_analysis); + DRW_ibo_request(cache->batch.edit_mesh_analysis, &mbuflist->ibo.tris); + DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_mesh_analysis, &mbuflist->vbo.mesh_analysis); } /* Per Material */ - MDEPS_ASSERT( - surface_per_mat, tris_per_mat, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco); + MDEPS_ASSERT_FLAG( + SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco); + MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG); for (int i = 0; i < cache->mat_len; i++) { if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->surface_per_mat[i], &mbufcache->tris_per_mat[i]); + DRW_ibo_request(cache->surface_per_mat[i], &cache->tris_per_mat[i]); /* Order matters. First ones override latest VBO's attributes. */ - DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.lnor); - DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.pos_nor); + DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.lnor); + DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.pos_nor); if (cache->cd_used.uv != 0) { - DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.uv); + DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.uv); } if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) { - DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.tan); + DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan); } if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { - DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.vcol); + DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol); } if (cache->cd_used.orco != 0) { - DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.orco); + DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco); } } } - mbufcache = (do_cage) ? &cache->cage : &cache->final; + mbuflist = (do_cage) ? &cache->cage.buff : &cache->final.buff; /* Edit Mesh */ - MDEPS_ASSERT(batch.edit_triangles, ibo.tris, vbo.pos_nor, vbo.edit_data); + MDEPS_ASSERT(edit_triangles, ibo.tris, vbo.pos_nor, vbo.edit_data); if (DRW_batch_requested(cache->batch.edit_triangles, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edit_triangles, &mbufcache->ibo.tris); - DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_triangles, &mbufcache->vbo.edit_data); + DRW_ibo_request(cache->batch.edit_triangles, &mbuflist->ibo.tris); + DRW_vbo_request(cache->batch.edit_triangles, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_triangles, &mbuflist->vbo.edit_data); } - MDEPS_ASSERT(batch.edit_vertices, ibo.points, vbo.pos_nor, vbo.edit_data); + MDEPS_ASSERT(edit_vertices, ibo.points, vbo.pos_nor, vbo.edit_data); if (DRW_batch_requested(cache->batch.edit_vertices, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_vertices, &mbufcache->ibo.points); - DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_vertices, &mbufcache->vbo.edit_data); + DRW_ibo_request(cache->batch.edit_vertices, &mbuflist->ibo.points); + DRW_vbo_request(cache->batch.edit_vertices, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_vertices, &mbuflist->vbo.edit_data); } - MDEPS_ASSERT(batch.edit_edges, ibo.lines, vbo.pos_nor, vbo.edit_data); + MDEPS_ASSERT(edit_edges, ibo.lines, vbo.pos_nor, vbo.edit_data); if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.edit_edges, &mbufcache->ibo.lines); - DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_edges, &mbufcache->vbo.edit_data); + DRW_ibo_request(cache->batch.edit_edges, &mbuflist->ibo.lines); + DRW_vbo_request(cache->batch.edit_edges, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_edges, &mbuflist->vbo.edit_data); } - MDEPS_ASSERT(batch.edit_vnor, ibo.points, vbo.pos_nor); + MDEPS_ASSERT(edit_vnor, ibo.points, vbo.pos_nor); if (DRW_batch_requested(cache->batch.edit_vnor, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_vnor, &mbufcache->ibo.points); - DRW_vbo_request(cache->batch.edit_vnor, &mbufcache->vbo.pos_nor); + DRW_ibo_request(cache->batch.edit_vnor, &mbuflist->ibo.points); + DRW_vbo_request(cache->batch.edit_vnor, &mbuflist->vbo.pos_nor); } - MDEPS_ASSERT(batch.edit_lnor, ibo.tris, vbo.pos_nor, vbo.lnor); + MDEPS_ASSERT(edit_lnor, ibo.tris, vbo.pos_nor, vbo.lnor); if (DRW_batch_requested(cache->batch.edit_lnor, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_lnor, &mbufcache->ibo.tris); - DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_lnor, &mbufcache->vbo.lnor); + DRW_ibo_request(cache->batch.edit_lnor, &mbuflist->ibo.tris); + DRW_vbo_request(cache->batch.edit_lnor, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_lnor, &mbuflist->vbo.lnor); } - MDEPS_ASSERT(batch.edit_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdots_nor); + MDEPS_ASSERT(edit_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdots_nor); if (DRW_batch_requested(cache->batch.edit_fdots, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_fdots, &mbufcache->ibo.fdots); - DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_pos); - DRW_vbo_request(cache->batch.edit_fdots, &mbufcache->vbo.fdots_nor); + DRW_ibo_request(cache->batch.edit_fdots, &mbuflist->ibo.fdots); + DRW_vbo_request(cache->batch.edit_fdots, &mbuflist->vbo.fdots_pos); + DRW_vbo_request(cache->batch.edit_fdots, &mbuflist->vbo.fdots_nor); } - MDEPS_ASSERT(batch.edit_skin_roots, vbo.skin_roots); + MDEPS_ASSERT(edit_skin_roots, vbo.skin_roots); if (DRW_batch_requested(cache->batch.edit_skin_roots, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache->batch.edit_skin_roots, &mbufcache->vbo.skin_roots); + DRW_vbo_request(cache->batch.edit_skin_roots, &mbuflist->vbo.skin_roots); } /* Selection */ - MDEPS_ASSERT(batch.edit_selection_verts, ibo.points, vbo.pos_nor, vbo.vert_idx); + MDEPS_ASSERT(edit_selection_verts, ibo.points, vbo.pos_nor, vbo.vert_idx); if (DRW_batch_requested(cache->batch.edit_selection_verts, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_selection_verts, &mbufcache->ibo.points); - DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_selection_verts, &mbufcache->vbo.vert_idx); + DRW_ibo_request(cache->batch.edit_selection_verts, &mbuflist->ibo.points); + DRW_vbo_request(cache->batch.edit_selection_verts, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_selection_verts, &mbuflist->vbo.vert_idx); } - MDEPS_ASSERT(batch.edit_selection_edges, ibo.lines, vbo.pos_nor, vbo.edge_idx); + MDEPS_ASSERT(edit_selection_edges, ibo.lines, vbo.pos_nor, vbo.edge_idx); if (DRW_batch_requested(cache->batch.edit_selection_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.edit_selection_edges, &mbufcache->ibo.lines); - DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_selection_edges, &mbufcache->vbo.edge_idx); + DRW_ibo_request(cache->batch.edit_selection_edges, &mbuflist->ibo.lines); + DRW_vbo_request(cache->batch.edit_selection_edges, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_selection_edges, &mbuflist->vbo.edge_idx); } - MDEPS_ASSERT(batch.edit_selection_faces, ibo.tris, vbo.pos_nor, vbo.poly_idx); + MDEPS_ASSERT(edit_selection_faces, ibo.tris, vbo.pos_nor, vbo.poly_idx); if (DRW_batch_requested(cache->batch.edit_selection_faces, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edit_selection_faces, &mbufcache->ibo.tris); - DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.pos_nor); - DRW_vbo_request(cache->batch.edit_selection_faces, &mbufcache->vbo.poly_idx); + DRW_ibo_request(cache->batch.edit_selection_faces, &mbuflist->ibo.tris); + DRW_vbo_request(cache->batch.edit_selection_faces, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.edit_selection_faces, &mbuflist->vbo.poly_idx); } - MDEPS_ASSERT(batch.edit_selection_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdot_idx); + MDEPS_ASSERT(edit_selection_fdots, ibo.fdots, vbo.fdots_pos, vbo.fdot_idx); if (DRW_batch_requested(cache->batch.edit_selection_fdots, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edit_selection_fdots, &mbufcache->ibo.fdots); - DRW_vbo_request(cache->batch.edit_selection_fdots, &mbufcache->vbo.fdots_pos); - DRW_vbo_request(cache->batch.edit_selection_fdots, &mbufcache->vbo.fdot_idx); + DRW_ibo_request(cache->batch.edit_selection_fdots, &mbuflist->ibo.fdots); + DRW_vbo_request(cache->batch.edit_selection_fdots, &mbuflist->vbo.fdots_pos); + DRW_vbo_request(cache->batch.edit_selection_fdots, &mbuflist->vbo.fdot_idx); } /** @@ -1676,54 +1678,54 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, * but the selection code for UVs needs to support it first. So for now, only * display the cage in all cases. */ - mbufcache = (do_uvcage) ? &cache->uv_cage : &cache->final; + mbuflist = (do_uvcage) ? &cache->uv_cage.buff : &cache->final.buff; /* Edit UV */ - MDEPS_ASSERT(batch.edituv_faces, ibo.edituv_tris, vbo.uv, vbo.edituv_data); + MDEPS_ASSERT(edituv_faces, ibo.edituv_tris, vbo.uv, vbo.edituv_data); if (DRW_batch_requested(cache->batch.edituv_faces, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edituv_faces, &mbufcache->ibo.edituv_tris); - DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.uv); - DRW_vbo_request(cache->batch.edituv_faces, &mbufcache->vbo.edituv_data); + DRW_ibo_request(cache->batch.edituv_faces, &mbuflist->ibo.edituv_tris); + DRW_vbo_request(cache->batch.edituv_faces, &mbuflist->vbo.uv); + DRW_vbo_request(cache->batch.edituv_faces, &mbuflist->vbo.edituv_data); } - MDEPS_ASSERT(batch.edituv_faces_stretch_area, + MDEPS_ASSERT(edituv_faces_stretch_area, ibo.edituv_tris, vbo.uv, vbo.edituv_data, vbo.edituv_stretch_area); if (DRW_batch_requested(cache->batch.edituv_faces_stretch_area, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->ibo.edituv_tris); - DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.uv); - DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.edituv_data); - DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbufcache->vbo.edituv_stretch_area); + DRW_ibo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->ibo.edituv_tris); + DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->vbo.uv); + DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->vbo.edituv_data); + DRW_vbo_request(cache->batch.edituv_faces_stretch_area, &mbuflist->vbo.edituv_stretch_area); } - MDEPS_ASSERT(batch.edituv_faces_stretch_angle, + MDEPS_ASSERT(edituv_faces_stretch_angle, ibo.edituv_tris, vbo.uv, vbo.edituv_data, vbo.edituv_stretch_angle); if (DRW_batch_requested(cache->batch.edituv_faces_stretch_angle, GPU_PRIM_TRIS)) { - DRW_ibo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->ibo.edituv_tris); - DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.uv); - DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.edituv_data); - DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbufcache->vbo.edituv_stretch_angle); + DRW_ibo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->ibo.edituv_tris); + DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->vbo.uv); + DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->vbo.edituv_data); + DRW_vbo_request(cache->batch.edituv_faces_stretch_angle, &mbuflist->vbo.edituv_stretch_angle); } - MDEPS_ASSERT(batch.edituv_edges, ibo.edituv_lines, vbo.uv, vbo.edituv_data); + MDEPS_ASSERT(edituv_edges, ibo.edituv_lines, vbo.uv, vbo.edituv_data); if (DRW_batch_requested(cache->batch.edituv_edges, GPU_PRIM_LINES)) { - DRW_ibo_request(cache->batch.edituv_edges, &mbufcache->ibo.edituv_lines); - DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.uv); - DRW_vbo_request(cache->batch.edituv_edges, &mbufcache->vbo.edituv_data); + DRW_ibo_request(cache->batch.edituv_edges, &mbuflist->ibo.edituv_lines); + DRW_vbo_request(cache->batch.edituv_edges, &mbuflist->vbo.uv); + DRW_vbo_request(cache->batch.edituv_edges, &mbuflist->vbo.edituv_data); } - MDEPS_ASSERT(batch.edituv_verts, ibo.edituv_points, vbo.uv, vbo.edituv_data); + MDEPS_ASSERT(edituv_verts, ibo.edituv_points, vbo.uv, vbo.edituv_data); if (DRW_batch_requested(cache->batch.edituv_verts, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edituv_verts, &mbufcache->ibo.edituv_points); - DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.uv); - DRW_vbo_request(cache->batch.edituv_verts, &mbufcache->vbo.edituv_data); + DRW_ibo_request(cache->batch.edituv_verts, &mbuflist->ibo.edituv_points); + DRW_vbo_request(cache->batch.edituv_verts, &mbuflist->vbo.uv); + DRW_vbo_request(cache->batch.edituv_verts, &mbuflist->vbo.edituv_data); } - MDEPS_ASSERT(batch.edituv_fdots, ibo.edituv_fdots, vbo.fdots_uv, vbo.fdots_edituv_data); + MDEPS_ASSERT(edituv_fdots, ibo.edituv_fdots, vbo.fdots_uv, vbo.fdots_edituv_data); if (DRW_batch_requested(cache->batch.edituv_fdots, GPU_PRIM_POINTS)) { - DRW_ibo_request(cache->batch.edituv_fdots, &mbufcache->ibo.edituv_fdots); - DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_uv); - DRW_vbo_request(cache->batch.edituv_fdots, &mbufcache->vbo.fdots_edituv_data); + DRW_ibo_request(cache->batch.edituv_fdots, &mbuflist->ibo.edituv_fdots); + DRW_vbo_request(cache->batch.edituv_fdots, &mbuflist->vbo.fdots_uv); + DRW_vbo_request(cache->batch.edituv_fdots, &mbuflist->vbo.fdots_edituv_data); } MDEPS_ASSERT_MAP(vbo.lnor); @@ -1762,7 +1764,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP(ibo.edituv_points); MDEPS_ASSERT_MAP(ibo.edituv_fdots); - MDEPS_ASSERT_MAP(tris_per_mat); + MDEPS_ASSERT_MAP_INDEX(TRIS_PER_MAT_INDEX); /* Meh loose Scene const correctness here. */ const bool use_subsurf_fdots = scene ? BKE_modifiers_uses_subsurf_facedots(scene, ob) : false; @@ -1771,7 +1773,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, &cache->uv_cage, - &cache->uv_cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1789,7 +1790,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, &cache->cage, - &cache->cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1806,7 +1806,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, &cache->final, - &cache->final_extraction_cache, me, is_editmode, is_paint_mode, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.c b/source/blender/draw/intern/mesh_extractors/extract_mesh.c index 53827dcc7d9..44de07c8594 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.c +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.c @@ -33,11 +33,11 @@ #include "draw_cache_impl.h" -void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferList *mbuflist) { /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to - * `MeshBufferCache *`. What shows a different usage versus intent. */ - void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); + * `MeshBufferList *`. What shows a different usage versus intent. */ + void **buffer_ptr = (void **)POINTER_OFFSET(mbuflist, extractor->mesh_buffer_offset); void *buffer = *buffer_ptr; BLI_assert(buffer); return buffer; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index f24ccf1a028..d9f397fd8b8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -232,7 +232,7 @@ typedef struct MeshExtract { /** Used to know if the element callbacks are thread-safe and can be parallelized. */ bool use_threading; /** - * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index + * Offset in bytes of the buffer inside a MeshBufferList instance. Points to a vertex or index * buffer. */ size_t mesh_buffer_offset; @@ -252,11 +252,11 @@ MeshRenderData *mesh_render_data_create(Mesh *me, void mesh_render_data_free(MeshRenderData *mr); void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag); void mesh_render_data_update_loose_geom(MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, const eMRIterType iter_type, const eMRDataType data_flag); void mesh_render_data_update_polys_sorted(MeshRenderData *mr, - MeshBufferExtractionCache *cache, + MeshBufferCache *cache, const eMRDataType data_flag); void mesh_render_data_update_looptris(MeshRenderData *mr, const eMRIterType iter_type, @@ -270,7 +270,7 @@ typedef struct EditLoopData { uchar bweight; } EditLoopData; -void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc); +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferList *mbuflist); eMRIterType mesh_extract_iter_type(const MeshExtract *ext); const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, const bool do_hq_normals, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index 5bd5f7adaa8..d06fb91411e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -104,7 +104,7 @@ constexpr MeshExtract create_extractor_edituv_tris() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_tris); return extractor; } @@ -194,7 +194,7 @@ constexpr MeshExtract create_extractor_edituv_lines() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_lines); return extractor; } @@ -278,7 +278,7 @@ constexpr MeshExtract create_extractor_edituv_points() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_points); return extractor; } @@ -374,7 +374,7 @@ constexpr MeshExtract create_extractor_edituv_fdots() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_EditUvElem_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.edituv_fdots); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc index 0f41702a22c..ea58e1aeed8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -105,7 +105,7 @@ constexpr MeshExtract create_extractor_fdots() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.fdots); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index 0096da9e56f..54f5611106f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -168,7 +168,7 @@ constexpr MeshExtract create_extractor_lines() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines); return extractor; } @@ -180,12 +180,12 @@ constexpr MeshExtract create_extractor_lines() static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) { - BLI_assert(cache->final.ibo.lines); + BLI_assert(cache->final.buff.ibo.lines); /* Multiply by 2 because these are edges indices. */ const int start = mr->edge_len * 2; const int len = mr->edge_loose_len * 2; GPU_indexbuf_create_subrange_in_place( - cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len); + cache->final.buff.ibo.lines_loose, cache->final.buff.ibo.lines, start, len); cache->no_loose_wire = (len == 0); } @@ -213,7 +213,7 @@ constexpr MeshExtract create_extractor_lines_with_lines_loose() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines); return extractor; } @@ -228,7 +228,7 @@ static void extract_lines_loose_only_init(const MeshRenderData *mr, void *buf, void *UNUSED(tls_data)) { - BLI_assert(buf == cache->final.ibo.lines_loose); + BLI_assert(buf == cache->final.buff.ibo.lines_loose); UNUSED_VARS_NDEBUG(buf); extract_lines_loose_subbuffer(mr, cache); } @@ -240,7 +240,7 @@ constexpr MeshExtract create_extractor_lines_loose_only() extractor.data_type = MR_DATA_LOOSE_GEOM; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines_loose); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc index 7a37cf50264..522afcd44a1 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc @@ -185,7 +185,7 @@ constexpr MeshExtract create_extractor_lines_adjacency() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_LineAdjacency_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines_adjacency); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc index 5def7edb75a..494a43e97d1 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc @@ -114,7 +114,7 @@ constexpr MeshExtract create_extractor_lines_paint_mask() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_LinePaintMask_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.lines_paint_mask); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc index cc1a19b8d26..b801ba04162 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc @@ -169,7 +169,7 @@ constexpr MeshExtract create_extractor_points() extractor.use_threading = true; extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(GPUIndexBufBuilder); - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.points); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index e346177af52..54e733d3d86 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -105,20 +105,19 @@ static void extract_tris_finish(const MeshRenderData *mr, /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch * is created before the surfaces-per-material. */ - if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc_final = &cache->final; + if (mr->use_final_mesh && cache->tris_per_mat) { int mat_start = 0; for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc_final->tris_per_mat[i] == nullptr) { - mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); + if (cache->tris_per_mat[i] == nullptr) { + cache->tris_per_mat[i] = GPU_indexbuf_calloc(); } const int mat_tri_len = mr->poly_sorted.mat_tri_len[i]; /* Multiply by 3 because these are triangle indices. */ const int start = mat_start * 3; const int len = mat_tri_len * 3; - GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); + GPU_indexbuf_create_subrange_in_place(cache->tris_per_mat[i], ibo, start, len); mat_start += mat_tri_len; } } @@ -135,7 +134,7 @@ constexpr MeshExtract create_extractor_tris() extractor.data_type = MR_DATA_LOOPTRI | MR_DATA_POLYS_SORTED; extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris); return extractor; } @@ -197,17 +196,16 @@ static void extract_tris_single_mat_finish(const MeshRenderData *mr, /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch * is created before the surfaces-per-material. */ - if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc = &cache->final; + if (mr->use_final_mesh && cache->tris_per_mat) { for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc->tris_per_mat[i] == nullptr) { - mbc->tris_per_mat[i] = GPU_indexbuf_calloc(); + if (cache->tris_per_mat[i] == nullptr) { + cache->tris_per_mat[i] = GPU_indexbuf_calloc(); } /* Multiply by 3 because these are triangle indices. */ const int len = mr->tri_len * 3; - GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, 0, len); + GPU_indexbuf_create_subrange_in_place(cache->tris_per_mat[i], ibo, 0, len); } } } @@ -223,7 +221,7 @@ constexpr MeshExtract create_extractor_tris_single_mat() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(GPUIndexBufBuilder); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc index 302616d4da9..2e2444a8e3d 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc @@ -228,7 +228,7 @@ constexpr MeshExtract create_extractor_edge_fac() extractor.data_type = MR_DATA_POLY_NOR; extractor.data_size = sizeof(MeshExtract_EdgeFac_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edge_fac); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc index a7efb9c8a1b..5232346e51e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc @@ -253,7 +253,7 @@ constexpr MeshExtract create_extractor_edit_data() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(EditLoopData *); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edit_data); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc index 0378aadabd0..b8494428eed 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc @@ -128,7 +128,7 @@ constexpr MeshExtract create_extractor_edituv_data() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_EditUVData_Data); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edituv_data); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc index a60c0182e89..a947d98f955 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc @@ -222,7 +222,7 @@ constexpr MeshExtract create_extractor_edituv_edituv_stretch_angle() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_StretchAngle_Data); extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edituv_stretch_angle); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc index d79ac493c33..3db8cd79af5 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc @@ -143,7 +143,7 @@ constexpr MeshExtract create_extractor_edituv_stretch_area() extractor.data_type = MR_DATA_NONE; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edituv_stretch_area); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc index b7182d1b60f..28592417183 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc @@ -89,7 +89,7 @@ constexpr MeshExtract create_extractor_fdots_edituv_data() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_EditUVFdotData_Data); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_edituv_data); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc index 5e4ad54f7b6..fed66f0057d 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc @@ -105,7 +105,7 @@ constexpr MeshExtract create_extractor_fdots_nor() extractor.data_type = MR_DATA_LOOP_NOR; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_nor); return extractor; } @@ -186,7 +186,7 @@ constexpr MeshExtract create_extractor_fdots_nor_hq() extractor.data_type = MR_DATA_LOOP_NOR; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_nor); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc index e765fb8a8bf..33f9180e122 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc @@ -106,7 +106,7 @@ constexpr MeshExtract create_extractor_fdots_pos() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(float(*)[3]); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_pos); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc index 042a0d2debe..6fe714def30 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc @@ -114,7 +114,7 @@ constexpr MeshExtract create_extractor_fdots_uv() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_FdotUV_Data); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdots_uv); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc index 8344a615cbe..f9f66c27aaa 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc @@ -116,7 +116,7 @@ constexpr MeshExtract create_extractor_lnor() extractor.data_type = MR_DATA_LOOP_NOR; extractor.data_size = sizeof(GPUPackedNormal *); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.lnor); return extractor; } @@ -214,7 +214,7 @@ constexpr MeshExtract create_extractor_lnor_hq() extractor.data_type = MR_DATA_LOOP_NOR; extractor.data_size = sizeof(gpuHQNor *); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.lnor); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc index 075d54e268e..33a33c81bc2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc @@ -641,7 +641,7 @@ constexpr MeshExtract create_extractor_mesh_analysis() extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.mesh_analysis); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc index 269c0343e32..c5187a47892 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc @@ -102,7 +102,7 @@ constexpr MeshExtract create_extractor_orco() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_Orco_Data); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.orco); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index b8e5dcb613f..eb9a138590c 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -208,7 +208,7 @@ constexpr MeshExtract create_extractor_pos_nor() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_PosNor_Data); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.pos_nor); return extractor; } @@ -401,7 +401,7 @@ constexpr MeshExtract create_extractor_pos_nor_hq() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_PosNorHQ_Data); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.pos_nor); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc index dbb1e1f880b..fd91bc5258f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc @@ -128,7 +128,7 @@ constexpr MeshExtract create_extractor_sculpt_data() extractor.data_type = MR_DATA_NONE; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.sculpt_data); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc index cd8d46901c5..5ac30dd3be9 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc @@ -205,7 +205,7 @@ constexpr MeshExtract create_extractor_poly_idx() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(uint32_t *); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.poly_idx); return extractor; } @@ -220,7 +220,7 @@ constexpr MeshExtract create_extractor_edge_idx() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(uint32_t *); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.edge_idx); return extractor; } @@ -237,7 +237,7 @@ constexpr MeshExtract create_extractor_vert_idx() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(uint32_t *); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.vert_idx); return extractor; } @@ -279,7 +279,7 @@ constexpr MeshExtract create_extractor_fdot_idx() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(uint32_t *); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.fdot_idx); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc index ffca01d00dd..1847a360837 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc @@ -80,7 +80,7 @@ constexpr MeshExtract create_extractor_skin_roots() extractor.data_type = MR_DATA_NONE; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.skin_roots); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index 8f36bfdf1ef..1f8776fc98e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -226,7 +226,7 @@ constexpr MeshExtract create_extractor_tan() extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.tan); return extractor; } @@ -252,7 +252,7 @@ constexpr MeshExtract create_extractor_tan_hq() extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.tan); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index 013da7d674a..af279b08a59 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -123,7 +123,7 @@ constexpr MeshExtract create_extractor_uv() extractor.data_type = MR_DATA_NONE; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.uv); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index d810acfb617..2c7770c8e72 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -178,7 +178,7 @@ constexpr MeshExtract create_extractor_vcol() extractor.data_type = MR_DATA_NONE; extractor.data_size = 0; extractor.use_threading = false; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.vcol); return extractor; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc index c547a453aeb..bdb1410a755 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc @@ -176,7 +176,7 @@ constexpr MeshExtract create_extractor_weights() extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_Weight_Data); extractor.use_threading = true; - extractor.mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights); + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.weights); return extractor; } diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 39fb2882e4b..b7deea5069e 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -576,7 +576,7 @@ static int paste_from_file_exec(bContext *C, wmOperator *op) char *path; int retval; - path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0); + path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0, NULL); retval = paste_from_file(C, op->reports, path); MEM_freeN(path); @@ -1627,7 +1627,7 @@ static int insert_text_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - inserted_utf8 = RNA_string_get_alloc(op->ptr, "text", NULL, 0); + inserted_utf8 = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL); len = BLI_strlen_utf8(inserted_utf8); inserted_text = MEM_callocN(sizeof(char32_t) * (len + 1), "FONT_insert_text"); diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 8640ffa67cf..a8bd3b11bb1 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -890,9 +890,9 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent } case MOUSEMOVE: /* calculate new position */ { - /* only handle mousemove if not doing numinput */ + /* Only handle mouse-move if not doing numeric-input. */ if (has_numinput == false) { - /* update shift based on position of mouse */ + /* Update shift based on position of mouse. */ gpencil_mouse_update_shift(tgpi, op, event); /* update screen */ diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 894fb83d70e..5ecb6d9a212 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1944,9 +1944,9 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e if (ELEM(tgpi->flag, IN_CURVE_EDIT)) { break; } - /* only handle mousemove if not doing numinput */ + /* Only handle mouse-move if not doing numeric-input. */ if (has_numinput == false) { - /* update position of mouse */ + /* Update position of mouse. */ copy_v2_v2(tgpi->end, tgpi->mval); copy_v2_v2(tgpi->start, tgpi->origin); if (tgpi->flag == IDLE) { diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index c99c7f681b3..dde8a637e05 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -185,7 +185,7 @@ typedef enum ThemeColorID { TH_NODE_LAYOUT, TH_NODE_SHADER, TH_NODE_INTERFACE, - TH_NODE_CONVERTOR, + TH_NODE_CONVERTER, TH_NODE_GROUP, TH_NODE_FRAME, TH_NODE_MATTE, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b8f324cba83..a618daec4bc 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3350,7 +3350,7 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in if (pbuf) { if (UI_but_is_utf8(but)) { - buf_len -= BLI_utf8_invalid_strip(pbuf, (size_t)buf_len); + buf_len -= BLI_str_utf8_invalid_strip(pbuf, (size_t)buf_len); } ui_textedit_insert_buf(but, data, pbuf, buf_len); @@ -3527,7 +3527,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) if (but) { if (UI_but_is_utf8(but)) { - const int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr)); + const int strip = BLI_str_utf8_invalid_strip(but->editstr, strlen(but->editstr)); /* not a file?, strip non utf-8 chars */ if (strip) { /* won't happen often so isn't that annoying to keep it here for a while */ @@ -6025,7 +6025,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co * the slot menu fails to switch a second time. * * The active state of the button could be maintained some other way - * and remove this mousemove event. + * and remove this mouse-move event. */ WM_event_add_mousemove(data->window); @@ -8364,7 +8364,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s } } - /* wait for mousemove to enable drag */ + /* Wait for mouse-move to enable drag. */ if (state == BUTTON_STATE_WAIT_DRAG) { but->flag &= ~UI_SELECT; } @@ -8631,9 +8631,9 @@ static void button_activate_exit( ui_but_update(but); } - /* adds empty mousemove in queue for re-init handler, in case mouse is + /* Adds empty mouse-move in queue for re-initialize handler, in case mouse is * still over a button. We cannot just check for this ourselves because - * at this point the mouse may be over a button in another region */ + * at this point the mouse may be over a button in another region. */ if (mousemove) { WM_event_add_mousemove(CTX_wm_window(C)); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 351b73c320b..0e5a6a79137 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6489,6 +6489,17 @@ void uiTemplateCacheFile(uiLayout *layout, uiLayoutSetActive(row, engine_supports_procedural); uiItemR(row, &fileptr, "use_render_procedural", 0, NULL, ICON_NONE); + const bool use_render_procedural = RNA_boolean_get(&fileptr, "use_render_procedural"); + const bool use_prefetch = RNA_boolean_get(&fileptr, "use_prefetch"); + + row = uiLayoutRow(layout, false); + uiLayoutSetEnabled(row, use_render_procedural); + uiItemR(row, &fileptr, "use_prefetch", 0, NULL, ICON_NONE); + + sub = uiLayoutRow(layout, false); + uiLayoutSetEnabled(sub, use_prefetch && use_render_procedural); + uiItemR(sub, &fileptr, "prefetch_cache_size", 0, NULL, ICON_NONE); + row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame")); sub = uiLayoutRow(row, true); uiLayoutSetPropDecorate(sub, false); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index c9bfd883332..e13b69a9763 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -631,7 +631,7 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_NODE_SHADER: cp = ts->nodeclass_shader; break; - case TH_NODE_CONVERTOR: + case TH_NODE_CONVERTER: cp = ts->syntaxv; break; case TH_NODE_GROUP: diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index b8bbf2d3e70..956658bd2b7 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -8627,7 +8627,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * RNA_enum_set(op->ptr, "mode", mode); } - /* Only handle mousemove event in case we are in mouse mode. */ + /* Only handle mouse-move event in case we are in mouse mode. */ if (event->type == MOUSEMOVE || force_mousemove) { if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) { ARegion *region = CTX_wm_region(C); diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 85883a2d29a..8afc5c583e0 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4667,7 +4667,7 @@ typedef struct BrushEdit { int lastmouse[2]; float zfac; - /* optional cached view settings to avoid setting on every mousemove */ + /** Optional cached view settings to avoid setting on every mouse-move. */ PEData data; } BrushEdit; diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 6db148eb4e1..8bc2281db73 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -64,7 +64,9 @@ #include <stdio.h> -/***************************** Render Engines ********************************/ +/* -------------------------------------------------------------------- */ +/** \name Render Engines + * \{ */ /* Update 3D viewport render or draw engine on changes to the scene or view settings. */ void ED_render_view3d_update(Depsgraph *depsgraph, @@ -206,15 +208,15 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data) } } - /* Update CacheFiles to ensure that procedurals are properly taken into account. */ + /* Update #CacheFiles to ensure that procedurals are properly taken into account. */ LISTBASE_FOREACH (CacheFile *, cachefile, &bmain->cachefiles) { - /* Only update cachefiles which are set to use a render procedural. We do not use - * BKE_cachefile_uses_render_procedural here as we need to update regardless of the current - * engine or its settings. */ + /* Only update cache-files which are set to use a render procedural. + * We do not use #BKE_cachefile_uses_render_procedural here as we need to update regardless of + * the current engine or its settings. */ if (cachefile->use_render_procedural) { DEG_id_tag_update(&cachefile->id, ID_RECALC_COPY_ON_WRITE); - /* Rebuild relations so that modifiers are reconnected to or disconnected from the cachefile. - */ + /* Rebuild relations so that modifiers are reconnected to or disconnected from the + * cache-file. */ DEG_relations_tag_update(bmain); } } @@ -227,10 +229,16 @@ void ED_render_view_layer_changed(Main *bmain, bScreen *screen) } } -/***************************** Updates *********************************** - * ED_render_id_flush_update gets called from DEG_id_tag_update, to do * - * editor level updates when the ID changes. when these ID blocks are in * - * the dependency graph, we can get rid of the manual dependency checks. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Updates + * + * #ED_render_id_flush_update gets called from #DEG_id_tag_update, + * to do editor level updates when the ID changes. + * When these ID blocks are in the dependency graph, + * we can get rid of the manual dependency checks. + * \{ */ static void material_changed(Main *UNUSED(bmain), Material *ma) { @@ -336,3 +344,5 @@ void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id) break; } } + +/** \} */ diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index c141789f171..798f4898aaa 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -198,7 +198,8 @@ static int file_browse_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); FileBrowseOp *fbo = op->customdata; ID *id; - char *str, path[FILE_MAX]; + char *str; + int str_len; const char *path_prop = RNA_struct_find_property(op->ptr, "directory") ? "directory" : "filepath"; @@ -206,10 +207,11 @@ static int file_browse_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - str = RNA_string_get_alloc(op->ptr, path_prop, NULL, 0); + str = RNA_string_get_alloc(op->ptr, path_prop, NULL, 0, &str_len); /* Add slash for directories, important for some properties. */ if (RNA_property_subtype(fbo->prop) == PROP_DIRPATH) { + char path[FILE_MAX]; const bool is_relative = RNA_boolean_get(op->ptr, "relative_path"); id = fbo->ptr.owner_id; @@ -220,13 +222,13 @@ static int file_browse_exec(bContext *C, wmOperator *op) /* Do this first so '//' isn't converted to '//\' on windows. */ BLI_path_slash_ensure(path); if (is_relative) { - BLI_strncpy(path, str, FILE_MAX); + const int path_len = BLI_strncpy_rlen(path, str, FILE_MAX); BLI_path_rel(path, BKE_main_blendfile_path(bmain)); - str = MEM_reallocN(str, strlen(path) + 2); + str = MEM_reallocN(str, path_len + 2); BLI_strncpy(str, path, FILE_MAX); } else { - str = MEM_reallocN(str, strlen(str) + 2); + str = MEM_reallocN(str, str_len + 2); } } else { diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index bdb7c622cd2..763beb8671b 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -384,7 +384,7 @@ static int console_insert_exec(bContext *C, wmOperator *op) SpaceConsole *sc = CTX_wm_space_console(C); ARegion *region = CTX_wm_region(C); ConsoleLine *ci = console_history_verify(C); - char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0); + char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL); int len; if (str[0] == '\t' && str[1] == '\0') { @@ -860,7 +860,7 @@ static int console_history_append_exec(bContext *C, wmOperator *op) ScrArea *area = CTX_wm_area(C); ConsoleLine *ci = console_history_verify(C); /* own this text in the new line, don't free */ - char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0); + char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL); int cursor = RNA_int_get(op->ptr, "current_character"); const bool rem_dupes = RNA_boolean_get(op->ptr, "remove_duplicates"); int prev_len = ci->len; @@ -923,7 +923,7 @@ static int console_scrollback_append_exec(bContext *C, wmOperator *op) ConsoleLine *ci; /* own this text in the new line, don't free */ - char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0); + char *str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL); int type = RNA_enum_get(op->ptr, "type"); console_history_verify(C); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 29c1452b988..4f8feda3911 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3935,7 +3935,7 @@ static int tile_add_exec(bContext *C, wmOperator *op) } bool fill_tile = RNA_boolean_get(op->ptr, "fill"); - char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0); + char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0, NULL); /* BKE_image_add_tile assumes a pre-sorted list of tiles. */ BKE_image_sort_tiles(ima); diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c index 288b3d94b1d..c4f111264a3 100644 --- a/source/blender/editors/space_image/image_sequence.c +++ b/source/blender/editors/space_image/image_sequence.c @@ -68,7 +68,7 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges) RNA_BEGIN (op->ptr, itemptr, "files") { char head[FILE_MAX], tail[FILE_MAX]; ushort digits; - char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame"); /* use the first file in the list as base filename */ diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 94e53958524..a99396ecdf0 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -512,7 +512,7 @@ void FILE_OT_report_missing_files(wmOperatorType *ot) static int find_missing_files_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - const char *searchpath = RNA_string_get_alloc(op->ptr, "directory", NULL, 0); + const char *searchpath = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL); const bool find_all = RNA_boolean_get(op->ptr, "find_all"); BKE_bpath_missing_files_find(bmain, searchpath, op->reports, find_all); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 84741f61245..cca61148c85 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -671,8 +671,8 @@ int node_get_colorid(bNode *node) return TH_NODE_INPUT; case NODE_CLASS_OUTPUT: return (node->flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE; - case NODE_CLASS_CONVERTOR: - return TH_NODE_CONVERTOR; + case NODE_CLASS_CONVERTER: + return TH_NODE_CONVERTER; case NODE_CLASS_OP_COLOR: return TH_NODE_COLOR; case NODE_CLASS_OP_VECTOR: diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index ff8cbdb1a59..16b690dd6e4 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -254,11 +254,11 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name)); } else if ((prop = RNA_struct_find_property(op->ptr, "directory"))) { - char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0); + char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL); if ((prop = RNA_struct_find_property(op->ptr, "files"))) { RNA_PROP_BEGIN (op->ptr, itemptr, prop) { - char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); BLI_strncpy(load_data->name, filename, sizeof(load_data->name)); BLI_join_dirfile(load_data->path, sizeof(load_data->path), directory, filename); MEM_freeN(filename); @@ -944,7 +944,7 @@ int sequencer_image_seq_get_minmax_frame(wmOperator *op, RNA_BEGIN (op->ptr, itemptr, "files") { char *filename; int frame; - filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); if (filename) { if (BLI_path_frame_get(filename, &frame, &numdigits)) { @@ -973,7 +973,7 @@ void sequencer_image_seq_reserve_frames( { char *filename = NULL; RNA_BEGIN (op->ptr, itemptr, "files") { - filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); break; } RNA_END; @@ -1023,7 +1023,7 @@ static void sequencer_add_image_strip_load_files( else { size_t strip_frame = 0; RNA_BEGIN (op->ptr, itemptr, "files") { - char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); SEQ_add_image_load_file(seq, strip_frame, filename); MEM_freeN(filename); strip_frame++; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index afad8999e88..9a2225a44c5 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2826,7 +2826,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) } else { RNA_BEGIN (op->ptr, itemptr, "files") { - char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); BLI_strncpy(se->name, filename, sizeof(se->name)); MEM_freeN(filename); se++; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 2b78ecb245d..b5fcadbefe8 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3430,7 +3430,7 @@ static int text_insert_exec(bContext *C, wmOperator *op) text_drawcache_tag_update(st, 0); - str = RNA_string_get_alloc(op->ptr, "text", NULL, 0); + str = RNA_string_get_alloc(op->ptr, "text", NULL, 0, NULL); ED_text_undo_push_init(C); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 49299d73337..edc34d0d883 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -299,7 +299,9 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], cons ED_view3d_win_to_3d_int(ruler_info->area->spacedata.first, ruler_info->region, r_co, xy, r_co); } -/* use for mousemove events */ +/** + * Use for mouse-move events. + */ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, RulerInfo *ruler_info, RulerItem *ruler_item, diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 094ae080de0..d756e2c90a6 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1662,20 +1662,23 @@ void animrecord_check_state(TransInfo *t, struct Object *ob) } } -void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float inv_unit_scale) +void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float y_fac) { + float delta_x = td->loc[0] - td->iloc[0]; + float delta_y = (td->loc[1] - td->iloc[1]) * y_fac; + /* If the handles are to be moved too * (as side-effect of keyframes moving, to keep the general effect) * offset them by the same amount so that the general angles are maintained * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked). */ if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { - td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; - td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; + td2d->h1[0] = td2d->ih1[0] + delta_x; + td2d->h1[1] = td2d->ih1[1] + delta_y; } if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { - td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0]; - td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; + td2d->h2[0] = td2d->ih2[0] + delta_x; + td2d->h2[1] = td2d->ih2[1] + delta_y; } } diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index fa34e2555d6..9cb0400cad9 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -43,9 +43,7 @@ void sort_trans_data_dist(TransInfo *t); void createTransData(struct bContext *C, TransInfo *t); bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); -void transform_convert_flush_handle2D(TransData *td, - TransData2D *td2d, - const float inv_unit_scale); +void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float y_fac); void recalcData(TransInfo *t); /* transform_convert_mesh.c */ diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 8a3b254be5c..075db30fa61 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -158,8 +158,9 @@ static void TimeToTransData( /* Setup #TransData. */ td->loc = time; /* Usually #td2d->loc is used here. But this is for when the original location is not float[3]. */ + copy_v3_v3(td->iloc, td->loc); td->val = time; - td->ival = td->iloc[0] = *(time); + td->ival = *(time); td->center[0] = td->ival; td->center[1] = ypos; @@ -241,16 +242,15 @@ static int GPLayerToTransData(TransData *td, for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) { if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { - /* memory is calloc'ed, so that should zero everything nicely for us */ - td->val = &tfd->val; - td->ival = (float)gpf->framenum; + tfd->val = (float)gpf->framenum; + tfd->sdata = &gpf->framenum; + + td->val = td->loc = &tfd->val; /* XXX: It's not a 3d array. */ + td->ival = td->iloc[0] = (float)gpf->framenum; td->center[0] = td->ival; td->center[1] = ypos; - tfd->val = (float)gpf->framenum; - tfd->sdata = &gpf->framenum; - /* Advance `td` now. */ td++; tfd++; @@ -616,7 +616,11 @@ void recalcData_actedit(TransInfo *t) if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) { transform_snap_anim_flush_data(t, td, autosnap, td->loc); } - transform_convert_flush_handle2D(td, td2d, 1.0f); + + /* Constrain Y. */ + td->loc[1] = td->iloc[1]; + + transform_convert_flush_handle2D(td, td2d, 0.0f); } if (ac.datatype != ANIMCONT_MASK) { diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index f56d60b7376..5627a910ab4 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -131,12 +131,12 @@ static void autokeyframe_pose( ListBase dsources = {NULL, NULL}; - /* add datasource override for the camera object */ + /* Add data-source override for the camera object. */ ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan); /* only insert into active keyingset? */ if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { - /* run the active Keying Set on the current datasource */ + /* Run the active Keying Set on the current data-source. */ ANIM_apply_keyingset( C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time); } diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index ee6cb391fdc..bcbac009948 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -749,7 +749,7 @@ static void autokeyframe_object( /* Get flags used for inserting keyframes. */ flag = ANIM_get_keyframing_flags(scene, true); - /* add datasource override for the object */ + /* Add data-source override for the object. */ ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL); if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index be7fc65a6c2..08335924ddf 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -35,14 +35,14 @@ #include "transform_snap.h" /* -------------------------------------------------------------------- */ -/** \name Snappint in Anim Editors +/** \name Snapping in Anim Editors * \{ */ /** * This function returns the snapping 'mode' for Animation Editors only. * We cannot use the standard snapping due to NLA-strip scaling complexities. * - * TODO: these modifier checks should be key-mappable. + * TODO: these modifier checks should be accessible from the key-map. */ short getAnimEdit_SnapMode(TransInfo *t) { diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 823837e2a42..d0eed6a6eb1 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -178,7 +178,7 @@ void outputNumInput(NumInput *n, char *str, UnitSettings *unit_settings) } /* We might have cut some multi-bytes utf8 chars * (e.g. trailing '°' of degrees values can become only 'A')... */ - BLI_utf8_invalid_strip(&str[j * ln], strlen(&str[j * ln])); + BLI_str_utf8_invalid_strip(&str[j * ln], strlen(&str[j * ln])); } } diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 809294ad274..f8d2acc74a8 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -33,9 +33,6 @@ set(SRC intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc - intern/multi_function_network.cc - intern/multi_function_network_evaluation.cc - intern/multi_function_network_optimization.cc FN_cpp_type.hh FN_cpp_type_make.hh @@ -49,9 +46,6 @@ set(SRC FN_multi_function_builder.hh FN_multi_function_context.hh FN_multi_function_data_type.hh - FN_multi_function_network.hh - FN_multi_function_network_evaluation.hh - FN_multi_function_network_optimization.hh FN_multi_function_param_type.hh FN_multi_function_params.hh FN_multi_function_signature.hh @@ -68,7 +62,6 @@ if(WITH_GTESTS) tests/FN_cpp_type_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc - tests/FN_multi_function_network_test.cc tests/FN_multi_function_test.cc ) set(TEST_LIB diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index e2ba603415b..d58fb263d7e 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -130,7 +130,7 @@ class GVArray { } /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ - void get_single_to_uninitialized(void *r_value) const + void get_internal_single_to_uninitialized(void *r_value) const { type_->default_construct(r_value); this->get_internal_single(r_value); diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh deleted file mode 100644 index b303589106a..00000000000 --- a/source/blender/functions/FN_multi_function_network.hh +++ /dev/null @@ -1,536 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -/** \file - * \ingroup fn - * - * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The - * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function - * (which can be used in another network and so on). - * - * A MFNetwork is a graph data structure with two kinds of nodes: - * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to - * parameters of the referenced multi-function. - * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be - * used to represent node group inputs and outputs. - * - * Links represent data flow. Unlinked input sockets have no value. In order to execute a function - * node, all its inputs have to be connected to something. - * - * Links are only allowed between sockets with the exact same MFDataType. There are no implicit - * conversions. - * - * Every input and output parameter of a multi-function corresponds to exactly one input or output - * socket respectively. A multiple parameter belongs to exactly one input AND one output socket. - * - * There is an .to_dot() method that generates a graph in dot format for debugging purposes. - */ - -#include "FN_multi_function.hh" - -#include "BLI_vector_set.hh" - -namespace blender::fn { - -class MFNode; -class MFFunctionNode; -class MFDummyNode; -class MFSocket; -class MFInputSocket; -class MFOutputSocket; -class MFNetwork; - -class MFNode : NonCopyable, NonMovable { - protected: - MFNetwork *network_; - Span<MFInputSocket *> inputs_; - Span<MFOutputSocket *> outputs_; - bool is_dummy_; - int id_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - int id() const; - - MFNetwork &network(); - const MFNetwork &network() const; - - bool is_dummy() const; - bool is_function() const; - - MFDummyNode &as_dummy(); - const MFDummyNode &as_dummy() const; - - MFFunctionNode &as_function(); - const MFFunctionNode &as_function() const; - - MFInputSocket &input(int index); - const MFInputSocket &input(int index) const; - - MFOutputSocket &output(int index); - const MFOutputSocket &output(int index) const; - - Span<MFInputSocket *> inputs(); - Span<const MFInputSocket *> inputs() const; - - Span<MFOutputSocket *> outputs(); - Span<const MFOutputSocket *> outputs() const; - - bool has_unlinked_inputs() const; - - private: - void destruct_sockets(); -}; - -class MFFunctionNode : public MFNode { - private: - const MultiFunction *function_; - Span<int> input_param_indices_; - Span<int> output_param_indices_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - const MultiFunction &function() const; - - const MFInputSocket &input_for_param(int param_index) const; - const MFOutputSocket &output_for_param(int param_index) const; -}; - -class MFDummyNode : public MFNode { - protected: - StringRefNull name_; - MutableSpan<StringRefNull> input_names_; - MutableSpan<StringRefNull> output_names_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - Span<StringRefNull> input_names() const; - Span<StringRefNull> output_names() const; -}; - -class MFSocket : NonCopyable, NonMovable { - protected: - MFNode *node_; - bool is_output_; - int index_; - MFDataType data_type_; - int id_; - StringRefNull name_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - int id() const; - int index() const; - - const MFDataType &data_type() const; - - MFNode &node(); - const MFNode &node() const; - - bool is_input() const; - bool is_output() const; - - MFInputSocket &as_input(); - const MFInputSocket &as_input() const; - - MFOutputSocket &as_output(); - const MFOutputSocket &as_output() const; -}; - -class MFInputSocket : public MFSocket { - private: - MFOutputSocket *origin_; - - friend MFNetwork; - - public: - MFOutputSocket *origin(); - const MFOutputSocket *origin() const; -}; - -class MFOutputSocket : public MFSocket { - private: - Vector<MFInputSocket *, 1> targets_; - - friend MFNetwork; - - public: - Span<MFInputSocket *> targets(); - Span<const MFInputSocket *> targets() const; -}; - -class MFNetwork : NonCopyable, NonMovable { - private: - LinearAllocator<> allocator_; - - VectorSet<MFFunctionNode *> function_nodes_; - VectorSet<MFDummyNode *> dummy_nodes_; - - Vector<MFNode *> node_or_null_by_id_; - Vector<MFSocket *> socket_or_null_by_id_; - - public: - MFNetwork() = default; - ~MFNetwork(); - - MFFunctionNode &add_function(const MultiFunction &function); - MFDummyNode &add_dummy(StringRef name, - Span<MFDataType> input_types, - Span<MFDataType> output_types, - Span<StringRef> input_names, - Span<StringRef> output_names); - void add_link(MFOutputSocket &from, MFInputSocket &to); - - MFOutputSocket &add_input(StringRef name, MFDataType data_type); - MFInputSocket &add_output(StringRef name, MFDataType data_type); - - void relink(MFOutputSocket &old_output, MFOutputSocket &new_output); - - void remove(MFNode &node); - void remove(Span<MFNode *> nodes); - - int socket_id_amount() const; - int node_id_amount() const; - - Span<MFDummyNode *> dummy_nodes(); - Span<MFFunctionNode *> function_nodes(); - - MFNode *node_or_null_by_id(int id); - const MFNode *node_or_null_by_id(int id) const; - - MFSocket *socket_or_null_by_id(int id); - const MFSocket *socket_or_null_by_id(int id) const; - - void find_dependencies(Span<const MFInputSocket *> sockets, - VectorSet<const MFOutputSocket *> &r_dummy_sockets, - VectorSet<const MFInputSocket *> &r_unlinked_inputs) const; - - bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const; - - std::string to_dot(Span<const MFNode *> marked_nodes = {}) const; -}; - -/* -------------------------------------------------------------------- - * MFNode inline methods. - */ - -inline StringRefNull MFNode::name() const -{ - if (is_dummy_) { - return this->as_dummy().name(); - } - else { - return this->as_function().name(); - } -} - -inline int MFNode::id() const -{ - return id_; -} - -inline MFNetwork &MFNode::network() -{ - return *network_; -} - -inline const MFNetwork &MFNode::network() const -{ - return *network_; -} - -inline bool MFNode::is_dummy() const -{ - return is_dummy_; -} - -inline bool MFNode::is_function() const -{ - return !is_dummy_; -} - -inline MFDummyNode &MFNode::as_dummy() -{ - BLI_assert(is_dummy_); - return static_cast<MFDummyNode &>(*this); -} - -inline const MFDummyNode &MFNode::as_dummy() const -{ - BLI_assert(is_dummy_); - return static_cast<const MFDummyNode &>(*this); -} - -inline MFFunctionNode &MFNode::as_function() -{ - BLI_assert(!is_dummy_); - return static_cast<MFFunctionNode &>(*this); -} - -inline const MFFunctionNode &MFNode::as_function() const -{ - BLI_assert(!is_dummy_); - return static_cast<const MFFunctionNode &>(*this); -} - -inline MFInputSocket &MFNode::input(int index) -{ - return *inputs_[index]; -} - -inline const MFInputSocket &MFNode::input(int index) const -{ - return *inputs_[index]; -} - -inline MFOutputSocket &MFNode::output(int index) -{ - return *outputs_[index]; -} - -inline const MFOutputSocket &MFNode::output(int index) const -{ - return *outputs_[index]; -} - -inline Span<MFInputSocket *> MFNode::inputs() -{ - return inputs_; -} - -inline Span<const MFInputSocket *> MFNode::inputs() const -{ - return inputs_; -} - -inline Span<MFOutputSocket *> MFNode::outputs() -{ - return outputs_; -} - -inline Span<const MFOutputSocket *> MFNode::outputs() const -{ - return outputs_; -} - -inline bool MFNode::has_unlinked_inputs() const -{ - for (const MFInputSocket *socket : inputs_) { - if (socket->origin() == nullptr) { - return true; - } - } - return false; -} - -/* -------------------------------------------------------------------- - * MFFunctionNode inline methods. - */ - -inline StringRefNull MFFunctionNode::name() const -{ - return function_->name(); -} - -inline const MultiFunction &MFFunctionNode::function() const -{ - return *function_; -} - -inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const -{ - return this->input(input_param_indices_.first_index(param_index)); -} - -inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const -{ - return this->output(output_param_indices_.first_index(param_index)); -} - -/* -------------------------------------------------------------------- - * MFDummyNode inline methods. - */ - -inline StringRefNull MFDummyNode::name() const -{ - return name_; -} - -inline Span<StringRefNull> MFDummyNode::input_names() const -{ - return input_names_; -} - -inline Span<StringRefNull> MFDummyNode::output_names() const -{ - return output_names_; -} - -/* -------------------------------------------------------------------- - * MFSocket inline methods. - */ - -inline StringRefNull MFSocket::name() const -{ - return name_; -} - -inline int MFSocket::id() const -{ - return id_; -} - -inline int MFSocket::index() const -{ - return index_; -} - -inline const MFDataType &MFSocket::data_type() const -{ - return data_type_; -} - -inline MFNode &MFSocket::node() -{ - return *node_; -} - -inline const MFNode &MFSocket::node() const -{ - return *node_; -} - -inline bool MFSocket::is_input() const -{ - return !is_output_; -} - -inline bool MFSocket::is_output() const -{ - return is_output_; -} - -inline MFInputSocket &MFSocket::as_input() -{ - BLI_assert(this->is_input()); - return static_cast<MFInputSocket &>(*this); -} - -inline const MFInputSocket &MFSocket::as_input() const -{ - BLI_assert(this->is_input()); - return static_cast<const MFInputSocket &>(*this); -} - -inline MFOutputSocket &MFSocket::as_output() -{ - BLI_assert(this->is_output()); - return static_cast<MFOutputSocket &>(*this); -} - -inline const MFOutputSocket &MFSocket::as_output() const -{ - BLI_assert(this->is_output()); - return static_cast<const MFOutputSocket &>(*this); -} - -/* -------------------------------------------------------------------- - * MFInputSocket inline methods. - */ - -inline MFOutputSocket *MFInputSocket::origin() -{ - return origin_; -} - -inline const MFOutputSocket *MFInputSocket::origin() const -{ - return origin_; -} - -/* -------------------------------------------------------------------- - * MFOutputSocket inline methods. - */ - -inline Span<MFInputSocket *> MFOutputSocket::targets() -{ - return targets_; -} - -inline Span<const MFInputSocket *> MFOutputSocket::targets() const -{ - return targets_; -} - -/* -------------------------------------------------------------------- - * MFNetwork inline methods. - */ - -inline Span<MFDummyNode *> MFNetwork::dummy_nodes() -{ - return dummy_nodes_; -} - -inline Span<MFFunctionNode *> MFNetwork::function_nodes() -{ - return function_nodes_; -} - -inline MFNode *MFNetwork::node_or_null_by_id(int id) -{ - return node_or_null_by_id_[id]; -} - -inline const MFNode *MFNetwork::node_or_null_by_id(int id) const -{ - return node_or_null_by_id_[id]; -} - -inline MFSocket *MFNetwork::socket_or_null_by_id(int id) -{ - return socket_or_null_by_id_[id]; -} - -inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const -{ - return socket_or_null_by_id_[id]; -} - -inline int MFNetwork::socket_id_amount() const -{ - return socket_or_null_by_id_.size(); -} - -inline int MFNetwork::node_id_amount() const -{ - return node_or_null_by_id_.size(); -} - -} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_network_evaluation.hh b/source/blender/functions/FN_multi_function_network_evaluation.hh deleted file mode 100644 index 17cffa406f7..00000000000 --- a/source/blender/functions/FN_multi_function_network_evaluation.hh +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -/** \file - * \ingroup fn - */ - -#include "FN_multi_function_network.hh" - -namespace blender::fn { - -class MFNetworkEvaluationStorage; - -class MFNetworkEvaluator : public MultiFunction { - private: - MFSignature signature_; - Vector<const MFOutputSocket *> inputs_; - Vector<const MFInputSocket *> outputs_; - - public: - MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, Vector<const MFInputSocket *> outputs); - - void call(IndexMask mask, MFParams params, MFContext context) const override; - - private: - using Storage = MFNetworkEvaluationStorage; - - void copy_inputs_to_storage(MFParams params, Storage &storage) const; - void copy_outputs_to_storage( - MFParams params, - Storage &storage, - Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const; - - void evaluate_network_to_compute_outputs(MFContext &global_context, Storage &storage) const; - - void evaluate_function(MFContext &global_context, - const MFFunctionNode &function_node, - Storage &storage) const; - - bool can_do_single_value_evaluation(const MFFunctionNode &function_node, Storage &storage) const; - - void initialize_remaining_outputs(MFParams params, - Storage &storage, - Span<const MFInputSocket *> remaining_outputs) const; -}; - -} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh deleted file mode 100644 index 96664fa368e..00000000000 --- a/source/blender/functions/FN_multi_function_network_optimization.hh +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -#include "FN_multi_function_network.hh" - -#include "BLI_resource_scope.hh" - -namespace blender::fn::mf_network_optimization { - -void dead_node_removal(MFNetwork &network); -void constant_folding(MFNetwork &network, ResourceScope &scope); -void common_subnetwork_elimination(MFNetwork &network); - -} // namespace blender::fn::mf_network_optimization diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index 23309c9a5e6..d05948cc645 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -160,6 +160,21 @@ class MFSignatureBuilder { } } + void add(StringRef name, const MFParamType ¶m_type) + { + switch (param_type.interface_type()) { + case MFParamType::Input: + this->input(name, param_type.data_type()); + break; + case MFParamType::Mutable: + this->mutable_(name, param_type.data_type()); + break; + case MFParamType::Output: + this->output(name, param_type.data_type()); + break; + } + } + /* Context */ /** This indicates that the function accesses the context. This disables optimizations that diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc deleted file mode 100644 index b5c2c09a35a..00000000000 --- a/source/blender/functions/intern/multi_function_network.cc +++ /dev/null @@ -1,330 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "BLI_dot_export.hh" -#include "BLI_stack.hh" - -#include "FN_multi_function_network.hh" - -namespace blender::fn { - -MFNetwork::~MFNetwork() -{ - for (MFFunctionNode *node : function_nodes_) { - node->destruct_sockets(); - node->~MFFunctionNode(); - } - for (MFDummyNode *node : dummy_nodes_) { - node->destruct_sockets(); - node->~MFDummyNode(); - } -} - -void MFNode::destruct_sockets() -{ - for (MFInputSocket *socket : inputs_) { - socket->~MFInputSocket(); - } - for (MFOutputSocket *socket : outputs_) { - socket->~MFOutputSocket(); - } -} - -/** - * Add a new function node to the network. The caller keeps the ownership of the function. The - * function should not be freed before the network. A reference to the new node is returned. The - * node is owned by the network. - */ -MFFunctionNode &MFNetwork::add_function(const MultiFunction &function) -{ - Vector<int, 16> input_param_indices, output_param_indices; - - for (int param_index : function.param_indices()) { - switch (function.param_type(param_index).interface_type()) { - case MFParamType::Input: { - input_param_indices.append(param_index); - break; - } - case MFParamType::Output: { - output_param_indices.append(param_index); - break; - } - case MFParamType::Mutable: { - input_param_indices.append(param_index); - output_param_indices.append(param_index); - break; - } - } - } - - MFFunctionNode &node = *allocator_.construct<MFFunctionNode>().release(); - function_nodes_.add_new(&node); - - node.network_ = this; - node.is_dummy_ = false; - node.id_ = node_or_null_by_id_.append_and_get_index(&node); - node.function_ = &function; - node.input_param_indices_ = allocator_.construct_array_copy<int>(input_param_indices); - node.output_param_indices_ = allocator_.construct_array_copy<int>(output_param_indices); - - node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>( - input_param_indices.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>( - output_param_indices.size()); - - for (int i : input_param_indices.index_range()) { - int param_index = input_param_indices[i]; - MFParamType param = function.param_type(param_index); - BLI_assert(param.is_input_or_mutable()); - - MFInputSocket &socket = *node.inputs_[i]; - socket.data_type_ = param.data_type(); - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = false; - socket.name_ = function.param_name(param_index); - socket.origin_ = nullptr; - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - } - - for (int i : output_param_indices.index_range()) { - int param_index = output_param_indices[i]; - MFParamType param = function.param_type(param_index); - BLI_assert(param.is_output_or_mutable()); - - MFOutputSocket &socket = *node.outputs_[i]; - socket.data_type_ = param.data_type(); - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = true; - socket.name_ = function.param_name(param_index); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - } - - return node; -} - -/** - * Add a dummy node with the given input and output sockets. - */ -MFDummyNode &MFNetwork::add_dummy(StringRef name, - Span<MFDataType> input_types, - Span<MFDataType> output_types, - Span<StringRef> input_names, - Span<StringRef> output_names) -{ - assert_same_size(input_types, input_names); - assert_same_size(output_types, output_names); - - MFDummyNode &node = *allocator_.construct<MFDummyNode>().release(); - dummy_nodes_.add_new(&node); - - node.network_ = this; - node.is_dummy_ = true; - node.name_ = allocator_.copy_string(name); - node.id_ = node_or_null_by_id_.append_and_get_index(&node); - - node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>( - input_types.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>( - output_types.size()); - - node.input_names_ = allocator_.allocate_array<StringRefNull>(input_types.size()); - node.output_names_ = allocator_.allocate_array<StringRefNull>(output_types.size()); - - for (int i : input_types.index_range()) { - MFInputSocket &socket = *node.inputs_[i]; - socket.data_type_ = input_types[i]; - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = false; - socket.name_ = allocator_.copy_string(input_names[i]); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - node.input_names_[i] = socket.name_; - } - - for (int i : output_types.index_range()) { - MFOutputSocket &socket = *node.outputs_[i]; - socket.data_type_ = output_types[i]; - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = true; - socket.name_ = allocator_.copy_string(output_names[i]); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - node.output_names_[i] = socket.name_; - } - - return node; -} - -/** - * Connect two sockets. This invokes undefined behavior if the sockets belong to different - * networks, the sockets have a different data type, or the `to` socket is connected to something - * else already. - */ -void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to) -{ - BLI_assert(to.origin_ == nullptr); - BLI_assert(from.node_->network_ == to.node_->network_); - BLI_assert(from.data_type_ == to.data_type_); - from.targets_.append(&to); - to.origin_ = &from; -} - -MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type) -{ - return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0); -} - -MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type) -{ - return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0); -} - -void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output) -{ - BLI_assert(&old_output != &new_output); - BLI_assert(old_output.data_type_ == new_output.data_type_); - for (MFInputSocket *input : old_output.targets()) { - input->origin_ = &new_output; - } - new_output.targets_.extend(old_output.targets_); - old_output.targets_.clear(); -} - -void MFNetwork::remove(MFNode &node) -{ - for (MFInputSocket *socket : node.inputs_) { - if (socket->origin_ != nullptr) { - socket->origin_->targets_.remove_first_occurrence_and_reorder(socket); - } - socket_or_null_by_id_[socket->id_] = nullptr; - } - for (MFOutputSocket *socket : node.outputs_) { - for (MFInputSocket *other : socket->targets_) { - other->origin_ = nullptr; - } - socket_or_null_by_id_[socket->id_] = nullptr; - } - node.destruct_sockets(); - if (node.is_dummy()) { - MFDummyNode &dummy_node = node.as_dummy(); - dummy_node.~MFDummyNode(); - dummy_nodes_.remove_contained(&dummy_node); - } - else { - MFFunctionNode &function_node = node.as_function(); - function_node.~MFFunctionNode(); - function_nodes_.remove_contained(&function_node); - } - node_or_null_by_id_[node.id_] = nullptr; -} - -void MFNetwork::remove(Span<MFNode *> nodes) -{ - for (MFNode *node : nodes) { - this->remove(*node); - } -} - -void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets, - VectorSet<const MFOutputSocket *> &r_dummy_sockets, - VectorSet<const MFInputSocket *> &r_unlinked_inputs) const -{ - Set<const MFNode *> visited_nodes; - Stack<const MFInputSocket *> sockets_to_check; - sockets_to_check.push_multiple(sockets); - - while (!sockets_to_check.is_empty()) { - const MFInputSocket &socket = *sockets_to_check.pop(); - const MFOutputSocket *origin_socket = socket.origin(); - if (origin_socket == nullptr) { - r_unlinked_inputs.add(&socket); - continue; - } - - const MFNode &origin_node = origin_socket->node(); - - if (origin_node.is_dummy()) { - r_dummy_sockets.add(origin_socket); - continue; - } - - if (visited_nodes.add(&origin_node)) { - sockets_to_check.push_multiple(origin_node.inputs()); - } - } -} - -bool MFNetwork::have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const -{ - VectorSet<const MFOutputSocket *> dummy_sockets; - VectorSet<const MFInputSocket *> unlinked_inputs; - this->find_dependencies(sockets, dummy_sockets, unlinked_inputs); - return dummy_sockets.size() + unlinked_inputs.size() > 0; -} - -std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) const -{ - dot::DirectedGraph digraph; - digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); - - Map<const MFNode *, dot::NodeWithSocketsRef> dot_nodes; - - Vector<const MFNode *> all_nodes; - all_nodes.extend(function_nodes_.as_span().cast<const MFNode *>()); - all_nodes.extend(dummy_nodes_.as_span().cast<const MFNode *>()); - - for (const MFNode *node : all_nodes) { - dot::Node &dot_node = digraph.new_node(""); - - Vector<std::string> input_names, output_names; - for (const MFInputSocket *socket : node->inputs_) { - input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")"); - } - for (const MFOutputSocket *socket : node->outputs_) { - output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")"); - } - - dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names}; - dot_nodes.add_new(node, dot_node_ref); - } - - for (const MFDummyNode *node : dummy_nodes_) { - dot_nodes.lookup(node).node().set_background_color("#77EE77"); - } - for (const MFNode *node : marked_nodes) { - dot_nodes.lookup(node).node().set_background_color("#7777EE"); - } - - for (const MFNode *to_node : all_nodes) { - dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node); - - for (const MFInputSocket *to_socket : to_node->inputs_) { - const MFOutputSocket *from_socket = to_socket->origin_; - if (from_socket != nullptr) { - const MFNode *from_node = from_socket->node_; - dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node); - digraph.new_edge(from_dot_node.output(from_socket->index_), - to_dot_node.input(to_socket->index_)); - } - } - } - - return digraph.to_dot_string(); -} - -} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc deleted file mode 100644 index 9a0cb0c35ce..00000000000 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ /dev/null @@ -1,1083 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup fn - * - * The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller - * multi-functions. When called, it traverses the underlying MFNetwork and executes the required - * function nodes. - * - * There are many possible approaches to evaluate a function network. The approach implemented - * below has the following features: - * - It does not use recursion. Those could become problematic with long node chains. - * - It can handle all existing parameter types (including mutable parameters). - * - Avoids data copies in many cases. - * - Every node is executed at most once. - * - Can compute sub-functions on a single element, when the result is the same for all elements. - * - * Possible improvements: - * - Cache and reuse buffers. - * - Use "deepest depth first" heuristic to decide which order the inputs of a node should be - * computed. This reduces the number of required temporary buffers when they are reused. - */ - -#include "FN_multi_function_network_evaluation.hh" - -#include "BLI_resource_scope.hh" -#include "BLI_stack.hh" - -namespace blender::fn { - -struct Value; - -/** - * This keeps track of all the values that flow through the multi-function network. Therefore it - * maintains a mapping between output sockets and their corresponding values. Every `value` - * references some memory, that is owned either by the caller or this storage. - * - * A value can be owned by different sockets over time to avoid unnecessary copies. - */ -class MFNetworkEvaluationStorage { - private: - LinearAllocator<> allocator_; - IndexMask mask_; - Array<Value *> value_per_output_id_; - int64_t min_array_size_; - - public: - MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount); - ~MFNetworkEvaluationStorage(); - - /* Add the values that have been provided by the caller of the multi-function network. */ - void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array); - void add_vector_input_from_caller(const MFOutputSocket &socket, - const GVVectorArray &virtual_vector_array); - void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span); - void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array); - - /* Get input buffers for function node evaluations. */ - const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope); - const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope); - const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope); - const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope); - - /* Get output buffers for function node evaluations. */ - GMutableSpan get_single_output__full(const MFOutputSocket &socket); - GMutableSpan get_single_output__single(const MFOutputSocket &socket); - GVectorArray &get_vector_output__full(const MFOutputSocket &socket); - GVectorArray &get_vector_output__single(const MFOutputSocket &socket); - - /* Get mutable buffers for function node evaluations. */ - GMutableSpan get_mutable_single__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GMutableSpan get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GVectorArray &get_mutable_vector__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GVectorArray &get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - - /* Mark a node as being done with evaluation. This might free temporary buffers that are no - * longer needed. */ - void finish_node(const MFFunctionNode &node); - void finish_output_socket(const MFOutputSocket &socket); - void finish_input_socket(const MFInputSocket &socket); - - IndexMask mask() const; - bool socket_is_computed(const MFOutputSocket &socket); - bool is_same_value_for_every_index(const MFOutputSocket &socket); - bool socket_has_buffer_for_output(const MFOutputSocket &socket); -}; - -MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, - Vector<const MFInputSocket *> outputs) - : inputs_(std::move(inputs)), outputs_(std::move(outputs)) -{ - BLI_assert(outputs_.size() > 0); - MFSignatureBuilder signature{"Function Tree"}; - - for (const MFOutputSocket *socket : inputs_) { - BLI_assert(socket->node().is_dummy()); - - MFDataType type = socket->data_type(); - switch (type.category()) { - case MFDataType::Single: - signature.single_input(socket->name(), type.single_type()); - break; - case MFDataType::Vector: - signature.vector_input(socket->name(), type.vector_base_type()); - break; - } - } - - for (const MFInputSocket *socket : outputs_) { - BLI_assert(socket->node().is_dummy()); - - MFDataType type = socket->data_type(); - switch (type.category()) { - case MFDataType::Single: - signature.single_output(socket->name(), type.single_type()); - break; - case MFDataType::Vector: - signature.vector_output(socket->name(), type.vector_base_type()); - break; - } - } - - signature_ = signature.build(); - this->set_signature(&signature_); -} - -void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const -{ - if (mask.size() == 0) { - return; - } - - const MFNetwork &network = outputs_[0]->node().network(); - Storage storage(mask, network.socket_id_amount()); - - Vector<const MFInputSocket *> outputs_to_initialize_in_the_end; - - this->copy_inputs_to_storage(params, storage); - this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end); - this->evaluate_network_to_compute_outputs(context, storage); - this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end); -} - -BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params, - Storage &storage) const -{ - for (int input_index : inputs_.index_range()) { - int param_index = input_index + 0; - const MFOutputSocket &socket = *inputs_[input_index]; - switch (socket.data_type().category()) { - case MFDataType::Single: { - const GVArray &input_list = params.readonly_single_input(param_index); - storage.add_single_input_from_caller(socket, input_list); - break; - } - case MFDataType::Vector: { - const GVVectorArray &input_list_list = params.readonly_vector_input(param_index); - storage.add_vector_input_from_caller(socket, input_list_list); - break; - } - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage( - MFParams params, - Storage &storage, - Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const -{ - for (int output_index : outputs_.index_range()) { - int param_index = output_index + inputs_.size(); - const MFInputSocket &socket = *outputs_[output_index]; - const MFOutputSocket &origin = *socket.origin(); - - if (origin.node().is_dummy()) { - BLI_assert(inputs_.contains(&origin)); - /* Don't overwrite input buffers. */ - outputs_to_initialize_in_the_end.append(&socket); - continue; - } - - if (storage.socket_has_buffer_for_output(origin)) { - /* When two outputs will be initialized to the same values. */ - outputs_to_initialize_in_the_end.append(&socket); - continue; - } - - switch (socket.data_type().category()) { - case MFDataType::Single: { - GMutableSpan span = params.uninitialized_single_output(param_index); - storage.add_single_output_from_caller(origin, span); - break; - } - case MFDataType::Vector: { - GVectorArray &vector_array = params.vector_output(param_index); - storage.add_vector_output_from_caller(origin, vector_array); - break; - } - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs( - MFContext &global_context, Storage &storage) const -{ - Stack<const MFOutputSocket *, 32> sockets_to_compute; - for (const MFInputSocket *socket : outputs_) { - sockets_to_compute.push(socket->origin()); - } - - /* This is the main loop that traverses the MFNetwork. */ - while (!sockets_to_compute.is_empty()) { - const MFOutputSocket &socket = *sockets_to_compute.peek(); - const MFNode &node = socket.node(); - - if (storage.socket_is_computed(socket)) { - sockets_to_compute.pop(); - continue; - } - - BLI_assert(node.is_function()); - BLI_assert(!node.has_unlinked_inputs()); - const MFFunctionNode &function_node = node.as_function(); - - bool all_origins_are_computed = true; - for (const MFInputSocket *input_socket : function_node.inputs()) { - const MFOutputSocket *origin = input_socket->origin(); - if (origin != nullptr) { - if (!storage.socket_is_computed(*origin)) { - sockets_to_compute.push(origin); - all_origins_are_computed = false; - } - } - } - - if (all_origins_are_computed) { - this->evaluate_function(global_context, function_node, storage); - sockets_to_compute.pop(); - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context, - const MFFunctionNode &function_node, - Storage &storage) const -{ - - const MultiFunction &function = function_node.function(); - // std::cout << "Function: " << function.name() << "\n"; - - if (this->can_do_single_value_evaluation(function_node, storage)) { - /* The function output would be the same for all elements. Therefore, it is enough to call the - * function only on a single element. This can avoid many duplicate computations. */ - MFParamsBuilder params{function, 1}; - ResourceScope &scope = params.resource_scope(); - - for (int param_index : function.param_indices()) { - MFParamType param_type = function.param_type(param_index); - switch (param_type.category()) { - case MFParamType::SingleInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVArray &values = storage.get_single_input__single(socket, scope); - params.add_readonly_single_input(values); - break; - } - case MFParamType::VectorInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVVectorArray &values = storage.get_vector_input__single(socket, scope); - params.add_readonly_vector_input(values); - break; - } - case MFParamType::SingleOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_single_output__single(socket); - params.add_uninitialized_single_output(values); - break; - } - case MFParamType::VectorOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_vector_output__single(socket); - params.add_vector_output(values); - break; - } - case MFParamType::SingleMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__single(input, output, scope); - params.add_single_mutable(values); - break; - } - case MFParamType::VectorMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__single(input, output, scope); - params.add_vector_mutable(values); - break; - } - } - } - - function.call(IndexRange(1), params, global_context); - } - else { - MFParamsBuilder params{function, storage.mask().min_array_size()}; - ResourceScope &scope = params.resource_scope(); - - for (int param_index : function.param_indices()) { - MFParamType param_type = function.param_type(param_index); - switch (param_type.category()) { - case MFParamType::SingleInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVArray &values = storage.get_single_input__full(socket, scope); - params.add_readonly_single_input(values); - break; - } - case MFParamType::VectorInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVVectorArray &values = storage.get_vector_input__full(socket, scope); - params.add_readonly_vector_input(values); - break; - } - case MFParamType::SingleOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_single_output__full(socket); - params.add_uninitialized_single_output(values); - break; - } - case MFParamType::VectorOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_vector_output__full(socket); - params.add_vector_output(values); - break; - } - case MFParamType::SingleMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__full(input, output, scope); - params.add_single_mutable(values); - break; - } - case MFParamType::VectorMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__full(input, output, scope); - params.add_vector_mutable(values); - break; - } - } - } - - function.call(storage.mask(), params, global_context); - } - - storage.finish_node(function_node); -} - -bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node, - Storage &storage) const -{ - for (const MFInputSocket *socket : function_node.inputs()) { - if (!storage.is_same_value_for_every_index(*socket->origin())) { - return false; - } - } - if (storage.mask().min_array_size() >= 1) { - for (const MFOutputSocket *socket : function_node.outputs()) { - if (storage.socket_has_buffer_for_output(*socket)) { - return false; - } - } - } - return true; -} - -BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs( - MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const -{ - ResourceScope scope; - for (const MFInputSocket *socket : remaining_outputs) { - int param_index = inputs_.size() + outputs_.first_index_of(socket); - - switch (socket->data_type().category()) { - case MFDataType::Single: { - const GVArray &values = storage.get_single_input__full(*socket, scope); - GMutableSpan output_values = params.uninitialized_single_output(param_index); - values.materialize_to_uninitialized(storage.mask(), output_values.data()); - break; - } - case MFDataType::Vector: { - const GVVectorArray &values = storage.get_vector_input__full(*socket, scope); - GVectorArray &output_values = params.vector_output(param_index); - output_values.extend(storage.mask(), values); - break; - } - } - } -} - -/* -------------------------------------------------------------------- */ -/** \name Value Types - * \{ */ - -enum class ValueType { - InputSingle, - InputVector, - OutputSingle, - OutputVector, - OwnSingle, - OwnVector, -}; - -struct Value { - ValueType type; - - Value(ValueType type) : type(type) - { - } -}; - -struct InputSingleValue : public Value { - /** This virtual array has been provided by the code that called the multi-function network. */ - const GVArray &virtual_array; - - InputSingleValue(const GVArray &virtual_array) - : Value(ValueType::InputSingle), virtual_array(virtual_array) - { - } -}; - -struct InputVectorValue : public Value { - /** This virtual vector has been provided by the code that called the multi-function network. */ - const GVVectorArray &virtual_vector_array; - - InputVectorValue(const GVVectorArray &virtual_vector_array) - : Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array) - { - } -}; - -struct OutputValue : public Value { - bool is_computed = false; - - OutputValue(ValueType type) : Value(type) - { - } -}; - -struct OutputSingleValue : public OutputValue { - /** This span has been provided by the code that called the multi-function network. */ - GMutableSpan span; - - OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span) - { - } -}; - -struct OutputVectorValue : public OutputValue { - /** This vector array has been provided by the code that called the multi-function network. */ - GVectorArray *vector_array; - - OutputVectorValue(GVectorArray &vector_array) - : OutputValue(ValueType::OutputVector), vector_array(&vector_array) - { - } -}; - -struct OwnSingleValue : public Value { - /** This span has been allocated during the evaluation of the multi-function network and contains - * intermediate data. It has to be freed once the network evaluation is finished. */ - GMutableSpan span; - int max_remaining_users; - bool is_single_allocated; - - OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated) - : Value(ValueType::OwnSingle), - span(span), - max_remaining_users(max_remaining_users), - is_single_allocated(is_single_allocated) - { - } -}; - -struct OwnVectorValue : public Value { - /** This vector array has been allocated during the evaluation of the multi-function network and - * contains intermediate data. It has to be freed once the network evaluation is finished. */ - GVectorArray *vector_array; - int max_remaining_users; - - OwnVectorValue(GVectorArray &vector_array, int max_remaining_users) - : Value(ValueType::OwnVector), - vector_array(&vector_array), - max_remaining_users(max_remaining_users) - { - } -}; - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Storage methods - * \{ */ - -MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount) - : mask_(mask), - value_per_output_id_(socket_id_amount, nullptr), - min_array_size_(mask.min_array_size()) -{ -} - -MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage() -{ - for (Value *any_value : value_per_output_id_) { - if (any_value == nullptr) { - continue; - } - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - GMutableSpan span = value->span; - const CPPType &type = span.type(); - if (value->is_single_allocated) { - type.destruct(span.data()); - } - else { - type.destruct_indices(span.data(), mask_); - MEM_freeN(span.data()); - } - } - else if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - delete value->vector_array; - } - } -} - -IndexMask MFNetworkEvaluationStorage::mask() const -{ - return mask_; -} - -bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return false; - } - if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) { - return static_cast<OutputValue *>(any_value)->is_computed; - } - return true; -} - -bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - switch (any_value->type) { - case ValueType::OwnSingle: - return static_cast<OwnSingleValue *>(any_value)->span.size() == 1; - case ValueType::OwnVector: - return static_cast<OwnVectorValue *>(any_value)->vector_array->size() == 1; - case ValueType::InputSingle: - return static_cast<InputSingleValue *>(any_value)->virtual_array.is_single(); - case ValueType::InputVector: - return static_cast<InputVectorValue *>(any_value)->virtual_vector_array.is_single_vector(); - case ValueType::OutputSingle: - return static_cast<OutputSingleValue *>(any_value)->span.size() == 1; - case ValueType::OutputVector: - return static_cast<OutputVectorValue *>(any_value)->vector_array->size() == 1; - } - BLI_assert(false); - return false; -} - -bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return false; - } - - BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)); - return true; -} - -void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node) -{ - for (const MFInputSocket *socket : node.inputs()) { - this->finish_input_socket(*socket); - } - for (const MFOutputSocket *socket : node.outputs()) { - this->finish_output_socket(*socket); - } -} - -void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return; - } - - if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) { - static_cast<OutputValue *>(any_value)->is_computed = true; - } -} - -void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket) -{ - const MFOutputSocket &origin = *socket.origin(); - - Value *any_value = value_per_output_id_[origin.id()]; - if (any_value == nullptr) { - /* Can happen when a value has been forward to the next node. */ - return; - } - - switch (any_value->type) { - case ValueType::InputSingle: - case ValueType::OutputSingle: - case ValueType::InputVector: - case ValueType::OutputVector: { - break; - } - case ValueType::OwnSingle: { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - BLI_assert(value->max_remaining_users >= 1); - value->max_remaining_users--; - if (value->max_remaining_users == 0) { - GMutableSpan span = value->span; - const CPPType &type = span.type(); - if (value->is_single_allocated) { - type.destruct(span.data()); - } - else { - type.destruct_indices(span.data(), mask_); - MEM_freeN(span.data()); - } - value_per_output_id_[origin.id()] = nullptr; - } - break; - } - case ValueType::OwnVector: { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - BLI_assert(value->max_remaining_users >= 1); - value->max_remaining_users--; - if (value->max_remaining_users == 0) { - delete value->vector_array; - value_per_output_id_[origin.id()] = nullptr; - } - break; - } - } -} - -void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket, - const GVArray &virtual_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_array.size() >= min_array_size_); - - auto *value = allocator_.construct<InputSingleValue>(virtual_array).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_vector_input_from_caller( - const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_vector_array.size() >= min_array_size_); - - auto *value = allocator_.construct<InputVectorValue>(virtual_vector_array).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket, - GMutableSpan span) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(span.size() >= min_array_size_); - - auto *value = allocator_.construct<OutputSingleValue>(span).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket, - GVectorArray &vector_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(vector_array.size() >= min_array_size_); - - auto *value = allocator_.construct<OutputVectorValue>(vector_array).release(); - value_per_output_id_[socket.id()] = value; -} - -GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().single_type(); - void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); - GMutableSpan span(type, buffer, min_array_size_); - - auto *value = - allocator_.construct<OwnSingleValue>(span, socket.targets().size(), false).release(); - value_per_output_id_[socket.id()] = value; - - return span; - } - - BLI_assert(any_value->type == ValueType::OutputSingle); - return static_cast<OutputSingleValue *>(any_value)->span; -} - -GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().single_type(); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - GMutableSpan span(type, buffer, 1); - - auto *value = - allocator_.construct<OwnSingleValue>(span, socket.targets().size(), true).release(); - value_per_output_id_[socket.id()] = value; - - return value->span; - } - - BLI_assert(any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast<OutputSingleValue *>(any_value)->span; - BLI_assert(span.size() == 1); - return span; -} - -GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().vector_base_type(); - GVectorArray *vector_array = new GVectorArray(type, min_array_size_); - - auto *value = - allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release(); - value_per_output_id_[socket.id()] = value; - - return *value->vector_array; - } - - BLI_assert(any_value->type == ValueType::OutputVector); - return *static_cast<OutputVectorValue *>(any_value)->vector_array; -} - -GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().vector_base_type(); - GVectorArray *vector_array = new GVectorArray(type, 1); - - auto *value = - allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release(); - value_per_output_id_[socket.id()] = value; - - return *value->vector_array; - } - - BLI_assert(any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast<OutputVectorValue *>(any_value)->vector_array; - BLI_assert(vector_array.size() == 1); - return vector_array; -} - -GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &type = from.data_type().single_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(type == to.data_type().single_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span; - const GVArray &virtual_array = this->get_single_input__full(input, scope); - virtual_array.materialize_to_uninitialized(mask_, span.data()); - return span; - } - - if (from_any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value); - if (value->max_remaining_users == 1 && !value->is_single_allocated) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return value->span; - } - } - - const GVArray &virtual_array = this->get_single_input__full(input, scope); - void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); - GMutableSpan new_array_ref(type, new_buffer, min_array_size_); - virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data()); - - OwnSingleValue *new_value = - allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), false).release(); - value_per_output_id_[to.id()] = new_value; - return new_array_ref; -} - -GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &type = from.data_type().single_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(type == to.data_type().single_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span; - BLI_assert(span.size() == 1); - const GVArray &virtual_array = this->get_single_input__single(input, scope); - virtual_array.get_single_to_uninitialized(span[0]); - return span; - } - - if (from_any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - BLI_assert(value->span.size() == 1); - return value->span; - } - } - - const GVArray &virtual_array = this->get_single_input__single(input, scope); - - void *new_buffer = allocator_.allocate(type.size(), type.alignment()); - virtual_array.get_single_to_uninitialized(new_buffer); - GMutableSpan new_array_ref(type, new_buffer, 1); - - OwnSingleValue *new_value = - allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), true).release(); - value_per_output_id_[to.id()] = new_value; - return new_array_ref; -} - -GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &base_type = from.data_type().vector_base_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(base_type == to.data_type().vector_base_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array; - const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope); - vector_array.extend(mask_, virtual_vector_array); - return vector_array; - } - - if (from_any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return *value->vector_array; - } - } - - const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope); - - GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_); - new_vector_array->extend(mask_, virtual_vector_array); - - OwnVectorValue *new_value = - allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release(); - value_per_output_id_[to.id()] = new_value; - - return *new_vector_array; -} - -GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &base_type = from.data_type().vector_base_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(base_type == to.data_type().vector_base_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array; - BLI_assert(vector_array.size() == 1); - const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope); - vector_array.extend({0}, virtual_vector_array); - return vector_array; - } - - if (from_any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return *value->vector_array; - } - } - - const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope); - - GVectorArray *new_vector_array = new GVectorArray(base_type, 1); - new_vector_array->extend({0}, virtual_vector_array); - - OwnVectorValue *new_value = - allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release(); - value_per_output_id_[to.id()] = new_value; - return *new_vector_array; -} - -const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket, - ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - if (value->is_single_allocated) { - return scope.construct<GVArray_For_SingleValueRef>( - __func__, value->span.type(), min_array_size_, value->span.data()); - } - - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast<InputSingleValue *>(any_value); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); - BLI_assert(value->is_computed); - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - - BLI_assert(false); - return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); -} - -const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket, - ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - BLI_assert(value->span.size() == 1); - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast<InputSingleValue *>(any_value); - BLI_assert(value->virtual_array.is_single()); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); - BLI_assert(value->is_computed); - BLI_assert(value->span.size() == 1); - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - - BLI_assert(false); - return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); -} - -const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( - const MFInputSocket &socket, ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - if (value->vector_array->size() == 1) { - GSpan span = (*value->vector_array)[0]; - return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_); - } - - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast<InputVectorValue *>(any_value); - return value->virtual_vector_array; - } - if (any_value->type == ValueType::OutputVector) { - OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); -} - -const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( - const MFInputSocket &socket, ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast<InputVectorValue *>(any_value); - BLI_assert(value->virtual_vector_array.is_single_vector()); - return value->virtual_vector_array; - } - if (any_value->type == ValueType::OutputVector) { - OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); -} - -/** \} */ - -} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc deleted file mode 100644 index 75c3583c5e5..00000000000 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ /dev/null @@ -1,501 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup fn - */ - -/* Used to check if two multi-functions have the exact same type. */ -#include <typeinfo> - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - -#include "BLI_disjoint_set.hh" -#include "BLI_ghash.h" -#include "BLI_map.hh" -#include "BLI_multi_value_map.hh" -#include "BLI_rand.h" -#include "BLI_stack.hh" - -namespace blender::fn::mf_network_optimization { - -/* -------------------------------------------------------------------- */ -/** \name Utility functions to find nodes in a network. - * \{ */ - -static bool set_tag_and_check_if_modified(bool &tag, bool new_value) -{ - if (tag != new_value) { - tag = new_value; - return true; - } - - return false; -} - -static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes) -{ - Array<bool> is_to_the_left(network.node_id_amount(), false); - Stack<MFNode *> nodes_to_check; - - for (MFNode *node : nodes) { - is_to_the_left[node->id()] = true; - nodes_to_check.push(node); - } - - while (!nodes_to_check.is_empty()) { - MFNode &node = *nodes_to_check.pop(); - - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin = input_socket->origin(); - if (origin != nullptr) { - MFNode &origin_node = origin->node(); - if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) { - nodes_to_check.push(&origin_node); - } - } - } - } - - return is_to_the_left; -} - -static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes) -{ - Array<bool> is_to_the_right(network.node_id_amount(), false); - Stack<MFNode *> nodes_to_check; - - for (MFNode *node : nodes) { - is_to_the_right[node->id()] = true; - nodes_to_check.push(node); - } - - while (!nodes_to_check.is_empty()) { - MFNode &node = *nodes_to_check.pop(); - - for (MFOutputSocket *output_socket : node.outputs()) { - for (MFInputSocket *target_socket : output_socket->targets()) { - MFNode &target_node = target_socket->node(); - if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) { - nodes_to_check.push(&target_node); - } - } - } - } - - return is_to_the_right; -} - -static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network, - Span<bool> id_mask, - bool mask_value) -{ - Vector<MFNode *> nodes; - for (int id : id_mask.index_range()) { - if (id_mask[id] == mask_value) { - MFNode *node = network.node_or_null_by_id(id); - if (node != nullptr) { - nodes.append(node); - } - } - } - return nodes; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Dead Node Removal - * \{ */ - -/** - * Unused nodes are all those nodes that no dummy node depends upon. - */ -void dead_node_removal(MFNetwork &network) -{ - Array<bool> node_is_used_mask = mask_nodes_to_the_left(network, - network.dummy_nodes().cast<MFNode *>()); - Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false); - network.remove(nodes_to_remove); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Constant Folding - * \{ */ - -static bool function_node_can_be_constant(MFFunctionNode *node) -{ - if (node->has_unlinked_inputs()) { - return false; - } - if (node->function().depends_on_context()) { - return false; - } - return true; -} - -static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network) -{ - Vector<MFNode *> non_constant_nodes; - non_constant_nodes.extend(network.dummy_nodes().cast<MFNode *>()); - - for (MFFunctionNode *node : network.function_nodes()) { - if (!function_node_can_be_constant(node)) { - non_constant_nodes.append(node); - } - } - return non_constant_nodes; -} - -static bool output_has_non_constant_target_node(MFOutputSocket *output_socket, - Span<bool> is_not_constant_mask) -{ - for (MFInputSocket *target_socket : output_socket->targets()) { - MFNode &target_node = target_socket->node(); - bool target_is_not_constant = is_not_constant_mask[target_node.id()]; - if (target_is_not_constant) { - return true; - } - } - return false; -} - -static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket) -{ - for (MFInputSocket *target_socket : output_socket->targets()) { - if (target_socket->node().is_dummy()) { - return target_socket; - } - } - return nullptr; -} - -static Vector<MFInputSocket *> find_constant_inputs_to_fold( - MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes) -{ - Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network); - Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes); - Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false); - - Vector<MFInputSocket *> sockets_to_compute; - for (MFNode *node : constant_nodes) { - if (node->inputs().size() == 0) { - continue; - } - - for (MFOutputSocket *output_socket : node->outputs()) { - MFDataType data_type = output_socket->data_type(); - if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) { - MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket); - if (dummy_target == nullptr) { - dummy_target = &network.add_output("Dummy", data_type); - network.add_link(*output_socket, *dummy_target); - r_temporary_nodes.append(&dummy_target->node().as_dummy()); - } - - sockets_to_compute.append(dummy_target); - } - } - } - return sockets_to_compute; -} - -static void prepare_params_for_constant_folding(const MultiFunction &network_fn, - MFParamsBuilder ¶ms, - ResourceScope &scope) -{ - for (int param_index : network_fn.param_indices()) { - MFParamType param_type = network_fn.param_type(param_index); - MFDataType data_type = param_type.data_type(); - - switch (data_type.category()) { - case MFDataType::Single: { - /* Allocates memory for a single constant folded value. */ - const CPPType &cpp_type = data_type.single_type(); - void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment()); - GMutableSpan array{cpp_type, buffer, 1}; - params.add_uninitialized_single_output(array); - break; - } - case MFDataType::Vector: { - /* Allocates memory for a constant folded vector. */ - const CPPType &cpp_type = data_type.vector_base_type(); - GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1); - params.add_vector_output(vector_array); - break; - } - } - } -} - -static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn, - MFParamsBuilder ¶ms, - ResourceScope &scope, - MFNetwork &network) -{ - Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr}; - - for (int param_index : network_fn.param_indices()) { - MFParamType param_type = network_fn.param_type(param_index); - MFDataType data_type = param_type.data_type(); - - const MultiFunction *constant_fn = nullptr; - - switch (data_type.category()) { - case MFDataType::Single: { - const CPPType &cpp_type = data_type.single_type(); - GMutableSpan array = params.computed_array(param_index); - void *buffer = array.data(); - scope.add(buffer, array.type().destruct_fn(), AT); - - constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer); - break; - } - case MFDataType::Vector: { - GVectorArray &vector_array = params.computed_vector_array(param_index); - GSpan array = vector_array[0]; - constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array); - break; - } - } - - MFFunctionNode &folded_node = network.add_function(*constant_fn); - folded_sockets[param_index] = &folded_node.output(0); - } - return folded_sockets; -} - -static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes( - MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope) -{ - MFNetworkEvaluator network_fn{{}, sockets_to_compute}; - - MFContextBuilder context; - MFParamsBuilder params{network_fn, 1}; - prepare_params_for_constant_folding(network_fn, params, scope); - network_fn.call({0}, params, context); - return add_constant_folded_sockets(network_fn, params, scope, network); -} - -class MyClass { - MFDummyNode node; -}; - -/** - * Find function nodes that always output the same value and replace those with constant nodes. - */ -void constant_folding(MFNetwork &network, ResourceScope &scope) -{ - Vector<MFDummyNode *> temporary_nodes; - Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes); - if (inputs_to_fold.size() == 0) { - return; - } - - Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes( - network, inputs_to_fold, scope); - - for (int i : inputs_to_fold.index_range()) { - MFOutputSocket &original_socket = *inputs_to_fold[i]->origin(); - network.relink(original_socket, *folded_sockets[i]); - } - - network.remove(temporary_nodes.as_span().cast<MFNode *>()); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Common Sub-network Elimination - * \{ */ - -static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes) -{ - if (node.function().depends_on_context()) { - return BLI_rng_get_uint(rng); - } - if (node.has_unlinked_inputs()) { - return BLI_rng_get_uint(rng); - } - - uint64_t combined_inputs_hash = 394659347u; - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin_socket = input_socket->origin(); - uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()], - origin_socket->index()); - combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash); - } - - uint64_t function_hash = node.function().hash(); - uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash); - return node_hash; -} - -/** - * Produces a hash for every node. Two nodes with the same hash should have a high probability of - * outputting the same values. - */ -static Array<uint64_t> compute_node_hashes(MFNetwork &network) -{ - RNG *rng = BLI_rng_new(0); - Array<uint64_t> node_hashes(network.node_id_amount()); - Array<bool> node_is_hashed(network.node_id_amount(), false); - - /* No dummy nodes are not assumed to output the same values. */ - for (MFDummyNode *node : network.dummy_nodes()) { - uint64_t node_hash = BLI_rng_get_uint(rng); - node_hashes[node->id()] = node_hash; - node_is_hashed[node->id()] = true; - } - - Stack<MFFunctionNode *> nodes_to_check; - nodes_to_check.push_multiple(network.function_nodes()); - - while (!nodes_to_check.is_empty()) { - MFFunctionNode &node = *nodes_to_check.peek(); - if (node_is_hashed[node.id()]) { - nodes_to_check.pop(); - continue; - } - - /* Make sure that origin nodes are hashed first. */ - bool all_dependencies_ready = true; - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin_socket = input_socket->origin(); - if (origin_socket != nullptr) { - MFNode &origin_node = origin_socket->node(); - if (!node_is_hashed[origin_node.id()]) { - all_dependencies_ready = false; - nodes_to_check.push(&origin_node.as_function()); - } - } - } - if (!all_dependencies_ready) { - continue; - } - - uint64_t node_hash = compute_node_hash(node, rng, node_hashes); - node_hashes[node.id()] = node_hash; - node_is_hashed[node.id()] = true; - nodes_to_check.pop(); - } - - BLI_rng_free(rng); - return node_hashes; -} - -static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network, - Span<uint64_t> node_hashes) -{ - MultiValueMap<uint64_t, MFNode *> nodes_by_hash; - for (int id : IndexRange(network.node_id_amount())) { - MFNode *node = network.node_or_null_by_id(id); - if (node != nullptr) { - uint64_t node_hash = node_hashes[id]; - nodes_by_hash.add(node_hash, node); - } - } - return nodes_by_hash; -} - -static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b) -{ - if (&a == &b) { - return true; - } - if (typeid(a) == typeid(b)) { - return a.equals(b); - } - return false; -} - -static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b) -{ - if (cache.in_same_set(a.id(), b.id())) { - return true; - } - - if (a.is_dummy() || b.is_dummy()) { - return false; - } - if (!functions_are_equal(a.as_function().function(), b.as_function().function())) { - return false; - } - for (int i : a.inputs().index_range()) { - const MFOutputSocket *origin_a = a.input(i).origin(); - const MFOutputSocket *origin_b = b.input(i).origin(); - if (origin_a == nullptr || origin_b == nullptr) { - return false; - } - if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) { - return false; - } - } - - cache.join(a.id(), b.id()); - return true; -} - -static void relink_duplicate_nodes(MFNetwork &network, - MultiValueMap<uint64_t, MFNode *> &nodes_by_hash) -{ - DisjointSet same_node_cache{network.node_id_amount()}; - - for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) { - if (nodes_with_same_hash.size() <= 1) { - continue; - } - - Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash; - while (nodes_to_check.size() >= 2) { - Vector<MFNode *, 16> remaining_nodes; - - MFNode &deduplicated_node = *nodes_to_check[0]; - for (MFNode *node : nodes_to_check.as_span().drop_front(1)) { - /* This is true with fairly high probability, but hash collisions can happen. So we have to - * check if the node actually output the same values. */ - if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) { - for (int i : deduplicated_node.outputs().index_range()) { - network.relink(node->output(i), deduplicated_node.output(i)); - } - } - else { - remaining_nodes.append(node); - } - } - nodes_to_check = std::move(remaining_nodes); - } - } -} - -/** - * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node - * groups were used to create the network. - */ -void common_subnetwork_elimination(MFNetwork &network) -{ - Array<uint64_t> node_hashes = compute_node_hashes(network); - MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes); - relink_duplicate_nodes(network, nodes_by_hash); -} - -/** \} */ - -} // namespace blender::fn::mf_network_optimization diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc deleted file mode 100644 index 7b9738e5ca4..00000000000 --- a/source/blender/functions/tests/FN_multi_function_network_test.cc +++ /dev/null @@ -1,280 +0,0 @@ -/* Apache License, Version 2.0 */ - -#include "testing/testing.h" - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network.hh" -#include "FN_multi_function_network_evaluation.hh" - -namespace blender::fn::tests { -namespace { - -TEST(multi_function_network, Test1) -{ - CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; }); - CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; }); - - MFNetwork network; - - MFNode &node1 = network.add_function(add_10_fn); - MFNode &node2 = network.add_function(multiply_fn); - MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>()); - MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>()); - network.add_link(node1.output(0), node2.input(0)); - network.add_link(node1.output(0), node2.input(1)); - network.add_link(node2.output(0), output_socket); - network.add_link(input_socket, node1.input(0)); - - MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}}; - - { - Array<int> values = {4, 6, 1, 2, 0}; - Array<int> results(values.size(), 0); - - MFParamsBuilder params(network_fn, values.size()); - params.add_readonly_single_input(values.as_span()); - params.add_uninitialized_single_output(results.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({0, 2, 3, 4}, params, context); - - EXPECT_EQ(results[0], 14 * 14); - EXPECT_EQ(results[1], 0); - EXPECT_EQ(results[2], 11 * 11); - EXPECT_EQ(results[3], 12 * 12); - EXPECT_EQ(results[4], 10 * 10); - } - { - int value = 3; - Array<int> results(5, 0); - - MFParamsBuilder params(network_fn, results.size()); - params.add_readonly_single_input(&value); - params.add_uninitialized_single_output(results.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({1, 2, 4}, params, context); - - EXPECT_EQ(results[0], 0); - EXPECT_EQ(results[1], 13 * 13); - EXPECT_EQ(results[2], 13 * 13); - EXPECT_EQ(results[3], 0); - EXPECT_EQ(results[4], 13 * 13); - } -} - -class ConcatVectorsFunction : public MultiFunction { - public: - ConcatVectorsFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Concat Vectors"}; - signature.vector_mutable<int>("A"); - signature.vector_input<int>("B"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray &a = params.vector_mutable(0); - const GVVectorArray &b = params.readonly_vector_input(1); - a.extend(mask, b); - } -}; - -class AppendFunction : public MultiFunction { - public: - AppendFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Append"}; - signature.vector_mutable<int>("Vector"); - signature.single_input<int>("Value"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0); - const VArray<int> &values = params.readonly_single_input<int>(1); - - for (int64_t i : mask) { - vectors.append(i, values[i]); - } - } -}; - -class SumVectorFunction : public MultiFunction { - public: - SumVectorFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Sum Vectors"}; - signature.vector_input<int>("Vector"); - signature.single_output<int>("Sum"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0); - MutableSpan<int> sums = params.uninitialized_single_output<int>(1); - - for (int64_t i : mask) { - int sum = 0; - for (int j : IndexRange(vectors.get_vector_size(i))) { - sum += vectors.get_vector_element(i, j); - } - sums[i] = sum; - } - } -}; - -class CreateRangeFunction : public MultiFunction { - public: - CreateRangeFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Create Range"}; - signature.single_input<int>("Size"); - signature.vector_output<int>("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size"); - GVectorArray_TypedMutableRef<int> ranges = params.vector_output<int>(1, "Range"); - - for (int64_t i : mask) { - int size = sizes[i]; - for (int j : IndexRange(size)) { - ranges.append(i, j); - } - } - } -}; - -TEST(multi_function_network, Test2) -{ - CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; }); - - ConcatVectorsFunction concat_vectors_fn; - AppendFunction append_fn; - SumVectorFunction sum_fn; - CreateRangeFunction create_range_fn; - - MFNetwork network; - - MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>()); - MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>()); - MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>()); - MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>()); - - MFNode &node1 = network.add_function(add_3_fn); - MFNode &node2 = network.add_function(create_range_fn); - MFNode &node3 = network.add_function(concat_vectors_fn); - MFNode &node4 = network.add_function(sum_fn); - MFNode &node5 = network.add_function(append_fn); - MFNode &node6 = network.add_function(sum_fn); - - network.add_link(input2, node1.input(0)); - network.add_link(node1.output(0), node2.input(0)); - network.add_link(node2.output(0), node3.input(1)); - network.add_link(input1, node3.input(0)); - network.add_link(input1, node4.input(0)); - network.add_link(node4.output(0), node5.input(1)); - network.add_link(node3.output(0), node5.input(0)); - network.add_link(node5.output(0), node6.input(0)); - network.add_link(node3.output(0), output1); - network.add_link(node6.output(0), output2); - - // std::cout << network.to_dot() << "\n\n"; - - MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}}; - - { - Array<int> input_value_1 = {3, 6}; - int input_value_2 = 4; - - GVectorArray output_value_1(CPPType::get<int32_t>(), 5); - Array<int> output_value_2(5, -1); - - MFParamsBuilder params(network_fn, 5); - GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5}; - params.add_readonly_vector_input(inputs_1); - params.add_readonly_single_input(&input_value_2); - params.add_vector_output(output_value_1); - params.add_uninitialized_single_output(output_value_2.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({1, 2, 4}, params, context); - - EXPECT_EQ(output_value_1[0].size(), 0); - EXPECT_EQ(output_value_1[1].size(), 9); - EXPECT_EQ(output_value_1[2].size(), 9); - EXPECT_EQ(output_value_1[3].size(), 0); - EXPECT_EQ(output_value_1[4].size(), 9); - - EXPECT_EQ(output_value_2[0], -1); - EXPECT_EQ(output_value_2[1], 39); - EXPECT_EQ(output_value_2[2], 39); - EXPECT_EQ(output_value_2[3], -1); - EXPECT_EQ(output_value_2[4], 39); - } - { - GVectorArray input_value_1(CPPType::get<int32_t>(), 3); - GVectorArray_TypedMutableRef<int> input_value_1_ref{input_value_1}; - input_value_1_ref.extend(0, {3, 4, 5}); - input_value_1_ref.extend(1, {1, 2}); - - Array<int> input_value_2 = {4, 2, 3}; - - GVectorArray output_value_1(CPPType::get<int32_t>(), 3); - Array<int> output_value_2(3, -1); - - MFParamsBuilder params(network_fn, 3); - params.add_readonly_vector_input(input_value_1); - params.add_readonly_single_input(input_value_2.as_span()); - params.add_vector_output(output_value_1); - params.add_uninitialized_single_output(output_value_2.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({0, 1, 2}, params, context); - - EXPECT_EQ(output_value_1[0].size(), 10); - EXPECT_EQ(output_value_1[1].size(), 7); - EXPECT_EQ(output_value_1[2].size(), 6); - - EXPECT_EQ(output_value_2[0], 45); - EXPECT_EQ(output_value_2[1], 16); - EXPECT_EQ(output_value_2[2], 15); - } -} - -} // namespace -} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 3d73e020eb2..91c72a51dd6 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -4,6 +4,7 @@ #include "FN_multi_function.hh" #include "FN_multi_function_builder.hh" +#include "FN_multi_function_test_common.hh" namespace blender::fn::tests { namespace { @@ -59,33 +60,6 @@ TEST(multi_function, AddFunction) EXPECT_EQ(output[2], 36); } -class AddPrefixFunction : public MultiFunction { - public: - AddPrefixFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Add Prefix"}; - signature.single_input<std::string>("Prefix"); - signature.single_mutable<std::string>("Strings"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix"); - MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings"); - - for (int64_t i : mask) { - strings[i] = prefixes[i] + strings[i]; - } - } -}; - TEST(multi_function, AddPrefixFunction) { AddPrefixFunction fn; @@ -113,43 +87,13 @@ TEST(multi_function, AddPrefixFunction) EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation"); } -class CreateRangeFunction : public MultiFunction { - public: - CreateRangeFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Create Range"}; - signature.single_input<uint>("Size"); - signature.vector_output<uint>("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<uint> &sizes = params.readonly_single_input<uint>(0, "Size"); - GVectorArray &ranges = params.vector_output(1, "Range"); - - for (int64_t i : mask) { - uint size = sizes[i]; - for (uint j : IndexRange(size)) { - ranges.append(i, &j); - } - } - } -}; - TEST(multi_function, CreateRangeFunction) { CreateRangeFunction fn; - GVectorArray ranges(CPPType::get<uint>(), 5); - GVectorArray_TypedMutableRef<uint> ranges_ref{ranges}; - Array<uint> sizes = {3, 0, 6, 1, 4}; + GVectorArray ranges(CPPType::get<int>(), 5); + GVectorArray_TypedMutableRef<int> ranges_ref{ranges}; + Array<int> sizes = {3, 0, 6, 1, 4}; MFParamsBuilder params(fn, ranges.size()); params.add_readonly_single_input(sizes.as_span()); @@ -172,34 +116,6 @@ TEST(multi_function, CreateRangeFunction) EXPECT_EQ(ranges_ref[2][1], 1); } -class GenericAppendFunction : public MultiFunction { - private: - MFSignature signature_; - - public: - GenericAppendFunction(const CPPType &type) - { - MFSignatureBuilder signature{"Append"}; - signature.vector_mutable("Vector", type); - signature.single_input("Value", type); - signature_ = signature.build(); - this->set_signature(&signature_); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray &vectors = params.vector_mutable(0, "Vector"); - const GVArray &values = params.readonly_single_input(1, "Value"); - - for (int64_t i : mask) { - BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer); - values.get(i, buffer); - vectors.append(i, buffer); - values.type().destruct(buffer); - } - } -}; - TEST(multi_function, GenericAppendFunction) { GenericAppendFunction fn(CPPType::get<int32_t>()); diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh new file mode 100644 index 00000000000..51c8fac8a96 --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_test_common.hh @@ -0,0 +1,174 @@ +/* Apache License, Version 2.0 */ + +#include "FN_multi_function.hh" + +namespace blender::fn::tests { + +class AddPrefixFunction : public MultiFunction { + public: + AddPrefixFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Add Prefix"}; + signature.single_input<std::string>("Prefix"); + signature.single_mutable<std::string>("Strings"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix"); + MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings"); + + for (int64_t i : mask) { + strings[i] = prefixes[i] + strings[i]; + } + } +}; + +class CreateRangeFunction : public MultiFunction { + public: + CreateRangeFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Create Range"}; + signature.single_input<int>("Size"); + signature.vector_output<int>("Range"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size"); + GVectorArray &ranges = params.vector_output(1, "Range"); + + for (int64_t i : mask) { + int size = sizes[i]; + for (int j : IndexRange(size)) { + ranges.append(i, &j); + } + } + } +}; + +class GenericAppendFunction : public MultiFunction { + private: + MFSignature signature_; + + public: + GenericAppendFunction(const CPPType &type) + { + MFSignatureBuilder signature{"Append"}; + signature.vector_mutable("Vector", type); + signature.single_input("Value", type); + signature_ = signature.build(); + this->set_signature(&signature_); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &vectors = params.vector_mutable(0, "Vector"); + const GVArray &values = params.readonly_single_input(1, "Value"); + + for (int64_t i : mask) { + BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer); + values.get(i, buffer); + vectors.append(i, buffer); + values.type().destruct(buffer); + } + } +}; + +class ConcatVectorsFunction : public MultiFunction { + public: + ConcatVectorsFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Concat Vectors"}; + signature.vector_mutable<int>("A"); + signature.vector_input<int>("B"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &a = params.vector_mutable(0); + const GVVectorArray &b = params.readonly_vector_input(1); + a.extend(mask, b); + } +}; + +class AppendFunction : public MultiFunction { + public: + AppendFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Append"}; + signature.vector_mutable<int>("Vector"); + signature.single_input<int>("Value"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0); + const VArray<int> &values = params.readonly_single_input<int>(1); + + for (int64_t i : mask) { + vectors.append(i, values[i]); + } + } +}; + +class SumVectorFunction : public MultiFunction { + public: + SumVectorFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Sum Vectors"}; + signature.vector_input<int>("Vector"); + signature.single_output<int>("Sum"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0); + MutableSpan<int> sums = params.uninitialized_single_output<int>(1); + + for (int64_t i : mask) { + int sum = 0; + for (int j : IndexRange(vectors.get_vector_size(i))) { + sum += vectors.get_vector_element(i, j); + } + sums[i] = sum; + } + } +}; + +} // namespace blender::fn::tests diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp index 355aa5c22f0..bd6f496c8ec 100644 --- a/source/blender/io/collada/collada_internal.cpp +++ b/source/blender/io/collada/collada_internal.cpp @@ -162,7 +162,7 @@ void UnitConverter::calculate_scale(Scene &sce) * Translation map. * Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be * included. Look at the IDREF XSD declaration for more. - * Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars, + * Follows strictly the COLLADA XSD declaration which explicitly allows non-English chars, * like special chars (e.g. micro sign), umlauts and so on. * The COLLADA spec also allows additional chars for member access ('.'), these * must obviously be removed too, otherwise they would be heavily misinterpreted. diff --git a/source/blender/makesdna/DNA_cachefile_defaults.h b/source/blender/makesdna/DNA_cachefile_defaults.h index 521b72567d4..74fbe5012ab 100644 --- a/source/blender/makesdna/DNA_cachefile_defaults.h +++ b/source/blender/makesdna/DNA_cachefile_defaults.h @@ -40,6 +40,8 @@ .handle = NULL, \ .handle_filepath[0] = '\0', \ .handle_readers = NULL, \ + .use_prefetch = 1, \ + .prefetch_cache_size = 4096, \ } /** \} */ diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index ae4ade49be1..0f4c53a6e7e 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -101,7 +101,15 @@ typedef struct CacheFile { */ char use_render_procedural; - char _pad1[7]; + char _pad1[3]; + + /** Enable data prefetching when using the Cycles Procedural. */ + char use_prefetch; + + /** Size in megabytes for the prefetch cache used by the Cycles Procedural. */ + int prefetch_cache_size; + + char _pad2[7]; char velocity_unit; /* Name of the velocity property in the archive. */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index f206bde4705..b943a8fad5a 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -1029,10 +1029,8 @@ void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *val void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len); int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop); void RNA_property_string_get_default(PointerRNA *ptr, PropertyRNA *prop, char *value); -char *RNA_property_string_get_default_alloc(PointerRNA *ptr, - PropertyRNA *prop, - char *fixedbuf, - int fixedlen); +char *RNA_property_string_get_default_alloc( + PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len); int RNA_property_string_default_length(PointerRNA *ptr, PropertyRNA *prop); int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop); @@ -1238,7 +1236,8 @@ bool RNA_enum_icon_from_value(const EnumPropertyItem *item, int value, int *r_ic bool RNA_enum_name_from_value(const EnumPropertyItem *item, int value, const char **r_name); void RNA_string_get(PointerRNA *ptr, const char *name, char *value); -char *RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen); +char *RNA_string_get_alloc( + PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len); int RNA_string_length(PointerRNA *ptr, const char *name); void RNA_string_set(PointerRNA *ptr, const char *name, const char *value); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 41c31dfebcb..c838032f1bb 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -3397,7 +3397,7 @@ char *RNA_property_string_get_alloc( buf = fixedbuf; } else { - buf = MEM_mallocN(sizeof(char) * (length + 1), "RNA_string_get_alloc"); + buf = MEM_mallocN(sizeof(char) * (length + 1), __func__); } #ifndef NDEBUG @@ -3537,10 +3537,8 @@ void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop, strcpy(value, sprop->defaultvalue); } -char *RNA_property_string_get_default_alloc(PointerRNA *ptr, - PropertyRNA *prop, - char *fixedbuf, - int fixedlen) +char *RNA_property_string_get_default_alloc( + PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len) { char *buf; int length; @@ -3553,11 +3551,15 @@ char *RNA_property_string_get_default_alloc(PointerRNA *ptr, buf = fixedbuf; } else { - buf = MEM_callocN(sizeof(char) * (length + 1), "RNA_string_get_alloc"); + buf = MEM_callocN(sizeof(char) * (length + 1), __func__); } RNA_property_string_get_default(ptr, prop, buf); + if (r_len) { + *r_len = length; + } + return buf; } @@ -6587,15 +6589,18 @@ void RNA_string_get(PointerRNA *ptr, const char *name, char *value) } } -char *RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen) +char *RNA_string_get_alloc( + PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len) { PropertyRNA *prop = RNA_struct_find_property(ptr, name); if (prop) { - /* TODO: pass length. */ - return RNA_property_string_get_alloc(ptr, prop, fixedbuf, fixedlen, NULL); + return RNA_property_string_get_alloc(ptr, prop, fixedbuf, fixedlen, r_len); } printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); + if (r_len != NULL) { + *r_len = 0; + } return NULL; } @@ -8166,7 +8171,7 @@ bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index) } case PROP_STRING: { - char *value = RNA_property_string_get_default_alloc(ptr, prop, NULL, 0); + char *value = RNA_property_string_get_default_alloc(ptr, prop, NULL, 0, NULL); RNA_property_string_set(ptr, prop, value); MEM_freeN(value); return true; diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index cb0a490417d..74d924b8937 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -150,6 +150,23 @@ static void rna_def_cachefile(BlenderRNA *brna) "determine which file to use in a file sequence"); RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + /* ----------------- Cache controls ----------------- */ + + prop = RNA_def_property(srna, "use_prefetch", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Use Prefetch", + "When enabled, the Cycles Procedural will preload animation data for faster updates"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "prefetch_cache_size", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text( + prop, + "Prefetch Cache Size", + "Memory usage limit in megabytes for the Cycles Procedural cache, if the data does not " + "fit within the limit, rendering is aborted"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + /* ----------------- Axis Conversion ----------------- */ prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 8e6ff961721..9a33849b645 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -122,7 +122,7 @@ static void rna_idname_validate(const char *name, char *r_name) { BLI_strncpy(r_name, name, MAX_ID_NAME - 2); - BLI_utf8_invalid_strip(r_name, strlen(r_name)); + BLI_str_utf8_invalid_strip(r_name, strlen(r_name)); } static void rna_Main_ID_remove(Main *bmain, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 57295f5e132..98521b43f52 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -85,7 +85,8 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" + +#include "FN_multi_function.hh" #include "WM_types.h" @@ -925,7 +926,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, { blender::ResourceScope scope; blender::LinearAllocator<> &allocator = scope.linear_allocator(); - blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope); + blender::nodes::NodeMultiFunctions mf_by_node{tree, scope}; Map<DOutputSocket, GMutablePointer> group_inputs; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e255922787e..5c529d5295b 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -844,7 +844,7 @@ class GeometryNodesEvaluator { } /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr); + const MultiFunction *multi_function = params_.mf_by_node->try_get(node); if (multi_function != nullptr) { this->execute_multi_function_node(node, *multi_function, node_state); return; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh index d8c60d31986..5151be07aa2 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -20,12 +20,14 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "FN_generic_pointer.hh" #include "DNA_modifier_types.h" +#include "FN_multi_function.hh" + namespace geo_log = blender::nodes::geometry_nodes_eval_log; namespace blender::modifiers::geometry_nodes { @@ -45,7 +47,7 @@ struct GeometryNodesEvaluationParams { * necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value * and then it can be freed. */ Vector<DSocket> force_compute_sockets; - nodes::MultiFunctionByNode *mf_by_node; + nodes::NodeMultiFunctions *mf_by_node; const NodesModifierData *modifier_; Depsgraph *depsgraph; Object *self_object; diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index ce427281db3..db0b769684e 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -358,13 +358,8 @@ static bool get_show_adaptive_options(const bContext *C, Panel *panel) /* Don't show adaptive options if the cycles experimental feature set is disabled. */ Scene *scene = CTX_data_scene(C); - PointerRNA scene_ptr; - RNA_id_pointer_create(&scene->id, &scene_ptr); - if (BKE_scene_uses_cycles(scene)) { - PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); - if (RNA_enum_get(&cycles_ptr, "feature_set") != 1) { /* EXPERIMENTAL */ - return false; - } + if (!BKE_scene_uses_cycles_experimental_features(scene)) { + return false; } return true; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index aff21e9d906..9335950a062 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -357,11 +357,10 @@ set(SRC intern/node_common.c intern/node_exec.cc intern/node_geometry_exec.cc + intern/node_multi_function.cc intern/node_socket.cc - intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/node_util.c - intern/type_callbacks.cc intern/type_conversions.cc composite/node_composite_util.h @@ -378,13 +377,12 @@ set(SRC NOD_geometry_exec.hh NOD_geometry_nodes_eval_log.hh NOD_math_functions.hh - NOD_node_tree_multi_function.hh + NOD_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h NOD_static_types.h NOD_texture.h - NOD_type_callbacks.hh NOD_type_conversions.hh intern/node_common.h intern/node_exec.h diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh new file mode 100644 index 00000000000..2f4b104fb4c --- /dev/null +++ b/source/blender/nodes/NOD_multi_function.hh @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "FN_multi_function.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +namespace blender::nodes { + +using namespace fn::multi_function_types; + +class NodeMultiFunctions; + +/** + * Utility class to help nodes build a multi-function for themselves. + */ +class NodeMultiFunctionBuilder : NonCopyable, NonMovable { + private: + ResourceScope &resource_scope_; + bNode &node_; + bNodeTree &tree_; + const MultiFunction *built_fn_ = nullptr; + + friend NodeMultiFunctions; + + public: + NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); + + /** + * Assign a multi-function for the current node. The input and output parameters of the function + * have to match the available sockets in the node. + */ + void set_matching_fn(const MultiFunction *fn); + void set_matching_fn(const MultiFunction &fn); + + /** + * Utility method for creating and assigning a multi-function when it can't have a static + * lifetime. + */ + template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args); + + bNode &node(); + bNodeTree &tree(); + + ResourceScope &resource_scope(); +}; + +/** + * Gives access to multi-functions for all nodes in a node tree that support them. + */ +class NodeMultiFunctions { + private: + Map<const bNode *, const MultiFunction *> map_; + + public: + NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); + + const MultiFunction *try_get(const DNode &node) const; +}; + +/* -------------------------------------------------------------------- + * NodeMultiFunctionBuilder inline methods. + */ + +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, + bNode &node, + bNodeTree &tree) + : resource_scope_(resource_scope), node_(node), tree_(tree) +{ +} + +inline bNode &NodeMultiFunctionBuilder::node() +{ + return node_; +} + +inline bNodeTree &NodeMultiFunctionBuilder::tree() +{ + return tree_; +} + +inline ResourceScope &NodeMultiFunctionBuilder::resource_scope() +{ + return resource_scope_; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) +{ + built_fn_ = fn; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) +{ + this->set_matching_fn(&fn); +} + +template<typename T, typename... Args> +inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) +{ + const T &fn = resource_scope_.construct<T>(__func__, std::forward<Args>(args)...); + this->set_matching_fn(&fn); +} + +/* -------------------------------------------------------------------- + * NodeMultiFunctions inline methods. + */ + +inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const +{ + return map_.lookup_default(node->bnode(), nullptr); +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh deleted file mode 100644 index 7eeeaef0b98..00000000000 --- a/source/blender/nodes/NOD_node_tree_multi_function.hh +++ /dev/null @@ -1,390 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -/** \file - * \ingroup nodes - * - * This file allows you to generate a multi-function network from a user-generated node tree. - */ - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network.hh" - -#include "NOD_derived_node_tree.hh" -#include "NOD_type_callbacks.hh" - -#include "BLI_multi_value_map.hh" -#include "BLI_resource_scope.hh" - -namespace blender::nodes { - -/** - * A MFNetworkTreeMap maps various components of a node tree to components of a fn::MFNetwork. This - * is necessary for further processing of a multi-function network that has been generated from a - * node tree. - */ -class MFNetworkTreeMap { - private: - /** - * Store by id instead of using a hash table to avoid unnecessary hash table lookups. - * - * Input sockets in a node tree can have multiple corresponding sockets in the generated - * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes. - */ - const DerivedNodeTree &tree_; - fn::MFNetwork &network_; - MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_; - - public: - MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network) - : tree_(tree), network_(network) - { - } - - const DerivedNodeTree &tree() const - { - return tree_; - } - - const fn::MFNetwork &network() const - { - return network_; - } - - fn::MFNetwork &network() - { - return network_; - } - - void add(const DSocket &dsocket, fn::MFSocket &socket) - { - BLI_assert(dsocket->is_input() == socket.is_input()); - BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DInputSocket &dsocket, fn::MFInputSocket &socket) - { - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket) - { - /* There can be at most one matching output socket. */ - BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DTreeContext &context, - Span<const InputSocketRef *> dsockets, - Span<fn::MFInputSocket *> sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DInputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add(const DTreeContext &context, - Span<const OutputSocketRef *> dsockets, - Span<fn::MFOutputSocket *> sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add_try_match(const DNode &dnode, fn::MFNode &node) - { - this->add_try_match(*dnode.context(), - dnode->inputs().cast<const SocketRef *>(), - node.inputs().cast<fn::MFSocket *>()); - this->add_try_match(*dnode.context(), - dnode->outputs().cast<const SocketRef *>(), - node.outputs().cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const InputSocketRef *> dsockets, - Span<fn::MFInputSocket *> sockets) - { - this->add_try_match( - context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const OutputSocketRef *> dsockets, - Span<fn::MFOutputSocket *> sockets) - { - this->add_try_match( - context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const SocketRef *> dsockets, - Span<fn::MFSocket *> sockets) - { - int used_sockets = 0; - for (const SocketRef *dsocket : dsockets) { - if (!dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*dsocket->typeinfo())) { - continue; - } - fn::MFSocket *socket = sockets[used_sockets]; - this->add(DSocket(&context, dsocket), *socket); - used_sockets++; - } - } - - fn::MFOutputSocket &lookup(const DOutputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket)[0]->as_output(); - } - - Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket).cast<fn::MFInputSocket *>(); - } - - fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket) - { - Span<fn::MFInputSocket *> sockets = this->lookup(dsocket); - BLI_assert(sockets.size() == 1); - fn::MFInputSocket &socket = *sockets[0]; - BLI_assert(socket.node().is_dummy()); - return socket; - } - - fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket) - { - fn::MFOutputSocket &socket = this->lookup(dsocket); - BLI_assert(socket.node().is_dummy()); - return socket; - } - - bool is_mapped(const DSocket &dsocket) const - { - return !sockets_by_dsocket_.lookup(dsocket).is_empty(); - } -}; - -/** - * This data is necessary throughout the generation of a MFNetwork from a node tree. - */ -struct CommonMFNetworkBuilderData { - ResourceScope &scope; - fn::MFNetwork &network; - MFNetworkTreeMap &network_map; - const DerivedNodeTree &tree; -}; - -class MFNetworkBuilderBase { - protected: - CommonMFNetworkBuilderData &common_; - - public: - MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common) - { - } - - /** - * Returns the network that is currently being built. - */ - fn::MFNetwork &network() - { - return common_.network; - } - - /** - * Returns the map between the node tree and the multi-function network that is being built. - */ - MFNetworkTreeMap &network_map() - { - return common_.network_map; - } - - /** - * Returns a resource collector that will only be destructed after the multi-function network is - * destructed. - */ - ResourceScope &resource_scope() - { - return common_.scope; - } - - /** - * Constructs a new function that will live at least as long as the MFNetwork. - */ - template<typename T, typename... Args> T &construct_fn(Args &&...args) - { - BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), ""); - void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T)); - T *fn = new (buffer) T(std::forward<Args>(args)...); - common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str()); - return *fn; - } -}; - -/** - * This class is used by socket implementations to define how an unlinked input socket is handled - * in a multi-function network. - */ -class SocketMFNetworkBuilder : public MFNetworkBuilderBase { - private: - bNodeSocket *bsocket_; - fn::MFOutputSocket *built_socket_ = nullptr; - - public: - SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket) - : MFNetworkBuilderBase(common), bsocket_(dsocket->bsocket()) - { - } - - /** - * Returns the socket that is currently being built. - */ - bNodeSocket &bsocket() - { - return *bsocket_; - } - - /** - * Utility method that returns bsocket->default_value for the current socket. - */ - template<typename T> T *socket_default_value() - { - return static_cast<T *>(bsocket_->default_value); - } - - /** - * Builds a function node for that socket that outputs the given constant value. - */ - template<typename T> void set_constant_value(T value) - { - this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value)); - } - void set_constant_value(const CPPType &type, const void *value) - { - /* The value has live as long as the generated mf network. */ - this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value); - } - - template<typename T, typename... Args> void construct_generator_fn(Args &&...args) - { - const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...); - this->set_generator_fn(fn); - } - - /** - * Uses the first output of the given multi-function as value of the socket. - */ - void set_generator_fn(const fn::MultiFunction &fn) - { - fn::MFFunctionNode &node = common_.network.add_function(fn); - this->set_socket(node.output(0)); - } - - /** - * Define a multi-function socket that outputs the value of the bsocket. - */ - void set_socket(fn::MFOutputSocket &socket) - { - built_socket_ = &socket; - } - - fn::MFOutputSocket *built_socket() - { - return built_socket_; - } -}; - -/** - * This class is used by node implementations to define how a user-level node expands into - * multi-function nodes internally. - */ -class NodeMFNetworkBuilder : public MFNetworkBuilderBase { - private: - DNode dnode_; - - public: - NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, DNode dnode) - : MFNetworkBuilderBase(common), dnode_(dnode) - { - } - - /** - * Tells the builder to build a function that corresponds to the node that is being built. It - * will try to match up sockets. - */ - template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&...args) - { - T &function = this->construct_fn<T>(std::forward<Args>(args)...); - this->set_matching_fn(function); - return function; - } - - const fn::MultiFunction &get_not_implemented_fn() - { - return this->get_default_fn("Not Implemented (" + dnode_->name() + ")"); - } - - const fn::MultiFunction &get_default_fn(StringRef name); - - const void set_not_implemented() - { - this->set_matching_fn(this->get_not_implemented_fn()); - } - - /** - * Tells the builder that the given function corresponds to the node that is being built. It will - * try to match up sockets. For that it skips unavailable and non-data sockets. - */ - void set_matching_fn(const fn::MultiFunction &function) - { - fn::MFFunctionNode &node = common_.network.add_function(function); - common_.network_map.add_try_match(dnode_, node); - } - - /** - * Returns the node that is currently being built. - */ - bNode &bnode() - { - return *dnode_->bnode(); - } - - /** - * Returns the node that is currently being built. - */ - const DNode &dnode() const - { - return dnode_; - } -}; - -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope); - -using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>; -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope); - -} // namespace blender::nodes diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index d21e0938356..cc657d6f91d 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -67,7 +67,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_OP_COLOR, N_("Color")); func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector")); func(calldata, NODE_CLASS_OP_FILTER, N_("Filter")); - func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor")); + func(calldata, NODE_CLASS_CONVERTER, N_("Converter")); func(calldata, NODE_CLASS_MATTE, N_("Matte")); func(calldata, NODE_CLASS_DISTORT, N_("Distort")); func(calldata, NODE_CLASS_GROUP, N_("Group")); diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.c b/source/blender/nodes/composite/nodes/node_composite_idMask.c index a81dce10284..84563e7560b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_idMask.c +++ b/source/blender/nodes/composite/nodes/node_composite_idMask.c @@ -38,7 +38,7 @@ void register_node_type_cmp_idmask(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_idmask_in, cmp_node_idmask_out); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_math.c b/source/blender/nodes/composite/nodes/node_composite_math.c index c2982964a9f..2191c6bcdc3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.c +++ b/source/blender/nodes/composite/nodes/node_composite_math.c @@ -36,7 +36,7 @@ void register_node_type_cmp_math(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_math_in, cmp_node_math_out); node_type_label(&ntype, node_math_label); node_type_update(&ntype, node_math_update); diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.c b/source/blender/nodes/composite/nodes/node_composite_premulkey.c index b5dc15a2789..be76bbf01cf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.c +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.c @@ -38,7 +38,7 @@ void register_node_type_cmp_premulkey(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_premulkey_in, cmp_node_premulkey_out); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c index 03d27caa701..001b197e23a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c @@ -40,7 +40,7 @@ void register_node_type_cmp_sephsva(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_sephsva_in, cmp_node_sephsva_out); nodeRegisterType(&ntype); @@ -63,7 +63,7 @@ void register_node_type_cmp_combhsva(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_combhsva_in, cmp_node_combhsva_out); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c index ee8d6b0c186..e08f27db254 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c @@ -40,7 +40,7 @@ void register_node_type_cmp_seprgba(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_seprgba_in, cmp_node_seprgba_out); nodeRegisterType(&ntype); @@ -63,7 +63,7 @@ void register_node_type_cmp_combrgba(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_combrgba_in, cmp_node_combrgba_out); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c index a4aa6e4bf77..b3884296600 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c @@ -43,7 +43,7 @@ void register_node_type_cmp_sepycca(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_sepycca_in, cmp_node_sepycca_out); node_type_init(&ntype, node_composit_init_mode_sepycca); @@ -72,7 +72,7 @@ void register_node_type_cmp_combycca(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_combycca_in, cmp_node_combycca_out); node_type_init(&ntype, node_composit_init_mode_combycca); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c index 6c416b4f485..4da79ec7981 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c @@ -38,7 +38,7 @@ void register_node_type_cmp_sepyuva(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_sepyuva_in, cmp_node_sepyuva_out); nodeRegisterType(&ntype); @@ -61,7 +61,7 @@ void register_node_type_cmp_combyuva(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_combyuva_in, cmp_node_combyuva_out); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.c b/source/blender/nodes/composite/nodes/node_composite_setalpha.c index 1488ff1a25f..1b44cc011e9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.c +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.c @@ -45,7 +45,7 @@ void register_node_type_cmp_setalpha(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_setalpha_in, cmp_node_setalpha_out); node_type_init(&ntype, node_composit_init_setalpha); node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c index ec5c79cc087..b09d5119bc4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.c +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c @@ -146,7 +146,7 @@ void register_node_type_cmp_switch_view(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out); ntype.initfunc_api = init_switch_view; diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c b/source/blender/nodes/composite/nodes/node_composite_valToRgb.c index 0d46341b610..ed6dbfa2bf3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c +++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.c @@ -43,7 +43,7 @@ void register_node_type_cmp_valtorgb(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_valtorgb_in, cmp_node_valtorgb_out); node_type_size(&ntype, 240, 200, 320); node_type_init(&ntype, node_composit_init_valtorgb); @@ -66,7 +66,7 @@ void register_node_type_cmp_rgbtobw(void) { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0); + cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, cmp_node_rgbtobw_in, cmp_node_rgbtobw_out); node_type_size_preset(&ntype, NODE_SIZE_SMALL); diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index 9fbd6712827..96a8f29c3e9 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -30,7 +30,7 @@ #include "BLT_translation.h" #include "NOD_function.h" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "node_util.h" diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 7a83ff8e016..58e7d82beea 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -58,7 +58,7 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{ "And", [](bool a, bool b) { return a && b; }}; @@ -68,20 +68,21 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: - return and_fn; + return &and_fn; case NODE_BOOLEAN_MATH_OR: - return or_fn; + return &or_fn; case NODE_BOOLEAN_MATH_NOT: - return not_fn; + return ¬_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_boolean_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -89,11 +90,11 @@ void register_node_type_fn_boolean_math() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTOR, 0); + fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out); node_type_label(&ntype, node_boolean_math_label); node_type_update(&ntype, node_boolean_math_update); - ntype.expand_in_mf_network = node_boolean_expand_in_mf_network; + ntype.build_multi_function = fn_node_boolean_math_build_multi_function; ntype.draw_buttons = fn_node_boolean_math_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 6c8df8f2ea0..918dd24e520 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -64,7 +64,7 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &node) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{ "Less Than", [](float a, float b) { return a < b; }}; @@ -81,26 +81,27 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &node) switch (node.custom1) { case NODE_FLOAT_COMPARE_LESS_THAN: - return less_than_fn; + return &less_than_fn; case NODE_FLOAT_COMPARE_LESS_EQUAL: - return less_equal_fn; + return &less_equal_fn; case NODE_FLOAT_COMPARE_GREATER_THAN: - return greater_than_fn; + return &greater_than_fn; case NODE_FLOAT_COMPARE_GREATER_EQUAL: - return greater_equal_fn; + return &greater_equal_fn; case NODE_FLOAT_COMPARE_EQUAL: - return equal_fn; + return &equal_fn; case NODE_FLOAT_COMPARE_NOT_EQUAL: - return not_equal_fn; + return ¬_equal_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_compare_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -108,11 +109,11 @@ void register_node_type_fn_float_compare() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTOR, 0); + fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out); node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); - ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_compare_build_multi_function; ntype.draw_buttons = geo_node_float_compare_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 26cde576400..40b8f27f895 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -50,7 +50,7 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SO<float, int> round_fn{"Round", [](float a) { return (int)round(a); }}; @@ -63,22 +63,23 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: - return round_fn; + return &round_fn; case FN_NODE_FLOAT_TO_INT_FLOOR: - return floor_fn; + return &floor_fn; case FN_NODE_FLOAT_TO_INT_CEIL: - return ceil_fn; + return &ceil_fn; case FN_NODE_FLOAT_TO_INT_TRUNCATE: - return trunc_fn; + return &trunc_fn; } BLI_assert_unreachable(); - return blender::fn::dummy_multi_function; + return nullptr; } -static void node_float_to_int_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_to_int_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -86,10 +87,10 @@ void register_node_type_fn_float_to_int() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 0); + fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out); node_type_label(&ntype, node_float_to_int_label); - ntype.expand_in_mf_network = node_float_to_int_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_to_int_build_multi_function; ntype.draw_buttons = fn_node_float_to_int_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index f16bdef2f38..560ace57aba 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -29,14 +29,14 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(layout, ptr, "string", 0, "", ICON_NONE); } -static void fn_node_input_string_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_input_string_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); - - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(string); + builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>( + std::move(string)); } static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -78,7 +78,7 @@ void register_node_type_fn_input_string() node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out); node_type_init(&ntype, fn_node_input_string_init); node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); - ntype.expand_in_mf_network = fn_node_input_string_expand_in_mf_network; + ntype.build_multi_function = fn_node_input_string_build_multi_function; ntype.draw_buttons = fn_node_input_string_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 2cd4eb1d9df..244c045de9a 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -32,16 +32,14 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE); } -static void fn_node_vector_input_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_vector_input_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage); blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<blender::float3>>(vector); } - static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector), @@ -58,7 +56,7 @@ void register_node_type_fn_input_vector() node_type_init(&ntype, fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); - ntype.expand_in_mf_network = fn_node_vector_input_expand_in_mf_network; + ntype.build_multi_function = fn_node_vector_input_build_multi_function; ntype.draw_buttons = fn_node_input_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index a3c9f44b6a1..47ec9adf6bd 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -67,10 +67,11 @@ class RandomFloatFunction : public blender::fn::MultiFunction { } }; -static void fn_node_random_float_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_random_float_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - builder.construct_and_set_matching_fn<RandomFloatFunction>(); + static RandomFloatFunction fn; + builder.set_matching_fn(fn); } void register_node_type_fn_random_float() @@ -79,6 +80,6 @@ void register_node_type_fn_random_float() fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); - ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network; + ntype.build_multi_function = fn_node_random_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 07a89a1fa8c..d6b23c38ee4 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -80,7 +80,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_ATTRIBUTE, N_("Attribute")); func(calldata, NODE_CLASS_OP_COLOR, N_("Color")); func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector")); - func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor")); + func(calldata, NODE_CLASS_CONVERTER, N_("Converter")); func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 30f6dc2394f..67b7065ca94 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -204,7 +204,7 @@ void register_node_type_geo_switch() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0); + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); node_type_init(&ntype, geo_node_switch_init); node_type_update(&ntype, blender::nodes::geo_node_switch_update); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index ffa20579acc..a3bbca90731 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -19,7 +19,6 @@ #include "DEG_depsgraph_query.h" #include "NOD_geometry_exec.hh" -#include "NOD_type_callbacks.hh" #include "NOD_type_conversions.hh" #include "node_geometry_util.hh" diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/nodes/intern/node_multi_function.cc index 2be78f929db..c91899ed8c2 100644 --- a/source/blender/nodes/NOD_type_callbacks.hh +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -14,21 +14,27 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#pragma once - -#include <optional> - -#include "BKE_node.h" - -#include "FN_multi_function_data_type.hh" +#include "NOD_multi_function.hh" namespace blender::nodes { -using fn::CPPType; -using fn::MFDataType; - -std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype); -bool socket_is_mf_data_socket(const bNodeSocketType &stype); -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder); +NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) +{ + for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { + bNodeTree *btree = tree_ref->btree(); + for (const NodeRef *node : tree_ref->nodes()) { + bNode *bnode = node->bnode(); + if (bnode->typeinfo->build_multi_function == nullptr) { + continue; + } + NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; + bnode->typeinfo->build_multi_function(builder); + const MultiFunction *fn = builder.built_fn_; + if (fn != nullptr) { + map_.add_new(bnode, fn); + } + } + } +} } // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 2202ef2b64a..c761dfa473a 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -45,7 +45,6 @@ #include "MEM_guardedalloc.h" -#include "NOD_node_tree_multi_function.hh" #include "NOD_socket.h" #include "FN_cpp_type_make.hh" diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc deleted file mode 100644 index 7ab6495f733..00000000000 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ /dev/null @@ -1,409 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "NOD_node_tree_multi_function.hh" -#include "NOD_type_conversions.hh" - -#include "FN_multi_function_network_evaluation.hh" - -#include "BLI_color.hh" -#include "BLI_float2.hh" -#include "BLI_float3.hh" - -namespace blender::nodes { - -const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name) -{ - Vector<fn::MFDataType, 10> input_types; - Vector<fn::MFDataType, 10> output_types; - - for (const InputSocketRef *dsocket : dnode_->inputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - input_types.append(*data_type); - } - } - } - for (const OutputSocketRef *dsocket : dnode_->outputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - output_types.append(*data_type); - } - } - } - - const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_DefaultOutput>( - name, input_types, output_types); - return fn; -} - -static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode) -{ - constexpr int stack_capacity = 10; - - Vector<fn::MFDataType, stack_capacity> input_types; - Vector<StringRef, stack_capacity> input_names; - Vector<const InputSocketRef *, stack_capacity> input_dsockets; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - input_types.append(*data_type); - input_names.append(dsocket->name()); - input_dsockets.append(dsocket); - } - } - } - - Vector<fn::MFDataType, stack_capacity> output_types; - Vector<StringRef, stack_capacity> output_names; - Vector<const OutputSocketRef *, stack_capacity> output_dsockets; - - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - output_types.append(*data_type); - output_names.append(dsocket->name()); - output_dsockets.append(dsocket); - } - } - } - - fn::MFDummyNode &dummy_node = common.network.add_dummy( - dnode->name(), input_types, output_types, input_names, output_names); - - common.network_map.add(*dnode.context(), input_dsockets, dummy_node.inputs()); - common.network_map.add(*dnode.context(), output_dsockets, dummy_node.outputs()); -} - -static bool has_data_sockets(const DNode &dnode) -{ - for (const InputSocketRef *socket : dnode->inputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - for (const OutputSocketRef *socket : dnode->outputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - return false; -} - -static void foreach_node_to_insert(CommonMFNetworkBuilderData &common, - FunctionRef<void(DNode)> callback) -{ - common.tree.foreach_node([&](const DNode dnode) { - if (dnode->is_group_node()) { - return; - } - /* Don't insert non-root group input/output nodes, because they will be inlined. */ - if (!dnode.context()->is_root()) { - if (dnode->is_group_input_node() || dnode->is_group_output_node()) { - return; - } - } - callback(dnode); - }); -} - -/** - * Expands all function nodes in the multi-function network. Nodes that don't have an expand - * function, but do have data sockets, will get corresponding dummy nodes. - */ -static void insert_nodes(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network != nullptr) { - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - } - else if (has_data_sockets(dnode)) { - insert_dummy_node(common, dnode); - } - }); -} - -static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common, - fn::MFDataType type) -{ - const fn::MultiFunction *default_fn; - if (type.is_single()) { - default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>( - AT, type.single_type(), type.single_type().default_value()); - } - else { - default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>( - AT, fn::GSpan(type.vector_base_type())); - } - - fn::MFNode &node = common.network.add_function(*default_fn); - return node.output(0); -} - -static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common, - const DInputSocket &dsocket) -{ - BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo())); - - SocketMFNetworkBuilder builder{common, dsocket}; - socket_expand_in_mf_network(builder); - - fn::MFOutputSocket *built_socket = builder.built_socket(); - BLI_assert(built_socket != nullptr); - return built_socket; -} - -static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - for (const InputSocketRef *socket_ref : dnode->inputs()) { - const DInputSocket to_dsocket{dnode.context(), socket_ref}; - if (!to_dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*to_dsocket->typeinfo())) { - continue; - } - - Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(to_dsocket); - BLI_assert(to_sockets.size() >= 1); - const fn::MFDataType to_type = to_sockets[0]->data_type(); - - Vector<DSocket> from_dsockets; - to_dsocket.foreach_origin_socket([&](DSocket socket) { from_dsockets.append(socket); }); - if (from_dsockets.size() > 1) { - fn::MFOutputSocket &from_socket = insert_default_value_for_type(common, to_type); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(from_socket, *to_socket); - } - continue; - } - if (from_dsockets.is_empty()) { - /* The socket is not linked. Need to use the value of the socket itself. */ - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - if (from_dsockets[0]->is_input()) { - DInputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - DOutputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket); - const fn::MFDataType from_type = from_socket->data_type(); - - if (from_type != to_type) { - const fn::MultiFunction *conversion_fn = - get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type); - if (conversion_fn != nullptr) { - fn::MFNode &node = common.network.add_function(*conversion_fn); - common.network.add_link(*from_socket, node.input(0)); - from_socket = &node.output(0); - } - else { - from_socket = &insert_default_value_for_type(common, to_type); - } - } - - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*from_socket, *to_socket); - } - } - }); -} - -/** - * Expands all function nodes contained in the given node tree within the given multi-function - * network. - * - * Returns a mapping between the original node tree and the generated nodes/sockets for further - * processing. - */ -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope) -{ - MFNetworkTreeMap network_map{tree, network}; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - insert_nodes(common); - insert_links_and_unlinked_inputs(common); - - return network_map; -} - -/** - * A single node is allowed to expand into multiple nodes before evaluation. Depending on what - * nodes it expands to, it belongs a different type of the ones below. - */ -enum class NodeExpandType { - SingleFunctionNode, - MultipleFunctionNodes, - HasDummyNodes, -}; - -/** - * Checks how the given node expanded in the multi-function network. If it is only a single - * function node, the corresponding function is returned as well. - */ -static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map, - const DNode &dnode, - const fn::MultiFunction **r_single_function) -{ - const fn::MFFunctionNode *single_function_node = nullptr; - bool has_multiple_nodes = false; - bool has_dummy_nodes = false; - - auto check_mf_node = [&](fn::MFNode &mf_node) { - if (mf_node.is_function()) { - if (single_function_node == nullptr) { - single_function_node = &mf_node.as_function(); - } - if (&mf_node != single_function_node) { - has_multiple_nodes = true; - } - } - else { - BLI_assert(mf_node.is_dummy()); - has_dummy_nodes = true; - } - }; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - check_mf_node(mf_input->node()); - } - } - } - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - check_mf_node(mf_output.node()); - } - } - - if (has_dummy_nodes) { - return NodeExpandType::HasDummyNodes; - } - if (has_multiple_nodes) { - return NodeExpandType::MultipleFunctionNodes; - } - *r_single_function = &single_function_node->function(); - return NodeExpandType::SingleFunctionNode; -} - -static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple( - const DNode &dnode, - fn::MFNetwork &network, - MFNetworkTreeMap &network_map, - ResourceScope &scope) -{ - Vector<const fn::MFOutputSocket *> dummy_fn_inputs; - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo()); - fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type); - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - network.add_link(fn_input, *mf_input); - dummy_fn_inputs.append(&fn_input); - } - } - } - Vector<const fn::MFInputSocket *> dummy_fn_outputs; - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - MFDataType data_type = mf_output.data_type(); - fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type); - network.add_link(mf_output, fn_output); - dummy_fn_outputs.append(&fn_output); - } - } - - fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>( - __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs)); - return fn_evaluator; -} - -/** - * Returns a single multi-function for every node that supports it. This makes it easier to reuse - * the multi-function implementation of nodes in different contexts. - */ -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope) -{ - /* Build a network that nodes can insert themselves into. However, the individual nodes are not - * connected. */ - fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__); - MFNetworkTreeMap network_map{tree, network}; - MultiFunctionByNode functions_by_node; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - tree.foreach_node([&](DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network == nullptr) { - /* This node does not have a multi-function implementation. */ - return; - } - - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - - const fn::MultiFunction *single_function = nullptr; - const NodeExpandType expand_type = get_node_expand_type(network_map, dnode, &single_function); - - switch (expand_type) { - case NodeExpandType::HasDummyNodes: { - /* Dummy nodes cannot be executed, so skip them. */ - break; - } - case NodeExpandType::SingleFunctionNode: { - /* This is the common case. Most nodes just expand to a single function. */ - functions_by_node.add_new(dnode, single_function); - break; - } - case NodeExpandType::MultipleFunctionNodes: { - /* If a node expanded into multiple functions, a new function has to be created that - * combines those. */ - const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple( - dnode, network, network_map, scope); - functions_by_node.add_new(dnode, &fn); - break; - } - } - }); - - return functions_by_node; -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc deleted file mode 100644 index 881a02c92e9..00000000000 --- a/source/blender/nodes/intern/type_callbacks.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "NOD_node_tree_multi_function.hh" -#include "NOD_type_callbacks.hh" - -namespace blender::nodes { - -std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype) -{ - const CPPType *cpp_type = stype.get_base_cpp_type ? stype.get_base_cpp_type() : nullptr; - if (cpp_type != nullptr) { - return MFDataType::ForSingle(*cpp_type); - } - return {}; -} - -bool socket_is_mf_data_socket(const bNodeSocketType &stype) -{ - if (!socket_mf_type_get(stype).has_value()) { - return false; - } - if (stype.expand_in_mf_network == nullptr && stype.get_base_cpp_value == nullptr) { - return false; - } - return true; -} - -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder) -{ - bNodeSocket &socket = builder.bsocket(); - if (socket.typeinfo->expand_in_mf_network != nullptr) { - socket.typeinfo->expand_in_mf_network(builder); - } - else if (socket.typeinfo->get_base_cpp_value != nullptr) { - const CPPType &type = *socket.typeinfo->get_base_cpp_type(); - void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(), - type.alignment()); - socket.typeinfo->get_base_cpp_value(socket, buffer); - builder.set_constant_value(type, buffer); - } - else { - BLI_assert_unreachable(); - } -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 98edc133a1d..c3a675fcd20 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -130,7 +130,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_TEXTURE, N_("Texture")); func(calldata, NODE_CLASS_OP_COLOR, N_("Color")); func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector")); - func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor")); + func(calldata, NODE_CLASS_CONVERTER, N_("Converter")); func(calldata, NODE_CLASS_SCRIPT, N_("Script")); func(calldata, NODE_CLASS_GROUP, N_("Group")); func(calldata, NODE_CLASS_INTERFACE, N_("Interface")); diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index dc44f0fa98f..a75354d3381 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -72,7 +72,7 @@ #ifdef __cplusplus # include "FN_multi_function_builder.hh" -# include "NOD_node_tree_multi_function.hh" +# include "NOD_multi_function.hh" # include "BLI_color.hh" # include "BLI_float3.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_blackbody.c b/source/blender/nodes/shader/nodes/node_shader_blackbody.c index c946a76ab51..95c35affc27 100644 --- a/source/blender/nodes/shader/nodes/node_shader_blackbody.c +++ b/source/blender/nodes/shader/nodes/node_shader_blackbody.c @@ -52,7 +52,7 @@ void register_node_type_sh_blackbody(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BLACKBODY, "Blackbody", NODE_CLASS_CONVERTOR, 0); + sh_node_type_base(&ntype, SH_NODE_BLACKBODY, "Blackbody", NODE_CLASS_CONVERTER, 0); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_socket_templates(&ntype, sh_node_blackbody_in, sh_node_blackbody_out); node_type_init(&ntype, NULL); diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 4f77421cfe0..b90397e4892 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -51,7 +51,7 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } -static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{ "Clamp (Min Max)", @@ -65,7 +65,7 @@ static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuil return clamp_f(value, b, a); }}; - int clamp_type = builder.bnode().custom1; + int clamp_type = builder.node().custom1; if (clamp_type == NODE_CLAMP_MINMAX) { builder.set_matching_fn(minmax_fn); } @@ -78,11 +78,11 @@ void register_node_type_sh_clamp(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); - ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network; + ntype.build_multi_function = sh_node_clamp_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index f1d5040a292..df075d6e973 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -143,9 +143,10 @@ class CurveVecFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_vec_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap); @@ -162,7 +163,7 @@ void register_node_type_sh_curve_vec(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); node_type_gpu(&ntype, gpu_shader_curve_vec); - ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_vec_build_multi_function; nodeRegisterType(&ntype); } @@ -317,9 +318,10 @@ class CurveRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_rgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap); @@ -336,7 +338,7 @@ void register_node_type_sh_curve_rgb(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); node_type_gpu(&ntype, gpu_shader_curve_rgb); - ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index ad7abd9d491..f48e824ccb5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -261,9 +261,10 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { } }; -static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_map_range_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); bool clamp = bnode.custom1 != 0; int interpolation_type = bnode.custom2; @@ -301,7 +302,6 @@ static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetwork break; } default: - builder.set_not_implemented(); break; } } @@ -310,12 +310,12 @@ void register_node_type_sh_map_range(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_map_range_in, sh_node_map_range_out); node_type_init(&ntype, node_shader_init_map_range); node_type_update(&ntype, node_shader_update_map_range); node_type_gpu(&ntype, gpu_shader_map_range); - ntype.expand_in_mf_network = sh_node_map_range_expand_in_mf_network; + ntype.build_multi_function = sh_node_map_range_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 7a846031456..66c50b6de46 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -69,11 +69,9 @@ static int gpu_shader_math(GPUMaterial *mat, return 0; } -static const blender::fn::MultiFunction &get_base_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) { - const int mode = builder.bnode().custom1; - + const int mode = node.custom1; const blender::fn::MultiFunction *base_fn = nullptr; blender::nodes::try_dispatch_float_math_fl_to_fl( @@ -82,7 +80,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_to_fl( @@ -92,7 +90,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl( @@ -102,36 +100,51 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) -{ - const blender::fn::MultiFunction &base_function = get_base_multi_function(builder); +class ClampWrapperFunction : public blender::fn::MultiFunction { + private: + const blender::fn::MultiFunction &fn_; - const blender::nodes::DNode &dnode = builder.dnode(); - blender::fn::MFNetwork &network = builder.network(); - blender::fn::MFFunctionNode &base_node = network.add_function(base_function); + public: + ClampWrapperFunction(const blender::fn::MultiFunction &fn) : fn_(fn) + { + this->set_signature(&fn.signature()); + } - builder.network_map().add_try_match(*dnode.context(), dnode->inputs(), base_node.inputs()); + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext context) const override + { + fn_.call(mask, params, context); + + /* Assumes the output parameter is the last one. */ + const int output_param_index = this->param_amount() - 1; + /* This has actually been initialized in the call above. */ + blender::MutableSpan<float> results = params.uninitialized_single_output<float>( + output_param_index); + + for (const int i : mask) { + float &value = results[i]; + CLAMP(value, 0.0f, 1.0f); + } + } +}; + +static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + const blender::fn::MultiFunction *base_function = get_base_multi_function(builder.node()); - const bool clamp_output = builder.bnode().custom2 != 0; + const bool clamp_output = builder.node().custom2 != 0; if (clamp_output) { - static blender::fn::CustomMF_SI_SO<float, float> clamp_fn{"Clamp", [](float value) { - CLAMP(value, 0.0f, 1.0f); - return value; - }}; - blender::fn::MFFunctionNode &clamp_node = network.add_function(clamp_fn); - network.add_link(base_node.output(0), clamp_node.input(0)); - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - clamp_node.output(0)); + builder.construct_and_set_matching_fn<ClampWrapperFunction>(*base_function); } else { - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - base_node.output(0)); + builder.set_matching_fn(base_function); } } @@ -139,12 +152,12 @@ void register_node_type_sh_math(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out); node_type_label(&ntype, node_math_label); node_type_gpu(&ntype, gpu_shader_math); node_type_update(&ntype, node_math_update); - ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_math_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 47011caeeb6..ade35a40366 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -174,9 +174,9 @@ class MixRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_mix_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &node = builder.bnode(); + bNode &node = builder.node(); bool clamp = node.custom2 & SHD_MIXRGB_CLAMP; int mix_type = node.custom1; builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type); @@ -191,7 +191,7 @@ void register_node_type_sh_mix_rgb(void) node_type_label(&ntype, node_blend_label); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); - ntype.expand_in_mf_network = sh_node_mix_rgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_mix_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c b/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c index 951755be4f3..dfecb830b35 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c @@ -61,7 +61,7 @@ void register_node_type_sh_sephsv(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTOR, 0); + sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_sephsv_in, sh_node_sephsv_out); node_type_exec(&ntype, NULL, NULL, node_shader_exec_sephsv); node_type_gpu(&ntype, gpu_shader_sephsv); @@ -109,7 +109,7 @@ void register_node_type_sh_combhsv(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTOR, 0); + sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_combhsv_in, sh_node_combhsv_out); node_type_exec(&ntype, NULL, NULL, node_shader_exec_combhsv); node_type_gpu(&ntype, gpu_shader_combhsv); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index a7239154633..d9cbee33c0f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -96,7 +96,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static SeparateRGBFunction fn; builder.set_matching_fn(fn); @@ -106,11 +106,11 @@ void register_node_type_sh_seprgb(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb); node_type_gpu(&ntype, gpu_shader_seprgb); - ntype.expand_in_mf_network = sh_node_seprgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_seprgb_build_multi_function; nodeRegisterType(&ntype); } @@ -153,7 +153,7 @@ static int gpu_shader_combrgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_rgb", in, out); } -static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{ "Combine RGB", @@ -165,11 +165,11 @@ void register_node_type_sh_combrgb(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb); node_type_gpu(&ntype, gpu_shader_combrgb); - ntype.expand_in_mf_network = sh_node_combrgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_combrgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index efa9581c414..3048ed1e9fc 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -81,7 +81,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction { } }; -static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static MF_SeparateXYZ separate_fn; builder.set_matching_fn(separate_fn); @@ -91,10 +91,10 @@ void register_node_type_sh_sepxyz(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out); node_type_gpu(&ntype, gpu_shader_sepxyz); - ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_sepxyz_build_multi_function; nodeRegisterType(&ntype); } @@ -120,7 +120,7 @@ static int gpu_shader_combxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_xyz", in, out); } -static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{ "Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }}; @@ -131,10 +131,10 @@ void register_node_type_sh_combxyz(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out); node_type_gpu(&ntype, gpu_shader_combxyz); - ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_combxyz_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c b/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c index 4216207d643..25c30aa4081 100644 --- a/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c +++ b/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c @@ -50,7 +50,7 @@ void register_node_type_sh_shadertorgb(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SHADERTORGB, "Shader to RGB", NODE_CLASS_CONVERTOR, 0); + sh_node_type_base(&ntype, SH_NODE_SHADERTORGB, "Shader to RGB", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_shadertorgb_in, sh_node_shadertorgb_out); node_type_init(&ntype, NULL); node_type_storage(&ntype, "", NULL, NULL); diff --git a/source/blender/nodes/shader/nodes/node_shader_squeeze.c b/source/blender/nodes/shader/nodes/node_shader_squeeze.c index c7b6af3980a..ca7bdf41df9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_squeeze.c +++ b/source/blender/nodes/shader/nodes/node_shader_squeeze.c @@ -61,7 +61,7 @@ void register_node_type_sh_squeeze(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value", NODE_CLASS_CONVERTOR, 0); + sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_squeeze_in, sh_node_squeeze_out); node_type_storage(&ntype, "", NULL, NULL); node_type_exec(&ntype, NULL, NULL, node_shader_exec_squeeze); diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 5b2eb300aac..62f6b38c79f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -163,9 +163,10 @@ class ColorBandFunction : public blender::fn::MultiFunction { } }; -static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_valtorgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); const ColorBand *color_band = (const ColorBand *)bnode.storage; builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band); } @@ -174,14 +175,14 @@ void register_node_type_sh_valtorgb(void) { static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_valtorgb_in, sh_node_valtorgb_out); node_type_init(&ntype, node_shader_init_valtorgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_valtorgb); node_type_gpu(&ntype, gpu_shader_valtorgb); - ntype.expand_in_mf_network = sh_node_valtorgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_valtorgb_build_multi_function; nodeRegisterType(&ntype); } @@ -220,7 +221,7 @@ void register_node_type_sh_rgbtobw(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0); + sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, sh_node_rgbtobw_in, sh_node_rgbtobw_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_rgbtobw); node_type_gpu(&ntype, gpu_shader_rgbtobw); diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 495c8d12824..602d5a1cf56 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -39,9 +39,9 @@ static int gpu_shader_value(GPUMaterial *mat, return GPU_stack_link(mat, node, "set_value", in, out, link); } -static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - const bNodeSocket *bsocket = builder.dnode()->output(0).bsocket(); + const bNodeSocket *bsocket = (bNodeSocket *)builder.node().outputs.first; const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value; builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value); } @@ -53,7 +53,7 @@ void register_node_type_sh_value(void) sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); node_type_socket_templates(&ntype, nullptr, sh_node_value_out); node_type_gpu(&ntype, gpu_shader_value); - ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network; + ntype.build_multi_function = sh_node_value_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 419a11201aa..4424db6aed1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -183,12 +183,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node } } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { using blender::float3; - NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1); + NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1); const blender::fn::MultiFunction *multi_fn = nullptr; @@ -199,7 +198,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3( @@ -209,7 +208,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3( @@ -219,7 +218,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl( @@ -229,7 +228,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3( @@ -239,7 +238,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl3( @@ -248,7 +247,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl( @@ -257,15 +256,16 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -278,7 +278,7 @@ void register_node_type_sh_vect_math(void) node_type_label(&ntype, node_vector_math_label); node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); - ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_math_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index 3b2c2fa5a03..bc51b7e29ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -100,11 +100,10 @@ static float3 sh_node_vector_rotate_euler(const float3 vector, return result + center; } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { - bool invert = builder.bnode().custom2; - const int mode = builder.bnode().custom1; + bool invert = node.custom2; + const int mode = node.custom1; switch (mode) { case NODE_VECTOR_ROTATE_TYPE_AXIS: { @@ -113,13 +112,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_X: { float3 axis = float3(1.0f, 0.0f, 0.0f); @@ -128,13 +127,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: { float3 axis = float3(0.0f, 1.0f, 0.0f); @@ -143,13 +142,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: { float3 axis = float3(0.0f, 0.0f, 1.0f); @@ -158,13 +157,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: { if (invert) { @@ -172,24 +171,24 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, true); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, false); }}; - return fn; + return &fn; } default: BLI_assert_unreachable(); - return builder.get_not_implemented_fn(); + return nullptr; } } -static void sh_node_vector_rotate_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_rotate_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -211,7 +210,7 @@ void register_node_type_sh_vector_rotate(void) node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out); node_type_gpu(&ntype, gpu_shader_vector_rotate); node_type_update(&ntype, node_shader_update_vector_rotate); - ntype.expand_in_mf_network = sh_node_vector_rotate_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_rotate_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.c b/source/blender/nodes/shader/nodes/node_shader_wavelength.c index 30f69557020..f978537ee85 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wavelength.c +++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.c @@ -62,7 +62,7 @@ void register_node_type_sh_wavelength(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_WAVELENGTH, "Wavelength", NODE_CLASS_CONVERTOR, 0); + sh_node_type_base(&ntype, SH_NODE_WAVELENGTH, "Wavelength", NODE_CLASS_CONVERTER, 0); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_socket_templates(&ntype, sh_node_wavelength_in, sh_node_wavelength_out); node_type_init(&ntype, NULL); diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index f771b4934b2..7452007639c 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -101,7 +101,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_OP_COLOR, N_("Color")); func(calldata, NODE_CLASS_PATTERN, N_("Patterns")); func(calldata, NODE_CLASS_TEXTURE, N_("Textures")); - func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor")); + func(calldata, NODE_CLASS_CONVERTER, N_("Converter")); func(calldata, NODE_CLASS_DISTORT, N_("Distort")); func(calldata, NODE_CLASS_GROUP, N_("Group")); func(calldata, NODE_CLASS_INTERFACE, N_("Interface")); diff --git a/source/blender/nodes/texture/nodes/node_texture_distance.c b/source/blender/nodes/texture/nodes/node_texture_distance.c index 489318514e5..f7deac9ff4a 100644 --- a/source/blender/nodes/texture/nodes/node_texture_distance.c +++ b/source/blender/nodes/texture/nodes/node_texture_distance.c @@ -61,7 +61,7 @@ void register_node_type_tex_distance(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_DISTANCE, "Distance", NODE_CLASS_CONVERTOR, 0); + tex_node_type_base(&ntype, TEX_NODE_DISTANCE, "Distance", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, inputs, outputs); node_type_storage(&ntype, "", NULL, NULL); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_math.c b/source/blender/nodes/texture/nodes/node_texture_math.c index 53022c9e120..ab226a4dd38 100644 --- a/source/blender/nodes/texture/nodes/node_texture_math.c +++ b/source/blender/nodes/texture/nodes/node_texture_math.c @@ -335,7 +335,7 @@ void register_node_type_tex_math(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0); + tex_node_type_base(&ntype, TEX_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, inputs, outputs); node_type_label(&ntype, node_math_label); node_type_storage(&ntype, "", NULL, NULL); diff --git a/source/blender/nodes/texture/nodes/node_texture_valToNor.c b/source/blender/nodes/texture/nodes/node_texture_valToNor.c index 2fb04777848..5ccd44b8bf0 100644 --- a/source/blender/nodes/texture/nodes/node_texture_valToNor.c +++ b/source/blender/nodes/texture/nodes/node_texture_valToNor.c @@ -80,7 +80,7 @@ void register_node_type_tex_valtonor(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_VALTONOR, "Value to Normal", NODE_CLASS_CONVERTOR, 0); + tex_node_type_base(&ntype, TEX_NODE_VALTONOR, "Value to Normal", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c index 8d365e13fc4..2446ef05e0c 100644 --- a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c +++ b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c @@ -63,7 +63,7 @@ void register_node_type_tex_valtorgb(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTOR, 0); + tex_node_type_base(&ntype, TEX_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, valtorgb_in, valtorgb_out); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, valtorgb_init); @@ -105,7 +105,7 @@ void register_node_type_tex_rgbtobw(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0); + tex_node_type_base(&ntype, TEX_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); node_type_socket_templates(&ntype, rgbtobw_in, rgbtobw_out); node_type_exec(&ntype, NULL, NULL, rgbtobw_exec); diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c index 7cca3ae4700..acd809fb8d5 100644 --- a/source/blender/python/intern/bpy_app_icons.c +++ b/source/blender/python/intern/bpy_app_icons.c @@ -35,7 +35,7 @@ /* We may want to load direct from file. */ PyDoc_STRVAR( bpy_app_icons_new_triangles_doc, - ".. function:: new_triangles(range, coords, colors)" + ".. function:: new_triangles(range, coords, colors)\n" "\n" " Create a new icon from triangle geometry.\n" "\n" @@ -91,7 +91,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a } PyDoc_STRVAR(bpy_app_icons_new_triangles_from_file_doc, - ".. function:: new_triangles_from_file(filename)" + ".. function:: new_triangles_from_file(filename)\n" "\n" " Create a new icon from triangle geometry.\n" "\n" @@ -122,7 +122,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self), } PyDoc_STRVAR(bpy_app_icons_release_doc, - ".. function:: release(icon_id)" + ".. function:: release(icon_id)\n" "\n" " Release the icon.\n"); static PyObject *bpy_app_icons_release(PyObject *UNUSED(self), PyObject *args, PyObject *kw) diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 604c9900355..86bd840ce31 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -102,7 +102,7 @@ /* <cache type>-<resolution X>x<resolution Y>-<rendersize>%(<view_id>)-<frame no>.dcf */ #define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" #define DCACHE_IMAGES_PER_FILE 100 -#define DCACHE_CURRENT_VERSION 1 +#define DCACHE_CURRENT_VERSION 2 #define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ typedef struct DiskCacheHeaderEntry { @@ -496,24 +496,34 @@ static size_t deflate_imbuf_to_file(ImBuf *ibuf, int level, DiskCacheHeaderEntry *header_entry) { - if (ibuf->rect) { - return BLI_gzip_mem_to_file_at_pos( - ibuf->rect, header_entry->size_raw, file, header_entry->offset, level); + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + + /* Apply compression if wanted, otherwise just write directly to the file. */ + if (level > 0) { + return BLI_file_zstd_from_mem_at_pos( + data, header_entry->size_raw, file, header_entry->offset, level); } - return BLI_gzip_mem_to_file_at_pos( - ibuf->rect_float, header_entry->size_raw, file, header_entry->offset, level); + fseek(file, header_entry->offset, SEEK_SET); + return fwrite(data, 1, header_entry->size_raw, file); } static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) { - if (ibuf->rect) { - return BLI_ungzip_file_to_mem_at_pos( - ibuf->rect, header_entry->size_raw, file, header_entry->offset); + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + char header[4]; + fseek(file, header_entry->offset, SEEK_SET); + if (fread(header, 1, sizeof(header), file) != sizeof(header)) { + return 0; + } + + /* Check if the data is compressed or raw. */ + if (BLI_file_magic_is_zstd(header)) { + return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); } - return BLI_ungzip_file_to_mem_at_pos( - ibuf->rect_float, header_entry->size_raw, file, header_entry->offset); + fseek(file, header_entry->offset, SEEK_SET); + return fread(data, 1, header_entry->size_raw, file); } static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index e92afee08cd..b83ee92f117 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -476,6 +476,6 @@ bool SEQ_edit_remove_gaps(Scene *scene, void SEQ_edit_sequence_name_set(Scene *scene, Sequence *seq, const char *new_name) { BLI_strncpy_utf8(seq->name + 2, new_name, MAX_NAME - 2); - BLI_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2)); + BLI_str_utf8_invalid_strip(seq->name + 2, strlen(seq->name + 2)); SEQ_sequence_lookup_tag(scene, SEQ_LOOKUP_TAG_INVALID); } diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index b7fbb9bb82b..4d65726fe2b 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -48,10 +48,6 @@ set(INC ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern ) -set(INC_SYS - ${ZLIB_INCLUDE_DIRS} -) - set(SRC intern/wm.c intern/wm_cursors.c diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 5cc361fc1bd..b9a3dd0c3fb 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3471,8 +3471,8 @@ void wm_event_do_handlers(bContext *C) } CTX_wm_area_set(C, NULL); - /* NOTE: do not escape on WM_HANDLER_BREAK, - * mousemove needs handled for previous area. */ + /* NOTE: do not escape on #WM_HANDLER_BREAK, + * mouse-move needs handled for previous area. */ } } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index b53ad0ee927..f83511e76f0 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -28,11 +28,10 @@ * winsock stuff. */ #include <errno.h> +#include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */ #include <stddef.h> #include <string.h> -#include "zlib.h" /* wm_read_exotic() */ - #ifdef WIN32 /* Need to include windows.h so _WIN32_IE is defined. */ # include <windows.h> @@ -51,6 +50,7 @@ #include "BLI_blenlib.h" #include "BLI_fileops_types.h" +#include "BLI_filereader.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_system.h" @@ -481,53 +481,64 @@ static void wm_init_userdef(Main *bmain) /* intended to check for non-blender formats but for now it only reads blends */ static int wm_read_exotic(const char *name) { - int len; - gzFile gzfile; + /* make sure we're not trying to read a directory.... */ + + int namelen = strlen(name); + if (namelen > 0 && ELEM(name[namelen - 1], '/', '\\')) { + return BKE_READ_EXOTIC_FAIL_PATH; + } + + /* open the file. */ + const int filedes = BLI_open(name, O_BINARY | O_RDONLY, 0); + if (filedes == -1) { + return BKE_READ_EXOTIC_FAIL_OPEN; + } + + FileReader *rawfile = BLI_filereader_new_file(filedes); + if (rawfile == NULL) { + return BKE_READ_EXOTIC_FAIL_OPEN; + } + + /* read the header (7 bytes are enough to identify all known types). */ char header[7]; - int retval; + if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { + rawfile->close(rawfile); + return BKE_READ_EXOTIC_FAIL_FORMAT; + } + rawfile->seek(rawfile, 0, SEEK_SET); - /* make sure we're not trying to read a directory.... */ + /* check for uncompressed .blend */ + if (STREQLEN(header, "BLENDER", 7)) { + rawfile->close(rawfile); + return BKE_READ_EXOTIC_OK_BLEND; + } - len = strlen(name); - if (len > 0 && ELEM(name[len - 1], '/', '\\')) { - retval = BKE_READ_EXOTIC_FAIL_PATH; + /* check for compressed .blend */ + FileReader *compressed_file = NULL; + if (BLI_file_magic_is_gzip(header)) { + /* In earlier versions of Blender (before 3.0), compressed files used Gzip instead of Zstd. + * While these files will no longer be written, there still needs to be reading support. */ + compressed_file = BLI_filereader_new_gzip(rawfile); + } + else if (BLI_file_magic_is_zstd(header)) { + compressed_file = BLI_filereader_new_zstd(rawfile); } - else { - gzfile = BLI_gzopen(name, "rb"); - if (gzfile == NULL) { - retval = BKE_READ_EXOTIC_FAIL_OPEN; - } - else { - len = gzread(gzfile, header, sizeof(header)); - gzclose(gzfile); - if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) { - retval = BKE_READ_EXOTIC_OK_BLEND; - } - else { - /* We may want to support loading other file formats - * from their header bytes or file extension. - * This used to be supported in the code below and may be added - * back at some point. */ -#if 0 - WM_cursor_wait(true); - if (is_foo_format(name)) { - read_foo(name); - retval = BKE_READ_EXOTIC_OK_OTHER; - } - else -#endif - { - retval = BKE_READ_EXOTIC_FAIL_FORMAT; - } -#if 0 - WM_cursor_wait(false); -#endif - } + /* If a compression signature matches, try decompressing the start and check if it's a .blend */ + if (compressed_file != NULL) { + size_t len = compressed_file->read(compressed_file, header, sizeof(header)); + compressed_file->close(compressed_file); + if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) { + return BKE_READ_EXOTIC_OK_BLEND; } } + else { + rawfile->close(rawfile); + } + + /* Add check for future file formats here. */ - return retval; + return BKE_READ_EXOTIC_FAIL_FORMAT; } /** \} */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index b5a038757c2..f1a8f4ffd71 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2497,7 +2497,7 @@ static int radial_control_get_path(PointerRNA *ctx_ptr, /* get an rna string path from the operator's properties */ char *str; - if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0))) { + if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0, NULL))) { return 1; } |