From 00812008129bdab48d328775b09332af12118867 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 20 Aug 2021 13:14:39 +0200 Subject: Functions: remove multi-function network The multi-function network system was able to compose multiple multi-functions into a new one and to evaluate that efficiently. This functionality was heavily used by the particle nodes prototype a year ago. However, since then we only used multi-functions without the need to compose them in geometry nodes. The upcoming "fields" in geometry nodes will need a way to compose multi-functions again. Unfortunately, the code removed in this commit was not ideal for this different kind of function composition. I've been working on an alternative that will be added separately when it becomes needed. I've had to update all the function nodes, because their interface depended on the multi-function network data structure a bit. The actual multi-function implementations are still the same though. --- source/blender/blenkernel/BKE_node.h | 15 +- source/blender/blenkernel/intern/simulation.cc | 4 - source/blender/functions/CMakeLists.txt | 7 - .../blender/functions/FN_generic_virtual_array.hh | 2 +- .../blender/functions/FN_multi_function_network.hh | 536 ---------- .../FN_multi_function_network_evaluation.hh | 62 -- .../FN_multi_function_network_optimization.hh | 29 - .../functions/FN_multi_function_signature.hh | 15 + .../functions/intern/multi_function_network.cc | 330 ------ .../intern/multi_function_network_evaluation.cc | 1083 -------------------- .../intern/multi_function_network_optimization.cc | 501 --------- .../tests/FN_multi_function_network_test.cc | 280 ----- .../functions/tests/FN_multi_function_test.cc | 92 +- .../tests/FN_multi_function_test_common.hh | 174 ++++ source/blender/modifiers/intern/MOD_nodes.cc | 5 +- .../modifiers/intern/MOD_nodes_evaluator.cc | 2 +- .../modifiers/intern/MOD_nodes_evaluator.hh | 6 +- source/blender/nodes/CMakeLists.txt | 6 +- source/blender/nodes/NOD_multi_function.hh | 130 +++ .../blender/nodes/NOD_node_tree_multi_function.hh | 390 ------- source/blender/nodes/NOD_type_callbacks.hh | 34 - .../blender/nodes/function/node_function_util.hh | 2 +- .../nodes/function/nodes/node_fn_boolean_math.cc | 19 +- .../nodes/function/nodes/node_fn_float_compare.cc | 25 +- .../nodes/function/nodes/node_fn_float_to_int.cc | 19 +- .../nodes/function/nodes/node_fn_input_string.cc | 12 +- .../nodes/function/nodes/node_fn_input_vector.cc | 10 +- .../nodes/function/nodes/node_fn_random_float.cc | 9 +- source/blender/nodes/intern/node_geometry_exec.cc | 1 - source/blender/nodes/intern/node_multi_function.cc | 40 + source/blender/nodes/intern/node_socket.cc | 1 - .../nodes/intern/node_tree_multi_function.cc | 409 -------- source/blender/nodes/intern/type_callbacks.cc | 60 -- source/blender/nodes/shader/node_shader_util.h | 2 +- .../nodes/shader/nodes/node_shader_clamp.cc | 6 +- .../nodes/shader/nodes/node_shader_curves.cc | 14 +- .../nodes/shader/nodes/node_shader_map_range.cc | 8 +- .../blender/nodes/shader/nodes/node_shader_math.cc | 67 +- .../nodes/shader/nodes/node_shader_mixRgb.cc | 6 +- .../nodes/shader/nodes/node_shader_sepcombRGB.cc | 8 +- .../nodes/shader/nodes/node_shader_sepcombXYZ.cc | 8 +- .../nodes/shader/nodes/node_shader_valToRgb.cc | 7 +- .../nodes/shader/nodes/node_shader_value.cc | 6 +- .../nodes/shader/nodes/node_shader_vector_math.cc | 28 +- .../shader/nodes/node_shader_vector_rotate.cc | 37 +- 45 files changed, 534 insertions(+), 3973 deletions(-) delete mode 100644 source/blender/functions/FN_multi_function_network.hh delete mode 100644 source/blender/functions/FN_multi_function_network_evaluation.hh delete mode 100644 source/blender/functions/FN_multi_function_network_optimization.hh delete mode 100644 source/blender/functions/intern/multi_function_network.cc delete mode 100644 source/blender/functions/intern/multi_function_network_evaluation.cc delete mode 100644 source/blender/functions/intern/multi_function_network_optimization.cc delete mode 100644 source/blender/functions/tests/FN_multi_function_network_test.cc create mode 100644 source/blender/functions/tests/FN_multi_function_test_common.hh create mode 100644 source/blender/nodes/NOD_multi_function.hh delete mode 100644 source/blender/nodes/NOD_node_tree_multi_function.hh delete mode 100644 source/blender/nodes/NOD_type_callbacks.hh create mode 100644 source/blender/nodes/intern/node_multi_function.cc delete mode 100644 source/blender/nodes/intern/node_tree_multi_function.cc delete mode 100644 source/blender/nodes/intern/type_callbacks.cc diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index caa7ab6de0a..c4393246926 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; 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/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 c9398ceb547..f429243e2de 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -129,7 +129,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 inputs_; - Span 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 inputs(); - Span inputs() const; - - Span outputs(); - Span outputs() const; - - bool has_unlinked_inputs() const; - - private: - void destruct_sockets(); -}; - -class MFFunctionNode : public MFNode { - private: - const MultiFunction *function_; - Span input_param_indices_; - Span 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 input_names_; - MutableSpan output_names_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - Span input_names() const; - Span 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 targets_; - - friend MFNetwork; - - public: - Span targets(); - Span targets() const; -}; - -class MFNetwork : NonCopyable, NonMovable { - private: - LinearAllocator<> allocator_; - - VectorSet function_nodes_; - VectorSet dummy_nodes_; - - Vector node_or_null_by_id_; - Vector socket_or_null_by_id_; - - public: - MFNetwork() = default; - ~MFNetwork(); - - MFFunctionNode &add_function(const MultiFunction &function); - MFDummyNode &add_dummy(StringRef name, - Span input_types, - Span output_types, - Span input_names, - Span 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 nodes); - - int socket_id_amount() const; - int node_id_amount() const; - - Span dummy_nodes(); - Span 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 sockets, - VectorSet &r_dummy_sockets, - VectorSet &r_unlinked_inputs) const; - - bool have_dummy_or_unlinked_dependencies(Span sockets) const; - - std::string to_dot(Span 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(*this); -} - -inline const MFDummyNode &MFNode::as_dummy() const -{ - BLI_assert(is_dummy_); - return static_cast(*this); -} - -inline MFFunctionNode &MFNode::as_function() -{ - BLI_assert(!is_dummy_); - return static_cast(*this); -} - -inline const MFFunctionNode &MFNode::as_function() const -{ - BLI_assert(!is_dummy_); - return static_cast(*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 MFNode::inputs() -{ - return inputs_; -} - -inline Span MFNode::inputs() const -{ - return inputs_; -} - -inline Span MFNode::outputs() -{ - return outputs_; -} - -inline Span 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 MFDummyNode::input_names() const -{ - return input_names_; -} - -inline Span 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(*this); -} - -inline const MFInputSocket &MFSocket::as_input() const -{ - BLI_assert(this->is_input()); - return static_cast(*this); -} - -inline MFOutputSocket &MFSocket::as_output() -{ - BLI_assert(this->is_output()); - return static_cast(*this); -} - -inline const MFOutputSocket &MFSocket::as_output() const -{ - BLI_assert(this->is_output()); - return static_cast(*this); -} - -/* -------------------------------------------------------------------- - * MFInputSocket inline methods. - */ - -inline MFOutputSocket *MFInputSocket::origin() -{ - return origin_; -} - -inline const MFOutputSocket *MFInputSocket::origin() const -{ - return origin_; -} - -/* -------------------------------------------------------------------- - * MFOutputSocket inline methods. - */ - -inline Span MFOutputSocket::targets() -{ - return targets_; -} - -inline Span MFOutputSocket::targets() const -{ - return targets_; -} - -/* -------------------------------------------------------------------- - * MFNetwork inline methods. - */ - -inline Span MFNetwork::dummy_nodes() -{ - return dummy_nodes_; -} - -inline Span 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 inputs_; - Vector outputs_; - - public: - MFNetworkEvaluator(Vector inputs, Vector 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 &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 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 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().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(input_param_indices); - node.output_param_indices_ = allocator_.construct_array_copy(output_param_indices); - - node.inputs_ = allocator_.construct_elements_and_pointer_array( - input_param_indices.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array( - 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 input_types, - Span output_types, - Span input_names, - Span output_names) -{ - assert_same_size(input_types, input_names); - assert_same_size(output_types, output_names); - - MFDummyNode &node = *allocator_.construct().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( - input_types.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array( - output_types.size()); - - node.input_names_ = allocator_.allocate_array(input_types.size()); - node.output_names_ = allocator_.allocate_array(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 nodes) -{ - for (MFNode *node : nodes) { - this->remove(*node); - } -} - -void MFNetwork::find_dependencies(Span sockets, - VectorSet &r_dummy_sockets, - VectorSet &r_unlinked_inputs) const -{ - Set visited_nodes; - Stack 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 sockets) const -{ - VectorSet dummy_sockets; - VectorSet unlinked_inputs; - this->find_dependencies(sockets, dummy_sockets, unlinked_inputs); - return dummy_sockets.size() + unlinked_inputs.size() > 0; -} - -std::string MFNetwork::to_dot(Span marked_nodes) const -{ - dot::DirectedGraph digraph; - digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); - - Map dot_nodes; - - Vector all_nodes; - all_nodes.extend(function_nodes_.as_span().cast()); - all_nodes.extend(dummy_nodes_.as_span().cast()); - - for (const MFNode *node : all_nodes) { - dot::Node &dot_node = digraph.new_node(""); - - Vector 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_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 inputs, - Vector 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 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 &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 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 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(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(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(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(any_value)->span.size() == 1; - case ValueType::OwnVector: - return static_cast(any_value)->vector_array->size() == 1; - case ValueType::InputSingle: - return static_cast(any_value)->virtual_array.is_single(); - case ValueType::InputVector: - return static_cast(any_value)->virtual_vector_array.is_single_vector(); - case ValueType::OutputSingle: - return static_cast(any_value)->span.size() == 1; - case ValueType::OutputVector: - return static_cast(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(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(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(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(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(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(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(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(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(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(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(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(*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(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(*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(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(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(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(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(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(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(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(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(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(*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(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(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(*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(any_value); - if (value->is_single_allocated) { - return scope.construct( - __func__, value->span.type(), min_array_size_, value->span.data()); - } - - return scope.construct(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast(any_value); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast(any_value); - BLI_assert(value->is_computed); - return scope.construct(__func__, value->span); - } - - BLI_assert(false); - return scope.construct(__func__, CPPType::get()); -} - -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(any_value); - BLI_assert(value->span.size() == 1); - return scope.construct(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast(any_value); - BLI_assert(value->virtual_array.is_single()); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast(any_value); - BLI_assert(value->is_computed); - BLI_assert(value->span.size() == 1); - return scope.construct(__func__, value->span); - } - - BLI_assert(false); - return scope.construct(__func__, CPPType::get()); -} - -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(any_value); - if (value->vector_array->size() == 1) { - GSpan span = (*value->vector_array)[0]; - return scope.construct(__func__, span, min_array_size_); - } - - return scope.construct(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast(any_value); - return value->virtual_vector_array; - } - if (any_value->type == ValueType::OutputVector) { - OutputVectorValue *value = static_cast(any_value); - return scope.construct(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct(__func__, GSpan(CPPType::get()), 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(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast(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(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct(__func__, GSpan(CPPType::get()), 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 - -#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 mask_nodes_to_the_left(MFNetwork &network, Span nodes) -{ - Array is_to_the_left(network.node_id_amount(), false); - Stack 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 mask_nodes_to_the_right(MFNetwork &network, Span nodes) -{ - Array is_to_the_right(network.node_id_amount(), false); - Stack 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 find_nodes_based_on_mask(MFNetwork &network, - Span id_mask, - bool mask_value) -{ - Vector 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 node_is_used_mask = mask_nodes_to_the_left(network, - network.dummy_nodes().cast()); - Vector 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 find_non_constant_nodes(MFNetwork &network) -{ - Vector non_constant_nodes; - non_constant_nodes.extend(network.dummy_nodes().cast()); - - 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 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 find_constant_inputs_to_fold( - MFNetwork &network, Vector &r_temporary_nodes) -{ - Vector non_constant_nodes = find_non_constant_nodes(network); - Array is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes); - Vector constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false); - - Vector 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(AT, cpp_type, 1); - params.add_vector_output(vector_array); - break; - } - } - } -} - -static Array add_constant_folded_sockets(const MultiFunction &network_fn, - MFParamsBuilder ¶ms, - ResourceScope &scope, - MFNetwork &network) -{ - Array 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(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(AT, array); - break; - } - } - - MFFunctionNode &folded_node = network.add_function(*constant_fn); - folded_sockets[param_index] = &folded_node.output(0); - } - return folded_sockets; -} - -static Array compute_constant_sockets_and_add_folded_nodes( - MFNetwork &network, Span 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 temporary_nodes; - Vector inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes); - if (inputs_to_fold.size() == 0) { - return; - } - - Array 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()); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Common Sub-network Elimination - * \{ */ - -static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span 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 compute_node_hashes(MFNetwork &network) -{ - RNG *rng = BLI_rng_new(0); - Array node_hashes(network.node_id_amount()); - Array 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 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 group_nodes_by_hash(MFNetwork &network, - Span node_hashes) -{ - MultiValueMap 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 &nodes_by_hash) -{ - DisjointSet same_node_cache{network.node_id_amount()}; - - for (Span nodes_with_same_hash : nodes_by_hash.values()) { - if (nodes_with_same_hash.size() <= 1) { - continue; - } - - Vector nodes_to_check = nodes_with_same_hash; - while (nodes_to_check.size() >= 2) { - Vector 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 node_hashes = compute_node_hashes(network); - MultiValueMap 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 add_10_fn("add 10", [](int value) { return value + 10; }); - CustomMF_SI_SI_SO 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()); - MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle()); - 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 values = {4, 6, 1, 2, 0}; - Array 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 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("A"); - signature.vector_input("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("Vector"); - signature.single_input("Value"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray_TypedMutableRef vectors = params.vector_mutable(0); - const VArray &values = params.readonly_single_input(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("Vector"); - signature.single_output("Sum"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VVectorArray &vectors = params.readonly_vector_input(0); - MutableSpan sums = params.uninitialized_single_output(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("Size"); - signature.vector_output("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray &sizes = params.readonly_single_input(0, "Size"); - GVectorArray_TypedMutableRef ranges = params.vector_output(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 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()); - MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle()); - MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector()); - MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle()); - - 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 input_value_1 = {3, 6}; - int input_value_2 = 4; - - GVectorArray output_value_1(CPPType::get(), 5); - Array 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(), 3); - GVectorArray_TypedMutableRef 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 input_value_2 = {4, 2, 3}; - - GVectorArray output_value_1(CPPType::get(), 3); - Array 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("Prefix"); - signature.single_mutable("Strings"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray &prefixes = params.readonly_single_input(0, "Prefix"); - MutableSpan strings = params.single_mutable(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("Size"); - signature.vector_output("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray &sizes = params.readonly_single_input(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(), 5); - GVectorArray_TypedMutableRef ranges_ref{ranges}; - Array sizes = {3, 0, 6, 1, 4}; + GVectorArray ranges(CPPType::get(), 5); + GVectorArray_TypedMutableRef ranges_ref{ranges}; + Array 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()); 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("Prefix"); + signature.single_mutable("Strings"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray &prefixes = params.readonly_single_input(0, "Prefix"); + MutableSpan strings = params.single_mutable(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("Size"); + signature.vector_output("Range"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray &sizes = params.readonly_single_input(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("A"); + signature.vector_input("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("Vector"); + signature.single_input("Value"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray_TypedMutableRef vectors = params.vector_mutable(0); + const VArray &values = params.readonly_single_input(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("Vector"); + signature.single_output("Sum"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VVectorArray &vectors = params.readonly_vector_input(0); + MutableSpan sums = params.uninitialized_single_output(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/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 3853b345c14..620c7ef438a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -84,7 +84,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" using blender::destruct_ptr; using blender::float3; @@ -858,7 +859,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 group_inputs; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 47dfd9bc8f6..5646e37707c 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -836,7 +836,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 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/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 46fb9f54bfe..8680fcee49a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -345,11 +345,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 @@ -366,13 +365,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 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 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 +inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) +{ + const T &fn = resource_scope_.construct(__func__, std::forward(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 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 dsockets, - Span 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 dsockets, - Span 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(), - node.inputs().cast()); - this->add_try_match(*dnode.context(), - dnode->outputs().cast(), - node.outputs().cast()); - } - - void add_try_match(const DTreeContext &context, - Span dsockets, - Span sockets) - { - this->add_try_match( - context, dsockets.cast(), sockets.cast()); - } - - void add_try_match(const DTreeContext &context, - Span dsockets, - Span sockets) - { - this->add_try_match( - context, dsockets.cast(), sockets.cast()); - } - - void add_try_match(const DTreeContext &context, - Span dsockets, - Span 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 lookup(const DInputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket).cast(); - } - - fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket) - { - Span 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 T &construct_fn(Args &&...args) - { - BLI_STATIC_ASSERT((std::is_base_of_v), ""); - void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T)); - T *fn = new (buffer) T(std::forward(args)...); - common_.scope.add(destruct_ptr(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 T *socket_default_value() - { - return static_cast(bsocket_->default_value); - } - - /** - * Builds a function node for that socket that outputs the given constant value. - */ - template void set_constant_value(T value) - { - this->construct_generator_fn>(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(type, value); - } - - template void construct_generator_fn(Args &&...args) - { - const fn::MultiFunction &fn = this->construct_fn(std::forward(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 T &construct_and_set_matching_fn(Args &&...args) - { - T &function = this->construct_fn(std::forward(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; -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope); - -} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/nodes/NOD_type_callbacks.hh deleted file mode 100644 index 2be78f929db..00000000000 --- a/source/blender/nodes/NOD_type_callbacks.hh +++ /dev/null @@ -1,34 +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 - -#include "BKE_node.h" - -#include "FN_multi_function_data_type.hh" - -namespace blender::nodes { - -using fn::CPPType; -using fn::MFDataType; - -std::optional socket_mf_type_get(const bNodeSocketType &stype); -bool socket_is_mf_data_socket(const bNodeSocketType &stype); -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder); - -} // namespace blender::nodes 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..0ba9080918c 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 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); } @@ -93,7 +94,7 @@ void register_node_type_fn_boolean_math() 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..16ffb761a15 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 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); } @@ -112,7 +113,7 @@ void register_node_type_fn_float_compare() 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..52acfefe615 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 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(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); } @@ -89,7 +90,7 @@ void register_node_type_fn_float_to_int() fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 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(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); - - builder.construct_and_set_matching_fn>(string); + builder.construct_and_set_matching_fn>( + 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(bnode.storage); blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn>(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(); + 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/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/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc new file mode 100644 index 00000000000..c91899ed8c2 --- /dev/null +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -0,0 +1,40 @@ +/* + * 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_multi_function.hh" + +namespace blender::nodes { + +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 4be3fd2468b..528616eb23a 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -44,7 +44,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 input_types; - Vector output_types; - - for (const InputSocketRef *dsocket : dnode_->inputs()) { - if (dsocket->is_available()) { - std::optional 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 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( - name, input_types, output_types); - return fn; -} - -static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode) -{ - constexpr int stack_capacity = 10; - - Vector input_types; - Vector input_names; - Vector input_dsockets; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - std::optional 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 output_types; - Vector output_names; - Vector output_dsockets; - - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - std::optional 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 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( - AT, type.single_type(), type.single_type().default_value()); - } - else { - default_fn = &common.scope.construct( - 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 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 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 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 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( - __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(__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 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_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_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 4f77421cfe0..f105f8bcaf9 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 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); } @@ -82,7 +82,7 @@ void register_node_type_sh_clamp(void) 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(*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(*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..e4739e2864d 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; } } @@ -315,7 +315,7 @@ void register_node_type_sh_map_range(void) 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..c30f2948ab1 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 results = params.uninitialized_single_output( + 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 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(*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); } } @@ -144,7 +157,7 @@ void register_node_type_sh_math(void) 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(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_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index a7239154633..2779fc6bf68 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); @@ -110,7 +110,7 @@ void register_node_type_sh_seprgb(void) 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 fn{ "Combine RGB", @@ -169,7 +169,7 @@ void register_node_type_sh_combrgb(void) 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..1fd794cdd0a 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); @@ -94,7 +94,7 @@ void register_node_type_sh_sepxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 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 fn{ "Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }}; @@ -134,7 +134,7 @@ void register_node_type_sh_combxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 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_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 5b2eb300aac..1bc42ab0cc6 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(*color_band); } @@ -181,7 +182,7 @@ void register_node_type_sh_valtorgb(void) 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); } 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>(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 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 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 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 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 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); } -- cgit v1.2.3 From ef502127ddc0f5a06e76cb2f634655b8e9c8f740 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 20 Aug 2021 08:40:13 -0300 Subject: Fix T90795: Moving keys in Grease Pencil Dopesheet crashes Blender `td->loc` is referenced but not initialized. --- source/blender/editors/transform/transform_convert_action.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 8a3b254be5c..a5565b5fb88 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -241,16 +241,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++; -- cgit v1.2.3 From accf3045be40433045c197cfcdbcbc32a7724403 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 20 Aug 2021 09:05:52 -0300 Subject: Fix memory leak while processing mouse event Assignment missed. --- intern/ghost/intern/GHOST_SystemWin32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { -- cgit v1.2.3 From 9bfc47c9334bc4fbecbe7871fff9af4cc46c8832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 14:29:05 +0200 Subject: Alembic Procedural: basic cache control settings This adds a setting to enable data caching, and another one to set the maximum cache size in megabytes. When caching is enabled we load the data for the entire animation in memory, as we already do, however, if the data exceeds the memory limit, render is aborted. When caching is disabled, we simply load the data for the current frame in memory. Ref D10197 Reviewed By: brecht Differential Revision: https://developer.blender.org/D11163 --- intern/cycles/blender/blender_object.cpp | 3 + intern/cycles/render/alembic.cpp | 70 ++++++++++++++++++++++ intern/cycles/render/alembic.h | 30 ++++++++++ intern/cycles/render/alembic_read.cpp | 16 ++++- .../editors/interface/interface_templates.c | 15 +++++ source/blender/makesdna/DNA_cachefile_defaults.h | 2 + source/blender/makesdna/DNA_cachefile_types.h | 10 +++- source/blender/makesrna/intern/rna_cachefile.c | 17 ++++++ 8 files changed, 159 insertions(+), 4 deletions(-) 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/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(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(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(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 class DataStore { node->set(*socket, value); } + size_t memory_used() const + { + if constexpr (is_array::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(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 get_relevant_sample_times(AlembicProcedural *proc, return result; } - // load the data for the entire animation - const double start_frame = static_cast(proc->get_start_frame()); - const double end_frame = static_cast(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(proc->get_start_frame()); + end_frame = static_cast(proc->get_end_frame()); + } + else { + // load the data for the current frame + start_frame = static_cast(proc->get_frame()); + end_frame = start_frame; + } const double frame_rate = static_cast(proc->get_frame_rate()); const double start_time = start_frame / frame_rate; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 351b73c320b..cdb35d19855 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); @@ -6510,6 +6521,10 @@ void uiTemplateCacheFile(uiLayout *layout, uiItemR(layout, &fileptr, "velocity_name", 0, NULL, ICON_NONE); uiItemR(layout, &fileptr, "velocity_unit", 0, NULL, ICON_NONE); + row = uiLayoutRow(layout, false); + uiLayoutSetActive(row, engine_supports_procedural && use_render_procedural); + uiItemR(row, &fileptr, "default_radius", 0, NULL, ICON_NONE); + /* TODO: unused for now, so no need to expose. */ #if 0 row = uiLayoutRow(layout, false); 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/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); -- cgit v1.2.3 From 6a404bc633586a96d635e2e3bf2e207f87ab3785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 14:48:47 +0200 Subject: Cleanup, remove extra code from previous commit This got accidentally introduced while revising dependencies between patches for this feature, did not notice until it was too late. --- source/blender/editors/interface/interface_templates.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index cdb35d19855..0e5a6a79137 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6521,10 +6521,6 @@ void uiTemplateCacheFile(uiLayout *layout, uiItemR(layout, &fileptr, "velocity_name", 0, NULL, ICON_NONE); uiItemR(layout, &fileptr, "velocity_unit", 0, NULL, ICON_NONE); - row = uiLayoutRow(layout, false); - uiLayoutSetActive(row, engine_supports_procedural && use_render_procedural); - uiItemR(row, &fileptr, "default_radius", 0, NULL, ICON_NONE); - /* TODO: unused for now, so no need to expose. */ #if 0 row = uiLayoutRow(layout, false); -- cgit v1.2.3 From 1b5f17b86771faa87d36ff12183aa6dbb5c1e9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 15:00:58 +0200 Subject: Cleanup, use BKE_scene_uses_cycles_experimental_features --- source/blender/modifiers/intern/MOD_subsurf.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) 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; -- cgit v1.2.3 From cd8d9383e74a798e75ebced95cdd6d8de9f02369 Mon Sep 17 00:00:00 2001 From: Alaska Date: Fri, 20 Aug 2021 17:43:15 +0200 Subject: Fix T90804: small grammatical error in noise threshold description Differential Revision: https://developer.blender.org/D12277 --- intern/cycles/blender/addon/properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, -- cgit v1.2.3 From 86622467c573e0703eea2554ebb0da5c67cbfb8c Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 20 Aug 2021 13:46:38 -0400 Subject: DocPy: Update Dependancies Updates sphinx and the theme to the latest version along with any of their dependencies. Note that we will be sticking to sphinx 4.1.1 until sphinx 4.2 for the same reasons listed in: https://developer.blender.org/rBM8334 --- doc/python_api/requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 -- cgit v1.2.3 From b6a1bf757d9f0675f0fb5f21a72ff4ee31f8d6d9 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 20 Aug 2021 14:54:46 -0400 Subject: DocPy: Cleanup missing newline resulting in wrong html generation --- source/blender/python/intern/bpy_app_icons.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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) -- cgit v1.2.3 From 0d7aab2375e6bb06e89dad851550b283a1ff805c Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 20 Aug 2021 17:48:42 -0700 Subject: Refactor: BLF Kerning Cache After Use Optimization of font kerning by only caching kerning values after a pair is encountered. Also saves unscaled values so they don't have to be rebuilt between font size changes. See D12274 for more details and speed comparison. Differential Revision: https://developer.blender.org/D12274 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf.c | 1 - source/blender/blenfont/intern/blf_font.c | 96 +++++++++++++--------- source/blender/blenfont/intern/blf_glyph.c | 50 ----------- source/blender/blenfont/intern/blf_internal.h | 4 - .../blender/blenfont/intern/blf_internal_types.h | 9 +- 5 files changed, 62 insertions(+), 98 deletions(-) diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 9168e7aa19c..2f9eb0753ac 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); } } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 00d3cfb09eb..02847b74df1 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -308,17 +308,6 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) 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); - } -} - /* Fast path for runs of ASCII characters. Given that common UTF-8 * input will consist of an overwhelming majority of ASCII * characters. @@ -348,6 +337,31 @@ BLI_INLINE GlyphBLF *blf_utf8_next_fast( return g; } +/* 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) & ~FT_TYPEOF(x) 63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) + + /* Round to even 64ths, then divide by 64. */ + return FT_PIX_ROUND(scaled) >> 6; + +#undef FT_PIX_FLOOR +#undef FT_PIX_ROUND +} + BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g, @@ -355,18 +369,28 @@ 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) { + FT_Vector delta = {KERNING_ENTRY_UNSET}; - if (g_prev != NULL && FT_HAS_KERNING(font->face)) { + /* Get unscaled kerning value from our cache if ASCII. */ if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - *pen_x_p += font->kerning_cache->ascii_table[c][c_prev]; + delta.x = 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; - } + + /* 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] = 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); } } } @@ -388,8 +412,6 @@ static void blf_font_draw_ex(FontBLF *font, return; } - blf_font_ensure_ascii_kerning(font, gc); - blf_batch_draw_begin(font); while ((i < len) && str[i]) { @@ -435,8 +457,6 @@ 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--) { @@ -536,8 +556,6 @@ 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]) { @@ -694,8 +712,6 @@ 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]; i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -725,8 +741,6 @@ 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); s = BLI_str_find_prev_char_utf8(str, &str[i]); i = (size_t)((s != NULL) ? s - str : 0); @@ -778,8 +792,6 @@ 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]) { g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -869,8 +881,6 @@ 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]; @@ -1123,8 +1133,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, 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); @@ -1205,7 +1213,9 @@ void blf_font_free(FontBLF *font) blf_glyph_cache_free(gc); } - blf_kerning_cache_clear(font); + if (font->kerning_cache) { + MEM_freeN(font->kerning_cache); + } FT_Done_Face(font->face); if (font->filename) { @@ -1246,7 +1256,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 +1316,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; } 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..ab2a26b1e06 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -121,10 +121,6 @@ int blf_font_count_missing_chars(struct FontBLF *font, 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. */ -- cgit v1.2.3 From aed5a27755531aeef1e6f7a19dbde066cd9f2aba Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 13:22:47 +1000 Subject: Correct build error from 0d7aab2375e6bb06e89dad851550b283a1ff805c --- source/blender/blenfont/intern/blf_font.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 02847b74df1..436c4712e25 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -352,11 +352,11 @@ static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) } /* Copies of internal FreeType macros needed here. */ -#define FT_PIX_FLOOR(x) ((x) & ~FT_TYPEOF(x) 63) +#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 FT_PIX_ROUND(scaled) >> 6; + return (int)FT_PIX_ROUND(scaled) >> 6; #undef FT_PIX_FLOOR #undef FT_PIX_ROUND @@ -385,7 +385,7 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, /* 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] = delta.x; + font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; } if (delta.x != 0) { -- cgit v1.2.3 From c671bfe14e8d8c72a146b74e4dcc1c8ee7ab5bb4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 13:23:39 +1000 Subject: Cleanup: spelling in comments & minor cleanup Also hyphenate 'mouse-move' use doxy sections in render_update.c & move function comment from the header to the source. --- intern/ghost/GHOST_IEvent.h | 6 ++-- source/blender/blenkernel/BKE_cachefile.h | 5 ---- source/blender/blenkernel/intern/cachefile.c | 8 +++++- source/blender/draw/engines/eevee/eevee_lookdev.c | 2 +- .../blender/editors/gpencil/gpencil_interpolate.c | 4 +-- source/blender/editors/gpencil/gpencil_primitive.c | 4 +-- .../blender/editors/interface/interface_handlers.c | 8 +++--- source/blender/editors/mesh/editmesh_tools.c | 2 +- source/blender/editors/physics/particle_edit.c | 2 +- source/blender/editors/render/render_update.c | 32 ++++++++++++++-------- .../editors/space_view3d/view3d_gizmo_ruler.c | 4 ++- .../editors/transform/transform_convert_armature.c | 4 +-- .../editors/transform/transform_convert_object.c | 2 +- .../editors/transform/transform_snap_animation.c | 4 +-- source/blender/io/collada/collada_internal.cpp | 2 +- .../blender/windowmanager/intern/wm_event_system.c | 4 +-- 16 files changed, 53 insertions(+), 40 deletions(-) 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.
- * Use the getType() method to retrieve the type of event and the getData() + * to the #GHOST_IEventConsumer::processEvent() method of event consumers.
+ * 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/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/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/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/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/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b8f324cba83..76f6640c714 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -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/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 -/***************************** 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_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_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/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/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. */ } } -- cgit v1.2.3 From 47e68537f83075dd78ed66aac3216643389b0e3a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 17:41:40 +1000 Subject: Cleanup: organize blf_font.c functions using doxy-sections Functions in this file were scattered and not well organized. --- source/blender/blenfont/intern/blf_font.c | 743 ++++++++++++++++-------------- 1 file changed, 403 insertions(+), 340 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 436c4712e25..47fa3544474 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -71,6 +71,38 @@ static FT_Library ft_lib; 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,56 +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); -} +/** \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 @@ -337,31 +321,6 @@ BLI_INLINE GlyphBLF *blf_utf8_next_fast( return g; } -/* 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 -} - BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g, @@ -395,6 +354,12 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Drawing: GPU + * \{ */ + static void blf_font_draw_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, @@ -535,6 +500,12 @@ 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, @@ -680,6 +651,16 @@ void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct Res 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, @@ -773,6 +754,12 @@ 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, @@ -847,73 +834,230 @@ void blf_font_boundbox( blf_glyph_cache_release(font); } -/* -------------------------------------------------------------------- */ -/** \name Word-Wrap Support - * \{ */ +void blf_font_width_and_height(FontBLF *font, + const char *str, + size_t len, + float *r_width, + float *r_height, + struct ResultBLF *r_info) +{ + float xa, ya; + rctf box; -/** - * Generic function to add word-wrap support for other existing functions. - * - * Wraps on spaces and respects newlines. - * Intentionally ignores non-unix newlines, tabs and more advanced text formatting. - * - * \note If we want rich text - we better have a higher level API to handle that - * (color, bold, switching fonts... etc). - */ -static void blf_font_wrap_apply(FontBLF *font, - const char *str, - size_t len, - struct ResultBLF *r_info, - void (*callback)(FontBLF *font, - GlyphCacheBLF *gc, - const char *str, - size_t len, - int pen_y, - void *userdata), - void *userdata) + 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, len, &box, r_info); + } + else { + blf_font_boundbox(font, 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, size_t len, struct ResultBLF *r_info) { - unsigned int c, c_prev = BLI_UTF8_ERR; - GlyphBLF *g, *g_prev = NULL; - int pen_x = 0, pen_y = 0; - size_t i = 0; - int lines = 0; - int pen_x_next = 0; + float xa; + rctf box; - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + } + else { + xa = 1.0f; + } - struct WordWrapVars { - int wrap_width; - size_t start, last[2]; - } wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}}; + 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; +} - // printf("%s wrapping (%d, %d) `%s`:\n", __func__, len, strlen(str), str); - while ((i < len) && str[i]) { +float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +{ + float ya; + rctf box; - /* wrap vars */ - size_t i_curr = i; - bool do_draw = false; + if (font->flags & BLF_ASPECT) { + ya = font->aspect[1]; + } + else { + ya = 1.0f; + } - g = blf_utf8_next_fast(font, gc, str, &i, &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); + } + return BLI_rctf_size_y(&box) * ya; +} - 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 blf_font_fixed_width(FontBLF *font) +{ + const unsigned int c = ' '; - /** - * Implementation Detail (utf8). - * - * Take care with single byte offsets here, - * since this is utf8 we can't be sure a single byte is a single character. - * - * This is _only_ done when we know for sure the character is ascii (newline or a space). - */ - pen_x_next = pen_x + g->advance_i; - if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) { - do_draw = true; + 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; + } + } + + blf_glyph_cache_release(font); + return g->advance; +} + +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) +{ + 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; + } + + 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); + + 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, + size_t 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, len, user_fn, user_data, r_info, 0); + blf_glyph_cache_release(font); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Word-Wrap with Callback + * \{ */ + +/** + * Generic function to add word-wrap support for other existing functions. + * + * Wraps on spaces and respects newlines. + * Intentionally ignores non-unix newlines, tabs and more advanced text formatting. + * + * \note If we want rich text - we better have a higher level API to handle that + * (color, bold, switching fonts... etc). + */ +static void blf_font_wrap_apply(FontBLF *font, + const char *str, + size_t len, + struct ResultBLF *r_info, + void (*callback)(FontBLF *font, + GlyphCacheBLF *gc, + const char *str, + size_t len, + int pen_y, + void *userdata), + void *userdata) +{ + unsigned int c, c_prev = BLI_UTF8_ERR; + GlyphBLF *g, *g_prev = NULL; + int pen_x = 0, pen_y = 0; + size_t i = 0; + int lines = 0; + int pen_x_next = 0; + + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + + 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]) { + + /* wrap vars */ + size_t i_curr = i; + bool do_draw = false; + + 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); + + /** + * Implementation Detail (utf8). + * + * Take care with single byte offsets here, + * since this is utf8 we can't be sure a single byte is a single character. + * + * This is _only_ done when we know for sure the character is ascii (newline or a space). + */ + pen_x_next = pen_x + g->advance_i; + if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) { + do_draw = true; } else if (UNLIKELY(((i < len) && str[i]) == 0)) { /* need check here for trailing newline, else we draw it */ @@ -1018,216 +1162,120 @@ void blf_font_draw_buffer__wrap(FontBLF *font, /** \} */ -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 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 < 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; - } - - 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); - } - - if (font->kerning_cache) { - MEM_freeN(font->kerning_cache); - } + 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) { @@ -1366,54 +1414,69 @@ 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) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Font Configure + * \{ */ + +void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) { - float descender; + GlyphCacheBLF *gc; + FT_Error err; - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - descender = gc->descender; + blf_glyph_cache_acquire(font); - blf_glyph_cache_release(font); - return descender; -} + 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; + } + } -float blf_font_ascender(FontBLF *font) -{ - float ascender; + 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); - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - ascender = gc->ascender; + blf_glyph_cache_release(font); + return; + } - blf_glyph_cache_release(font); - return ascender; -} + font->size = size; + font->dpi = dpi; -char *blf_display_name(FontBLF *font) -{ - if (!font->face->family_name) { - return NULL; + if (!gc) { + blf_glyph_cache_new(font); } - return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); + blf_glyph_cache_release(font); } + +/** \} */ -- cgit v1.2.3 From 0b7947e85528bbf5c4e1f41354a41dc113937088 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 17:44:25 +1000 Subject: Cleanup: minor changes to blf_font.c - Use early return when kerning isn't used. - Remove early return that prevented matching acquire/release calls. --- source/blender/blenfont/intern/blf_font.c | 78 ++++++++++++++----------------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 47fa3544474..75a2e893119 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -328,29 +328,31 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const uint c, int *pen_x_p) { - if (FT_HAS_KERNING(font->face) && g_prev != NULL) { - FT_Vector delta = {KERNING_ENTRY_UNSET}; + if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { + return; + } - /* 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]; - } + FT_Vector delta = {KERNING_ENTRY_UNSET}; - /* 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); - } + /* 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 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 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 (delta.x != 0) { - /* Convert unscaled design units to pixels and move pen. */ - *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); - } + /* 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); } } @@ -1447,35 +1449,27 @@ void blf_font_free(FontBLF *font) 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) { + 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. */ - 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; + 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); + } + } } - font->size = size; - font->dpi = dpi; - - if (!gc) { - blf_glyph_cache_new(font); - } blf_glyph_cache_release(font); } -- cgit v1.2.3 From 34a05f39be2b79dd1c508c374a47cee6792174f9 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Sat, 21 Aug 2021 14:02:50 +0530 Subject: Clang: warn about C++20 designated initializers With the ongoing transition to C++ files, Windows build breaks often because of designated initializers. Now we have two compilers to catch the MSVC build error on. Reviewed By: #platform_macos, brecht, campbellbarton Differential Revision: https://developer.blender.org/D11940 --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2868324bf46..5a555876d21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1598,6 +1598,10 @@ 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) + # Designated initializer is a C++20 feature & breaks MSVC build. Dropping MSVC 2019 or + # updating to C++20 allows removing this. + 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) -- cgit v1.2.3 From 2b170f16d6ded9b3bcb428121b27274ae8637555 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Thu, 19 Aug 2021 23:57:00 +0200 Subject: Refactor low-level blendfile reading into separate files Instead of handling mmap, compression etc. all directly in readfile.c, refactor the code to use a generic FileReader. This makes it easier to add new compression methods or similar, and allows to reuse the logic in other places (e.g. thumbnail reading). Reviewed By: campbellbarton, brecht, mont29 Differential Revision: https://developer.blender.org/D5799 --- source/blender/blenlib/BLI_fileops.h | 2 + source/blender/blenlib/BLI_filereader.h | 79 ++++ source/blender/blenlib/CMakeLists.txt | 4 + source/blender/blenlib/intern/fileops.c | 7 + source/blender/blenlib/intern/filereader_file.c | 80 ++++ source/blender/blenlib/intern/filereader_gzip.c | 108 +++++ source/blender/blenlib/intern/filereader_memory.c | 145 +++++++ source/blender/blenloader/BLO_undofile.h | 14 + source/blender/blenloader/intern/readfile.c | 449 ++++----------------- source/blender/blenloader/intern/readfile.h | 32 +- source/blender/blenloader/intern/undofile.c | 95 +++++ source/blender/blenloader/intern/versioning_250.c | 3 +- .../blender/blenloader/intern/versioning_legacy.c | 3 +- source/blender/blenloader/intern/writefile.c | 2 +- source/blender/windowmanager/intern/wm_files.c | 88 ++-- 15 files changed, 667 insertions(+), 444 deletions(-) create mode 100644 source/blender/blenlib/BLI_filereader.h create mode 100644 source/blender/blenlib/intern/filereader_file.c create mode 100644 source/blender/blenlib/intern/filereader_gzip.c create mode 100644 source/blender/blenlib/intern/filereader_memory.c diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 7cfecc798a7..12fa73279c8 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -166,6 +166,8 @@ size_t BLI_gzip_mem_to_file_at_pos(void *buf, 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) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BLI_file_magic_is_gzip(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..dbcaa7cb1b6 --- /dev/null +++ b/source/blender/blenlib/BLI_filereader.h @@ -0,0 +1,79 @@ +/* + * 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 +#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, + * Gzip takes 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 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/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index b6603dce378..d2ba9e74c90 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -75,6 +75,9 @@ 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/fnmatch.c intern/freetypefont.c intern/gsqueue.c @@ -194,6 +197,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 diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index ac034d2b5cd..6fc2222241b 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -255,6 +255,13 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g #undef CHUNK +bool BLI_file_magic_is_gzip(const char header[4]) +{ + /* 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; +} + /** * 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 /* for read close */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include /* 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 + +#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 + +#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/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/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e48c305fc4b..82fbb2083df 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 /* for isdigit. */ #include /* for open flags (O_BINARY, O_RDONLY). */ #include @@ -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,60 @@ 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`. */ } - - /* '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 +1261,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 +1295,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 +1303,21 @@ 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; + + if (file == NULL) { + /* Compression initialization failed. */ + mem_file->close(mem_file); + return NULL; } - fd->flags |= FD_FLAGS_NOT_MY_BUFFER; + FileData *fd = filedata_new(reports); + fd->file = file; return blo_decode_and_check(fd, reports->reports); } @@ -1597,11 +1332,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 +1342,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 +1350,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 +1806,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 +1971,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 +3389,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 +3690,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 +3876,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 +3967,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 +3989,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 +4022,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 +4102,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 +5561,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 +5581,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 /* for open close read */ -# include /* odd include order-issue */ +# include /* 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 /* for open close read */ -# include /* odd include order-issue */ +# include /* 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..4de8e35f402 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -78,12 +78,12 @@ #include #include #include +#include #ifdef WIN32 # include "BLI_winstuff.h" # include "winsock2.h" # include -# include /* odd include order-issue */ #else # include /* FreeBSD, for write() and close(). */ #endif diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index b53ad0ee927..c3789abf2dd 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -28,11 +28,10 @@ * winsock stuff. */ #include +#include /* for open flags (O_BINARY, O_RDONLY). */ #include #include -#include "zlib.h" /* wm_read_exotic() */ - #ifdef WIN32 /* Need to include windows.h so _WIN32_IE is defined. */ # include @@ -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,59 @@ 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)) { + compressed_file = BLI_filereader_new_gzip(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; } /** \} */ -- cgit v1.2.3 From 2ea66af742bca4b427f88de13254414730a33776 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Sat, 21 Aug 2021 03:15:31 +0200 Subject: Add support for Zstandard compression for .blend files Compressing blendfiles can help save a lot of disk space, but the slowdown while loading and saving is a major annoyance. Currently Blender uses Zlib (aka gzip aka Deflate) for compression, but there are now several more modern algorithms that outperform it in every way. In this patch, I decided for Zstandard aka Zstd for several reasons: - It is widely supported, both in other programs and libraries as well as in general-purpose compression utilities on Unix - It is extremely flexible - spanning several orders of magnitude of compression speeds depending on the level setting. - It is pretty much on the Pareto frontier for all of its configurations (meaning that no other algorithm is both faster and more efficient). One downside of course is that older versions of Blender will not be able to read these files, but one can always just re-save them without compression or decompress the file manually with an external tool. The implementation here saves additional metadata into the compressed file in order to allow for efficient seeking when loading. This is standard-compliant and will be ignored by other tools that support Zstd. If the metadata is not present (e.g. because you manually compressed a .blend file with another tool), Blender will fall back to sequential reading. Saving is multithreaded to improve performance. Loading is currently not multithreaded since it's not easy to predict the access patterns of the loading code when seeking is supported. In the future, we might want to look into making this more predictable or disabling seeking for the main .blend file, which would then allow for multiple background threads that decompress data ahead of time. The compression level was chosen to get sizes comparable to previous versions at much higher speeds. In the future, this could be exposed as an option. Reviewed By: campbellbarton, brecht, mont29 Differential Revision: https://developer.blender.org/D5799 --- build_files/cmake/Modules/FindZstd.cmake | 66 +++++ build_files/cmake/platform/platform_apple.cmake | 3 + build_files/cmake/platform/platform_unix.cmake | 1 + build_files/cmake/platform/platform_win32.cmake | 3 + source/blender/blenlib/BLI_fileops.h | 2 + source/blender/blenlib/BLI_filereader.h | 4 +- source/blender/blenlib/CMakeLists.txt | 3 + source/blender/blenlib/intern/fileops.c | 27 ++ source/blender/blenlib/intern/filereader_zstd.c | 335 ++++++++++++++++++++++++ source/blender/blenloader/CMakeLists.txt | 2 +- source/blender/blenloader/intern/readfile.c | 9 + source/blender/blenloader/intern/writefile.c | 297 ++++++++++++++++----- source/blender/windowmanager/CMakeLists.txt | 4 - source/blender/windowmanager/intern/wm_files.c | 5 + 14 files changed, 689 insertions(+), 72 deletions(-) create mode 100644 build_files/cmake/Modules/FindZstd.cmake create mode 100644 source/blender/blenlib/intern/filereader_zstd.c 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/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 12fa73279c8..377b7bc3bc2 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -168,6 +168,8 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BLI_file_magic_is_gzip(const char header[4]); +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 index dbcaa7cb1b6..8d1fa3d1596 100644 --- a/source/blender/blenlib/BLI_filereader.h +++ b/source/blender/blenlib/BLI_filereader.h @@ -61,7 +61,7 @@ typedef struct FileReader { * * 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, - * Gzip takes over the base FileReader and will clean it up when their clean() is called. + * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called. */ /* Create FileReader from raw file descriptor. */ @@ -71,6 +71,8 @@ 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(); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index d2ba9e74c90..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} ) @@ -78,6 +79,7 @@ set(SRC 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 @@ -327,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 6fc2222241b..31825c69737 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -262,6 +262,33 @@ bool BLI_file_magic_is_gzip(const char header[4]) return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08; } +bool BLI_file_magic_is_zstd(const char header[4]) +{ + /* 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; + } + if ((magic >> 4) == 0x184D2A5) { + return true; + } + return false; +} + /** * 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_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 +#include + +#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/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 82fbb2083df..49c3497f996 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1233,6 +1233,12 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, 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`. */ + } + } /* Clean up `rawfile` if it wasn't taken over. */ if (rawfile != NULL) { @@ -1309,6 +1315,9 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe if (BLI_file_magic_is_gzip(mem)) { file = BLI_filereader_new_gzip(mem_file); } + else if (BLI_file_magic_is_zstd(mem)) { + file = BLI_filereader_new_zstd(mem_file); + } if (file == NULL) { /* Compression initialization failed. */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 4de8e35f402..6f43fbf1fa0 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -101,7 +101,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 +134,21 @@ #include +#include + /* 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 +159,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 +180,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 +204,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 +212,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 +384,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 +410,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 +436,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 +456,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 +498,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 +516,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 +542,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 +565,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 +603,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 +1323,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 +1344,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/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_files.c b/source/blender/windowmanager/intern/wm_files.c index c3789abf2dd..f83511e76f0 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -516,8 +516,13 @@ static int wm_read_exotic(const char *name) /* 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); + } /* If a compression signature matches, try decompressing the start and check if it's a .blend */ if (compressed_file != NULL) { -- cgit v1.2.3 From 67c29bc5a273b66e278bd20c18187b425acf1869 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Sat, 21 Aug 2021 03:26:57 +0200 Subject: Use Zstandard compression for the sequencer cache Reviewed By: campbellbarton, brecht, mont29 Differential Revision: https://developer.blender.org/D5799 --- source/blender/blenlib/BLI_fileops.h | 19 +-- source/blender/blenlib/intern/fileops.c | 217 +++++++------------------- source/blender/sequencer/intern/image_cache.c | 32 ++-- 3 files changed, 82 insertions(+), 186 deletions(-) diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 377b7bc3bc2..906a56ce909 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -154,20 +154,15 @@ 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) - ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); 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; diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 31825c69737..532a29b8147 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -31,6 +31,7 @@ #include #include "zlib.h" +#include "zstd.h" #ifdef WIN32 # include "BLI_fileops_types.h" @@ -61,200 +62,90 @@ #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 (gzwrite(gzfile, buffer, readsize) <= 0) { - rval = -1; /* error happened in writing */ - fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err)); + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { break; } + total_written += output.pos; } - gzclose(gzfile); - close(file); - - return rval; -} -#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) -{ - gzFile gzfile; - int readsize, size, alloc_size = 0; - char *mem = NULL; - const int chunk_size = 512 * 1024; - - size = 0; - - 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; - } - - readsize = gzread(gzfile, mem + size, chunk_size); - if (readsize > 0) { - size += readsize; + /* 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; } - else { + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { break; } + total_written += output.pos; } - gzclose(gzfile); - - if (size == 0) { - MEM_freeN(mem); - mem = NULL; - } - else if (alloc_size != size) { - mem = MEM_reallocN(mem, size); - } - - *r_size = size; + MEM_freeN(out_buf); + ZSTD_freeCCtx(ctx); - return mem; + return ZSTD_isError(ret) ? 0 : total_written; } -#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) +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) { - 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); + fseek(file, file_offset, SEEK_SET); - if (strm.avail_in != 0 || ret != Z_STREAM_END) { - return 0; - } + ZSTD_DCtx *ctx = ZSTD_createDCtx(); - deflateEnd(&strm); - return (size_t)strm.total_out; -} + size_t in_len = ZSTD_DStreamInSize(); + void *in_buf = MEM_mallocN(in_len, __func__); + ZSTD_inBuffer input = {in_buf, in_len, 0}; -/* 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) -{ - 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_outBuffer output = {buf, len, 0}; - do { - strm.avail_in = fread(in, 1, chunk, file); - strm.next_in = in; - if (ferror(file)) { - inflateEnd(&strm); - return 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; } - do { - strm.avail_out = len; - strm.next_out = (Bytef *)buf + strm.total_out; + /* 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); - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR) { - return 0; + if (ZSTD_isError(ret)) { + break; } - } while (strm.avail_out == 0); + } + } - } while (ret != Z_STREAM_END); + MEM_freeN(in_buf); + ZSTD_freeDCtx(ctx); - inflateEnd(&strm); - return (size_t)strm.total_out; + return ZSTD_isError(ret) ? 0 : output.pos; } -#undef CHUNK - bool BLI_file_magic_is_gzip(const char header[4]) { /* GZIP itself starts with the magic bytes 0x1f 0x8b. 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 @@ /* -x-%()-.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) -- cgit v1.2.3 From 721fad37a1981a564404b5f708a405503dc18f45 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Sat, 21 Aug 2021 23:31:51 +0200 Subject: Fix Windows builds after Zstandard commits --- source/blender/blenloader/intern/writefile.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 6f43fbf1fa0..90d58514eb5 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -78,7 +78,6 @@ #include #include #include -#include #ifdef WIN32 # include "BLI_winstuff.h" -- cgit v1.2.3 From a1e91fbef3dc9a5d5c8456cd9a887aac1bdb652c Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sun, 22 Aug 2021 09:26:06 -0700 Subject: BLF: Remove space_userpref.py font_kerning_style Remove `font_kerning_style` from `space_userpref.py` since this is no longer valid. See more details in D12276 Differential Revision: https://developer.blender.org/D12276 Reviewed by Campbell Barton --- release/scripts/startup/bl_ui/space_userpref.py | 1 - 1 file changed, 1 deletion(-) 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) -- cgit v1.2.3 From b4773334732350162f02aab6b6154dfc73165f85 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Sun, 22 Aug 2021 22:15:28 +0530 Subject: Cleanup: fix comment about compiler support. Differential Revision: https://developer.blender.org/D12288 --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a555876d21..47712f0ac1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1598,8 +1598,7 @@ 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) - # Designated initializer is a C++20 feature & breaks MSVC build. Dropping MSVC 2019 or - # updating to C++20 allows removing this. + # 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) -- cgit v1.2.3 From 0de3d4e8c750b795ee193d6e5d5cd26832cb23c7 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Sun, 22 Aug 2021 23:48:54 -0300 Subject: Fix T90847: snap to face of Add Primitive tool not working in edit mode BVHTree was being created but not balanced. Error introduced in {rBfcc844f8fbd0}. --- source/blender/blenkernel/intern/bvhutils.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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"); -- cgit v1.2.3 From aa067bef5e382dddb57aeba2554be72eafbab176 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 23 Aug 2021 15:01:50 +1000 Subject: Cleanup: use BLI_str_utf8 prefix Rename: - BLI_str_utf8_invalid_byte (was BLI_utf8_invalid_byte) - BLI_str_utf8_invalid_strip (was BLI_utf8_invalid_strip) --- source/blender/blenkernel/intern/lib_id.c | 4 ++-- source/blender/blenkernel/intern/text.c | 4 ++-- source/blender/blenlib/BLI_string_utf8.h | 4 ++-- source/blender/blenlib/intern/freetypefont.c | 2 +- source/blender/blenlib/intern/string_utf8.c | 6 +++--- source/blender/blenlib/tests/BLI_string_utf8_test.cc | 4 ++-- source/blender/editors/interface/interface_handlers.c | 4 ++-- source/blender/editors/util/numinput.c | 2 +- source/blender/makesrna/intern/rna_main_api.c | 2 +- source/blender/sequencer/intern/strip_edit.c | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) 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/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_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/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/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/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 76f6640c714..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 */ 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/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/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); } -- cgit v1.2.3 From 62f2204d65ef52b5efc38cb3657f1f97fa73acd7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 23 Aug 2021 15:01:52 +1000 Subject: Cleanup: rename len to str_len for BLF functions Make it obvious which variable this is the length of. --- source/blender/blenfont/BLF_api.h | 49 +++++----- source/blender/blenfont/intern/blf.c | 92 +++++++++--------- source/blender/blenfont/intern/blf_default.c | 8 +- source/blender/blenfont/intern/blf_font.c | 133 +++++++++++++++----------- source/blender/blenfont/intern/blf_internal.h | 39 +++++--- 5 files changed, 179 insertions(+), 142 deletions(-) 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 2f9eb0753ac..86d67c80fd4 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -521,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); @@ -530,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); @@ -560,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; } @@ -589,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); } @@ -606,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) @@ -621,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; } @@ -652,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; } @@ -673,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); @@ -681,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) @@ -735,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) @@ -894,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 75a2e893119..af68e1563d1 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -365,7 +365,7 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, 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) { @@ -374,14 +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_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)) { @@ -407,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; @@ -426,7 +426,7 @@ static void blf_font_draw_ascii_ex( 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)) { @@ -456,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; @@ -474,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)) { @@ -512,7 +515,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) 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) { @@ -531,7 +534,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, /* 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)) { @@ -646,10 +649,13 @@ 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); } @@ -685,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; @@ -695,7 +701,7 @@ size_t blf_font_width_to_strlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - 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); @@ -713,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; @@ -724,7 +730,7 @@ size_t blf_font_width_to_rstrlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - 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); @@ -765,7 +771,7 @@ size_t blf_font_width_to_rstrlen( 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) @@ -781,7 +787,7 @@ static void blf_font_boundbox_ex(FontBLF *font, box->ymin = 32000.0f; box->ymax = -32000.0f; - 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)) { @@ -829,16 +835,16 @@ 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, len, r_box, r_info, 0); + 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, - size_t len, + const size_t str_len, float *r_width, float *r_height, struct ResultBLF *r_info) @@ -856,16 +862,19 @@ void blf_font_width_and_height(FontBLF *font, } if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); + blf_font_boundbox__wrap(font, str, str_len, &box, r_info); } else { - blf_font_boundbox(font, str, len, &box, r_info); + 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, size_t len, struct ResultBLF *r_info) +float blf_font_width(FontBLF *font, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) { float xa; rctf box; @@ -878,15 +887,18 @@ float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBL } if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); + blf_font_boundbox__wrap(font, str, str_len, &box, r_info); } else { - blf_font_boundbox(font, str, len, &box, r_info); + 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, size_t len, struct ResultBLF *r_info) +float blf_font_height(FontBLF *font, + const char *str, + const size_t str_len, + struct ResultBLF *r_info) { float ya; rctf box; @@ -899,10 +911,10 @@ float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultB } if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); + blf_font_boundbox__wrap(font, str, str_len, &box, r_info); } else { - blf_font_boundbox(font, str, len, &box, r_info); + blf_font_boundbox(font, str, str_len, &box, r_info); } return BLI_rctf_size_y(&box) * ya; } @@ -930,7 +942,7 @@ float blf_font_fixed_width(FontBLF *font) static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, - size_t len, + const size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data, struct ResultBLF *r_info, @@ -942,12 +954,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, size_t i = 0, i_curr; rcti gbox; - if (len == 0) { + if (str_len == 0) { /* early output. */ return; } - while ((i < len) && str[i]) { + while ((i < str_len) && str[i]) { i_curr = i; g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -981,13 +993,13 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, } void blf_font_boundbox_foreach_glyph(FontBLF *font, const char *str, - size_t len, + 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, len, user_fn, user_data, r_info, 0); + blf_font_boundbox_foreach_glyph_ex(font, gc, str, str_len, user_fn, user_data, r_info, 0); blf_glyph_cache_release(font); } @@ -1008,12 +1020,12 @@ void blf_font_boundbox_foreach_glyph(FontBLF *font, */ 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) @@ -1032,8 +1044,8 @@ static void blf_font_wrap_apply(FontBLF *font, 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; @@ -1061,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; @@ -1112,54 +1124,61 @@ 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); } /** \} */ @@ -1170,14 +1189,14 @@ void blf_font_draw_buffer__wrap(FontBLF *font, int blf_font_count_missing_chars(FontBLF *font, const char *str, - const size_t len, + const size_t str_len, int *r_tot_chars) { int missing = 0; size_t i = 0; *r_tot_chars = 0; - while (i < len) { + while (i < str_len) { unsigned int c; if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index ab2a26b1e06..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,7 +125,7 @@ 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); -- cgit v1.2.3 From 0682af0d63a44b050d57bdaf7699e364a311d711 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 23 Aug 2021 15:01:53 +1000 Subject: RNA: add length augmented to RNA_string_get_alloc This was noted as a TODO as it wraps RNA_property_string_get_alloc which takes a length return argument. --- intern/cycles/blender/blender_util.h | 2 +- source/blender/editors/curve/editfont.c | 4 ++-- source/blender/editors/space_buttons/buttons_ops.c | 12 ++++++----- source/blender/editors/space_console/console_ops.c | 6 +++--- source/blender/editors/space_image/image_ops.c | 2 +- .../blender/editors/space_image/image_sequence.c | 2 +- source/blender/editors/space_info/info_ops.c | 2 +- .../editors/space_sequencer/sequencer_add.c | 10 ++++----- .../editors/space_sequencer/sequencer_edit.c | 2 +- source/blender/editors/space_text/text_ops.c | 2 +- source/blender/makesrna/RNA_access.h | 9 ++++---- source/blender/makesrna/intern/rna_access.c | 25 +++++++++++++--------- source/blender/windowmanager/intern/wm_operators.c | 2 +- 13 files changed, 43 insertions(+), 37 deletions(-) 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/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/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_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/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/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; } -- cgit v1.2.3 From 9564b6cf2388fc869608f32a77cbc18032810e58 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 16 Aug 2021 08:38:15 +0200 Subject: Fix T90651: camera reconstruction crash without scene camera This was working differently in 2.79, tried tracking this down and it seems this was wrong since the 2.8 beginning in {rB7907dfc40018}. This would not only crash without an active scene camera, but would also result in different tracks from different camera's constraints could not be selected. So select id depends on corresponding camera, remove the dependency on scene camera completely. Maniphest Tasks: T90651 Differential Revision: https://developer.blender.org/D12230 --- source/blender/draw/engines/overlay/overlay_extra.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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. */ -- cgit v1.2.3 From 8165333de9d52e9c5bd4bea4c15d00d5f2092d47 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 23 Aug 2021 13:47:40 +0200 Subject: Pipeline: Use more explicit cuda versions. --- build_files/config/pipeline_config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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: -- cgit v1.2.3 From 5aa3167e48b202f4883ce948ba8e3469e3eaf3ec Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 19 Aug 2021 14:03:48 +0200 Subject: Fix T90772: Image Editor not sampling color from the the currently selected pass Caused by {rBebaa3fcedd23}. Seems this above commit assumed an ImageUser's multi_index is only used for Multiview/Stereo? This is not the case, multi_index also stores the index for layer/pass combination. If we call both BKE_image_multilayer_index and BKE_image_multiview_index (even though this is not appropriate/needed for multilayer images?), we might end up overwriting multi_index again. note: looking at this I was also wondering why we update the ImageUser in image-buffer-aquiring funnctions [and not from the UI, e.g. template_image_layers, but that is a whole different story I guess, see comment in T90772 as well] note2: this could also use a utility function (this is not the only place where this is done), this is fo a cleanup commit. Maniphest Tasks: T90772 Differential Revision: https://developer.blender.org/D12267 --- source/blender/draw/engines/image/image_engine.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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); -- cgit v1.2.3 From b4b3f518aa04877fb89605acb86ddbc8dc25656d Mon Sep 17 00:00:00 2001 From: YimingWu Date: Mon, 23 Aug 2021 20:45:57 +0800 Subject: GPencil: Fix memory leak in split & trim functions Authored by Henrik Dick (weasel) Reviewed By YimingWu (NicksBest), Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D12284 --- source/blender/blenkernel/intern/gpencil_geom.cc | 28 ++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) 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; } -- cgit v1.2.3 From 2f0e350ffd0ce1bf7ef617a678178dbfa185d3d0 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 23 Aug 2021 09:46:30 -0300 Subject: Fix T90872: Dopesheet messes up keyframe handles Y coordinate was not being constrained. Caused by {rBb0d9e6797fb866e7a58876c7977c98a190070310} --- source/blender/editors/transform/transform_convert_action.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index a5565b5fb88..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; @@ -615,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) { -- cgit v1.2.3 From 27f138f4c819d6416adb5fafc24d3e447969c467 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 23 Aug 2021 09:48:52 -0300 Subject: Cleanup: rename parameter in transform utility `inv_unit_scale` is not descriptive. --- source/blender/editors/transform/transform_convert.c | 13 ++++++++----- source/blender/editors/transform/transform_convert.h | 4 +--- 2 files changed, 9 insertions(+), 8 deletions(-) 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 */ -- cgit v1.2.3 From cd118c5581f482afc8554ff88b5b6f3b552b1682 Mon Sep 17 00:00:00 2001 From: Mikhail Matrosov Date: Mon, 23 Aug 2021 15:13:35 +0200 Subject: Fix T90423: black pixels after shadow terminator geometry offset Solves an error in the principled diffuse BSDF, where it was not correctly rejecting directions outside the hemisphere. Differential Revision: https://developer.blender.org/D12283 --- intern/cycles/kernel/closure/bsdf_principled_diffuse.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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); } -- cgit v1.2.3 From 21d4a888b80235470cf95ad775fb4a6308bc5de0 Mon Sep 17 00:00:00 2001 From: James Partsafas Date: Mon, 23 Aug 2021 16:23:58 +0200 Subject: Fix T88107: rename Convertor to Converter nodes to correct spelling Differential Revision: https://developer.blender.org/D11198 --- source/blender/blenkernel/BKE_node.h | 2 +- source/blender/editors/include/UI_resources.h | 2 +- source/blender/editors/interface/resources.c | 2 +- source/blender/editors/space_node/node_draw.cc | 4 ++-- source/blender/nodes/composite/node_composite_tree.c | 2 +- source/blender/nodes/composite/nodes/node_composite_idMask.c | 2 +- source/blender/nodes/composite/nodes/node_composite_math.c | 2 +- source/blender/nodes/composite/nodes/node_composite_premulkey.c | 2 +- source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c | 4 ++-- source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c | 4 ++-- source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c | 4 ++-- source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c | 4 ++-- source/blender/nodes/composite/nodes/node_composite_setalpha.c | 2 +- source/blender/nodes/composite/nodes/node_composite_switchview.c | 2 +- source/blender/nodes/composite/nodes/node_composite_valToRgb.c | 4 ++-- source/blender/nodes/function/nodes/node_fn_boolean_math.cc | 2 +- source/blender/nodes/function/nodes/node_fn_float_compare.cc | 2 +- source/blender/nodes/function/nodes/node_fn_float_to_int.cc | 2 +- source/blender/nodes/geometry/node_geometry_tree.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_switch.cc | 2 +- source/blender/nodes/shader/node_shader_tree.c | 2 +- source/blender/nodes/shader/nodes/node_shader_blackbody.c | 2 +- source/blender/nodes/shader/nodes/node_shader_clamp.cc | 2 +- source/blender/nodes/shader/nodes/node_shader_map_range.cc | 2 +- source/blender/nodes/shader/nodes/node_shader_math.cc | 2 +- source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c | 4 ++-- source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc | 4 ++-- source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc | 4 ++-- source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c | 2 +- source/blender/nodes/shader/nodes/node_shader_squeeze.c | 2 +- source/blender/nodes/shader/nodes/node_shader_valToRgb.cc | 4 ++-- source/blender/nodes/shader/nodes/node_shader_wavelength.c | 2 +- source/blender/nodes/texture/node_texture_tree.c | 2 +- source/blender/nodes/texture/nodes/node_texture_distance.c | 2 +- source/blender/nodes/texture/nodes/node_texture_math.c | 2 +- source/blender/nodes/texture/nodes/node_texture_valToNor.c | 2 +- source/blender/nodes/texture/nodes/node_texture_valToRgb.c | 4 ++-- 37 files changed, 48 insertions(+), 48 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c4393246926..06528cd213c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -346,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/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/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/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 6ec6315a238..3b1a55f55ab 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -670,8 +670,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/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/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 0ba9080918c..58e7d82beea 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -90,7 +90,7 @@ 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); 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 16ffb761a15..918dd24e520 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -109,7 +109,7 @@ 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); 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 52acfefe615..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 @@ -87,7 +87,7 @@ 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.build_multi_function = fn_node_float_to_int_build_multi_function; 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 0aa5c68aaf5..c9ce2de1ea1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -179,7 +179,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/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/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 f105f8bcaf9..b90397e4892 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -78,7 +78,7 @@ 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); 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 e4739e2864d..f48e824ccb5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -310,7 +310,7 @@ 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); diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index c30f2948ab1..66c50b6de46 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -152,7 +152,7 @@ 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); 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 2779fc6bf68..d9cbee33c0f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -106,7 +106,7 @@ 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); @@ -165,7 +165,7 @@ 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); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 1fd794cdd0a..3048ed1e9fc 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -91,7 +91,7 @@ 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.build_multi_function = sh_node_sepxyz_build_multi_function; @@ -131,7 +131,7 @@ 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.build_multi_function = sh_node_combxyz_build_multi_function; 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 1bc42ab0cc6..62f6b38c79f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -175,7 +175,7 @@ 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); @@ -221,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_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); -- cgit v1.2.3 From 8f4730e66f45c300bb64fb185118f7d66355bf95 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:28:08 +0200 Subject: Compositor: Full frame convert nodes Adds full frame implementation to all nodes in "Converter" sub-menu except "ID Mask" which is implemented separately. No functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12095 --- .../operations/COM_ColorRampOperation.cc | 10 + .../compositor/operations/COM_ColorRampOperation.h | 8 +- .../compositor/operations/COM_ConvertOperation.cc | 164 +++++++++++ .../compositor/operations/COM_ConvertOperation.h | 68 ++++- .../compositor/operations/COM_MathBaseOperation.cc | 322 +++++++++++++++++++++ .../compositor/operations/COM_MathBaseOperation.h | 154 +++++++++- .../operations/COM_SetAlphaMultiplyOperation.cc | 12 + .../operations/COM_SetAlphaMultiplyOperation.h | 8 +- .../operations/COM_SetAlphaReplaceOperation.cc | 13 + .../operations/COM_SetAlphaReplaceOperation.h | 8 +- 10 files changed, 750 insertions(+), 17 deletions(-) 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 inputs) +{ + for (BuffersIterator 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 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 inputs) +{ + BuffersIterator 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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &it) +{ + for (; !it.is_end(); ++it) { + copy_v4_v4(it.out, ColorSceneLinear4f(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 &it) +{ + for (; !it.is_end(); ++it) { + copy_v4_v4(it.out, ColorSceneLinear4f(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 inputs) +{ + for (BuffersIterator 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 inputs) +{ + for (BuffersIterator 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 inputs) final; + + protected: + virtual void update_memory_buffer_partial(BuffersIterator &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 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 inputs) override; }; } // namespace blender::compositor 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 inputs) +{ + BuffersIterator 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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 inputs) final; + + protected: + virtual void update_memory_buffer_partial(BuffersIterator &it) = 0; }; -class MathAddOperation : public MathBaseOperation { +template typename TFunctor> +class MathFunctor2Operation : public MathBaseOperation { + void update_memory_buffer_partial(BuffersIterator &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 { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; -class MathSubtractOperation : public MathBaseOperation { +class MathSubtractOperation : public MathFunctor2Operation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; -class MathMultiplyOperation : public MathBaseOperation { +class MathMultiplyOperation : public MathFunctor2Operation { 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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &it) override; }; -class MathLessThanOperation : public MathBaseOperation { +class MathLessThanOperation : public MathFunctor2Operation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; }; -class MathGreaterThanOperation : public MathBaseOperation { +class MathGreaterThanOperation : public MathFunctor2Operation { 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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &it) override; }; } // namespace blender::compositor 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 inputs) +{ + for (BuffersIterator 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 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 inputs) +{ + for (BuffersIterator 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 inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From a95e56b741709f7157a44196091ccad3ec369e5e Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:29:34 +0200 Subject: Compositor: Add sampling methods for full frame Current sampling methods have off by one issues on full frame: - Bilinear sampling do not fully sample bottom and left image border, creating edges. - Single elem buffers are not sampled at all when they should be at least on the borders to smooth edges. - EWA filtering is partially implemented on `ReadBufferOperation`, it needs to be moved to `MemoryBuffer` on full frame. In order to not affect tiled implementation, this commit creates specific sampling methods for full frame needs. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12164 --- .../blender/compositor/intern/COM_MemoryBuffer.cc | 36 ++++++++ .../blender/compositor/intern/COM_MemoryBuffer.h | 97 +++++++++++++++++++++- 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 6b954072a9a..16274d720cd 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -405,12 +405,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(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(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..d3c7566d246 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, @@ -582,6 +658,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 T get_relative_x(T x) const + { + return x - m_rect.xmin; + } + + template 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, -- cgit v1.2.3 From 064167fce70e3d7c382c374334a1bd0b520fe9fe Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:30:01 +0200 Subject: Compositor: Full frame transform nodes Adds full frame implementation to "Rotate", "Transform" and "Stabilize2D" nodes. To avoid sampling twice when concatenating scale and rotate operations, a `TransformOperation` is implemented with all the functionality. The nodes have no functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12165 --- source/blender/compositor/CMakeLists.txt | 2 + source/blender/compositor/COM_defines.h | 2 + source/blender/compositor/nodes/COM_RotateNode.cc | 25 +++- .../compositor/nodes/COM_Stabilize2dNode.cc | 100 ++++++++----- .../blender/compositor/nodes/COM_TransformNode.cc | 61 +++++--- .../compositor/operations/COM_RotateOperation.cc | 86 +++++++++++- .../compositor/operations/COM_RotateOperation.h | 37 ++++- .../compositor/operations/COM_ScaleOperation.cc | 13 +- .../compositor/operations/COM_ScaleOperation.h | 9 ++ .../operations/COM_TransformOperation.cc | 156 +++++++++++++++++++++ .../compositor/operations/COM_TransformOperation.h | 87 ++++++++++++ 11 files changed, 501 insertions(+), 77 deletions(-) create mode 100644 source/blender/compositor/operations/COM_TransformOperation.cc create mode 100644 source/blender/compositor/operations/COM_TransformOperation.h diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index ee287c65fe9..dba2d1e1e67 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -515,6 +515,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..ee9bea7b2c6 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -119,6 +119,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/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_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(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 inputs) +{ + ensureDegree(); + const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; + for (BuffersIterator 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 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_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(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(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(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(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 inputs) +{ + const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; + MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX]; + BuffersIterator it = output->iterate_with({input_scale}, area); + if (invert_) { + transform_inverted(it, input_img); + } + else { + transform(it, input_img); + } +} + +void TransformOperation::transform(BuffersIterator &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 &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 inputs) override; + + private: + /** Translate -> Rotate -> Scale. */ + void transform(BuffersIterator &it, const MemoryBuffer *input_img); + /** Scale -> Rotate -> Translate. */ + void transform_inverted(BuffersIterator &it, const MemoryBuffer *input_img); +}; + +} // namespace blender::compositor -- cgit v1.2.3 From 344aca3b1bf2718904455ea6cef1ffd8bedf51a6 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:30:18 +0200 Subject: Compositor: Full frame distort nodes Adds full frame implementation to "Displace", "Crop", "Flip", "Plane Track Deform", "Corner Pin", "Movie Distortion", "Lens Distortion" and "Map UV" nodes. The other nodes in "Distort" sub-menu are implemented separately in other commits. No functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12166 --- .../compositor/operations/COM_CropOperation.cc | 45 +++++ .../compositor/operations/COM_CropOperation.h | 13 +- .../compositor/operations/COM_DisplaceOperation.cc | 95 +++++++++-- .../compositor/operations/COM_DisplaceOperation.h | 22 ++- .../operations/COM_DisplaceSimpleOperation.cc | 52 ++++++ .../operations/COM_DisplaceSimpleOperation.h | 9 +- .../compositor/operations/COM_FlipOperation.cc | 38 +++++ .../compositor/operations/COM_FlipOperation.h | 9 +- .../compositor/operations/COM_MapUVOperation.cc | 97 ++++++++++- .../compositor/operations/COM_MapUVOperation.h | 21 ++- .../operations/COM_MovieDistortionOperation.cc | 47 +++++ .../operations/COM_MovieDistortionOperation.h | 9 +- .../operations/COM_PlaneCornerPinOperation.cc | 99 +++++++++++ .../operations/COM_PlaneCornerPinOperation.h | 7 + .../operations/COM_PlaneDistortCommonOperation.cc | 115 ++++++++++++- .../operations/COM_PlaneDistortCommonOperation.h | 16 +- .../operations/COM_PlaneTrackOperation.cc | 26 ++- .../operations/COM_PlaneTrackOperation.h | 4 + .../COM_ProjectorLensDistortionOperation.cc | 54 ++++++ .../COM_ProjectorLensDistortionOperation.h | 11 +- .../COM_ScreenLensDistortionOperation.cc | 189 +++++++++++++++++++-- .../operations/COM_ScreenLensDistortionOperation.h | 13 +- 22 files changed, 934 insertions(+), 57 deletions(-) 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 inputs) +{ + rcti crop_area; + BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax); + for (BuffersIterator 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 inputs) +{ + rcti op_area; + BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight()); + const MemoryBuffer *input = inputs[0]; + for (BuffersIterator 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 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 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 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 inputs) +{ + const MemoryBuffer *input_color = inputs[0]; + for (BuffersIterator 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 vector_read_fn_; + std::function scale_x_read_fn_; + std::function 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 inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span 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 inputs) +{ + const float width = this->getWidth(); + const float height = this->getHeight(); + const MemoryBuffer *input_color = inputs[0]; + for (BuffersIterator 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 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 inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + for (BuffersIterator 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 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 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 inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (BuffersIterator 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 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 inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span 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_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 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 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 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(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 inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + float uv[2]; + float deriv[2][2]; + BuffersIterator 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 UNUSED(inputs)) +{ + for (BuffersIterator 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 -#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 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 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(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 inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + const float height = this->getHeight(); + const float width = this->getWidth(); + float color[4]; + for (BuffersIterator 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 inputs) override; }; } // namespace blender::compositor 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(distortion_op)->get_constant_elem()[0]; + } + if (!m_dispersion_const && distortion_op->get_flags().is_constant_operation) { + m_dispersion = static_cast(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 inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (BuffersIterator 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 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, -- cgit v1.2.3 From daa7c59e38c8fe464004b3becd6956b880c38c92 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:30:31 +0200 Subject: Compositor: Full frame Bokeh Blur and Blur nodes Adds full frame implementation to these nodes operations. When enabling "extend bounds" node option, tiled implementation result is slightly different because it's using `TranslateOperation` with bilinear sampling for centering. Full frame always uses nearest to don't lose image quality. It has the disadvantage of causing image jiggling on backdrop when switching size values as it's not pixel perfect. This is fixed by rounding to even. No functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12167 --- source/blender/blenlib/BLI_math_base.h | 2 + source/blender/blenlib/intern/math_base_inline.c | 8 + source/blender/compositor/CMakeLists.txt | 4 + source/blender/compositor/COM_defines.h | 2 + .../blender/compositor/intern/COM_NodeOperation.cc | 6 +- .../blender/compositor/intern/COM_NodeOperation.h | 11 ++ .../compositor/operations/COM_BlurBaseOperation.cc | 95 +++++++-- .../compositor/operations/COM_BlurBaseOperation.h | 22 ++- .../operations/COM_BokehBlurOperation.cc | 178 +++++++++++++++-- .../compositor/operations/COM_BokehBlurOperation.h | 12 +- .../operations/COM_FastGaussianBlurOperation.cc | 106 ++++++++++ .../operations/COM_FastGaussianBlurOperation.h | 21 +- .../operations/COM_GammaCorrectOperation.cc | 57 ++++++ .../operations/COM_GammaCorrectOperation.h | 14 +- .../COM_GaussianAlphaBlurBaseOperation.cc | 168 ++++++++++++++++ .../COM_GaussianAlphaBlurBaseOperation.h | 62 ++++++ .../operations/COM_GaussianAlphaXBlurOperation.cc | 13 +- .../operations/COM_GaussianAlphaXBlurOperation.h | 23 +-- .../operations/COM_GaussianAlphaYBlurOperation.cc | 15 +- .../operations/COM_GaussianAlphaYBlurOperation.h | 23 +-- .../operations/COM_GaussianBlurBaseOperation.cc | 154 +++++++++++++++ .../operations/COM_GaussianBlurBaseOperation.h | 50 +++++ .../operations/COM_GaussianBokehBlurOperation.cc | 213 +++++++++++++++++---- .../operations/COM_GaussianBokehBlurOperation.h | 18 ++ .../operations/COM_GaussianXBlurOperation.cc | 15 +- .../operations/COM_GaussianXBlurOperation.h | 11 +- .../operations/COM_GaussianYBlurOperation.cc | 13 +- .../operations/COM_GaussianYBlurOperation.h | 11 +- .../COM_VariableSizeBokehBlurOperation.cc | 161 ++++++++++++++++ .../COM_VariableSizeBokehBlurOperation.h | 17 +- 30 files changed, 1330 insertions(+), 175 deletions(-) create mode 100644 source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc create mode 100644 source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h create mode 100644 source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc create mode 100644 source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h 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/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/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index dba2d1e1e67..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 diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index ee9bea7b2c6..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 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 modify_determined_resolution_fn_; + /** * \brief mutex reference for very special node initializations * \note only use when you really know what you are doing. @@ -517,6 +519,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 fn) + { + modify_determined_resolution_fn_ = fn; + } + /** * \brief get the render priority of this node. * \note only applicable for output operations like ViewerOperation 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(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(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 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 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 inputs) 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 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 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 inputs) +{ + MemoryBuffer *image = inputs[0]; + BuffersIterator 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 inputs) override; + void update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span 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 inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span 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 inputs) +{ + const MemoryBuffer *input = inputs[0]; + for (BuffersIterator 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 inputs) +{ + const MemoryBuffer *input = inputs[0]; + for (BuffersIterator 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 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 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 inputs) +{ + MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + const rcti &input_rect = input->get_rect(); + BuffersIterator it = output->iterate_with({input}, area); + + int min_input_coord = -1; + int max_input_coord = -1; + int elem_stride = -1; + std::function 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 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 inputs) +{ + MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + const rcti &input_rect = input->get_rect(); + BuffersIterator it = output->iterate_with({input}, area); + + int min_input_coord = -1; + int max_input_coord = -1; + int elem_stride = -1; + std::function 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 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 inputs) +{ + const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + BuffersIterator 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 inputs) +{ + const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX]; + MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX]; + for (BuffersIterator 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 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 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_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 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(max_size * p.scalar); + CLAMP(p.max_blur_scalar, 1, m_maxBlur); + + for (BuffersIterator 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 *clMemToCleanUp, std::list *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 inputs) override; }; +/* Currently unused. If ever used, it needs fullframe implementation. */ #ifdef COM_DEFOCUS_SEARCH class InverseSearchRadiusOperation : public NodeOperation { private: -- cgit v1.2.3 From 153b45037f5d27cf125d56ebb9aa77aca53d0981 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:30:46 +0200 Subject: Compositor: Full frame matte nodes Adds full frame implementation to Channel Key, Chroma Key, Color Key, Color Spill, Cryptomatte, Difference Key, Distance Key, Keying, Keying Screen and Luminance Key nodes. The other nodes in "Matte" sub-menu are submitted separately. No functional changes. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12220 --- .../operations/COM_ChannelMatteOperation.cc | 34 ++++++++ .../operations/COM_ChannelMatteOperation.h | 8 +- .../operations/COM_ChromaMatteOperation.cc | 55 +++++++++++++ .../operations/COM_ChromaMatteOperation.h | 8 +- .../operations/COM_ColorMatteOperation.cc | 37 +++++++++ .../operations/COM_ColorMatteOperation.h | 8 +- .../operations/COM_ColorSpillOperation.cc | 33 ++++++++ .../operations/COM_ColorSpillOperation.h | 8 +- .../operations/COM_CryptomatteOperation.cc | 30 +++++++ .../operations/COM_CryptomatteOperation.h | 8 +- .../operations/COM_DifferenceMatteOperation.cc | 41 ++++++++++ .../operations/COM_DifferenceMatteOperation.h | 8 +- .../operations/COM_DistanceRGBMatteOperation.cc | 42 +++++++++- .../operations/COM_DistanceRGBMatteOperation.h | 10 ++- .../operations/COM_DistanceYCCMatteOperation.cc | 2 +- .../operations/COM_DistanceYCCMatteOperation.h | 2 +- .../operations/COM_KeyingBlurOperation.cc | 63 ++++++++++++++ .../operations/COM_KeyingBlurOperation.h | 11 ++- .../operations/COM_KeyingClipOperation.cc | 85 +++++++++++++++++++ .../operations/COM_KeyingClipOperation.h | 11 ++- .../operations/COM_KeyingDespillOperation.cc | 29 +++++++ .../operations/COM_KeyingDespillOperation.h | 8 +- .../compositor/operations/COM_KeyingOperation.cc | 45 ++++++++++ .../compositor/operations/COM_KeyingOperation.h | 8 +- .../operations/COM_KeyingScreenOperation.cc | 95 ++++++++++++++++++---- .../operations/COM_KeyingScreenOperation.h | 12 ++- .../operations/COM_LuminanceMatteOperation.cc | 36 ++++++++ .../operations/COM_LuminanceMatteOperation.h | 8 +- 28 files changed, 701 insertions(+), 44 deletions(-) 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 inputs) +{ + for (BuffersIterator 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 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 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 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 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 inputs) +{ + const float hue = m_settings->t1; + const float sat = m_settings->t2; + const float val = m_settings->t3; + for (BuffersIterator 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 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 inputs) +{ + for (BuffersIterator 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 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 inputs) +{ + for (BuffersIterator 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 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 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 inputs) +{ + for (BuffersIterator 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 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 inputs) +{ + for (BuffersIterator 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 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_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 inputs) +{ + const MemoryBuffer *input = inputs[0]; + BuffersIterator it = output->iterate_with(inputs, area); + + int coord_max; + int elem_stride; + std::function 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 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 inputs) +{ + const MemoryBuffer *input = inputs[0]; + BuffersIterator 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 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 inputs) +{ + for (BuffersIterator 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 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 inputs) +{ + for (BuffersIterator 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 -#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 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 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 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 -#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 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 inputs) +{ + for (BuffersIterator 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 inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From 42f89b9212d31fbfdf387a29835b957d758e473e Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:30:56 +0200 Subject: Compositor: Fix incorrect copying of uchar buffers Row stride and the area x coordinate offset were not taken into account. --- .../blender/compositor/intern/COM_MemoryBuffer.cc | 33 +++++++++++++++++----- .../blender/compositor/intern/COM_MemoryBuffer.h | 2 ++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 16274d720cd..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); } diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index d3c7566d246..f3e15c2a495 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -583,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); -- cgit v1.2.3 From 4c6d207343bc43ce3a4568038397347f6bdf69c4 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:31:04 +0200 Subject: Compositor: Fix crash enabling buffer groups on full frame Full frame doesn't support this option as all operations are already buffered. UI option will be removed in the future. --- source/blender/compositor/intern/COM_NodeGraph.cc | 9 ++++++--- source/blender/compositor/intern/COM_NodeGraph.h | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) 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, -- cgit v1.2.3 From 7d17f2addf80f7ee315b49e0c281562050ee0667 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 23 Aug 2021 15:31:21 +0200 Subject: Fix T89998: Cryptomatte node output values doubled with Multi-View When using a Cryptomatte node and selecting 2 views in Multi-View, its output values are doubled. When selecting 3 tripled and so on. This causes incorrect compositing results for all the views. The node creates an input operation for each rendered cryptomatte pass. In Multi-View, passes are rendered for each view but compositor is executed per view and should only create operations for those corresponding to the current view being executed. Otherwise duplicated operations add up later in cryptomatte operation. Reviewed By: jbakker Maniphest Tasks: T89998 Differential Revision: https://developer.blender.org/D12216 --- source/blender/compositor/intern/COM_CompositorContext.h | 6 ++++++ source/blender/compositor/nodes/COM_CryptomatteNode.cc | 4 ++++ 2 files changed, 10 insertions(+) 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/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( -- cgit v1.2.3 From ebdae7573618637c60e836bbc00618f65a7a7c4f Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 23 Aug 2021 09:52:13 -0300 Subject: Cleanup: Move 'tris_per_mat' member out of 'MeshBufferCache' `MeshBufferCache` is a struct representing a list of buffers. As such, `GPUIndexBuf **tris_per_mat` is out of place as it does not represent one of the buffers in the list. In fact this member should be close to `GPUBatch **surface_per_mat` as they are related. The code for dependencies between buffer and batch had to be reworked as it relies on the member's position. Differential Revision: https://developer.blender.org/D12227 --- source/blender/draw/intern/draw_cache_extract.h | 5 +- source/blender/draw/intern/draw_cache_impl_mesh.c | 301 +++++++++++---------- .../mesh_extractors/extract_mesh_ibo_tris.cc | 18 +- 3 files changed, 165 insertions(+), 159 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 7dc468d1a73..a6f5e57e1ba 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -152,8 +152,6 @@ 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; /** @@ -226,6 +224,9 @@ typedef struct MeshBatchCache { 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 */ diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 52b76733b78..727cb6826a8 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(MeshBufferCache, buff_name) - offsetof(MeshBufferCache, vbo)) / sizeof(void *)) +#define BUFFER_LEN (sizeof(MeshBufferCache) / 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,100 @@ # 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(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_FLAG, + [BUFFER_INDEX(vbo.lnor)] = BATCH_FLAG(batch.surface, batch.edit_lnor, batch.wire_loops) | + SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.edge_fac)] = BATCH_FLAG(batch.wire_edges), + [BUFFER_INDEX(vbo.weights)] = BATCH_FLAG(batch.surface_weights), + [BUFFER_INDEX(vbo.uv)] = BATCH_FLAG(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_FLAG, + [BUFFER_INDEX(vbo.tan)] = SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.vcol)] = BATCH_FLAG(batch.surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.sculpt_data)] = BATCH_FLAG(batch.sculpt_overlays), + [BUFFER_INDEX(vbo.orco)] = SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.edit_data)] = BATCH_FLAG( + batch.edit_triangles, batch.edit_edges, batch.edit_vertices), + [BUFFER_INDEX(vbo.edituv_data)] = BATCH_FLAG(batch.edituv_faces, + batch.edituv_faces_stretch_area, + batch.edituv_faces_stretch_angle, + batch.edituv_edges, + batch.edituv_verts), + [BUFFER_INDEX(vbo.edituv_stretch_area)] = BATCH_FLAG(batch.edituv_faces_stretch_area), + [BUFFER_INDEX(vbo.edituv_stretch_angle)] = BATCH_FLAG(batch.edituv_faces_stretch_angle), + [BUFFER_INDEX(vbo.mesh_analysis)] = BATCH_FLAG(batch.edit_mesh_analysis), + [BUFFER_INDEX(vbo.fdots_pos)] = BATCH_FLAG(batch.edit_fdots, batch.edit_selection_fdots), + [BUFFER_INDEX(vbo.fdots_nor)] = BATCH_FLAG(batch.edit_fdots), + [BUFFER_INDEX(vbo.fdots_uv)] = BATCH_FLAG(batch.edituv_fdots), + [BUFFER_INDEX(vbo.fdots_edituv_data)] = BATCH_FLAG(batch.edituv_fdots), + [BUFFER_INDEX(vbo.skin_roots)] = BATCH_FLAG(batch.edit_skin_roots), + [BUFFER_INDEX(vbo.vert_idx)] = BATCH_FLAG(batch.edit_selection_verts), + [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(batch.edit_selection_edges), + [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(batch.edit_selection_faces), + [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(batch.edit_selection_fdots), + + [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(batch.surface, + batch.surface_weights, + batch.edit_triangles, + batch.edit_lnor, + batch.edit_mesh_analysis, + batch.edit_selection_faces, + batch.sculpt_overlays), + [BUFFER_INDEX(ibo.lines)] = BATCH_FLAG( + batch.edit_edges, batch.edit_selection_edges, batch.all_edges, batch.wire_edges), + [BUFFER_INDEX(ibo.lines_loose)] = BATCH_FLAG(batch.loose_edges), + [BUFFER_INDEX(ibo.points)] = BATCH_FLAG( + batch.edit_vnor, batch.edit_vertices, batch.edit_selection_verts), + [BUFFER_INDEX(ibo.fdots)] = BATCH_FLAG(batch.edit_fdots, batch.edit_selection_fdots), + [BUFFER_INDEX(ibo.lines_paint_mask)] = BATCH_FLAG(batch.wire_loops), + [BUFFER_INDEX(ibo.lines_adjacency)] = BATCH_FLAG(batch.edge_detection), + [BUFFER_INDEX(ibo.edituv_tris)] = BATCH_FLAG( + batch.edituv_faces, batch.edituv_faces_stretch_area, batch.edituv_faces_stretch_angle), + [BUFFER_INDEX(ibo.edituv_lines)] = BATCH_FLAG(batch.edituv_edges, batch.wire_loops_uvs), + [BUFFER_INDEX(ibo.edituv_points)] = BATCH_FLAG(batch.edituv_verts), + [BUFFER_INDEX(ibo.edituv_fdots)] = BATCH_FLAG(batch.edituv_fdots), + [TRIS_PER_MAT_INDEX] = SURFACE_PER_MAT_FLAG, }; #ifndef NDEBUG @@ -231,7 +235,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 +665,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; @@ -708,6 +711,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); @@ -725,7 +731,7 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol); GPU_VERTBUF_DISCARD_SAFE(mbufcache->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); } @@ -744,16 +750,16 @@ static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache) 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); + 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; @@ -776,12 +782,12 @@ static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache) 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); + 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); } @@ -798,7 +804,7 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edit_data); GPU_VERTBUF_DISCARD_SAFE(mbufcache->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. */ @@ -812,7 +818,7 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor); GPU_VERTBUF_DISCARD_SAFE(mbufcache->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: @@ -830,7 +836,7 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data); GPU_VERTBUF_DISCARD_SAFE(mbufcache->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: @@ -877,9 +883,9 @@ static void mesh_batch_cache_clear(Mesh *me) 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; @@ -1579,11 +1585,12 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } /* 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); @@ -1762,7 +1769,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; 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..264cef16223 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; } } @@ -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); } } } -- cgit v1.2.3 From 6e51ef953168743a1023712b4180e7f97ea8d1c5 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 23 Aug 2021 12:33:12 -0300 Subject: Cleanup: rename 'MeshBufferCache' to 'MeshBufferList' `MeshBufferList` is more specific and can avoid confusion with `MeshBufferExtractionCache`. --- source/blender/draw/intern/draw_cache_extract.h | 25 ++++++------ .../blender/draw/intern/draw_cache_extract_mesh.cc | 46 ++++++++++++---------- source/blender/draw/intern/draw_cache_impl_mesh.c | 20 +++++----- .../draw/intern/mesh_extractors/extract_mesh.c | 6 +-- .../draw/intern/mesh_extractors/extract_mesh.h | 4 +- .../mesh_extractors/extract_mesh_ibo_edituv.cc | 8 ++-- .../mesh_extractors/extract_mesh_ibo_fdots.cc | 2 +- .../mesh_extractors/extract_mesh_ibo_lines.cc | 6 +-- .../extract_mesh_ibo_lines_adjacency.cc | 2 +- .../extract_mesh_ibo_lines_paint_mask.cc | 2 +- .../mesh_extractors/extract_mesh_ibo_points.cc | 2 +- .../mesh_extractors/extract_mesh_ibo_tris.cc | 4 +- .../mesh_extractors/extract_mesh_vbo_edge_fac.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_edit_data.cc | 2 +- .../extract_mesh_vbo_edituv_data.cc | 2 +- .../extract_mesh_vbo_edituv_stretch_angle.cc | 2 +- .../extract_mesh_vbo_edituv_stretch_area.cc | 2 +- .../extract_mesh_vbo_fdots_edituv_data.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_fdots_nor.cc | 4 +- .../mesh_extractors/extract_mesh_vbo_fdots_pos.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_fdots_uv.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_lnor.cc | 4 +- .../extract_mesh_vbo_mesh_analysis.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_orco.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_pos_nor.cc | 4 +- .../extract_mesh_vbo_sculpt_data.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_select_idx.cc | 8 ++-- .../mesh_extractors/extract_mesh_vbo_skin_roots.cc | 2 +- .../intern/mesh_extractors/extract_mesh_vbo_tan.cc | 4 +- .../intern/mesh_extractors/extract_mesh_vbo_uv.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_vcol.cc | 2 +- .../mesh_extractors/extract_mesh_vbo_weights.cc | 2 +- 32 files changed, 93 insertions(+), 88 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a6f5e57e1ba..246a2a6ee7e 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,7 +152,7 @@ typedef struct MeshBufferCache { GPUIndexBuf *edituv_points; GPUIndexBuf *edituv_fdots; } ibo; -} MeshBufferCache; +} MeshBufferList; /** * Data that are kept around between extractions to reduce rebuilding time. @@ -174,15 +174,16 @@ typedef struct MeshBufferExtractionCache { } poly_sorted; } MeshBufferExtractionCache; -#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \ - for (MeshBufferCache *mbc = &batch_cache->final; \ - mbc == &batch_cache->final || mbc == &batch_cache->cage || mbc == &batch_cache->uv_cage; \ - mbc = (mbc == &batch_cache->final) ? \ - &batch_cache->cage : \ - ((mbc == &batch_cache->cage) ? &batch_cache->uv_cage : NULL)) +#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbuflist) \ + for (MeshBufferList *mbuflist = &batch_cache->final; \ + mbuflist == &batch_cache->final || mbuflist == &batch_cache->cage || \ + mbuflist == &batch_cache->uv_cage; \ + mbuflist = (mbuflist == &batch_cache->final) ? \ + &batch_cache->cage : \ + ((mbuflist == &batch_cache->cage) ? &batch_cache->uv_cage : NULL)) typedef struct MeshBatchCache { - MeshBufferCache final, cage, uv_cage; + MeshBufferList final, cage, uv_cage; MeshBufferExtractionCache final_extraction_cache; MeshBufferExtractionCache cage_extraction_cache; @@ -261,8 +262,8 @@ typedef struct MeshBatchCache { } 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_VBO_LEN (sizeof(((MeshBufferList){0}).vbo) / sizeof(void *)) +#define MBC_IBO_LEN (sizeof(((MeshBufferList){0}).ibo) / sizeof(void *)) #define MBC_BATCH_INDEX(batch_name) \ ((offsetof(MeshBatchCache, batch_name) - offsetof(MeshBatchCache, batch)) / sizeof(void *)) @@ -307,7 +308,7 @@ BLI_STATIC_ASSERT(MBC_BATCH_INDEX(surface_per_mat) < 32, void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache *mbc, + MeshBufferList *mbuflist, MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 1bc2b8a9def..82ce4087ff8 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, @@ -561,7 +565,7 @@ 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, + MeshBufferList *mbuflist, MeshBufferExtractionCache *extraction_cache, Mesh *me, @@ -615,7 +619,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, #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 +651,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; } @@ -714,7 +718,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 +729,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 +742,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); } @@ -775,7 +779,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, extern "C" { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache *mbc, + MeshBufferList *mbuflist, MeshBufferExtractionCache *extraction_cache, Mesh *me, @@ -792,7 +796,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, { blender::draw::mesh_buffer_cache_create_requested(task_graph, cache, - mbc, + mbuflist, extraction_cache, me, is_editmode, diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 727cb6826a8..aa17464e13c 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -79,8 +79,8 @@ /* clang-format off */ -#define BUFFER_INDEX(buff_name) ((offsetof(MeshBufferCache, buff_name) - offsetof(MeshBufferCache, vbo)) / sizeof(void *)) -#define BUFFER_LEN (sizeof(MeshBufferCache) / sizeof(void *)) +#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) @@ -844,14 +844,14 @@ 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]); } } @@ -874,8 +874,8 @@ 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, mbuflist) { + mesh_buffer_list_clear(mbuflist); } mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache); @@ -1508,7 +1508,7 @@ 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 *mbufcache = &cache->final; /* Initialize batches and request VBO's & IBO's. */ MDEPS_ASSERT(batch.surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); 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..acf73ec9b1b 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; @@ -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..f148267f8f5 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; } @@ -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; } @@ -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 264cef16223..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 @@ -134,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; } @@ -221,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; } -- cgit v1.2.3 From eb0c50ac7866046f4c528a2a71874ee137aa9564 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 23 Aug 2021 12:35:43 -0300 Subject: Cleanup: rename 'MeshBufferExtractionCache' to 'MeshBufferCache' Matches the existing `MeshBatchCache`. --- source/blender/draw/intern/draw_cache_extract.h | 12 ++--- .../blender/draw/intern/draw_cache_extract_mesh.cc | 10 ++-- .../intern/draw_cache_extract_mesh_render_data.c | 53 ++++++++-------------- source/blender/draw/intern/draw_cache_impl_mesh.c | 22 ++++----- .../draw/intern/mesh_extractors/extract_mesh.h | 4 +- 5 files changed, 43 insertions(+), 58 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 246a2a6ee7e..0d730058ba4 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -159,7 +159,7 @@ typedef struct MeshBufferList { * * - Loose geometry. */ -typedef struct MeshBufferExtractionCache { +typedef struct MeshBufferCache { struct { int edge_len; int vert_len; @@ -172,7 +172,7 @@ typedef struct MeshBufferExtractionCache { int *mat_tri_len; int visible_tri_len; } poly_sorted; -} MeshBufferExtractionCache; +} MeshBufferCache; #define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbuflist) \ for (MeshBufferList *mbuflist = &batch_cache->final; \ @@ -185,9 +185,9 @@ typedef struct MeshBufferExtractionCache { typedef struct MeshBatchCache { MeshBufferList final, cage, uv_cage; - MeshBufferExtractionCache final_extraction_cache; - MeshBufferExtractionCache cage_extraction_cache; - MeshBufferExtractionCache uv_cage_extraction_cache; + MeshBufferCache final_extraction_cache; + MeshBufferCache cage_extraction_cache; + MeshBufferCache uv_cage_extraction_cache; struct { /* Surfaces / Render */ @@ -309,7 +309,7 @@ BLI_STATIC_ASSERT(MBC_BATCH_INDEX(surface_per_mat) < 32, void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferList *mbuflist, - MeshBufferExtractionCache *extraction_cache, + 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 82ce4087ff8..5bdcf615e22 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -497,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) @@ -542,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) { @@ -566,7 +566,7 @@ 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, MeshBufferList *mbuflist, - MeshBufferExtractionCache *extraction_cache, + MeshBufferCache *extraction_cache, Mesh *me, const bool is_editmode, @@ -780,7 +780,7 @@ extern "C" { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferList *mbuflist, - MeshBufferExtractionCache *extraction_cache, + MeshBufferCache *extraction_cache, Mesh *me, const bool is_editmode, 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 aa17464e13c..d3a1b09ba9d 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -856,16 +856,16 @@ static void mesh_buffer_list_clear(MeshBufferList *mbuflist) } } -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; + 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(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->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) @@ -878,9 +878,9 @@ static void mesh_batch_cache_clear(Mesh *me) mesh_buffer_list_clear(mbuflist); } - 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); + mesh_buffer_cache_clear(&cache->final_extraction_cache); + mesh_buffer_cache_clear(&cache->cage_extraction_cache); + mesh_buffer_cache_clear(&cache->uv_cage_extraction_cache); for (int i = 0; i < cache->mat_len; i++) { GPU_INDEXBUF_DISCARD_SAFE(cache->tris_per_mat[i]); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index acf73ec9b1b..d9f397fd8b8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -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, -- cgit v1.2.3 From cbe4036406ed21d5659b266dddbbe03a1a945721 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 23 Aug 2021 12:46:14 -0300 Subject: Cleanup: Isolate the batch list struct into a struct called MeshBatchList This allows for a simplification of macros and combines with `MeshBufferList`. --- source/blender/draw/intern/draw_cache_extract.h | 156 +++++++++--------- source/blender/draw/intern/draw_cache_impl_mesh.c | 191 +++++++++++----------- 2 files changed, 172 insertions(+), 175 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 0d730058ba4..855fef952d8 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -154,6 +154,81 @@ typedef struct MeshBufferList { } ibo; } 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. * @@ -189,49 +264,15 @@ typedef struct MeshBatchCache { MeshBufferCache cage_extraction_cache; MeshBufferCache uv_cage_extraction_cache; - 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; + MeshBatchList 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; @@ -261,47 +302,6 @@ typedef struct MeshBatchCache { bool no_loose_wire; } MeshBatchCache; -#define MBC_BATCH_LEN (sizeof(((MeshBatchCache){0}).batch) / 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_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) diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index d3a1b09ba9d..6cf6e3bd0d8 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -140,81 +140,78 @@ #define SURFACE_PER_MAT_FLAG (1u << MBC_BATCH_LEN) static const DRWBatchFlag g_buffer_deps[] = { - [BUFFER_INDEX(vbo.pos_nor)] = BATCH_FLAG(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) | + [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(batch.surface, batch.edit_lnor, batch.wire_loops) | - SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.edge_fac)] = BATCH_FLAG(batch.wire_edges), - [BUFFER_INDEX(vbo.weights)] = BATCH_FLAG(batch.surface_weights), - [BUFFER_INDEX(vbo.uv)] = BATCH_FLAG(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) | + [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(batch.surface) | SURFACE_PER_MAT_FLAG, - [BUFFER_INDEX(vbo.sculpt_data)] = BATCH_FLAG(batch.sculpt_overlays), + [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( - batch.edit_triangles, batch.edit_edges, batch.edit_vertices), - [BUFFER_INDEX(vbo.edituv_data)] = BATCH_FLAG(batch.edituv_faces, - batch.edituv_faces_stretch_area, - batch.edituv_faces_stretch_angle, - batch.edituv_edges, - batch.edituv_verts), - [BUFFER_INDEX(vbo.edituv_stretch_area)] = BATCH_FLAG(batch.edituv_faces_stretch_area), - [BUFFER_INDEX(vbo.edituv_stretch_angle)] = BATCH_FLAG(batch.edituv_faces_stretch_angle), - [BUFFER_INDEX(vbo.mesh_analysis)] = BATCH_FLAG(batch.edit_mesh_analysis), - [BUFFER_INDEX(vbo.fdots_pos)] = BATCH_FLAG(batch.edit_fdots, batch.edit_selection_fdots), - [BUFFER_INDEX(vbo.fdots_nor)] = BATCH_FLAG(batch.edit_fdots), - [BUFFER_INDEX(vbo.fdots_uv)] = BATCH_FLAG(batch.edituv_fdots), - [BUFFER_INDEX(vbo.fdots_edituv_data)] = BATCH_FLAG(batch.edituv_fdots), - [BUFFER_INDEX(vbo.skin_roots)] = BATCH_FLAG(batch.edit_skin_roots), - [BUFFER_INDEX(vbo.vert_idx)] = BATCH_FLAG(batch.edit_selection_verts), - [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(batch.edit_selection_edges), - [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(batch.edit_selection_faces), - [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(batch.edit_selection_fdots), - - [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(batch.surface, - batch.surface_weights, - batch.edit_triangles, - batch.edit_lnor, - batch.edit_mesh_analysis, - batch.edit_selection_faces, - batch.sculpt_overlays), + [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( - batch.edit_edges, batch.edit_selection_edges, batch.all_edges, batch.wire_edges), - [BUFFER_INDEX(ibo.lines_loose)] = BATCH_FLAG(batch.loose_edges), - [BUFFER_INDEX(ibo.points)] = BATCH_FLAG( - batch.edit_vnor, batch.edit_vertices, batch.edit_selection_verts), - [BUFFER_INDEX(ibo.fdots)] = BATCH_FLAG(batch.edit_fdots, batch.edit_selection_fdots), - [BUFFER_INDEX(ibo.lines_paint_mask)] = BATCH_FLAG(batch.wire_loops), - [BUFFER_INDEX(ibo.lines_adjacency)] = BATCH_FLAG(batch.edge_detection), + 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( - batch.edituv_faces, batch.edituv_faces_stretch_area, batch.edituv_faces_stretch_angle), - [BUFFER_INDEX(ibo.edituv_lines)] = BATCH_FLAG(batch.edituv_edges, batch.wire_loops_uvs), - [BUFFER_INDEX(ibo.edituv_points)] = BATCH_FLAG(batch.edituv_verts), - [BUFFER_INDEX(ibo.edituv_fdots)] = BATCH_FLAG(batch.edituv_fdots), + 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, }; @@ -1511,7 +1508,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MeshBufferList *mbufcache = &cache->final; /* 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); /* Order matters. First ones override latest VBO's attributes. */ @@ -1524,52 +1521,52 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, DRW_vbo_request(cache->batch.surface, &mbufcache->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); } - 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); } - 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); } - 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); } - 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); } - 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); } - 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); /* 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); } - 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); } - 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); /* For paint overlay. Active layer should have been queried. */ @@ -1577,7 +1574,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, DRW_vbo_request(cache->batch.wire_loops_uvs, &mbufcache->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); @@ -1612,66 +1609,66 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mbufcache = (do_cage) ? &cache->cage : &cache->final; /* 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); } - 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); } - 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); } - 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); } - 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); } - 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); } - 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); } /* 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); } - 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); } - 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); } - 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); @@ -1686,13 +1683,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mbufcache = (do_uvcage) ? &cache->uv_cage : &cache->final; /* 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); } - MDEPS_ASSERT(batch.edituv_faces_stretch_area, + MDEPS_ASSERT(edituv_faces_stretch_area, ibo.edituv_tris, vbo.uv, vbo.edituv_data, @@ -1703,7 +1700,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, 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); } - MDEPS_ASSERT(batch.edituv_faces_stretch_angle, + MDEPS_ASSERT(edituv_faces_stretch_angle, ibo.edituv_tris, vbo.uv, vbo.edituv_data, @@ -1714,19 +1711,19 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, 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); } - 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); } - 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); } - 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); -- cgit v1.2.3 From be1891e895c012b36ca574a90b5d90fc1433152e Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 23 Aug 2021 13:28:55 -0300 Subject: Cleanup: move the buffer list to 'MeshBufferCache' The cache is used to fill the buffer list. --- source/blender/draw/intern/draw_cache_extract.h | 24 +- .../blender/draw/intern/draw_cache_extract_mesh.cc | 13 +- source/blender/draw/intern/draw_cache_impl_mesh.c | 305 ++++++++++----------- .../mesh_extractors/extract_mesh_ibo_lines.cc | 6 +- 4 files changed, 169 insertions(+), 179 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 855fef952d8..db96d6a774f 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -235,6 +235,8 @@ BLI_STATIC_ASSERT(MBC_BATCH_LEN < 32, "Number of batches exceeded the limit of b * - Loose geometry. */ typedef struct MeshBufferCache { + MeshBufferList buff; + struct { int edge_len; int vert_len; @@ -249,20 +251,15 @@ typedef struct MeshBufferCache { } poly_sorted; } MeshBufferCache; -#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbuflist) \ - for (MeshBufferList *mbuflist = &batch_cache->final; \ - mbuflist == &batch_cache->final || mbuflist == &batch_cache->cage || \ - mbuflist == &batch_cache->uv_cage; \ - mbuflist = (mbuflist == &batch_cache->final) ? \ - &batch_cache->cage : \ - ((mbuflist == &batch_cache->cage) ? &batch_cache->uv_cage : NULL)) +#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \ + for (MeshBufferCache *mbc = &batch_cache->final; \ + mbc == &batch_cache->final || mbc == &batch_cache->cage || mbc == &batch_cache->uv_cage; \ + mbc = (mbc == &batch_cache->final) ? \ + &batch_cache->cage : \ + ((mbc == &batch_cache->cage) ? &batch_cache->uv_cage : NULL)) typedef struct MeshBatchCache { - MeshBufferList final, cage, uv_cage; - - MeshBufferCache final_extraction_cache; - MeshBufferCache cage_extraction_cache; - MeshBufferCache uv_cage_extraction_cache; + MeshBufferCache final, cage, uv_cage; MeshBatchList batch; @@ -307,8 +304,7 @@ typedef struct MeshBatchCache { 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, - MeshBufferList *mbuflist, + MeshBatchCache *mbc, MeshBufferCache *extraction_cache, Mesh *me, const bool is_editmode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 5bdcf615e22..06c449fe590 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -565,8 +565,7 @@ 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, - MeshBufferList *mbuflist, - MeshBufferCache *extraction_cache, + MeshBufferCache *mbc, Mesh *me, const bool is_editmode, @@ -617,6 +616,8 @@ 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(mbuflist->type.name)) { \ @@ -705,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; @@ -779,8 +780,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, extern "C" { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferList *mbuflist, - MeshBufferCache *extraction_cache, + MeshBufferCache *mbc, Mesh *me, const bool is_editmode, @@ -796,8 +796,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, { blender::draw::mesh_buffer_cache_create_requested(task_graph, cache, - mbuflist, - extraction_cache, + mbc, me, is_editmode, is_paint_mode, diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 6cf6e3bd0d8..18664498d00 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -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); @@ -722,11 +722,11 @@ 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 = BATCH_MAP(vbo.uv, vbo.tan, vbo.vcol, vbo.orco); mesh_batch_cache_discard_batch(cache, batch_map); @@ -735,17 +735,17 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache) 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); + 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, @@ -771,13 +771,13 @@ 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); + 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, @@ -797,9 +797,9 @@ 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 = BATCH_MAP(vbo.edit_data, vbo.fdots_nor); mesh_batch_cache_discard_batch(cache, batch_map); @@ -810,10 +810,10 @@ 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 = BATCH_MAP(ibo.lines_paint_mask, vbo.pos_nor, vbo.lnor); mesh_batch_cache_discard_batch(cache, batch_map); @@ -829,9 +829,9 @@ 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 = BATCH_MAP(vbo.edituv_data, vbo.fdots_edituv_data); mesh_batch_cache_discard_batch(cache, batch_map); @@ -855,6 +855,8 @@ static void mesh_buffer_list_clear(MeshBufferList *mbuflist) static void mesh_buffer_cache_clear(MeshBufferCache *mbc) { + mesh_buffer_list_clear(&mbc->buff); + MEM_SAFE_FREE(mbc->loose_geom.verts); MEM_SAFE_FREE(mbc->loose_geom.edges); mbc->loose_geom.edge_len = 0; @@ -871,14 +873,10 @@ static void mesh_batch_cache_clear(Mesh *me) if (!cache) { return; } - FOREACH_MESH_BUFFER_CACHE (cache, mbuflist) { - mesh_buffer_list_clear(mbuflist); + FOREACH_MESH_BUFFER_CACHE (cache, mbc) { + mesh_buffer_cache_clear(mbc); } - mesh_buffer_cache_clear(&cache->final_extraction_cache); - mesh_buffer_cache_clear(&cache->cage_extraction_cache); - mesh_buffer_cache_clear(&cache->uv_cage_extraction_cache); - for (int i = 0; i < cache->mat_len; i++) { GPU_INDEXBUF_DISCARD_SAFE(cache->tris_per_mat[i]); } @@ -1086,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; } /** \} */ @@ -1319,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 @@ -1417,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 @@ -1457,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. */ @@ -1505,80 +1503,80 @@ 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; - MeshBufferList *mbufcache = &cache->final; + MeshBufferList *mbuflist = &cache->final.buff; /* Initialize batches and request VBO's & IBO's. */ 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(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(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(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(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(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(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(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(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(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(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 */ @@ -1589,90 +1587,90 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { 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(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(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(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(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(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(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(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(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(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(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(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); } /** @@ -1680,14 +1678,14 @@ 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(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(edituv_faces_stretch_area, ibo.edituv_tris, @@ -1695,10 +1693,10 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, 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(edituv_faces_stretch_angle, ibo.edituv_tris, @@ -1706,28 +1704,28 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, 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(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(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(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); @@ -1775,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, @@ -1793,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, @@ -1810,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_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index f148267f8f5..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 @@ -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); } @@ -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); } -- cgit v1.2.3