tornavis/source/blender/compositor/intern/COM_ExecutionSystem.h

205 lines
6.4 KiB
C++

/* SPDX-FileCopyrightText: 2011 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <functional>
#include "atomic_ops.h"
#include "BLI_index_range.hh"
#include "BLI_threads.h"
#include "BLI_vector.hh"
#include "COM_CompositorContext.h"
#include "COM_SharedOperationBuffers.h"
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_vec_types.h"
namespace blender::realtime_compositor {
class RenderContext;
}
namespace blender::compositor {
class ProfilerData;
/**
* \page execution Execution model
* In order to get to an efficient model for execution, several steps are being done. these steps
* are explained below.
*
* \section EM_Step1 Step 1: translating blender node system to the new compositor system
* Blenders node structure is based on C structs (DNA). These structs are not efficient in the new
* architecture. We want to use classes in order to simplify the system. during this step the
* blender node_tree is evaluated and converted to a CPP node system.
*
* \see ExecutionSystem
* \see Converter.convert
* \see Node
*
* \section EM_Step2 Step2: translating nodes to operations
* Ungrouping the GroupNodes. Group nodes are node_tree's in node_tree's.
* The new system only supports a single level of node_tree.
* We will 'flatten' the system in a single level.
* \see GroupNode
* \see ExecutionSystemHelper.ungroup
*
* Every node has the ability to convert itself to operations. The node itself is responsible to
* create a correct NodeOperation setup based on its internal settings. Most Node only need to
* convert it to its NodeOperation. Like a ColorToBWNode doesn't check anything, but replaces
* itself with a ConvertColorToBWOperation. More complex nodes can use different NodeOperation
* based on settings; like MixNode. based on the selected Mixtype a different operation will be
* used. for more information see the page about creating new Nodes. [@subpage newnode]
*
* \see ExecutionSystem.convert_to_operations
* \see Node.convert_to_operations
* \see NodeOperation base class for all operations in the system
*
* \section EM_Step3 Step3: add additional conversions to the operation system
* - Data type conversions: the system has 3 data types DataType::Value, DataType::Vector,
* DataType::Color. The user can connect a Value socket to a color socket. As values are ordered
* differently than colors a conversion happens.
*
* - Image size conversions: the system can automatically convert when resolutions do not match.
* An NodeInput has a resize mode. This can be any of the following settings.
* - [@ref InputSocketResizeMode.ResizeMode::Center]:
* The center of both images are aligned
* - [@ref InputSocketResizeMode.ResizeMode::FitWidth]:
* The width of both images are aligned
* - [@ref InputSocketResizeMode.ResizeMode::FitHeight]:
* The height of both images are aligned
* - [@ref InputSocketResizeMode.ResizeMode::FitAny]:
* The width, or the height of both images are aligned to make sure that it fits.
* - [@ref InputSocketResizeMode.ResizeMode::Stretch]:
* The width and the height of both images are aligned.
* - [@ref InputSocketResizeMode.ResizeMode::None]:
* Bottom left of the images are aligned.
*
* \see COM_convert_data_type Datatype conversions
* \see Converter.convert_resolution Image size conversions
*/
/* Forward declarations. */
class ExecutionModel;
class NodeOperation;
/**
* \brief the ExecutionSystem contains the whole compositor tree.
*/
class ExecutionSystem {
private:
/**
* Contains operations active buffers data. Buffers will be disposed once reader operations are
* finished.
*/
SharedOperationBuffers active_buffers_;
/**
* \brief the context used during execution
*/
CompositorContext context_;
/**
* \brief vector of operations
*/
Vector<NodeOperation *> operations_;
/**
* Active execution model implementation.
*/
ExecutionModel *execution_model_;
/**
* Number of cpu threads available for work execution.
*/
int num_work_threads_;
ThreadMutex work_mutex_;
ThreadCondition work_finished_cond_;
ProfilerData &profiler_data_;
public:
/**
* \brief Create a new ExecutionSystem and initialize it with the
* editingtree.
*
* \param editingtree: [bNodeTree *]
* \param rendering: [true false]
*/
ExecutionSystem(RenderData *rd,
Scene *scene,
bNodeTree *editingtree,
bool rendering,
bool fastcalculation,
const char *view_name,
realtime_compositor::RenderContext *render_context,
ProfilerData &profiler_data);
/**
* Destructor
*/
~ExecutionSystem();
void set_operations(Span<NodeOperation *> operations);
/**
* \brief execute this system
* - initialize the NodeOperation's
* - schedule the outputs based on their priority
* - deinitialize the NodeOperation's
*/
void execute();
/**
* \brief get the reference to the compositor context
*/
const CompositorContext &get_context() const
{
return context_;
}
/**
* Multi-threadedly execute given work function passing work_rect splits as argument.
*/
void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func);
/**
* Multi-threaded execution of given work function passing work_rect splits as argument.
* Once finished, caller thread will call reduce_func for each thread result.
*/
template<typename TResult>
void execute_work(const rcti &work_rect,
std::function<TResult(const rcti &split_rect)> work_func,
TResult &join,
std::function<void(TResult &join, const TResult &chunk)> reduce_func)
{
Array<TResult> chunks(num_work_threads_);
int num_started = 0;
execute_work(work_rect, [&](const rcti &split_rect) {
const int current = atomic_fetch_and_add_int32(&num_started, 1);
chunks[current] = work_func(split_rect);
});
for (const int i : IndexRange(num_started)) {
reduce_func(join, chunks[i]);
}
}
bool is_breaked() const;
private:
/* allow the DebugInfo class to look at internals */
friend class DebugInfo;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionSystem")
#endif
};
} // namespace blender::compositor