Class Fragment

class Fragment

The fragment of the application.

A fragment is a building block of the Application. It is a Directed Acyclic Graph (DAG) of operators. A fragment can be assigned to a physical node of a Holoscan cluster during execution. The run-time execution manages communication across fragments. In a Fragment, Operators (Graph Nodes) are connected to each other by flows (Graph Edges).

Subclassed by holoscan::Application

Public Functions

Fragment() = default

virtual ~Fragment() = default

Fragment(Fragment&&) = default

Fragment &operator=(Fragment&&) = default

Fragment &name(const std::string &name) &

Set the name of the operator.

Parameters

name – The name of the operator.

Returns

The reference to this operator.

Fragment &&name(const std::string &name) &&

Set the name of the operator.

Parameters

name – The name of the operator.

Returns

The reference to this operator.

const std::string &name() const

Get the name of the fragment.

Returns

The name of the fragment.

Fragment &application(Application *app)

Set the application of the fragment.

Parameters

app – The pointer to the application of the fragment.

Returns

The reference to this fragment.

void config(const std::string &config_file, const std::string &prefix = "")

Set the configuration of the fragment.

The configuration file is a YAML file that has the information of GXF extension paths and some parameter values for operators.

The extensions field in the YAML configuration file is a list of GXF extension paths. The paths can be absolute or relative to the current working directory, considering paths in LD_LIBRARY_PATH environment variable. The paths consists of the following parts:

  • GXF core extensions

    • built-in extensions such as libgxf_std.so and libgxf_cuda.so.

    • GXF core extensions are copied to the lib directory of the build/installation directory.

  • Other GXF extensions

    • GXF extensions that are required for operators that this fragment uses.

    • these paths are usually relative to the build/installation directory.

The extension paths are used to load dependent GXF extensions at runtime when run() method is called.

For other fields in the YAML file, you can freely define the parameter values for operators/fragments.

For example:

Copy
Copied!
            

extensions: - libgxf_std.so - libgxf_cuda.so - libgxf_multimedia.so - libgxf_serialization.so - libmy_recorder.so - libstream_playback.so replayer: directory: "../data/endoscopy/video" basename: "surgical_video" frame_rate: 0 # as specified in timestamps repeat: false # default: false realtime: true # default: true count: 0 # default: 0 (no frame count restriction) recorder: out_directory: "/tmp" basename: "tensor_out"

You can get the value of this configuration file by calling from_config() method.

Parameters
  • config_file – The path to the configuration file.

  • prefix – The prefix string that is prepended to the key of the configuration. (not implemented yet)

Config &config()

Get the configuration of the fragment.

Returns

The reference to the configuration of the fragment (Config object.)

Graph &graph()

Get the graph of the fragment.

Returns

The reference to the graph of the fragment (Graph object.)

Executor &executor()

Get the executor of the fragment.

Returns

The reference to the executor of the fragment (Executor object.)

ArgList from_config(const std::string &key)

Get the Argument(s) from the configuration file.

For the given key, this method returns the value of the configuration file.

For example:

Copy
Copied!
            

source: "replayer" do_record: false # or 'true' if you want to record input video stream. aja: width: 1920 height: 1080 rdma: true

from_config("aja") returns an ArgList (vector-like) object that contains the following items:

  • Arg("width") = 1920

  • Arg("height") = 1080

  • Arg("rdma") = true

You can use ‘.’ (dot) to access nested fields.

from_config("aja.rdma") returns an ArgList object that contains only one item and it can be converted to bool through ArgList::as() method:

Copy
Copied!
            

bool is_rdma = from_config("aja.rdma").as<bool>();

Parameters

key – The key of the configuration.

Returns

The argument list of the configuration for the key.

template<typename OperatorT, typename StringT, typename ...ArgsT, typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
inline std::shared_ptr<OperatorT> make_operator(const StringT &name, ArgsT&&... args)

Create a new operator.

Template Parameters

OperatorT – The type of the operator.

Parameters
  • name – The name of the operator.

  • args – The arguments for the operator.

Returns

The shared pointer to the operator.

template<typename OperatorT, typename ...ArgsT>
inline std::shared_ptr<OperatorT> make_operator(ArgsT&&... args)

Create a new operator.

Template Parameters

OperatorT – The type of the operator.

Parameters

args – The arguments for the operator.

Returns

The shared pointer to the operator.

template<typename ResourceT, typename StringT, typename ...ArgsT, typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
inline std::shared_ptr<ResourceT> make_resource(const StringT &name, ArgsT&&... args)

Create a new (operator) resource.

Template Parameters

OperatorT – The type of the resource.

Parameters
  • name – The name of the resource.

  • args – The arguments for the resource.

Returns

The shared pointer to the resource.

template<typename ResourceT, typename ...ArgsT>
inline std::shared_ptr<ResourceT> make_resource(ArgsT&&... args)

Create a new (operator) resource.

Template Parameters

OperatorT – The type of the resource.

Parameters

args – The arguments for the resource.

Returns

The shared pointer to the resource.

template<typename ConditionT, typename StringT, typename ...ArgsT, typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
inline std::shared_ptr<ConditionT> make_condition(const StringT &name, ArgsT&&... args)

