MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BackendWindowHandler.cpp
Go to the documentation of this file.
2
4#include "VKSwapchain.hpp"
5
9
10namespace MayaFlux::Core {
11
13{
14 vk::Device device = context.get_device();
15
16 device.waitIdle();
17
18 for (auto& img : image_available) {
19 if (img) {
20 device.destroySemaphore(img);
21 }
22 }
23 image_available.clear();
24
25 for (auto& render : render_finished) {
26 if (render) {
27 device.destroySemaphore(render);
28 }
29 }
30 render_finished.clear();
31
32 for (auto& fence : in_flight) {
33 if (fence) {
34 device.destroyFence(fence);
35 }
36 }
37 in_flight.clear();
38
39 if (swapchain) {
40 swapchain->cleanup();
41 swapchain.reset();
42 }
43
44 if (surface) {
45 context.destroy_surface(surface);
46 surface = nullptr;
47 }
48
49 window->set_graphics_registered(false);
50}
51
53 : m_context(context)
54 , m_command_manager(command_manager)
55{
56}
57
58void BackendWindowHandler::setup_backend_service(const std::shared_ptr<Registry::Service::DisplayService>& display_service)
59{
60 display_service->submit_and_present = [this](
61 const std::shared_ptr<void>& window_ptr,
62 uint64_t primary_cmd_bits) {
63 auto window = std::static_pointer_cast<Window>(window_ptr);
64 vk::CommandBuffer primary_cmd = *reinterpret_cast<vk::CommandBuffer*>(&primary_cmd_bits);
65
66 this->submit_and_present(window, primary_cmd);
67 };
68
69 display_service->wait_idle = [this]() {
70 m_context.get_device().waitIdle();
71 };
72
73 display_service->resize_surface = [this](const std::shared_ptr<void>& window_ptr, uint32_t width, uint32_t height) {
74 auto window = std::static_pointer_cast<Window>(window_ptr);
75 window->set_size(width, height);
76 for (auto& ctx : m_window_contexts) {
77 if (ctx.window == window) {
78 ctx.needs_recreation = true;
79 break;
80 }
81 }
82 };
83
84 display_service->get_swapchain_image_count = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
85 auto window = std::static_pointer_cast<Window>(window_ptr);
86 for (const auto& ctx : m_window_contexts) {
87 if (ctx.window == window) {
88 return static_cast<uint32_t>(ctx.swapchain->get_image_count());
89 }
90 }
91 return 0;
92 };
93
94 display_service->get_swapchain_format = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
95 auto window = std::static_pointer_cast<Window>(window_ptr);
96 for (const auto& ctx : m_window_contexts) {
97 if (ctx.window == window) {
98 return static_cast<int>(ctx.swapchain->get_image_format());
99 }
100 }
101 return 0;
102 };
103
104 display_service->get_swapchain_extent = [this](
105 const std::shared_ptr<void>& window_ptr,
106 uint32_t& out_width,
107 uint32_t& out_height) {
108 auto window = std::static_pointer_cast<Window>(window_ptr);
109 auto* context = find_window_context(window);
110
111 if (context && context->swapchain) {
112 auto extent = context->swapchain->get_extent();
113 out_width = extent.width;
114 out_height = extent.height;
115 } else {
116 out_width = 0;
117 out_height = 0;
118 }
119 };
120
121 display_service->acquire_next_swapchain_image = [this](const std::shared_ptr<void>& window_ptr) -> uint64_t {
122 auto window = std::static_pointer_cast<Window>(window_ptr);
123 auto* ctx = find_window_context(window);
124 if (!ctx) {
126 "Window '{}' not registered for swapchain acquisition",
127 window->get_create_info().title);
128 return 0;
129 }
130
131 auto device = m_context.get_device();
132 size_t frame_index = ctx->current_frame;
133 auto& in_flight = ctx->in_flight[frame_index];
134 auto& image_available = ctx->image_available[frame_index];
135
136 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
138 "Fence timeout during swapchain acquisition for window '{}'",
139 window->get_create_info().title);
140 return 0;
141 }
142
143 auto image_index_opt = ctx->swapchain->acquire_next_image(image_available);
144 if (!image_index_opt.has_value()) {
145 ctx->needs_recreation = true;
146 return 0;
147 }
148
149 ctx->current_image_index = image_index_opt.value();
150
151 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
153 "Failed to reset fence for window '{}'",
154 window->get_create_info().title);
155 return 0;
156 }
157
158 const auto& images = ctx->swapchain->get_images();
159 VkImage raw = images[ctx->current_image_index];
160 return reinterpret_cast<uint64_t>(raw);
161 };
162
163 display_service->get_current_image_view = [this](const std::shared_ptr<void>& window_ptr) -> void* {
164 auto window = std::static_pointer_cast<Window>(window_ptr);
165 auto* context = find_window_context(window);
166
167 if (!context || !context->swapchain) {
168 return nullptr;
169 }
170
171 const auto& image_views = context->swapchain->get_image_views();
172 if (context->current_image_index >= image_views.size()) {
174 "Invalid current_image_index {} for window '{}' (swapchain has {} images)",
175 context->current_image_index,
176 window->get_create_info().title,
177 image_views.size());
178 return nullptr;
179 }
180
181 static thread_local vk::ImageView view;
182 view = image_views[context->current_image_index];
183 return static_cast<void*>(&view);
184 };
185}
186
188{
189 auto it = std::ranges::find_if(m_window_contexts,
190 [window](const auto& config) { return config.window == window; });
191 return it != m_window_contexts.end() ? &(*it) : nullptr;
192}
193
194const WindowRenderContext* BackendWindowHandler::find_window_context(const std::shared_ptr<Window>& window) const
195{
196 auto it = std::ranges::find_if(m_window_contexts,
197 [window](const auto& config) { return config.window == window; });
198 return it != m_window_contexts.end() ? &(*it) : nullptr;
199}
200
201bool BackendWindowHandler::register_window(const std::shared_ptr<Window>& window)
202{
203 if (window->is_graphics_registered() || find_window_context(window)) {
204 return false;
205 }
206
207 vk::SurfaceKHR surface = m_context.create_surface(window);
208 if (!surface) {
210 "Failed to create Vulkan surface for window '{}'",
211 window->get_create_info().title);
212 return false;
213 }
214
215 if (!m_context.update_present_family(surface)) {
217 "No presentation support for window '{}'", window->get_create_info().title);
218 m_context.destroy_surface(surface);
219 return false;
220 }
221
222 WindowRenderContext config;
223 config.window = window;
224 config.surface = surface;
225 config.swapchain = std::make_unique<VKSwapchain>();
226
227 if (!config.swapchain->create(m_context, surface, window->get_create_info())) {
229 "Failed to create swapchain for window '{}'", window->get_create_info().title);
230 return false;
231 }
232
233 if (!create_sync_objects(config)) {
235 "Failed to create sync objects for window '{}'",
236 window->get_create_info().title);
237 config.swapchain->cleanup();
238 m_context.destroy_surface(surface);
239 return false;
240 }
241
242 m_window_contexts.emplace_back(std::move(config));
243 window->set_graphics_registered(true);
244
245 window->set_event_callback([this, window_ptr = window](const WindowEvent& event) {
247 auto* config = find_window_context(window_ptr);
248 if (config) {
249 config->needs_recreation = true;
250 }
251 }
252 });
253
255 "Registered window '{}' for graphics processing", window->get_create_info().title);
256
257 return true;
258}
259
260bool BackendWindowHandler::create_sync_objects(WindowRenderContext& config)
261{
262 auto device = m_context.get_device();
263 uint32_t image_count = config.swapchain->get_image_count();
264
265 try {
266 config.image_available.resize(image_count);
267 config.render_finished.resize(image_count);
268 config.in_flight.resize(image_count);
269
270 vk::SemaphoreCreateInfo semaphore_info {};
271 vk::FenceCreateInfo fence_info {};
272 fence_info.flags = vk::FenceCreateFlagBits::eSignaled;
273
274 for (uint32_t i = 0; i < image_count; ++i) {
275 config.image_available[i] = device.createSemaphore(semaphore_info);
276 config.render_finished[i] = device.createSemaphore(semaphore_info);
277 }
278
279 for (auto& i : config.in_flight) {
280 i = device.createFence(fence_info);
281 }
282
283 config.current_frame = 0;
284
285 return true;
286 } catch (const vk::SystemError& e) {
288 "Failed to create sync objects: {}", e.what());
289 return false;
290 }
291}
292
293void BackendWindowHandler::recreate_swapchain_for_context(WindowRenderContext& context)
294{
295 m_context.wait_idle();
296
297 if (recreate_swapchain_internal(context)) {
298 context.needs_recreation = false;
300 "Recreated swapchain for window '{}' ({}x{})",
301 context.window->get_create_info().title,
302 context.window->get_state().current_width,
303 context.window->get_state().current_height);
304 }
305}
306
307bool BackendWindowHandler::recreate_swapchain_internal(WindowRenderContext& context)
308{
309 const auto& state = context.window->get_state();
310
311 if (!context.swapchain->recreate(state.current_width, state.current_height)) {
313 "Failed to recreate swapchain for window '{}'",
314 context.window->get_create_info().title);
315 return false;
316 }
317
318 return true;
319}
320
321void BackendWindowHandler::render_window(const std::shared_ptr<Window>& window)
322{
324 "render_window() is not implemented in BackendWindowHandler. Dynamic rendering is expected to be handled externally.");
325}
326
327void BackendWindowHandler::render_all_windows()
328{
330 "render_all_windows() is not implemented in BackendWindowHandler. Dynamic rendering is expected to be handled externally.");
331}
332
333void BackendWindowHandler::submit_and_present(
334 const std::shared_ptr<Window>& window,
335 const vk::CommandBuffer& command_buffer)
336{
337 auto* ctx = find_window_context(window);
338 if (!ctx) {
340 "Window not registered for submit_and_present");
341 return;
342 }
343
344 auto graphics_queue = m_context.get_graphics_queue();
345
346 size_t frame_index = ctx->current_frame;
347 auto& in_flight = ctx->in_flight[frame_index];
348 auto& image_available = ctx->image_available[frame_index];
349 auto& render_finished = ctx->render_finished[frame_index];
350
351 vk::SubmitInfo submit_info {};
352 vk::PipelineStageFlags wait_stages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
353 submit_info.waitSemaphoreCount = 1;
354 submit_info.pWaitSemaphores = &image_available;
355 submit_info.pWaitDstStageMask = wait_stages;
356 submit_info.commandBufferCount = 1;
357 submit_info.pCommandBuffers = &command_buffer;
358 submit_info.signalSemaphoreCount = 1;
359 submit_info.pSignalSemaphores = &render_finished;
360
361 try {
362 auto result = graphics_queue.submit(1, &submit_info, in_flight);
363 } catch (const vk::SystemError& e) {
365 "Failed to submit primary command buffer: {}", e.what());
366 return;
367 }
368
369 bool present_success = ctx->swapchain->present(
370 ctx->current_image_index, render_finished, graphics_queue);
371
372 if (!present_success) {
373 ctx->needs_recreation = true;
374 }
375
376 ctx->current_frame = (frame_index + 1) % ctx->in_flight.size();
377
379 "Window '{}': frame submitted and presented",
380 window->get_create_info().title);
381}
382
383bool BackendWindowHandler::is_window_registered(const std::shared_ptr<Window>& window) const
384{
385 return find_window_context(window) != nullptr;
386}
387
388void BackendWindowHandler::unregister_window(const std::shared_ptr<Window>& window)
389{
390 auto it = m_window_contexts.begin();
391 while (it != m_window_contexts.end()) {
392 if (it->window == window) {
393 it->cleanup(m_context);
395 "Unregistered window '{}'", it->window->get_create_info().title);
396 it = m_window_contexts.erase(it);
397 return;
398 }
399 ++it;
400 }
401}
402
403void BackendWindowHandler::handle_window_resize()
404{
405 for (auto& context : m_window_contexts) {
406 if (context.needs_recreation) {
407 recreate_swapchain_for_context(context);
408 }
409 }
410}
411
412void BackendWindowHandler::cleanup()
413{
414 for (auto& config : m_window_contexts) {
415 config.cleanup(m_context);
416 }
417 m_window_contexts.clear();
418}
419
420}
#define MF_INFO(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
bool register_window(const std::shared_ptr< Window > &window)
std::vector< WindowRenderContext > m_window_contexts
void setup_backend_service(const std::shared_ptr< Registry::Service::DisplayService > &display_service)
BackendWindowHandler(VKContext &context, VKCommandManager &command_manager)
void submit_and_present(const std::shared_ptr< Window > &window, const vk::CommandBuffer &command_buffer)
bool create_sync_objects(WindowRenderContext &config)
Create synchronization objects for a window's swapchain.
WindowRenderContext * find_window_context(const std::shared_ptr< Window > &window)
Manages Vulkan command pools and command buffers.
bool update_present_family(vk::SurfaceKHR surface)
Update presentation support for a surface.
vk::Device get_device() const
Get logical device.
Definition VKContext.hpp:49
vk::SurfaceKHR create_surface(std::shared_ptr< Window > window)
Create surface from window's native handles.
Definition VKContext.cpp:58
void destroy_surface(vk::SurfaceKHR surface)
Destroy a specific surface Called when window is unregistered.
High-level wrapper for Vulkan instance and device.
Definition VKContext.hpp:16
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ GraphicsSubsystem
Graphics subsystem operations (Vulkan, rendering pipeline)
@ GraphicsCallback
Graphics/visual rendering callback - frame-rate real-time.
@ Core
Core engine, backend, subsystems.
Event data for window and input events.
std::vector< vk::Semaphore > image_available
std::unique_ptr< VKSwapchain > swapchain
std::vector< vk::Semaphore > render_finished