Geometry Nodes: use lazy evaluation in repeat zone #112421

Merged
Jacques Lucke merged 26 commits from JacquesLucke/blender:lazy-repeat-zone into main 2023-09-22 08:58:26 +02:00
3 changed files with 90 additions and 83 deletions
Showing only changes of commit a6af4d6919 - Show all commits

View File

@ -3,10 +3,18 @@ if "%GIT%" == "" (
goto EOF
)
cd "%BLENDER_DIR%"
for /f "delims=" %%i in ('"%GIT%" rev-parse --abbrev-ref HEAD') do echo Branch_name=%%i
for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Branch_hash=%%i
cd "%BLENDER_DIR%/locale"
for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Locale_hash=%%i
cd "%BLENDER_DIR%/scripts/addons"
for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Addons_Hash=%%i
for /f "delims=" %%i in ('"%GIT%" rev-parse --abbrev-ref HEAD') do echo Addons_Branch_name=%%i
for /f "delims=" %%i in ('"%GIT%" rev-parse HEAD') do echo Addons_Branch_hash=%%i
if "%SVN%" == "" (
echo SVN not found, cannot library information.
goto EOF
)
set BUILD_VS_LIBDIR=%BLENDER_DIR%..\lib\win64_vc15
for /f "delims=" %%i in ('"%SVN%" info --show-item=url --no-newline %BUILD_VS_LIBDIR% ') do echo Libs_URL=%%i
for /f "delims=" %%i in ('"%SVN%" info --show-item=revision --no-newline %BUILD_VS_LIBDIR% ') do echo Libs_Revision=%%i
for /f "delims=" %%i in ('"%SVN%" info --show-item=last-changed-date --no-newline %BUILD_VS_LIBDIR% ') do echo Libs_LastChange=%%i
cd "%BLENDER_DIR%"
:EOF

View File

