MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
GraphicsSubsystem.cpp
Go to the documentation of this file.
2
7
10
13
14namespace MayaFlux::Core {
15
16std::unique_ptr<IGraphicsBackend> create_graphics_backend(GlobalGraphicsConfig::GraphicsApi api)
17{
18 switch (api) {
20 return std::make_unique<VulkanBackend>();
24 default:
25 return nullptr;
26 }
27}
28
30 : m_backend(create_graphics_backend(graphics_config.requested_api))
31 , m_frame_clock(std::make_shared<Vruta::FrameClock>(60))
32 , m_subsystem_tokens {
33 .Buffer = Buffers::ProcessingToken::GRAPHICS_BACKEND,
34 .Node = Nodes::ProcessingToken::VISUAL_RATE,
35 .Task = Vruta::ProcessingToken::FRAME_ACCURATE
36 }
37 , m_graphics_config(graphics_config)
38{
39}
40
45
47{
49 "Initializing Graphics Subsystem...");
50
51 m_handle = &handle;
52
53 if (!m_backend->initialize(m_graphics_config)) {
54 error<std::runtime_error>(
57 std::source_location::current(),
58 "No graphics backend available");
59 }
60
63 }
64
66
68
70
71 m_is_ready = true;
72
74 "Graphics Subsystem initialized (Target FPS: {})",
75 m_frame_clock->frame_rate());
76}
77
79{
80 try {
81 if (auto vulkan_backend = dynamic_cast<VulkanBackend*>(m_backend.get())) {
82 Portal::Graphics::initialize(std::shared_ptr<VulkanBackend>(vulkan_backend, [](VulkanBackend*) { }));
83 }
84 } catch (std::exception& e) {
85 error_rethrow(
88 std::source_location::current(),
89 "Failed to initialize Portal::Graphics subsystem: {}",
90 e.what());
91 }
92}
93
95{
96 if (!m_handle) {
97 error<std::runtime_error>(
100 std::source_location::current(),
101 "Cannot register frame processor: no processing handle");
102 }
103
104 auto scheduler = m_handle->tasks;
105 if (!scheduler.is_valid()) {
106 error<std::runtime_error>(
109 std::source_location::current(),
110 "Cannot register frame processor: no scheduler available");
111 }
112
113 scheduler.register_token_processor(
114 [this](const std::vector<std::shared_ptr<Vruta::Routine>>& tasks, uint64_t processing_units) {
115 this->process_frame_coroutines_impl(tasks, processing_units);
116 });
117
119 "Registered custom FRAME_ACCURATE processor");
120}
121
123{
124 if (!m_is_ready) {
125 error<std::runtime_error>(
128 std::source_location::current(),
129 "Subsystem is not initialized. Please initialize before registering callbacks.");
130 }
131
133
135 "Graphics callbacks registered (self-driven mode)");
136}
137
139 const std::vector<std::shared_ptr<Vruta::Routine>>& tasks,
140 uint64_t processing_units)
141{
142 if (tasks.empty()) {
143 return;
144 }
145
146 uint64_t current_frame = m_frame_clock->current_position();
147
148 if (processing_units == 0) {
149 processing_units = 1;
150 }
151
152 for (uint64_t i = 0; i < processing_units; ++i) {
153 uint64_t frame_to_process = current_frame + i;
154
155 for (auto& routine : tasks) {
156 if (!routine || !routine->is_active()) {
157 continue;
158 }
159
160 if (routine->requires_clock_sync()) {
161 if (frame_to_process >= routine->next_execution()) {
162 routine->try_resume(frame_to_process);
163 }
164 } else {
165 routine->try_resume(frame_to_process);
166 }
167 }
168 }
169}
170
172{
173 if (m_running.load(std::memory_order_acquire)) {
175 "Graphics thread already running!");
176 return;
177 }
178
179 m_paused.store(false, std::memory_order_release);
180
181 m_frame_clock->reset();
182
183 m_graphics_thread = std::thread([this]() {
184 m_graphics_thread_id = std::this_thread::get_id();
185 m_running.store(true);
186
188 "Graphics thread started (ID: {}, Target FPS: {})",
189 std::hash<std::thread::id> {}(m_graphics_thread_id),
190 m_frame_clock->frame_rate());
191
192 try {
194 } catch (const std::exception& e) {
195 error_rethrow(
198 std::source_location::current(),
199 "Graphics thread error");
200 }
201
203 "Graphics thread stopped.");
204 });
205}
206
208{
209 if (!m_running.load(std::memory_order_acquire)) {
210 return;
211 }
212
214 "Stopping Graphics Subsystem...");
215
216 m_running.store(false, std::memory_order_release);
217
218 if (m_graphics_thread.joinable()) {
219 m_graphics_thread.join();
220 }
221
222 m_backend->wait_idle();
223
226 }
227
228 for (auto& window : m_registered_windows) {
229 window->set_graphics_registered(false);
230 }
231
233 "Graphics Subsystem stopped.");
234}
235
237{
238 if (!m_running.load(std::memory_order_acquire)) {
240 "Cannot pause - graphics thread not running");
241 return;
242 }
243
244 m_paused.store(true, std::memory_order_release);
245
247 "Graphics processing paused");
248}
249
251{
252 if (!m_running.load(std::memory_order_acquire)) {
254 "Cannot resume - graphics thread not running");
255 return;
256 }
257
258 m_paused.store(false, std::memory_order_release);
259
261 "Graphics processing resumed");
262}
263
265{
266 return m_frame_clock->frame_rate();
267}
268
270{
271 return m_frame_clock->get_measured_fps();
272}
273
275{
276 m_frame_clock->set_target_fps(fps);
277
279 "Target FPS updated to {}", fps);
280}
281
283{
284 if (!m_handle) {
285 return;
286 }
287
288 for (auto& [name, hook] : m_handle->pre_process_hooks) {
289 hook(1);
290 }
291
293
295
297
299 m_backend->handle_window_resize();
301
303
305 for (auto& [name, hook] : m_handle->post_process_hooks) {
306 hook(1);
307 }
308}
309
311{
312 for (auto& window : m_handle->windows.get_processing_windows()) {
313
314 if (window->is_graphics_registered()) {
315 continue;
316 }
317
318 if (m_backend->register_window(window)) {
319 m_registered_windows.push_back(window);
320 } else {
322 "Failed to register window '{}' for graphics processing",
323 window->get_create_info().title);
324 continue;
325 }
326 }
327}
328
330{
331 for (auto& window : m_registered_windows) {
332 if (window->should_close() && window->is_graphics_registered()) {
333 m_backend->unregister_window(window);
334 window->set_graphics_registered(false);
335 }
336 }
337
339 std::remove_if(
341 [](const std::shared_ptr<Window>& win) { return win->should_close(); }),
343}
344
346{
347 for (auto& window : m_registered_windows) {
348 m_backend->unregister_window(window);
349 window->set_graphics_registered(false);
350 }
351
352 m_registered_windows.clear();
353}
354
356{
357 for (auto& window : m_registered_windows) {
358 m_backend->render_window(window);
359 }
360}
361
363{
364 while (m_running.load(std::memory_order_acquire)) {
365 if (m_paused.load(std::memory_order_acquire)) {
366 std::this_thread::sleep_for(std::chrono::milliseconds(16));
367 continue;
368 }
369
370 m_frame_clock->tick();
371
372 process();
373
374 m_frame_clock->wait_for_next_frame();
375
376 if (m_frame_clock->is_frame_late()) {
377 uint64_t lag = m_frame_clock->get_frame_lag();
378 if (lag > 2) {
380 "Frame lag detected: {} frames behind (Measured FPS: {:.1f})",
381 lag, m_frame_clock->get_measured_fps());
382 }
383 }
384 }
385}
386
388{
389 while (!m_running.load(std::memory_order_acquire))
390 std::this_thread::yield();
391}
392
394{
395 stop();
396
398
400
402
403 m_backend->cleanup();
404
405 m_is_ready = false;
406
408 "Graphics Subsystem shutdown complete.");
409}
410
411}
#define MF_INFO(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
void process(uint32_t processing_units)
Process all channels in token domain.
void initialize(SubsystemProcessingHandle &handle) override
Initialize with graphics configuration.
void register_frame_processor()
Register custom frame processor with scheduler.
void initialize_graphics_portal()
Initialize Portal::Graphics subsystem.
void cleanup_closed_windows()
Cleanup resources for windows that have been closed.
void start() override
Start the subsystem's processing/event loops.
void pause() override
Pause the subsystem's processing/event loops.
std::vector< std::shared_ptr< Window > > m_registered_windows
void register_windows_for_processing()
Register markend windows from window manager for swapchain processing.
void wait_until_running() override
Block until the subsystem's processing loop is confirmed live.
void graphics_thread_loop()
Graphics thread main loop.
uint32_t get_target_fps() const
Get target frame rate.
void process_frame_coroutines_impl(const std::vector< std::shared_ptr< Vruta::Routine > > &tasks, uint64_t processing_units)
Process all FRAME_ACCURATE coroutines for given frames.
void render_all_windows()
Render all registered windows.
SubsystemProcessingHandle * m_handle
Reference to processing handle.
std::shared_ptr< Vruta::FrameClock > m_frame_clock
double get_measured_fps() const
Get actual measured FPS.
void set_target_fps(uint32_t fps)
Set target frame rate (can be changed at runtime)
std::unique_ptr< IGraphicsBackend > m_backend
void stop() override
Stop the subsystem's processing/event loops.
void register_callbacks() override
Register callback hooks for this domain.
GraphicsSubsystem(const GlobalGraphicsConfig &graphics_config)
GraphicsSubsystem constructor.
void teardown_windows()
Teardown all window resources during shutdown.
void resume() override
Resume the subsystem's processing/event loops.
void shutdown() override
Shutdown and cleanup subsystem resources.
GlobalGraphicsConfig m_graphics_config
Graphics/windowing configuration.
void process()
Unified processing callback (alternative to separate methods)
void process(uint32_t num_samples)
Process all nodes in token domain.
std::map< std::string, ProcessHook > post_process_hooks
NodeProcessingHandle nodes
Node processing interface.
std::map< std::string, ProcessHook > pre_process_hooks
BufferProcessingHandle buffers
Buffer processing interface.
Unified interface combining buffer and node processing for subsystems.
void register_clock(Vruta::ProcessingToken token, std::shared_ptr< Vruta::IClock > clock)
Register an externally-owned clock for a token domain.
void process(uint64_t processing_units)
Process all tasks in token domain.
Vulkan implementation of the IGraphicsBackend interface.
void process()
Process window events and frame hooks.
std::vector< std::shared_ptr< Core::Window > > get_processing_windows() const
Get list of windows that are open and not minimized.
std::unique_ptr< IGraphicsBackend > create_graphics_backend(GlobalGraphicsConfig::GraphicsApi api)
@ GraphicsSubsystem
Graphics subsystem operations (Vulkan, rendering pipeline)
@ Core
Core engine, backend, subsystems.
void stop()
Stop all Portal::Graphics operations.
Definition Graphics.cpp:69
void shutdown()
Shutdown Portal::Graphics subsystem.
Definition Graphics.cpp:87
bool is_initialized()
Check if Portal::Graphics is initialized.
Definition Graphics.cpp:109
bool initialize(const std::shared_ptr< Core::VulkanBackend > &backend)
Initialize Portal::Graphics Glue Layer.
Definition Graphics.cpp:16
void shutdown()
Shutdown Portal::Text.
Definition Text.cpp:61
bool initialize(std::optional< Core::TextConfig > config)
Initialize Portal::Text.
Definition Text.cpp:18
@ FRAME_ACCURATE
Coroutine is frame-accurate.
TextConfig text_config
Default font for Portal::Text.
uint32_t target_frame_rate
Target frame rate for visual processing (Hz)