Loading...
Searching...
No Matches
GfxContext.hpp
1#pragma once
2#include <span>
3#include <Process/Dataflow/CableData.hpp>
4
5#include <Gfx/AssetTable.hpp>
6#include <Gfx/Graph/Node.hpp>
7#include <Gfx/Graph/RenderClock.hpp>
8
9#include <score/tools/Timers.hpp>
10
11#include <ossia/dataflow/nodes/media.hpp>
12#include <ossia/dataflow/token_request.hpp>
13#include <ossia/detail/buffer_pool.hpp>
14#include <ossia/detail/flat_map.hpp>
15#include <ossia/detail/flat_set.hpp>
16#include <ossia/detail/hash_map.hpp>
17#include <ossia/detail/mutex.hpp>
18#include <ossia/detail/small_flat_map.hpp>
19#include <ossia/detail/variant.hpp>
20#include <ossia/gfx/port_index.hpp>
21#include <ossia/network/value/value.hpp>
22
23#include <concurrentqueue.h>
24#include <score_plugin_gfx_export.h>
25class QTimer;
26namespace score {
27class HighResolutionTimer;
28class Timers;
29}
30struct QRhiReadbackResult;
31namespace score::gfx
32{
33struct PreviewTap;
34struct PreviewHubNode;
35struct BackgroundNode;
36struct Graph;
37class OutputNode;
38}
39namespace score
40{
41struct DocumentContext;
42}
43namespace Gfx
44{
45using port_index = ossia::gfx::port_index;
47{
48 port_index first{};
49 port_index second{};
50 Process::CableType type{};
51
52 // type takes part in identity: the exec thread republishes the full edge
53 // set every tick and endTick dedups against prev_edges, so a comparison that
54 // ignored the cable type would make an Immediate/Delayed flip invisible. As
55 // it stands a type flip diffs as remove(old) + add(new) in the incremental
56 // path.
57 bool operator==(const EdgeSpec& other) const noexcept
58 {
59 return first == other.first && second == other.second && type == other.type;
60 }
61 bool operator!=(const EdgeSpec& other) const noexcept { return !(*this == other); }
62 bool operator<(const EdgeSpec& other) const noexcept
63 {
64 if(first != other.first)
65 return first < other.first;
66 if(second != other.second)
67 return second < other.second;
68 return type < other.type;
69 }
70};
71
73class SCORE_PLUGIN_GFX_EXPORT GfxContext : public QObject
74{
75 friend class GfxExecutionAction;
76
77public:
78 using NodePtr = std::unique_ptr<score::gfx::Node>;
79
80 explicit GfxContext(const score::DocumentContext& ctx);
82
83 int32_t register_node(NodePtr node);
84 int32_t register_preview_node(std::unique_ptr<score::gfx::OutputNode> node);
85 void connect_preview_node(EdgeSpec e);
86 void disconnect_preview_node(EdgeSpec e);
87 void unregister_node(int32_t idx);
88 void unregister_preview_node(int32_t idx);
89
99 int32_t add_preview(
100 port_index producer, QSize size, std::shared_ptr<QRhiReadbackResult> readback);
101 void set_preview_producer(int32_t id, port_index producer);
102 void set_preview_size(int32_t id, QSize size);
103 void remove_preview(int32_t id);
105 bool preview_is_tapped(int32_t id) const noexcept;
107 bool preview_is_on_hub(int32_t id) const noexcept;
110 void want_preview_frame(int32_t id) const noexcept;
112 static constexpr int max_preview_size = 1024;
113
114 // Synchronously tear down an output node's render list and drop it from the
115 // graph. Used by device-owned outputs (e.g. the offscreen BackgroundNode)
116 // whose destructor runs during shutdown before the async node-command queue
117 // is drained — without this the graph is left with a dangling output pointer
118 // and a RenderList referencing an already-freed QRhi.
119 void destroyOutput(score::gfx::OutputNode* node);
120
123 std::span<score::gfx::OutputNode* const> outputs() const noexcept;
124
125 void recompute_edges();
126 void recompute_graph();
127 void recompute_connections();
128 void recomputeTimers();
129 void recomputeGraphTopology();
130 void incrementalEdgeUpdate(
131 const ossia::flat_set<EdgeSpec>& old_edges,
132 const ossia::flat_set<EdgeSpec>& cur_edges);
133
134 void update_inputs();
135 void updateGraph();
136
160 void renderFrames(int frames);
161
163 double stepRate() const noexcept { return m_stepRate; }
164
167 void setStepRate(double fps);
168
170 bool executionStepped() const noexcept;
171
180 bool renderInProgress() const noexcept;
181
182 void send_message(score::gfx::Message&& msg) noexcept
183 {
184 tick_messages.enqueue(std::move(msg));
185 }
186
196 AssetTable& assets() noexcept { return m_assets; }
197 const AssetTable& assets() const noexcept { return m_assets; }
198
199private:
201 void updatePreviews();
202 struct Preview
203 {
204 port_index producer{-1, -1};
205 QSize size;
206 std::shared_ptr<QRhiReadbackResult> readback;
207 std::shared_ptr<score::gfx::PreviewTap> tap;
209 int32_t tappedNode{-1};
211 int hubInput{-1};
212 port_index hubProducer{-1, -1};
215 bool hubUnreadable{};
217 int32_t fallbackId{-1};
218 score::gfx::BackgroundNode* fallback{};
219 port_index fallbackProducer{-1, -1};
220 };
221 bool joinPreviewHub(Preview& p);
222 void releaseUnusedPreviewHub();
223 void leavePreviewHub(Preview& p);
224 void startPreviewFallback(Preview& p);
225 void stopPreviewFallback(Preview& p);
228 score::gfx::PreviewHubNode* m_previewHub{};
229 int32_t m_previewHubId{-1};
230 ossia::flat_map<int32_t, Preview> m_previews;
231 int32_t m_nextPreview{1};
232
233 void run_commands();
234 void add_preview_output(score::gfx::OutputNode& out);
235 void remove_preview_output();
236 void add_edge(EdgeSpec e);
237 void remove_edge(EdgeSpec e);
238 void forgetDeferredEdge(const EdgeSpec& spec) noexcept;
239 void remove_node(std::vector<std::unique_ptr<score::gfx::Node>>& nursery, int32_t id);
240
241 void on_no_vsync_timer(score::HighResolutionTimer* self);
242 void on_watchdog_timer(score::HighResolutionTimer* self);
243
244 bool frameInProgress() const noexcept;
245 bool refuseNestedRender(const char* entry) noexcept;
246
247 const score::DocumentContext& m_context;
248 std::atomic_int32_t index{1};
249 ossia::hash_map<int32_t, NodePtr> nodes;
250
251 score::gfx::Graph* m_graph{};
252 QThread m_thread;
253
254 double m_stepRate{60.};
255 int64_t m_stepFrame{};
256 int64_t m_stepPlayCount{-1};
257
258 struct NodeCommand
259 {
260 enum
261 {
262 ADD_NODE,
263 ADD_PREVIEW_NODE,
264 REMOVE_NODE,
265 REMOVE_PREVIEW_NODE,
266 RELINK
267 } cmd{};
268 int32_t index{};
269 std::unique_ptr<score::gfx::Node> node;
270 };
271
272 struct EdgeCommand
273 {
274 enum
275 {
276 CONNECT_PREVIEW_NODE,
277 DISCONNECT_PREVIEW_NODE
278 } cmd{};
279 EdgeSpec edge;
280 };
281
282 using Command = ossia::variant<NodeCommand, EdgeCommand>;
283 moodycamel::ConcurrentQueue<Command> tick_commands;
284 moodycamel::ConcurrentQueue<score::gfx::Message> tick_messages;
285 std::vector<std::pair<score::gfx::Message, int>> m_deferredMessages;
287 std::vector<score::gfx::Message> m_drainedMessages;
288 std::vector<std::pair<EdgeSpec, int>> m_deferredEdges;
289
290 std::mutex m_disownedLock;
291 ossia::flat_set<int32_t> m_disownedPending;
292 bool isDisownedPending(int32_t index);
293
294 std::mutex edges_lock;
295 ossia::flat_set<EdgeSpec> new_edges TS_GUARDED_BY(edges_lock);
296 ossia::flat_set<EdgeSpec> edges TS_GUARDED_BY(edges_lock);
297 ossia::flat_set<EdgeSpec> preview_edges TS_GUARDED_BY(edges_lock);
298 std::atomic_bool edges_changed{};
299 bool m_fullRebuildThisFrame{};
300
301 score::HighResolutionTimer* m_no_vsync_timer{};
302 score::HighResolutionTimer* m_watchdog_timer{};
303 // Rate == 0 with vsync off: zero-interval timer, renders back-to-back.
304 QTimer* m_freewheel_timer{};
305
306 // Per-output render clocks (the render-clock / genlock abstraction).
307 // Each TimerClock owns one shared HighResolutionTimer at a given
308 // manualRenderingRate and the coalesced set of outputs driven by it
309 // (clock #2, the default); the single DisplayVSyncClock wraps the
310 // swap-chain vsync callback (clock #1).
311 std::vector<std::unique_ptr<score::gfx::TimerClock>> m_renderClocks;
312 std::unique_ptr<score::gfx::DisplayVSyncClock> m_vsyncClock;
313
314 ossia::object_pool<std::vector<score::gfx::gfx_input>> m_buffers;
315
316 AssetTable m_assets;
317
318 score::Timers m_timers;
319
320 int m_renderDepth{};
321 int m_nestedRenders{};
322};
323
324}
Cross-RenderList content-hash dedup for decoded asset bytes.
Definition AssetTable.hpp:71
Definition GfxContext.hpp:74
AssetTable & assets() noexcept
Session-wide content-hash decode cache.
Definition GfxContext.hpp:196
Definition GfxExecContext.hpp:19
Definition Timers.hpp:14
Definition Timers.hpp:38
Base class for sink nodes (QWindow, spout, syphon, NDI output, ...)
Definition OutputNode.hpp:56
Binds the rendering pipeline to ossia processes.
Definition AssetTable.cpp:7
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Base toolkit upon which the software is built.
Definition Application.cpp:83
STL namespace.
Definition GfxContext.hpp:47
Definition DocumentContext.hpp:18
Definition BackgroundNode.hpp:16
Represents a graph of renderers.
Definition score-plugin-gfx/Gfx/Graph/Graph.hpp:25
The output that renders, for the editor's previews, the nodes no output of the patch renders.
Definition PreviewHubNode.hpp:24