@@ -556,6 +556,114 @@ Parameters:
556556
557557|===
558558
559+ ==== Dynamic Command Groups
560+
561+ [source,c++]
562+ ----
563+ namespace ext::oneapi::experimental {
564+ class dynamic_command_group {
565+ public:
566+ dynamic_command_group(
567+ command_graph<graph_state::modifiable> graph,
568+ const std::vector<std::function<void(handler &)>>& cgfList);
569+
570+ size_t get_active_cgf();
571+ void set_active_cgf(size_t cgfIndex);
572+ };
573+ ----
574+
575+ Dynamic command-groups can be added as nodes to a graph. They provide a mechanism that
576+ allows updating the command-group function of a node after the graph is finalized.
577+ There is always one command-group function in the dynamic command-group that is set
578+ as active. When a dynamic command-group node is executed, the kernel of the active
579+ command-group function will be run and all the other command-group functions in
580+ `cgfList` will be ignored.
581+
582+ See <<executable-graph-update, Executable Graph Update>> for more information
583+ about updating command-groups.
584+
585+ ===== Limitations
586+
587+ Dynamic command-groups can only be used to update kernels. Trying to update a command-group
588+ function that contains other operations will result in an error.
589+
590+ All the command-group functions in a dynamic command-group must have identical dependencies.
591+ It is not allowed for a dynamic command-group to have command-group functions that would
592+ result in a change to the graph topology when set to active. In practice, this means that
593+ any calls to `handler.depends_on()` must be identical for all the command-group functions
594+ in a dynamic command-group.
595+
596+ Table {counter: tableNumber}. Member functions of the `dynamic_command_group` class.
597+ [cols="2a,a"]
598+ |===
599+ |Member Function|Description
600+
601+ |
602+ [source,c++]
603+ ----
604+ dynamic_command_group(
605+ command_graph<graph_state::modifiable> graph,
606+ const std::vector<std::function<void(handler &)>>& cgfList);
607+ ----
608+
609+ |Constructs a dynamic command-group object that can be added as a node to a `command_graph`.
610+
611+ Parameters:
612+
613+ * `graph` - Graph to be associated with this `dynamic_command_group`.
614+ * `cgfList` - The list of command-group functions that can be activated for this dynamic command-group.
615+ The command-group function at index 0 will be active by default.
616+
617+ Exceptions:
618+
619+ * Throws synchronously with error code `invalid` if the graph wasn't created with
620+ the `property::graph::assume_buffer_outlives_graph` property and the `dynamic_command_group`
621+ is created with command-group functions that use buffers. See the
622+ <<assume-buffer-outlives-graph-property, Assume-Buffer-Outlives-Graph>>
623+ property for more information.
624+
625+ * Throws with error code `invalid` if the `dynamic_command_group` is created with
626+ command-group functions that are not kernel executions.
627+
628+ * Throws with error code `invalid` if the command-group functions in `cgfList` have
629+ event dependencies that are incompatible with each other and would result in
630+ different graph topologies when set to active.
631+
632+ |
633+ [source,c++]
634+ ----
635+ size_t get_active_cgf();
636+ ----
637+ |Returns the index of the currently active command-group function in this
638+ `dynamic_command_group`.
639+
640+ |
641+ [source,c++]
642+ ----
643+ void set_active_cgf(size_t cgfIndex);
644+ ----
645+ | Sets the command-group function with index `cgfIndex` as active. The index of the
646+ command-group function in a `dynamic_command_group` is identical to its index in the
647+ `cgfList` vector when it was passed to the `dynamic_command_group` constructor.
648+
649+ This change will be reflected immediately in the modifiable graph which contains this
650+ `dynamic_command_group`. The new value will not be reflected in any executable graphs
651+ created from that modifiable graph until `command_graph::update()` is called, passing
652+ the modified nodes, or a new executable graph is finalized from the modifiable graph.
653+
654+ Setting `cgfIndex` to the index of the currently active command-group function is
655+ a no-op.
656+
657+ Parameters:
658+
659+ * `cgfIndex` - The index of the command-group function that should be set as active.
660+
661+ Exceptions:
662+
663+ * Throw with error code `invalid` if `cgfIndex` is not a valid index.
664+
665+ |===
666+
559667==== Depends-On Property
560668
561669[source,c++]
@@ -631,6 +739,8 @@ public:
631739 template<typename T>
632740 node add(T cgf, const property_list& propList = {});
633741
742+ node add(dynamic_command_group& dynamicCG, const property_list& propList = {});
743+
634744 void make_edge(node& src, node& dest);
635745
636746 void print_graph(std::string path, bool verbose = false) const;
@@ -711,21 +821,39 @@ Updates to a graph will be scheduled after any in-flight executions of the same
711821graph and will not affect previous submissions of the same graph. The user is
712822not required to wait on any previous submissions of a graph before updating it.
713823
714- The only type of nodes that are currently able to be updated in a graph are
715- kernel execution nodes.
716-
717- The aspects of a kernel execution node that can be configured during update are:
718-
719- * Parameters to the kernel.
720- * Execution ND-Range of the kernel.
721-
722824To update an executable graph, the `property::graph::updatable` property must
723825have been set when the graph was created during finalization. Otherwise, an
724826exception will be thrown if a user tries to update an executable graph. This
725827guarantee allows the backend to provide a more optimized implementation, if
726828possible.
727829
728- ===== Individual Node Update
830+ ===== Supported Features
831+
832+ The only types of nodes that are currently able to be updated in a graph are
833+ kernel execution nodes.
834+
835+ There are two different API's that can be used to update a graph:
836+
837+ * <<individual-node-update, Individual Node Update>> which allows updating
838+ individual nodes of a command-graph.
839+ * <<whole-graph-update, Whole Graph Update>> which allows updating the
840+ entirety of the graph simultaneously by using another graph as a
841+ reference.
842+
843+ The aspects of a kernel execution node that can be changed during update are
844+ different depending on the API used to perform the update:
845+
846+ * For the <<individual-node-update, Individual Node Update>> API it's possible to update
847+ the kernel function, the parameters to the kernel, and the ND-Range.
848+ * For the <<whole-graph-update, Whole Graph Update>> API, only the parameters of the kernel
849+ and the ND-Range can be updated.
850+
851+ ===== Individual Node Update [[individual-node-update]]
852+
853+ Individual nodes of an executable graph can be updated directly. Depending on the attribute
854+ of the node that requires updating, different API's should be used:
855+
856+ ====== Parameter Updates
729857
730858Parameters to individual nodes in a graph in the `executable` state can be
731859updated between graph executions using dynamic parameters. A `dynamic_parameter`
@@ -739,14 +867,6 @@ Parameter updates are performed using a `dynamic_parameter` instance by calling
739867not registered, even if they use the same parameter value as a
740868`dynamic_parameter`.
741869
742- The other node configuration that can be updated is the execution range of the
743- kernel, this can be set through `node::update_nd_range()` or
744- `node::update_range()` but does not require any prior registration.
745-
746- The executable graph can then be updated by passing the updated nodes to
747- `command_graph<graph_state::executable>::update(node& node)` or
748- `command_graph<graph_state::executable>::update(const std::vector<node>& nodes)`.
749-
750870Since the structure of the graph became fixed when finalizing, updating
751871parameters on a node will not change the already defined dependencies between
752872nodes. This is important to note when updating buffer parameters to a node,
@@ -762,6 +882,41 @@ dynamic parameter for the buffer can be registered with all the nodes which
762882use the buffer as a parameter. Then a single `dynamic_parameter::update()` call
763883will maintain the graphs data dependencies.
764884
885+ ====== Execution Range Updates
886+
887+ Another configuration that can be updated is the execution range of the
888+ kernel, this can be set through `node::update_nd_range()` or
889+ `node::update_range()` but does not require any prior registration.
890+
891+ An alternative way to update the execution range of a node is to do so while
892+ updating command groups as described in the next section.
893+
894+ ====== Command Group Updates
895+
896+ The command-groups of a kernel node can be updated using dynamic command-groups.
897+ Dynamic command-groups allow replacing the command-group function of a kernel
898+ node with a different one. This effectively allows updating the kernel function
899+ and/or the kernel execution range.
900+
901+ Command-group updates are performed by creating an instance of the
902+ `dynamic_command_group` class. A dynamic command-group is created with a modifiable
903+ state graph and a list of possible command-group functions. Command-group functions
904+ within a dynamic command-group can then be set to active by using the member function
905+ `dynamic_command_group::set_active_cgf()`.
906+
907+ Dynamic command-groups are compatible with dynamic parameters. This means that
908+ dynamic parameters can be used in command-group functions that are part of
909+ dynamic command-groups. Updates to such dynamic parameters will be reflected
910+ in the command-group functions once they are activated.
911+
912+ ====== Committing Updates
913+
914+ Updating a node using the methods mentioned above will take effect immediately
915+ for nodes in modifiable command-graphs. However, for graphs that are in the executable
916+ state, in order to commit the update, the updated nodes must be passed to
917+ `command_graph<graph_state::executable>::update(node& node)` or
918+ `command_graph<graph_state::executable>::update(const std::vector<node>& nodes)`.
919+
765920===== Whole Graph Update [[whole-graph-update]]
766921
767922A graph in the executable state can have all of its nodes updated using the
@@ -1042,6 +1197,42 @@ Exceptions:
10421197|
10431198[source,c++]
10441199----
1200+ node add(dynamic_command_group& dynamicCG, const property_list& propList = {});
1201+ ----
1202+
1203+ | Adds the dynamic command-group `dynamicCG` as a node to the graph and sets the
1204+ current active command-group function in `dynamicCG` as the executable for future
1205+ executions of this graph node.
1206+
1207+ The current active command-group function in `dynamicCG` will be executed asynchronously
1208+ when the graph is submitted to a queue. The requisites of this command-group
1209+ function will be used to identify any dependent nodes in the graph
1210+ to form edges with. The other command-group functions in `dynamicCG` will be captured
1211+ into the graph but will not be executed in a graph submission unless they are
1212+ set to active.
1213+
1214+ Constraints:
1215+
1216+ * This member function is only available when the `command_graph` state is
1217+ `graph_state::modifiable`.
1218+
1219+ Parameters:
1220+
1221+ * `dynamicCG` - Dynamic command-group object to be added as a node.
1222+
1223+ * `propList` - Zero or more properties can be provided to the constructed node
1224+ via an instance of `property_list`. The `property::node::depends_on` property
1225+ can be passed here with a list of nodes to create dependency edges on.
1226+
1227+ Returns: The dynamic command-group object node which has been added to the graph.
1228+
1229+ Exceptions:
1230+
1231+ * Throws synchronously with error code `invalid` if a queue is recording
1232+ commands to the graph.
1233+ |
1234+ [source,c++]
1235+ ----
10451236void make_edge(node& src, node& dest);
10461237----
10471238
@@ -1157,8 +1348,9 @@ void update(node& node);
11571348----
11581349
11591350| Updates an executable graph node that corresponds to `node`. `node` must be a
1160- kernel execution node. Kernel arguments and the ND-range of the node will be
1161- updated inside the executable graph to reflect the current values in `node`.
1351+ kernel execution node. The command-group function of the node will be updated,
1352+ inside the executable graph, to reflect the current values in `node`. This
1353+ includes the kernel function, the kernel nd-range and the kernel parameters.
11621354
11631355Updating these values will not change the structure of the graph.
11641356
@@ -1190,9 +1382,10 @@ void update(const std::vector<node>& nodes);
11901382----
11911383
11921384| Updates all executable graph nodes that corresponds to the nodes contained in
1193- `nodes`. All nodes must be kernel nodes. Kernel arguments and the ND-range of
1194- each node will be updated inside the executable graph to reflect the current
1195- values in each node in `nodes`.
1385+ `nodes`. All nodes must be kernel nodes. The command-group function of each node
1386+ will be updated, inside the executable graph, to reflect the current values in
1387+ `nodes`. This includes the kernel function, the kernel nd-range and the kernel
1388+ parameters".
11961389
11971390Updating these values will not change the structure of the graph.
11981391
@@ -1712,6 +1905,10 @@ the call to `queue::submit()` or `command_graph::add()` along with the calls to
17121905handler functions and this will not be reflected on future executions of the
17131906graph.
17141907
1908+ Similarly, any command-group function inside a `dynamic_command_group` will be
1909+ evaluated once, in index order, when submitted to the graph using
1910+ `command_graph::add()`.
1911+
17151912Any code like this should be moved to a separate host-task and added to the
17161913graph via the recording or explicit APIs in order to be compatible with this
17171914extension.
@@ -2243,6 +2440,50 @@ node nodeA = myGraph.add([&](handler& cgh) {
22432440dynParamAccessor.update(bufferB.get_access());
22442441----
22452442
2443+ === Dynamic Command Groups
2444+
2445+ Example showing how a graph with a dynamic command group node can be updated.
2446+
2447+ [source,c++]
2448+ ----
2449+ queue Queue{};
2450+ exp_ext::command_graph Graph{Queue.get_context(), Queue.get_device()};
2451+
2452+ int *PtrA = malloc_device<int>(1024, Queue);
2453+ int *PtrB = malloc_device<int>(1024, Queue)
2454+
2455+ auto CgfA = [&](handler &cgh) {
2456+ cgh.parallel_for(1024, [=](item<1> Item) {
2457+ PtrA[Item.get_id()] = 1;
2458+ });
2459+ };
2460+
2461+ auto CgfB = [&](handler &cgh) {
2462+ cgh.parallel_for(512, [=](item<1> Item) {
2463+ PtrB[Item.get_id()] = 2;
2464+ });
2465+ };
2466+
2467+ // Construct a dynamic command-group with CgfA as the active cgf (index 0).
2468+ auto DynamicCG = exp_ext::dynamic_command_group(Graph, {CgfA, CgfB});
2469+
2470+ // Create a dynamic command-group graph node.
2471+ auto DynamicCGNode = Graph.add(DynamicCG);
2472+
2473+ auto ExecGraph = Graph.finalize(exp_ext::property::graph::updatable{});
2474+
2475+ // The graph will execute CgfA.
2476+ Queue.ext_oneapi_graph(ExecGraph).wait();
2477+
2478+ // Sets CgfB as active in the dynamic command-group (index 1).
2479+ DynamicCG.set_active_cgf(1);
2480+
2481+ // Calls update to update the executable graph node with the changes to DynamicCG.
2482+ ExecGraph.update(DynamicCGNode);
2483+
2484+ // The graph will execute CgfB.
2485+ Queue.ext_oneapi_graph(ExecGraph).wait();
2486+ ----
22462487=== Whole Graph Update
22472488
22482489Example that shows recording and updating several nodes with different
@@ -2444,6 +2685,16 @@ to ensure this is desired and makes sense to users.
24442685
24452686**UNRESOLVED** Needs more discussion.
24462687
2688+ === Updatable command-groups in the Record & Replay API:
2689+
2690+ Currently the only way to update command-groups in a graph is to use the
2691+ Explicit API. There is a limitation in some backends that requires all
2692+ the command-groups used for updating to be specified before the graph
2693+ is finalized. This restriction makes it hard to implement the
2694+ Record & Replay API in a performant manner.
2695+
2696+ **UNRESOLVED** Needs more discussion.
2697+
24472698=== Multi Device Graph
24482699
24492700Allow an executable graph to contain nodes targeting different devices.
0 commit comments