Create a new condition.

Template Parameters

OperatorT – The type of the condition.

Parameters
  • name – The name of the condition.

  • args – The arguments for the condition.

Returns

The shared pointer to the condition.

template<typename ConditionT, typename ...ArgsT>
inline std::shared_ptr<ConditionT> make_condition(ArgsT&&... args)

Create a new condition.

Template Parameters

OperatorT – The type of the condition.

Parameters

args – The arguments for the condition.

Returns

The shared pointer to the condition.

virtual void add_operator(const std::shared_ptr<Operator> &op)

Add an operator to the graph.

The information of the operator is stored in the Graph object. If the operator is already added, this method does nothing.

Parameters

op – The operator to be added.

virtual void add_flow(const std::shared_ptr<Operator> &upstream_op, const std::shared_ptr<Operator> &downstream_op)

Add a flow between two operators.

An output port of the upstream operator is connected to an input port of the downstream operator. The information about the flow (edge) is stored in the Graph object.

If the upstream operator or the downstream operator is not in the graph, it will be added to the graph.

If there are multiple output ports in the upstream operator or multiple input ports in the downstream operator, it shows an error message.

Parameters
  • upstream_op – The upstream operator.

  • downstream_op – The downstream operator.

virtual void add_flow(const std::shared_ptr<Operator> &upstream_op, const std::shared_ptr<Operator> &downstream_op, std::set<std::pair<std::string, std::string>> port_pairs)

Add a flow between two operators.

An output port of the upstream operator is connected to an input port of the downstream operator. The information about the flow (edge) is stored in the Graph object.

If the upstream operator or the downstream operator is not in the graph, it will be added to the graph.

In port_pairs, an empty port name (“”) can be used for specifying a port name if the operator has only one input/output port.

If a non-existent port name is specified in port_pairs, it first checks if there is a parameter with the same name but with a type of std::vector<holoscan::IOSpec*> in the downstream operator. If there is such a parameter (e.g., receivers), it creates a new input port with a specific label (<parameter name>:<index>. e.g., receivers:0), otherwise it shows an error message.

For example, if a parameter receivers want to have an arbitrary number of receivers,

Copy
Copied!
            

class HolovizOp : public holoscan::ops::GXFOperator { ... private: Parameter<std::vector<holoscan::IOSpec*>> receivers_; ...

Instead of creating a fixed number of input ports (e.g., source_video and tensor) and assigning them to the parameter (receivers):

Copy
Copied!
            

void HolovizOp::setup(OperatorSpec& spec) { ... auto& in_source_video = spec.input<holoscan::gxf::Entity>("source_video"); auto& in_tensor = spec.input<holoscan::gxf::Entity>("tensor"); spec.param(receivers_, "receivers", "Input Receivers", "List of input receivers.", {&in_source_video, &in_tensor}); ...

You can skip the creation of input ports and assign them to the parameter (receivers) as follows:

Copy
Copied!
            

void HolovizOp::setup(OperatorSpec& spec) { ... spec.param(receivers_, "receivers", "Input Receivers", "List of input receivers.", {&in_source_video, &in_tensor}); ...

This makes the following code possible in the Application’s compose() method:

Copy
Copied!
            

add_flow(source, visualizer_format_converter); add_flow(visualizer_format_converter, visualizer, {{"", "receivers"}}); add_flow(source, format_converter); add_flow(format_converter, lstm_inferer); add_flow(lstm_inferer, visualizer, {{"", "receivers"}});

Instead of:

Copy
Copied!
            

add_flow(source, visualizer_format_converter); add_flow(visualizer_format_converter, visualizer, {{"", "source_video"}}); add_flow(source, format_converter); add_flow(format_converter, lstm_inferer); add_flow(lstm_inferer, visualizer, {{"", "tensor"}});

By using the parameter (receivers) with std::vector<holoscan::IOSpec*> type, the framework creates input ports (receivers:0 and receivers:1) implicitly and connects them (and adds the references of the input ports to the receivers vector).

Parameters
  • upstream_op – The upstream operator.

  • downstream_op – The downstream operator.

  • port_pairs – The port pairs. The first element of the pair is the port of the upstream operator and the second element is the port of the downstream operator.

virtual void compose()

Compose a graph.

The graph is composed by adding operators and flows in this method.

virtual void run()

Initialize the graph and run the graph.

This method calls compose() to compose the graph, and runs the graph.

Protected Functions

template<typename ConfigT, typename ...ArgsT>
inline std::unique_ptr<Config> make_config(ArgsT&&... args)

template<typename GraphT>
inline std::unique_ptr<Graph> make_graph()

template<typename ExecutorT>
inline std::unique_ptr<Executor> make_executor()

Protected Attributes

std::string name_

The name of the fragment.

Application *app_ = nullptr

The application that this fragment belongs to.

std::unique_ptr<Config> config_

The configuration of the fragment.

std::unique_ptr<Graph> graph_

The graph of the fragment.

std::unique_ptr<Executor> executor_

The executor for the fragment.

© Copyright 2022, NVIDIA. Last updated on Jun 28, 2023.