@ -35,8 +35,8 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
float y,
PixelSampler sampler)
{
float3 mean_of_color[] = {float3(0.0f), float3(0.0f), float3(0.0f), float3(0.0f)};
float3 mean_of_squared_color[] = {float3(0.0f), float3(0.0f), float3(0.0f), float3(0.0f)};
float4 mean_of_color[] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
float4 mean_of_squared_color[] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
int quadrant_pixel_count[] = {0, 0, 0, 0};
/* Split surroundings of pixel into 4 overlapping regions. */
@ -49,33 +49,32 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
float4 color;
image_reader_->read_sampled(color, xx, yy, sampler);
const float3 v = color.xyz();
if (dx >= 0 && dy >= 0) {
const int quadrant_index = 0;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
if (dx <= 0 && dy >= 0) {
const int quadrant_index = 1;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
if (dx <= 0 && dy <= 0) {
const int quadrant_index = 2;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
if (dx >= 0 && dy <= 0) {
const int quadrant_index = 3;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
}
@ -88,9 +87,9 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
for (int i = 0; i < 4; i++) {
mean_of_color[i] /= quadrant_pixel_count[i];
mean_of_squared_color[i] /= quadrant_pixel_count[i];
float3 color_variance = mean_of_squared_color[i] - mean_of_color[i] * mean_of_color[i];
float4 color_variance = mean_of_squared_color[i] - mean_of_color[i] * mean_of_color[i];
float variance = math::dot(color_variance, float3(1.0f));
float variance = math::dot(color_variance.xyz(), float3(1.0f));
if (variance < min_var) {
min_var = variance;
min_index = i;
@ -100,11 +99,7 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
output[0] = mean_of_color[min_index].x;
output[1] = mean_of_color[min_index].y;
output[2] = mean_of_color[min_index].z;
/* No changes for alpha channel. */
float tmp[4];
image_reader_->read_sampled(tmp, x, y, sampler);
output[3] = tmp[3];
output[3] = mean_of_color[min_index].w; /* Also apply filter to alpha channel. */
}
void KuwaharaClassicOperation::set_kernel_size(int kernel_size)
@ -127,8 +122,8 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
const int x = it.x;
const int y = it.y;
float3 mean_of_color[] = {float3(0.0f), float3(0.0f), float3(0.0f), float3(0.0f)};
float3 mean_of_squared_color[] = {float3(0.0f), float3(0.0f), float3(0.0f), float3(0.0f)};
float4 mean_of_color[] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
float4 mean_of_squared_color[] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
int quadrant_pixel_count[] = {0, 0, 0, 0};
/* Split surroundings of pixel into 4 overlapping regions. */
@ -141,33 +136,32 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
float4 color;
image->read_elem(xx, yy, &color.x);
const float3 v = color.xyz();
if (dx >= 0 && dy >= 0) {
const int quadrant_index = 0;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
if (dx <= 0 && dy >= 0) {
const int quadrant_index = 1;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
if (dx <= 0 && dy <= 0) {
const int quadrant_index = 2;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
if (dx >= 0 && dy <= 0) {
const int quadrant_index = 3;
mean_of_color[quadrant_index] += v;
mean_of_squared_color[quadrant_index] += v * v;
mean_of_color[quadrant_index] += color;
mean_of_squared_color[quadrant_index] += color * color;
quadrant_pixel_count[quadrant_index]++;
}
}
@ -180,9 +174,9 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
for (int i = 0; i < 4; i++) {
mean_of_color[i] /= quadrant_pixel_count[i];
mean_of_squared_color[i] /= quadrant_pixel_count[i];
float3 color_variance = mean_of_squared_color[i] - mean_of_color[i] * mean_of_color[i];
float4 color_variance = mean_of_squared_color[i] - mean_of_color[i] * mean_of_color[i];
float variance = math::dot(color_variance, float3(1.0f));
float variance = math::dot(color_variance.xyz(), float3(1.0f));
if (variance < min_var) {
min_var = variance;
min_index = i;
@ -192,9 +186,7 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
it.out[0] = mean_of_color[min_index].x;
it.out[1] = mean_of_color[min_index].y;
it.out[2] = mean_of_color[min_index].z;
/* No changes for alpha channel. */
it.out[3] = image->get_value(x, y, 3);
it.out[3] = mean_of_color[min_index].w; /* Also apply filter to alpha channel. */
}
}

View File

@ -1474,10 +1474,13 @@ struct ZoneBuildInfo {
};
/**
* Describes what the individual inputs and outputs of the #LazyFunction mean that's created for
* the repeat body.
* Contains the lazy-function for the "body" of a zone. It contains all the nodes inside of the
* zone. The "body" function is wrapped by another lazy-function which represents the zone as a
* hole. The wrapper function might invoke the zone body multiple times (like for repeat zones).
*/
struct RepeatBodyIndices {
struct ZoneBodyFunction {
const LazyFunction *function = nullptr;
IndexRange main_inputs;
IndexRange main_outputs;
IndexRange border_link_inputs;
@ -1651,19 +1654,16 @@ class LazyFunctionForRepeatZone : public LazyFunction {
const bNodeTreeZone &zone_;
const bNode &repeat_output_bnode_;
const ZoneBuildInfo &zone_info_;
const LazyFunction &body_fn_;
const RepeatBodyIndices &body_indices_;
const ZoneBodyFunction &body_fn_;
public:
LazyFunctionForRepeatZone(const bNodeTreeZone &zone,
ZoneBuildInfo &zone_info,
const LazyFunction &body_fn,
const RepeatBodyIndices &body_indices)
const ZoneBodyFunction &body_fn)
: zone_(zone),
repeat_output_bnode_(*zone.output_node),
zone_info_(zone_info),
body_fn_(body_fn),
body_indices_(body_indices)
body_fn_(body_fn)
{
debug_name_ = "Repeat Zone";
@ -1702,12 +1702,12 @@ class LazyFunctionForRepeatZone : public LazyFunction {
zone_info.border_link_input_usage_indices = outputs_.index_range().take_back(
zone.border_links.size());
for (const auto item : body_indices.attribute_set_input_by_field_source_index.items()) {
for (const auto item : body_fn_.attribute_set_input_by_field_source_index.items()) {
const int index = inputs_.append_and_get_index_as(
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
zone_info.attribute_set_input_by_field_source_index.add_new(item.key, index);
}
for (const auto item : body_indices.attribute_set_input_by_caller_propagation_index.items()) {
for (const auto item : body_fn_.attribute_set_input_by_caller_propagation_index.items()) {
const int index = inputs_.append_and_get_index_as(
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
zone_info.attribute_set_input_by_caller_propagation_index.add_new(item.key, index);
@ -1765,7 +1765,7 @@ class LazyFunctionForRepeatZone : public LazyFunction {
const NodeGeometryRepeatOutput &node_storage) const
{
const int num_repeat_items = node_storage.items_num;
const int num_border_links = body_indices_.border_link_inputs.size();
const int num_border_links = body_fn_.border_link_inputs.size();
/* Number of iterations to evaluate. */
const int iterations = std::max<int>(
@ -1790,7 +1790,7 @@ class LazyFunctionForRepeatZone : public LazyFunction {
/* Create body nodes. */
VectorSet<lf::FunctionNode *> &lf_body_nodes = eval_storage.lf_body_nodes;
for ([[maybe_unused]] const int i : IndexRange(iterations)) {
lf::FunctionNode &lf_node = lf_graph.add_function(body_fn_);
lf::FunctionNode &lf_node = lf_graph.add_function(*body_fn_.function);
lf_body_nodes.add_new(&lf_node);
JacquesLucke marked this conversation as resolved Outdated

Shorter alias: lf_body_nodes.add_new(&lf_node);;

Shorter alias: `lf_body_nodes.add_new(&lf_node);`;
}
@ -1808,18 +1808,17 @@ class LazyFunctionForRepeatZone : public LazyFunction {
lf::FunctionNode &lf_node = *lf_body_nodes[iter_i];
for (const int i : IndexRange(num_border_links)) {
lf_graph.add_link(lf_input_node.output(zone_info_.border_link_input_indices[i]),
lf_node.input(body_indices_.border_link_inputs[i]));
lf_graph.add_link(lf_node.output(body_indices_.border_link_usages[i]),
lf_node.input(body_fn_.border_link_inputs[i]));
lf_graph.add_link(lf_node.output(body_fn_.border_link_usages[i]),
lf_border_link_usage_or_nodes[i]->input(iter_i));
}
for (const auto item : body_indices_.attribute_set_input_by_field_source_index.items()) {
for (const auto item : body_fn_.attribute_set_input_by_field_source_index.items()) {
lf_graph.add_link(
lf_input_node.output(
zone_info_.attribute_set_input_by_field_source_index.lookup(item.key)),
JacquesLucke marked this conversation as resolved
Review

std::move?

`std::move`?
lf_node.input(item.value));
}
for (const auto item : body_indices_.attribute_set_input_by_caller_propagation_index.items())
{
for (const auto item : body_fn_.attribute_set_input_by_caller_propagation_index.items()) {
lf_graph.add_link(
lf_input_node.output(
zone_info_.attribute_set_input_by_caller_propagation_index.lookup(item.key)),
@ -1832,13 +1831,13 @@ class LazyFunctionForRepeatZone : public LazyFunction {
lf::FunctionNode &lf_node = *lf_body_nodes[iter_i];
lf::FunctionNode &lf_next_node = *lf_body_nodes[iter_i + 1];
for (const int i : IndexRange(num_repeat_items)) {
lf_graph.add_link(lf_node.output(body_indices_.main_outputs[i]),
lf_next_node.input(body_indices_.main_inputs[i]));
lf_graph.add_link(lf_node.output(body_fn_.main_outputs[i]),
lf_next_node.input(body_fn_.main_inputs[i]));
/* TODO: Add back-link after being able to check for cyclic dependencies. */
// lf_graph.add_link(lf_next_node.output(body_indices_.main_input_usages[i]),
// lf_node.input(body_indices_.main_output_usages[i]));
// lf_graph.add_link(lf_next_node.output(body_fn_.main_input_usages[i]),
// lf_node.input(body_fn_.main_output_usages[i]));
static bool static_true = true;
lf_node.input(body_indices_.main_output_usages[i]).set_default_value(&static_true);
lf_node.input(body_fn_.main_output_usages[i]).set_default_value(&static_true);
}
}
@ -1855,9 +1854,9 @@ class LazyFunctionForRepeatZone : public LazyFunction {
for (const int i : IndexRange(num_repeat_items)) {
lf_graph.add_link(
lf_input_node.output(zone_info_.main_input_indices[i + main_inputs_offset]),
lf_first_body_node.input(body_indices_.main_inputs[i]));
lf_first_body_node.input(body_fn_.main_inputs[i]));
lf_graph.add_link(
lf_first_body_node.output(body_indices_.main_input_usages[i]),
lf_first_body_node.output(body_fn_.main_input_usages[i]),
lf_output_node.input(zone_info_.main_input_usage_indices[i + main_inputs_offset]));
}
}
@ -1865,10 +1864,10 @@ class LazyFunctionForRepeatZone : public LazyFunction {
/* Link last body node to input/output nodes. */
lf::FunctionNode &lf_last_body_node = *lf_body_nodes.as_span().last();
for (const int i : IndexRange(num_repeat_items)) {
lf_graph.add_link(lf_last_body_node.output(body_indices_.main_outputs[i]),
lf_graph.add_link(lf_last_body_node.output(body_fn_.main_outputs[i]),
lf_output_node.input(zone_info_.main_output_indices[i]));
lf_graph.add_link(lf_input_node.output(zone_info_.main_output_usage_indices[i]),
lf_last_body_node.input(body_indices_.main_output_usages[i]));
lf_last_body_node.input(body_fn_.main_output_usages[i]));
}
}
}
@ -2217,46 +2216,56 @@ struct GeometryNodesLazyFunctionGraphBuilder {
}
/**
* Builds a #LazyFunction for a repeat zone. For that it first builds a lazy-function graph
* from all the nodes in the zone, and then wraps that in another lazy-function that implements
* the repeating behavior.
* Builds a #LazyFunction for a repeat zone.
*/
void build_repeat_zone_function(const bNodeTreeZone &zone)
{
ZoneBuildInfo &zone_info = zone_build_infos_[zone.index];
/* Build a function for the loop body. */
ZoneBodyFunction &body_fn = this->build_zone_body_function(zone);
/* Wrap the loop body by another function that implements the repeat behavior. */
auto &zone_fn = scope_.construct<LazyFunctionForRepeatZone>(zone, zone_info, body_fn);
zone_info.lazy_function = &zone_fn;
}
/**
* Build a lazy-function for the "body" of a zone, i.e. for all the nodes within the zone.
*/
ZoneBodyFunction &build_zone_body_function(const bNodeTreeZone &zone)
{
lf::Graph &lf_body_graph = scope_.construct<lf::Graph>();
BuildGraphParams graph_params{lf_body_graph};
Vector<const lf::OutputSocket *, 16> lf_body_inputs;
Vector<const lf::InputSocket *, 16> lf_body_outputs;
RepeatBodyIndices &body_indices = scope_.construct<RepeatBodyIndices>();
ZoneBodyFunction &body_fn = scope_.construct<ZoneBodyFunction>();
lf::DummyNode &lf_main_input_node = this->build_dummy_node_for_sockets(
"Repeat Input", {}, zone.input_node->output_sockets().drop_back(1), lf_body_graph);
"Main Inputs", {}, zone.input_node->output_sockets().drop_back(1), lf_body_graph);
for (const int i : zone.input_node->output_sockets().drop_back(1).index_range()) {
const bNodeSocket &bsocket = zone.input_node->output_socket(i);
lf::OutputSocket &lf_socket = lf_main_input_node.output(i);
graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket);
}
lf_body_inputs.extend(lf_main_input_node.outputs());
body_indices.main_inputs = lf_body_inputs.index_range();
body_fn.main_inputs = lf_body_inputs.index_range();
lf::DummyNode &lf_main_output_node = this->build_dummy_node_for_sockets(
"Repeat Output", zone.output_node->input_sockets().drop_back(1), {}, lf_body_graph);
"Main Outputs", zone.output_node->input_sockets().drop_back(1), {}, lf_body_graph);
lf_body_outputs.extend(lf_main_output_node.inputs());
body_indices.main_outputs = lf_body_outputs.index_range();
body_fn.main_outputs = lf_body_outputs.index_range();
lf::Node &lf_main_input_usage_node = this->build_dummy_node_for_socket_usages(
"Input Usages", zone.input_node->output_sockets().drop_back(1), {}, lf_body_graph);
lf_body_outputs.extend(lf_main_input_usage_node.inputs());
body_indices.main_input_usages = lf_body_outputs.index_range().take_back(
body_fn.main_input_usages = lf_body_outputs.index_range().take_back(
lf_main_input_usage_node.inputs().size());
lf::Node &lf_main_output_usage_node = this->build_dummy_node_for_socket_usages(
"Output Usages", {}, zone.output_node->input_sockets().drop_back(1), lf_body_graph);
lf_body_inputs.extend(lf_main_output_usage_node.outputs());
body_indices.main_output_usages = lf_body_inputs.index_range().take_back(
body_fn.main_output_usages = lf_body_inputs.index_range().take_back(
lf_main_output_usage_node.outputs().size());
for (const int i : zone.output_node->input_sockets().drop_back(1).index_range()) {
@ -2270,13 +2279,13 @@ struct GeometryNodesLazyFunctionGraphBuilder {
lf::Node &lf_border_link_input_node = this->build_zone_border_links_input_node(zone,
lf_body_graph);
lf_body_inputs.extend(lf_border_link_input_node.outputs());
body_indices.border_link_inputs = lf_body_inputs.index_range().take_back(
body_fn.border_link_inputs = lf_body_inputs.index_range().take_back(
lf_border_link_input_node.outputs().size());
lf::Node &lf_border_link_usage_node = this->build_border_link_input_usage_node(zone,
lf_body_graph);
lf_body_outputs.extend(lf_border_link_usage_node.inputs());
body_indices.border_link_usages = lf_body_outputs.index_range().take_back(
body_fn.border_link_usages = lf_body_outputs.index_range().take_back(
lf_border_link_usage_node.inputs().size());
this->insert_nodes_and_zones(zone.child_nodes, zone.child_zones, graph_params);
@ -2314,15 +2323,15 @@ struct GeometryNodesLazyFunctionGraphBuilder {
lf::OutputSocket &lf_attribute_set_socket = *item.value;
if (lf_attribute_set_socket.node().is_dummy()) {
const int body_input_index = lf_body_inputs.append_and_get_index(&lf_attribute_set_socket);
body_indices.attribute_set_input_by_field_source_index.add_new(item.key, body_input_index);
body_fn.attribute_set_input_by_field_source_index.add_new(item.key, body_input_index);
}
}
for (const auto item : lf_attribute_set_by_caller_propagation_index.items()) {
lf::OutputSocket &lf_attribute_set_socket = *item.value;
if (lf_attribute_set_socket.node().is_dummy()) {
const int body_input_index = lf_body_inputs.append_and_get_index(&lf_attribute_set_socket);
body_indices.attribute_set_input_by_caller_propagation_index.add_new(item.key,
body_input_index);
body_fn.attribute_set_input_by_caller_propagation_index.add_new(item.key,
body_input_index);
}
}
this->link_attribute_set_inputs(lf_body_graph,
@ -2335,14 +2344,12 @@ struct GeometryNodesLazyFunctionGraphBuilder {
auto &logger = scope_.construct<GeometryNodesLazyFunctionLogger>(*lf_graph_info_);
auto &side_effect_provider = scope_.construct<GeometryNodesLazyFunctionSideEffectProvider>();
LazyFunction &body_graph_fn = scope_.construct<lf::GraphExecutor>(
body_fn.function = &scope_.construct<lf::GraphExecutor>(
lf_body_graph, lf_body_inputs, lf_body_outputs, &logger, &side_effect_provider, nullptr);
// std::cout << "\n\n" << lf_body_graph.to_dot() << "\n\n";
auto &fn = scope_.construct<LazyFunctionForRepeatZone>(
zone, zone_info, body_graph_fn, body_indices);
zone_info.lazy_function = &fn;
return body_fn;
}
lf::DummyNode &build_zone_border_links_input_node(const bNodeTreeZone &zone, lf::Graph &lf_graph)