MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
GraphicsSubsystem.cpp
Go to the documentation of this file.
2
7
10
12
13namespace MayaFlux::Core {
14
15std::unique_ptr<IGraphicsBackend> create_graphics_backend(GlobalGraphicsConfig::GraphicsApi api)
16{
17 switch (api) {
19 return std::make_unique<VulkanBackend>();
23 default:
24 return nullptr;
25 }
26}
27
29 : m_backend(create_graphics_backend(graphics_config.requested_api))
30 , m_frame_clock(std::make_shared<Vruta::FrameClock>(60))
31 , m_subsystem_tokens {
32 .Buffer = Buffers::ProcessingToken::GRAPHICS_BACKEND,
33 .Node = Nodes::ProcessingToken::VISUAL_RATE,
34 .Task = Vruta::ProcessingToken::FRAME_ACCURATE
35 }
36 , m_graphics_config(graphics_config)
37{
38}
39
44
46{
48 "Initializing Graphics Subsystem...");
49
50 m_handle = &handle;
51
52 if (!m_backend->initialize(m_graphics_config)) {
53 error<std::runtime_error>(
56 std::source_location::current(),
57 "No graphics backend available");
58 }
59
62 }
63
65
66 m_is_ready = true;
67
69 "Graphics Subsystem initialized (Target FPS: {})",
70 m_frame_clock->frame_rate());
71}
72
74{
75 try {
76 if (auto vulkan_backend = dynamic_cast<VulkanBackend*>(m_backend.get())) {
77 Portal::Graphics::initialize(std::shared_ptr<VulkanBackend>(vulkan_backend, [](VulkanBackend*) { }));
78 }
79 } catch (std::exception& e) {
80 error_rethrow(
83 std::source_location::current(),
84 "Failed to initialize Portal::Graphics subsystem: {}",
85 e.what());
86 }
87}
88
90{
91 if (!m_handle) {
92 error<std::runtime_error>(
95 std::source_location::current(),
96 "Cannot register frame processor: no processing handle");
97 }
98
99 auto scheduler = m_handle->tasks;
100 if (!scheduler.is_valid()) {
101 error<std::runtime_error>(
104 std::source_location::current(),
105 "Cannot register frame processor: no scheduler available");
106 }
107
108 scheduler.register_token_processor(
109 [this](const std::vector<std::shared_ptr<Vruta::Routine>>& tasks, uint64_t processing_units) {
110 this->process_frame_coroutines_impl(tasks, processing_units);
111 });
112
114 "Registered custom FRAME_ACCURATE processor");
115}
116
118{
119 if (!m_is_ready) {
120 error<std::runtime_error>(
123 std::source_location::current(),
124 "Subsystem is not initialized. Please initialize before registering callbacks.");
125 }
126
128
130 "Graphics callbacks registered (self-driven mode)");
131}
132
134 const std::vector<std::shared_ptr<Vruta::Routine>>& tasks,
135 uint64_t processing_units)
136{
137 if (tasks.empty()) {
138 return;
139 }
140
141 uint64_t current_frame = m_frame_clock->current_position();
142
143 if (processing_units == 0) {
144 processing_units = 1;
145 }
146
147 for (uint64_t i = 0; i < processing_units; ++i) {
148 uint64_t frame_to_process = current_frame + i;
149
150 for (auto& routine : tasks) {
151 if (!routine || !routine->is_active()) {
152 continue;
153 }
154
155 if (routine->requires_clock_sync()) {
156 if (frame_to_process >= routine->next_execution()) {
157 routine->try_resume(frame_to_process);
158 }
159 } else {
160 routine->try_resume(frame_to_process);
161 }
162 }
163 }
164}
165
167{
168 if (m_running.load(std::memory_order_acquire)) {
170 "Graphics thread already running!");
171 return;
172 }
173
174 m_running.store(true);
175 m_paused.store(false, std::memory_order_release);
176
177 m_frame_clock->reset();
178
179 m_graphics_thread = std::thread([this]() {
180 m_graphics_thread_id = std::this_thread::get_id();
181
183 "Graphics thread started (ID: {}, Target FPS: {})",
184 std::hash<std::thread::id> {}(m_graphics_thread_id),
185 m_frame_clock->frame_rate());
186
187 try {
189 } catch (const std::exception& e) {
190 error_rethrow(
193 std::source_location::current(),
194 "Graphics thread error");
195 }
196
198 "Graphics thread stopped.");
199 });
200}
201
203{
204 if (!m_running.load(std::memory_order_acquire)) {
205 return;
206 }
207
209 "Stopping Graphics Subsystem...");
210
211 m_running.store(false, std::memory_order_release);
212
213 if (m_graphics_thread.joinable()) {
214 m_graphics_thread.join();
215 }
216
218
219 m_backend->cleanup();
220
221 for (auto& window : m_registered_windows) {
222 window->set_graphics_registered(false);
223 }
224 m_registered_windows.clear();
225
227 "Graphics Subsystem stopped.");
228}
229
231{
232 if (!m_running.load(std::memory_order_acquire)) {
234 "Cannot pause - graphics thread not running");
235 return;
236 }
237
238 m_paused.store(true, std::memory_order_release);
239
241 "Graphics processing paused");
242}
243
245{
246 if (!m_running.load(std::memory_order_acquire)) {
248 "Cannot resume - graphics thread not running");
249 return;
250 }
251
252 m_paused.store(false, std::memory_order_release);
253
255 "Graphics processing resumed");
256}
257
259{
260 return m_frame_clock->frame_rate();
261}
262
264{
265 return m_frame_clock->get_measured_fps();
266}
267
269{
270 m_frame_clock->set_target_fps(fps);
271
273 "Target FPS updated to {}", fps);
274}
275
277{
278 if (!m_handle) {
279 return;
280 }
281
282 for (auto& [name, hook] : m_handle->pre_process_hooks) {
283 hook(1);
284 }
285
287
289
291
293 m_backend->handle_window_resize();
294
295 // render_all_windows();
297
299 for (auto& [name, hook] : m_handle->post_process_hooks) {
300 hook(1);
301 }
302}
303
305{
306 for (auto& window : m_handle->windows.get_processing_windows()) {
307
308 if (window->is_graphics_registered()) {
309 continue;
310 }
311
312 if (m_backend->register_window(window)) {
313 m_registered_windows.push_back(window);
314 } else {
316 "Failed to register window '{}' for graphics processing",
317 window->get_create_info().title);
318 continue;
319 }
320 }
321}
322
324{
325 for (auto& window : m_registered_windows) {
326 if (window->should_close() && window->is_graphics_registered()) {
327 m_backend->unregister_window(window);
328 window->set_graphics_registered(false);
329 }
330 }
331
333 std::remove_if(
335 [](const std::shared_ptr<Window>& win) { return win->should_close(); }),
337}
338
340{
341 for (auto& window : m_registered_windows) {
342 m_backend->render_window(window);
343 }
344}
345
347{
348 while (m_running.load(std::memory_order_acquire)) {
349 if (m_paused.load(std::memory_order_acquire)) {
350 std::this_thread::sleep_for(std::chrono::milliseconds(16));
351 continue;
352 }
353
354 m_frame_clock->tick();
355
356 process();
357
358 m_frame_clock->wait_for_next_frame();
359
360 if (m_frame_clock->is_frame_late()) {
361 uint64_t lag = m_frame_clock->get_frame_lag();
362 if (lag > 2) {
364 "Frame lag detected: {} frames behind (Measured FPS: {:.1f})",
365 lag, m_frame_clock->get_measured_fps());
366 }
367 }
368 }
369}
370
372{
373 stop();
374 m_is_ready = false;
375}
376
377}
#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 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 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 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 shutdown()
Shutdown Portal::Graphics subsystem.
Definition Graphics.cpp:69
bool initialize(const std::shared_ptr< Core::VulkanBackend > &backend)
Initialize Portal::Graphics Glue Layer.
Definition Graphics.cpp:16
uint32_t target_frame_rate
Target frame rate for visual processing (Hz)