MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
GpuComputeNode.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "GpuSync.hpp"
4
6
8
9/**
10 * @class GpuComputeContext
11 * @brief Context delivered to on_complete callbacks when a dispatch completes.
12 *
13 * Carries the primary float readback and the aux map from GpuChannelResult.
14 * The contents are valid only for the duration of the callback invocation;
15 * callers that need to retain data must copy out of primary or aux.
16 */
17class MAYAFLUX_API GpuComputeContext : public NodeContext {
18public:
20 : NodeContext(result.primary.empty() ? 0.0 : static_cast<double>(result.primary[0]))
21 , gpu_result(result)
22 {
23 }
24
25 /// @brief Reference to the full dispatch result. Valid during callback only.
27};
28
29/**
30 * @class GpuComputeNode
31 * @brief GpuSync node that owns a ShaderExecutionContext and dispatches it
32 * asynchronously, polling for completion on each subsequent compute_frame().
33 *
34 * Lifecycle per dispatch cycle:
35 * 1. set_dirty() is called (or continuous mode is active).
36 * 2. compute_frame() calls dispatch_core_async on the executor, stores the
37 * returned FenceID, and returns immediately. No GPU work blocks the caller.
38 * 3. On each subsequent compute_frame() while m_pending_fence is live,
39 * ShaderFoundry::is_fence_signaled is polled. While not signaled the call
40 * is a no-op.
41 * 4. Once signaled, readback_primary / readback_aux collect the result,
42 * on_complete callbacks are fired with a GpuComputeContext, and the fence
43 * is cleared. If continuous mode is active a new dispatch is armed
44 * immediately.
45 *
46 * The node does not prescribe what consumers do with the result. Callbacks
47 * write into whatever NDData or external storage they own. The node itself
48 * stores nothing beyond the last fence.
49 *
50 * Usage:
51 * @code
52 * auto exec = std::make_shared<Yantra::ShaderExecutionContext<>>(
53 * Yantra::GpuShaderConfig { "my_shader.comp", { 256, 1, 1 }, sizeof(MyPC) });
54 * exec->input(input_data).output(output_bytes).push(pc);
55 *
56 * auto node = std::make_shared<GpuComputeNode>(std::move(exec));
57 * node->on_complete([](GpuComputeContext& ctx) {
58 * auto result = Yantra::ShaderExecutionContext<>::read_output<float>(ctx.gpu_result, 1);
59 * // write into NDData, update state, etc.
60 * });
61 * node->set_dirty();
62 * @endcode
63 */
64class MAYAFLUX_API GpuComputeNode : public GpuSync {
65public:
66 /**
67 * @brief Construct with a configured ShaderExecutionContext.
68 * @param executor Configured executor. Must be non-null. Bindings, push
69 * constants, and shader path must already be declared.
70 * @param continuous If true, a new dispatch is armed immediately after each
71 * completion. If false, the node idles until set_dirty()
72 * is called again.
73 */
74 explicit GpuComputeNode(
75 std::shared_ptr<Yantra::ShaderExecutionContext<>> executor,
76 bool continuous = false);
77
78 ~GpuComputeNode() override = default;
79
80 /**
81 * @brief Arm a dispatch on the next compute_frame() call.
82 *
83 * No-op if a dispatch is already pending. In continuous mode this is
84 * called automatically after each completion and rarely needs to be
85 * called externally.
86 */
87 void set_dirty() { m_dirty = true; }
88
89 /**
90 * @brief Returns true if a dispatch has been submitted and not yet completed.
91 */
92 [[nodiscard]] bool is_pending() const
93 {
94 return m_pending_fence != Portal::Graphics::INVALID_FENCE;
95 }
96
97 /**
98 * @brief Returns true if the last dispatch completed and callbacks have fired.
99 *
100 * Resets to false when a new dispatch is armed.
101 */
102 [[nodiscard]] bool is_ready() const { return m_ready; }
103
104 /**
105 * @brief Register a callback fired once per completed dispatch.
106 * @param callback Receives a GpuComputeContext carrying the GpuChannelResult.
107 */
108 void on_complete(const TypedHook<GpuComputeContext>& callback);
109
110 /**
111 * @brief Remove a previously registered on_complete callback.
112 * @return True if found and removed.
113 */
114 bool remove_complete_hook(const TypedHook<GpuComputeContext>& callback);
115
116 // -------------------------------------------------------------------------
117 // GpuSync interface
118 // -------------------------------------------------------------------------
119
120 /**
121 * @brief Drive the async dispatch lifecycle.
122 *
123 * If dirty and no dispatch is pending: arms a new dispatch via
124 * dispatch_core_async, clears dirty, clears ready.
125 * If a dispatch is pending: polls the fence. On signal, collects the
126 * result, fires on_complete callbacks, sets ready. If continuous, arms
127 * the next dispatch immediately.
128 */
129 void compute_frame() override;
130
131 [[nodiscard]] bool needs_gpu_update() const override { return false; }
132 void clear_gpu_update_flag() override { }
133
134 // -------------------------------------------------------------------------
135 // Node interface stubs
136 // -------------------------------------------------------------------------
137
138 void update_context(double value) override;
139
141 {
142 update_context(0.0);
143 return *m_context_storage;
144 }
145
146 void notify_tick(double value) override;
147 void save_state() override { }
148 void restore_state() override { }
149
150private:
151 std::shared_ptr<Yantra::ShaderExecutionContext<>> m_executor;
152 bool m_continuous { false };
153 bool m_dirty { false };
154 bool m_ready { false };
155
156 Portal::Graphics::FenceID m_pending_fence { Portal::Graphics::INVALID_FENCE };
157
158 std::vector<TypedHook<GpuComputeContext>> m_complete_callbacks;
159
161 mutable std::unique_ptr<GpuComputeContext> m_context_storage;
162};
163
164} // namespace MayaFlux::Nodes::GpuSync
const Yantra::GpuChannelResult & gpu_result
Reference to the full dispatch result. Valid during callback only.
GpuComputeContext(const Yantra::GpuChannelResult &result)
Context delivered to on_complete callbacks when a dispatch completes.
std::shared_ptr< Yantra::ShaderExecutionContext<> > m_executor
void set_dirty()
Arm a dispatch on the next compute_frame() call.
void restore_state() override
Restores the node's state from the last save Recursively cascades through all connected modulator nod...
bool is_pending() const
Returns true if a dispatch has been submitted and not yet completed.
std::unique_ptr< GpuComputeContext > m_context_storage
void save_state() override
Saves the node's current state for later restoration Recursively cascades through all connected modul...
NodeContext & get_last_context() override
Retrieves the last created context object.
bool is_ready() const
Returns true if the last dispatch completed and callbacks have fired.
void clear_gpu_update_flag() override
Clear the "needs update" flag after GPU binding.
std::vector< TypedHook< GpuComputeContext > > m_complete_callbacks
bool needs_gpu_update() const override
Mark if this node needs GPU binding update.
GpuSync node that owns a ShaderExecutionContext and dispatches it asynchronously, polling for complet...
Base context class for node callbacks.
Definition Node.hpp:53
Concrete GpuExecutionContext for a single fixed shader with fixed bindings.
std::function< void(ContextT &)> TypedHook
Callback function type for node processing events, parameterised on context type.
Definition NodeUtils.hpp:28
Erased output of a GPU dispatch: reconstructed float data plus any raw auxiliary outputs keyed by bin...