Functions: improve handling of unused multi-function outputs
Previously, `ParamsBuilder` lazily allocated an array for an output when it was unused, but the called multi-function wanted to access it. Now, whether the multi-function supports an output to be unused is part of the signature. This way, the allocation can happen earlier when the parameters are build. The benefit is that this makes all methods of `MFParams` thread-safe again, removing the need for a mutex.
This commit is contained in:
parent
aea26830dc
commit
8625495b1c
|
@ -32,9 +32,6 @@ class ParamsBuilder {
|
|||
Vector<std::variant<GVArray, GMutableSpan, const GVVectorArray *, GVectorArray *>>
|
||||
actual_params_;
|
||||
|
||||
std::mutex mutex_;
|
||||
Vector<std::pair<int, GMutableSpan>> dummy_output_spans_;
|
||||
|
||||
friend class MFParams;
|
||||
|
||||
ParamsBuilder(const Signature &signature, const IndexMask mask)
|
||||
|
@ -127,9 +124,15 @@ class ParamsBuilder {
|
|||
const ParamType ¶m_type = signature_->params[param_index].type;
|
||||
BLI_assert(param_type.category() == ParamCategory::SingleOutput);
|
||||
const CPPType &type = param_type.data_type().single_type();
|
||||
/* An empty span indicates that this is ignored. */
|
||||
const GMutableSpan dummy_span{type};
|
||||
actual_params_.append_unchecked_as(std::in_place_type<GMutableSpan>, dummy_span);
|
||||
|
||||
if (bool(signature_->params[param_index].flag & ParamFlag::SupportsUnusedOutput)) {
|
||||
/* An empty span indicates that this is ignored. */
|
||||
const GMutableSpan dummy_span{type};
|
||||
actual_params_.append_unchecked_as(std::in_place_type<GMutableSpan>, dummy_span);
|
||||
}
|
||||
else {
|
||||
this->add_unused_output_for_unsupporting_function(type);
|
||||
}
|
||||
}
|
||||
|
||||
void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "")
|
||||
|
@ -210,6 +213,8 @@ class ParamsBuilder {
|
|||
{
|
||||
return actual_params_.size();
|
||||
}
|
||||
|
||||
void add_unused_output_for_unsupporting_function(const CPPType &type);
|
||||
};
|
||||
|
||||
class MFParams {
|
||||
|
@ -252,13 +257,11 @@ class MFParams {
|
|||
GMutableSpan uninitialized_single_output(int param_index, StringRef name = "")
|
||||
{
|
||||
this->assert_correct_param(param_index, name, ParamCategory::SingleOutput);
|
||||
BLI_assert(
|
||||
!bool(builder_->signature_->params[param_index].flag & ParamFlag::SupportsUnusedOutput));
|
||||
GMutableSpan span = std::get<GMutableSpan>(builder_->actual_params_[param_index]);
|
||||
if (!span.is_empty()) {
|
||||
return span;
|
||||
}
|
||||
/* The output is ignored by the caller, but the multi-function does not handle this case. So
|
||||
* create a temporary buffer that the multi-function can write to. */
|
||||
return this->ensure_dummy_single_output(param_index);
|
||||
BLI_assert(span.size() >= builder_->min_array_size_);
|
||||
return span;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,6 +276,8 @@ class MFParams {
|
|||
GMutableSpan uninitialized_single_output_if_required(int param_index, StringRef name = "")
|
||||
{
|
||||
this->assert_correct_param(param_index, name, ParamCategory::SingleOutput);
|
||||
BLI_assert(
|
||||
bool(builder_->signature_->params[param_index].flag & ParamFlag::SupportsUnusedOutput));
|
||||
return std::get<GMutableSpan>(builder_->actual_params_[param_index]);
|
||||
}
|
||||
|
||||
|
@ -342,8 +347,6 @@ class MFParams {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
GMutableSpan ensure_dummy_single_output(int param_index);
|
||||
};
|
||||
|
||||
} // namespace blender::fn::multi_function
|
||||
|
|
|
@ -15,10 +15,22 @@
|
|||
|
||||
namespace blender::fn::multi_function {
|
||||
|
||||
enum class ParamFlag {
|
||||
None = 0,
|
||||
/**
|
||||
* If set, the multi-function parameter can be accessed using
|
||||
* #MFParams::uninitialized_single_output_if_required which can result in better performance
|
||||
* because the output does not have to be computed when it is not needed.
|
||||
*/
|
||||
SupportsUnusedOutput = 1 << 0,
|
||||
};
|
||||
ENUM_OPERATORS(ParamFlag, ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
struct Signature {
|
||||
struct ParamInfo {
|
||||
ParamType type;
|
||||
const char *name;
|
||||
ParamFlag flag = ParamFlag::None;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -68,25 +80,27 @@ class SignatureBuilder {
|
|||
|
||||
/* Output Parameter Types */
|
||||
|
||||
template<typename T> void single_output(const char *name)
|
||||
template<typename T> void single_output(const char *name, const ParamFlag flag = ParamFlag::None)
|
||||
{
|
||||
this->single_output(name, CPPType::get<T>());
|
||||
this->single_output(name, CPPType::get<T>(), flag);
|
||||
}
|
||||
void single_output(const char *name, const CPPType &type)
|
||||
void single_output(const char *name, const CPPType &type, const ParamFlag flag = ParamFlag::None)
|
||||
{
|
||||
this->output(name, DataType::ForSingle(type));
|
||||
this->output(name, DataType::ForSingle(type), flag);
|
||||
}
|
||||
template<typename T> void vector_output(const char *name)
|
||||
template<typename T> void vector_output(const char *name, const ParamFlag flag = ParamFlag::None)
|
||||
{
|
||||
this->vector_output(name, CPPType::get<T>());
|
||||
this->vector_output(name, CPPType::get<T>(), flag);
|
||||
}
|
||||
void vector_output(const char *name, const CPPType &base_type)
|
||||
void vector_output(const char *name,
|
||||
const CPPType &base_type,
|
||||
const ParamFlag flag = ParamFlag::None)
|
||||
{
|
||||
this->output(name, DataType::ForVector(base_type));
|
||||
this->output(name, DataType::ForVector(base_type), flag);
|
||||
}
|
||||
void output(const char *name, DataType data_type)
|
||||
void output(const char *name, DataType data_type, const ParamFlag flag = ParamFlag::None)
|
||||
{
|
||||
signature_.params.append({ParamType(ParamType::Output, data_type), name});
|
||||
signature_.params.append({ParamType(ParamType::Output, data_type), name, flag});
|
||||
}
|
||||
|
||||
/* Mutable Parameter Types */
|
||||
|
|
|
@ -108,11 +108,18 @@ void MultiFunction::call_auto(IndexMask mask, MFParams params, Context context)
|
|||
break;
|
||||
}
|
||||
case ParamCategory::SingleOutput: {
|
||||
const GMutableSpan span = params.uninitialized_single_output_if_required(param_index);
|
||||
if (span.is_empty()) {
|
||||
offset_params.add_ignored_single_output();
|
||||
if (bool(signature_ref_->params[param_index].flag & ParamFlag::SupportsUnusedOutput)) {
|
||||
const GMutableSpan span = params.uninitialized_single_output_if_required(param_index);
|
||||
if (span.is_empty()) {
|
||||
offset_params.add_ignored_single_output();
|
||||
}
|
||||
else {
|
||||
const GMutableSpan sliced_span = span.slice(input_slice_range);
|
||||
offset_params.add_uninitialized_single_output(sliced_span);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const GMutableSpan span = params.uninitialized_single_output(param_index);
|
||||
const GMutableSpan sliced_span = span.slice(input_slice_range);
|
||||
offset_params.add_uninitialized_single_output(sliced_span);
|
||||
}
|
||||
|
|
|
@ -4,27 +4,16 @@
|
|||
|
||||
namespace blender::fn::multi_function {
|
||||
|
||||
GMutableSpan MFParams::ensure_dummy_single_output(int param_index)
|
||||
void ParamsBuilder::add_unused_output_for_unsupporting_function(const CPPType &type)
|
||||
{
|
||||
/* Lock because we are actually modifying #builder_ and it may be used by multiple threads. */
|
||||
std::lock_guard lock{builder_->mutex_};
|
||||
|
||||
for (const std::pair<int, GMutableSpan> &items : builder_->dummy_output_spans_) {
|
||||
if (items.first == param_index) {
|
||||
return items.second;
|
||||
}
|
||||
}
|
||||
|
||||
const CPPType &type = std::get_if<GMutableSpan>(&builder_->actual_params_[param_index])->type();
|
||||
void *buffer = builder_->scope_.linear_allocator().allocate(
|
||||
builder_->min_array_size_ * type.size(), type.alignment());
|
||||
void *buffer = scope_.linear_allocator().allocate(type.size() * min_array_size_,
|
||||
type.alignment());
|
||||
const GMutableSpan span{type, buffer, min_array_size_};
|
||||
actual_params_.append_unchecked_as(std::in_place_type<GMutableSpan>, span);
|
||||
if (!type.is_trivially_destructible()) {
|
||||
builder_->scope_.add_destruct_call(
|
||||
[&type, buffer, mask = builder_->mask_]() { type.destruct_indices(buffer, mask); });
|
||||
scope_.add_destruct_call(
|
||||
[&type, buffer, mask = mask_]() { type.destruct_indices(buffer, mask); });
|
||||
}
|
||||
const GMutableSpan span{type, buffer, builder_->min_array_size_};
|
||||
builder_->dummy_output_spans_.append({param_index, span});
|
||||
return span;
|
||||
}
|
||||
|
||||
} // namespace blender::fn::multi_function
|
||||
|
|
|
@ -45,10 +45,10 @@ class SeparateRGBAFunction : public mf::MultiFunction {
|
|||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Separate Color", signature};
|
||||
builder.single_input<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Red");
|
||||
builder.single_output<float>("Green");
|
||||
builder.single_output<float>("Blue");
|
||||
builder.single_output<float>("Alpha");
|
||||
builder.single_output<float>("Red", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Green", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Blue", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Alpha", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
@ -107,7 +107,7 @@ class SeparateHSVAFunction : public mf::MultiFunction {
|
|||
builder.single_output<float>("Hue");
|
||||
builder.single_output<float>("Saturation");
|
||||
builder.single_output<float>("Value");
|
||||
builder.single_output<float>("Alpha");
|
||||
builder.single_output<float>("Alpha", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
@ -145,7 +145,7 @@ class SeparateHSLAFunction : public mf::MultiFunction {
|
|||
builder.single_output<float>("Hue");
|
||||
builder.single_output<float>("Saturation");
|
||||
builder.single_output<float>("Lightness");
|
||||
builder.single_output<float>("Alpha");
|
||||
builder.single_output<float>("Alpha", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
|
|
@ -261,10 +261,10 @@ class SampleCurveFunction : public mf::MultiFunction {
|
|||
mf::SignatureBuilder builder{"Sample Curve", signature_};
|
||||
builder.single_input<int>("Curve Index");
|
||||
builder.single_input<float>("Length");
|
||||
builder.single_output<float3>("Position");
|
||||
builder.single_output<float3>("Tangent");
|
||||
builder.single_output<float3>("Normal");
|
||||
builder.single_output("Value", src_field_.cpp_type());
|
||||
builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float3>("Tangent", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float3>("Normal", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output("Value", src_field_.cpp_type(), mf::ParamFlag::SupportsUnusedOutput);
|
||||
this->set_signature(&signature_);
|
||||
|
||||
this->evaluate_source();
|
||||
|
|
|
@ -69,7 +69,7 @@ class ImageFieldsFunction : public mf::MultiFunction {
|
|||
mf::SignatureBuilder builder{"ImageFunction", signature};
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Alpha");
|
||||
builder.single_output<float>("Alpha", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
|
|
@ -142,7 +142,7 @@ class ProximityFunction : public mf::MultiFunction {
|
|||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Geometry Proximity", signature};
|
||||
builder.single_input<float3>("Source Position");
|
||||
builder.single_output<float3>("Position");
|
||||
builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Distance");
|
||||
return signature;
|
||||
}();
|
||||
|
|
|
@ -230,12 +230,13 @@ class RaycastFunction : public mf::MultiFunction {
|
|||
builder.single_input<float3>("Source Position");
|
||||
builder.single_input<float3>("Ray Direction");
|
||||
builder.single_input<float>("Ray Length");
|
||||
builder.single_output<bool>("Is Hit");
|
||||
builder.single_output<bool>("Is Hit", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float3>("Hit Position");
|
||||
builder.single_output<float3>("Hit Normal");
|
||||
builder.single_output<float>("Distance");
|
||||
builder.single_output<float3>("Hit Normal", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
|
||||
if (target_data_) {
|
||||
builder.single_output("Attribute", target_data_->type());
|
||||
builder.single_output(
|
||||
"Attribute", target_data_->type(), mf::ParamFlag::SupportsUnusedOutput);
|
||||
}
|
||||
this->set_signature(&signature_);
|
||||
}
|
||||
|
@ -245,9 +246,8 @@ class RaycastFunction : public mf::MultiFunction {
|
|||
/* Hit positions are always necessary for retrieving the attribute from the target if that
|
||||
* output is required, so always retrieve a span from the evaluator in that case (it's
|
||||
* expected that the evaluator is more likely to have a spare buffer that could be used). */
|
||||
MutableSpan<float3> hit_positions =
|
||||
(target_data_) ? params.uninitialized_single_output<float3>(4, "Hit Position") :
|
||||
params.uninitialized_single_output_if_required<float3>(4, "Hit Position");
|
||||
MutableSpan<float3> hit_positions = params.uninitialized_single_output<float3>(4,
|
||||
"Hit Position");
|
||||
|
||||
Array<int> hit_indices;
|
||||
if (target_data_) {
|
||||
|
|
|
@ -143,7 +143,7 @@ class SampleNearestSurfaceFunction : public mf::MultiFunction {
|
|||
|
||||
mf::SignatureBuilder builder{"Sample Nearest Surface", signature_};
|
||||
builder.single_input<float3>("Position");
|
||||
builder.single_output("Value", src_field_.cpp_type());
|
||||
builder.single_output("Value", src_field_.cpp_type(), mf::ParamFlag::SupportsUnusedOutput);
|
||||
this->set_signature(&signature_);
|
||||
}
|
||||
|
||||
|
|
|
@ -200,9 +200,9 @@ class ReverseUVSampleFunction : public mf::MultiFunction {
|
|||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Sample UV Surface", signature};
|
||||
builder.single_input<float2>("Sample UV");
|
||||
builder.single_output<bool>("Is Valid");
|
||||
builder.single_output<int>("Triangle Index");
|
||||
builder.single_output<float3>("Barycentric Weights");
|
||||
builder.single_output<bool>("Is Valid", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<int>("Triangle Index", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float3>("Barycentric Weights", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
|
|
@ -35,9 +35,9 @@ class MF_SeparateXYZ : public mf::MultiFunction {
|
|||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"Separate XYZ", signature};
|
||||
builder.single_input<float3>("XYZ");
|
||||
builder.single_output<float>("X");
|
||||
builder.single_output<float>("Y");
|
||||
builder.single_output<float>("Z");
|
||||
builder.single_output<float>("X", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Y", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Z", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
|
|
@ -135,8 +135,8 @@ class BrickFunction : public mf::MultiFunction {
|
|||
builder.single_input<float>("Bias");
|
||||
builder.single_input<float>("Brick Width");
|
||||
builder.single_input<float>("Row Height");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Fac");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
|
|
@ -55,7 +55,7 @@ class NodeTexChecker : public mf::MultiFunction {
|
|||
builder.single_input<ColorGeometry4f>("Color1");
|
||||
builder.single_input<ColorGeometry4f>("Color2");
|
||||
builder.single_input<float>("Scale");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac");
|
||||
return signature;
|
||||
}();
|
||||
|
|
|
@ -58,7 +58,7 @@ class GradientFunction : public mf::MultiFunction {
|
|||
mf::Signature signature;
|
||||
mf::SignatureBuilder builder{"GradientFunction", signature};
|
||||
builder.single_input<float3>("Vector");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac");
|
||||
return signature;
|
||||
}();
|
||||
|
|
|
@ -62,7 +62,7 @@ class MagicFunction : public mf::MultiFunction {
|
|||
builder.single_input<float>("Scale");
|
||||
builder.single_input<float>("Distortion");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Fac");
|
||||
builder.single_output<float>("Fac", mf::ParamFlag::SupportsUnusedOutput);
|
||||
return signature;
|
||||
}();
|
||||
this->set_signature(&signature);
|
||||
|
|
|
@ -190,7 +190,7 @@ class MusgraveFunction : public mf::MultiFunction {
|
|||
builder.single_input<float>("Gain");
|
||||
}
|
||||
|
||||
builder.single_output<float>("Fac");
|
||||
builder.single_output<float>("Fac", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
|
|
@ -115,8 +115,8 @@ class NoiseFunction : public mf::MultiFunction {
|
|||
builder.single_input<float>("Roughness");
|
||||
builder.single_input<float>("Distortion");
|
||||
|
||||
builder.single_output<float>("Fac");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Fac", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
|
|
@ -224,14 +224,14 @@ class VoronoiMinowskiFunction : public mf::MultiFunction {
|
|||
}
|
||||
builder.single_input<float>("Exponent");
|
||||
builder.single_input<float>("Randomness");
|
||||
builder.single_output<float>("Distance");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
if (dimensions != 1) {
|
||||
builder.single_output<float3>("Position");
|
||||
builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_output<float>("W");
|
||||
builder.single_output<float>("W", mf::ParamFlag::SupportsUnusedOutput);
|
||||
}
|
||||
|
||||
return signature;
|
||||
|
@ -661,14 +661,14 @@ class VoronoiMetricFunction : public mf::MultiFunction {
|
|||
builder.single_input<float>("Smoothness");
|
||||
}
|
||||
builder.single_input<float>("Randomness");
|
||||
builder.single_output<float>("Distance");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
if (dimensions != 1) {
|
||||
builder.single_output<float3>("Position");
|
||||
builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
|
||||
}
|
||||
if (ELEM(dimensions, 1, 4)) {
|
||||
builder.single_output<float>("W");
|
||||
builder.single_output<float>("W", mf::ParamFlag::SupportsUnusedOutput);
|
||||
}
|
||||
|
||||
return signature;
|
||||
|
|
|
@ -104,7 +104,7 @@ class WaveFunction : public mf::MultiFunction {
|
|||
builder.single_input<float>("Detail Scale");
|
||||
builder.single_input<float>("Detail Roughness");
|
||||
builder.single_input<float>("Phase Offset");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<float>("Fac");
|
||||
return signature;
|
||||
}();
|
||||
|
|
|
@ -92,8 +92,8 @@ class WhiteNoiseFunction : public mf::MultiFunction {
|
|||
builder.single_input<float>("W");
|
||||
}
|
||||
|
||||
builder.single_output<float>("Value");
|
||||
builder.single_output<ColorGeometry4f>("Color");
|
||||
builder.single_output<float>("Value", mf::ParamFlag::SupportsUnusedOutput);
|
||||
builder.single_output<ColorGeometry4f>("Color", mf::ParamFlag::SupportsUnusedOutput);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue