MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BackendWindowHandler.cpp
Go to the documentation of this file.
2
6#include "VKSwapchain.hpp"
7
11
12namespace MayaFlux::Core {
13
15{
16 vk::Device device = context.get_device();
17
18 device.waitIdle();
19
20 for (auto& img : image_available) {
21 if (img) {
22 device.destroySemaphore(img);
23 }
24 }
25 image_available.clear();
26
27 for (auto& render : render_finished) {
28 if (render) {
29 device.destroySemaphore(render);
30 }
31 }
32 render_finished.clear();
33
35
36 for (auto& fence : in_flight) {
37 if (fence) {
38 device.destroyFence(fence);
39 }
40 }
41 in_flight.clear();
42
43 if (depth_image && depth_image->is_initialized()) {
44 const auto& res = depth_image->get_image_resources();
45 if (res.image_view) {
46 device.destroyImageView(res.image_view);
47 }
48 if (res.memory) {
49 device.freeMemory(res.memory);
50 }
51 if (res.image) {
52 device.destroyImage(res.image);
53 }
54 depth_image.reset();
55 }
56
57 if (swapchain) {
58 swapchain->cleanup();
59 swapchain.reset();
60 }
61
62 if (surface) {
63 context.destroy_surface(surface);
64 surface = nullptr;
65 }
66
67 window->set_graphics_registered(false);
68}
69
71 : m_context(context)
72 , m_command_manager(command_manager)
73{
74}
75
76void BackendWindowHandler::setup_backend_service(const std::shared_ptr<Registry::Service::DisplayService>& display_service)
77{
78 display_service->submit_and_present = [this](
79 const std::shared_ptr<void>& window_ptr,
80 uint64_t primary_cmd_bits) {
81 auto window = std::static_pointer_cast<Window>(window_ptr);
82 vk::CommandBuffer primary_cmd = *reinterpret_cast<vk::CommandBuffer*>(&primary_cmd_bits);
83
84 this->submit_and_present(window, primary_cmd);
85 };
86
87 display_service->wait_idle = [this]() {
88 m_context.get_device().waitIdle();
89 };
90
91 display_service->resize_surface = [this](const std::shared_ptr<void>& window_ptr, uint32_t width, uint32_t height) {
92 auto window = std::static_pointer_cast<Window>(window_ptr);
93 window->set_size(width, height);
94 for (auto& ctx : m_window_contexts) {
95 if (ctx.window == window) {
96 ctx.needs_recreation = true;
97 break;
98 }
99 }
100 };
101
102 display_service->get_swapchain_image_count = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
103 auto window = std::static_pointer_cast<Window>(window_ptr);
104 for (const auto& ctx : m_window_contexts) {
105 if (ctx.window == window) {
106 return static_cast<uint32_t>(ctx.swapchain->get_image_count());
107 }
108 }
109 return 0;
110 };
111
112 display_service->get_swapchain_format = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
113 auto window = std::static_pointer_cast<Window>(window_ptr);
114 for (const auto& ctx : m_window_contexts) {
115 if (ctx.window == window) {
116 return static_cast<int>(ctx.swapchain->get_image_format());
117 }
118 }
119 return 0;
120 };
121
122 display_service->get_swapchain_extent = [this](
123 const std::shared_ptr<void>& window_ptr,
124 uint32_t& out_width,
125 uint32_t& out_height) {
126 auto window = std::static_pointer_cast<Window>(window_ptr);
127 auto* context = find_window_context(window);
128
129 if (context && context->swapchain) {
130 auto extent = context->swapchain->get_extent();
131 out_width = extent.width;
132 out_height = extent.height;
133 } else {
134 out_width = 0;
135 out_height = 0;
136 }
137 };
138
139 display_service->acquire_next_swapchain_image = [this](const std::shared_ptr<void>& window_ptr) -> uint64_t {
140 auto window = std::static_pointer_cast<Window>(window_ptr);
141 auto* ctx = find_window_context(window);
142 if (!ctx) {
144 "Window '{}' not registered for swapchain acquisition",
145 window->get_create_info().title);
146 return 0;
147 }
148
149 auto device = m_context.get_device();
150 size_t frame_index = ctx->current_frame;
151 auto& in_flight = ctx->in_flight[frame_index];
152 auto& image_available = ctx->image_available[frame_index];
153
154 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
156 "Fence timeout during swapchain acquisition for window '{}'",
157 window->get_create_info().title);
158 return 0;
159 }
160
161 auto image_index_opt = ctx->swapchain->acquire_next_image(image_available);
162 if (!image_index_opt.has_value()) {
163 ctx->needs_recreation = true;
164 return 0;
165 }
166
167 ctx->current_image_index = image_index_opt.value();
168
169 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
171 "Failed to reset fence for window '{}'",
172 window->get_create_info().title);
173 return 0;
174 }
175
176 const auto& images = ctx->swapchain->get_images();
177 VkImage raw = images[ctx->current_image_index];
178 return reinterpret_cast<uint64_t>(raw);
179 };
180
181 display_service->get_current_image_view = [this](const std::shared_ptr<void>& window_ptr) -> void* {
182 auto window = std::static_pointer_cast<Window>(window_ptr);
183 auto* context = find_window_context(window);
184
185 if (!context || !context->swapchain) {
186 return nullptr;
187 }
188
189 const auto& image_views = context->swapchain->get_image_views();
190 if (context->current_image_index >= image_views.size()) {
192 "Invalid current_image_index {} for window '{}' (swapchain has {} images)",
193 context->current_image_index,
194 window->get_create_info().title,
195 image_views.size());
196 return nullptr;
197 }
198
199 static thread_local vk::ImageView view;
200 view = image_views[context->current_image_index];
201 return static_cast<void*>(&view);
202 };
203
204 display_service->get_current_swapchain_image = [this](const std::shared_ptr<void>& window_ptr) -> uint64_t {
205 auto window = std::static_pointer_cast<Window>(window_ptr);
206 auto* ctx = find_window_context(window);
207 if (!ctx || !ctx->swapchain)
208 return 0;
209
210 const auto& images = ctx->swapchain->get_images();
211 if (ctx->current_image_index >= images.size())
212 return 0;
213
214 return reinterpret_cast<uint64_t>(static_cast<VkImage>(images[ctx->current_image_index]));
215 };
216
217 display_service->readback_swapchain_region = [this](
218 const std::shared_ptr<void>& window_ptr,
219 void* dst,
220 uint32_t /*x_offset*/,
221 uint32_t /*y_offset*/,
222 uint32_t pixel_width,
223 uint32_t pixel_height,
224 size_t byte_count) -> bool {
225 auto window = std::static_pointer_cast<Window>(window_ptr);
226 auto* ctx = find_window_context(window);
227
228 if (!ctx || !ctx->swapchain) {
230 "readback_swapchain_region: window '{}' has no swapchain",
231 window->get_create_info().title);
232 return false;
233 }
234
235 if (!m_resource_manager) {
237 "readback_swapchain_region: resource manager not set");
238 return false;
239 }
240
241 const auto& images = ctx->swapchain->get_images();
242 if (ctx->current_image_index >= images.size()) {
244 "readback_swapchain_region: invalid image index {} for '{}'",
245 ctx->current_image_index, window->get_create_info().title);
246 return false;
247 }
248
249 auto proxy = std::make_shared<VKImage>(
250 pixel_width, pixel_height, 1U,
251 ctx->swapchain->get_image_format(),
254 1U, 1U,
256
257 VKImageResources res {};
258 res.image = images[ctx->current_image_index];
259 proxy->set_image_resources(res);
260 proxy->set_current_layout(vk::ImageLayout::ePresentSrcKHR);
261
263 proxy,
264 dst,
265 byte_count,
266 vk::ImageLayout::ePresentSrcKHR,
267 vk::PipelineStageFlagBits::eBottomOfPipe);
268
269 return true;
270 };
271
272 display_service->ensure_depth_attachment = [this](const std::shared_ptr<void>& window_ptr) {
273 auto window = std::static_pointer_cast<Window>(window_ptr);
274 auto* ctx = find_window_context(window);
275 if (!ctx) {
277 "ensure_depth_attachment: window '{}' not registered",
278 window->get_create_info().title);
279 return;
280 }
281 ensure_depth_image(*ctx);
282 };
283
284 display_service->get_depth_image_view = [this](const std::shared_ptr<void>& window_ptr) -> void* {
285 auto window = std::static_pointer_cast<Window>(window_ptr);
286 auto* ctx = find_window_context(window);
287 if (!ctx || !ctx->depth_image || !ctx->depth_image->is_initialized()) {
288 return nullptr;
289 }
290 static thread_local vk::ImageView view;
291 view = ctx->depth_image->get_image_view();
292 return static_cast<void*>(&view);
293 };
294
295 display_service->get_depth_format = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
296 auto window = std::static_pointer_cast<Window>(window_ptr);
297 auto* ctx = find_window_context(window);
298 if (!ctx || !ctx->depth_image || !ctx->depth_image->is_initialized()) {
299 return static_cast<uint32_t>(vk::Format::eUndefined);
300 }
301 return static_cast<uint32_t>(ctx->depth_image->get_format());
302 };
303}
304
306{
307 auto it = std::ranges::find_if(m_window_contexts,
308 [window](const auto& config) { return config.window == window; });
309 return it != m_window_contexts.end() ? &(*it) : nullptr;
310}
311
312const WindowRenderContext* BackendWindowHandler::find_window_context(const std::shared_ptr<Window>& window) const
313{
314 auto it = std::ranges::find_if(m_window_contexts,
315 [window](const auto& config) { return config.window == window; });
316 return it != m_window_contexts.end() ? &(*it) : nullptr;
317}
318
319bool BackendWindowHandler::register_window(const std::shared_ptr<Window>& window)
320{
321 if (window->is_graphics_registered() || find_window_context(window)) {
322 return false;
323 }
324
325 vk::SurfaceKHR surface = m_context.create_surface(window);
326 if (!surface) {
328 "Failed to create Vulkan surface for window '{}'",
329 window->get_create_info().title);
330 return false;
331 }
332
333 if (!m_context.update_present_family(surface)) {
335 "No presentation support for window '{}'", window->get_create_info().title);
336 m_context.destroy_surface(surface);
337 return false;
338 }
339
340 WindowRenderContext config;
341 config.window = window;
342 config.surface = surface;
343 config.swapchain = std::make_unique<VKSwapchain>();
344
345 if (!config.swapchain->create(m_context, surface, window->get_create_info())) {
347 "Failed to create swapchain for window '{}'", window->get_create_info().title);
348 return false;
349 }
350
351 if (!create_sync_objects(config)) {
353 "Failed to create sync objects for window '{}'",
354 window->get_create_info().title);
355 config.swapchain->cleanup();
356 m_context.destroy_surface(surface);
357 return false;
358 }
359
360 m_window_contexts.emplace_back(std::move(config));
361 window->set_graphics_registered(true);
362
363 window->set_event_callback([this, window_ptr = window](const WindowEvent& event) {
365 auto* config = find_window_context(window_ptr);
366 if (config) {
367 config->needs_recreation = true;
368 }
369 }
370 });
371
373 "Registered window '{}' for graphics processing", window->get_create_info().title);
374
375 return true;
376}
377
378bool BackendWindowHandler::create_sync_objects(WindowRenderContext& config)
379{
380 auto device = m_context.get_device();
381 uint32_t image_count = config.swapchain->get_image_count();
382
383 try {
384 config.image_available.resize(image_count);
385 config.render_finished.resize(image_count);
386 config.in_flight.resize(image_count);
387 config.clear_command_buffers.resize(image_count);
388
389 vk::SemaphoreCreateInfo semaphore_info {};
390 vk::FenceCreateInfo fence_info {};
391 fence_info.flags = vk::FenceCreateFlagBits::eSignaled;
392
393 for (uint32_t i = 0; i < image_count; ++i) {
394 config.image_available[i] = device.createSemaphore(semaphore_info);
395 config.render_finished[i] = device.createSemaphore(semaphore_info);
396
397 config.clear_command_buffers[i] = m_command_manager.allocate_command_buffer(
398 vk::CommandBufferLevel::ePrimary);
399 }
400
401 for (auto& i : config.in_flight) {
402 i = device.createFence(fence_info);
403 }
404
405 config.current_frame = 0;
406
407 return true;
408 } catch (const vk::SystemError& e) {
410 "Failed to create sync objects: {}", e.what());
411 return false;
412 }
413}
414
415void BackendWindowHandler::recreate_swapchain_for_context(WindowRenderContext& context)
416{
417 m_context.wait_idle();
418
419 if (recreate_swapchain_internal(context)) {
420 context.needs_recreation = false;
422 "Recreated swapchain for window '{}' ({}x{})",
423 context.window->get_create_info().title,
424 context.window->get_state().current_width,
425 context.window->get_state().current_height);
426 }
427}
428
429bool BackendWindowHandler::recreate_swapchain_internal(WindowRenderContext& context)
430{
431 const auto& state = context.window->get_state();
432
433 if (!context.swapchain->recreate(state.current_width, state.current_height)) {
435 "Failed to recreate swapchain for window '{}'",
436 context.window->get_create_info().title);
437 return false;
438 }
439
440 if (context.depth_image) {
441 auto device = m_context.get_device();
442 const auto& res = context.depth_image->get_image_resources();
443 if (res.image_view) {
444 device.destroyImageView(res.image_view);
445 }
446 if (res.memory) {
447 device.freeMemory(res.memory);
448 }
449 if (res.image) {
450 device.destroyImage(res.image);
451 }
452 context.depth_image.reset();
453 }
454
455 return true;
456}
457
458void BackendWindowHandler::ensure_depth_image(WindowRenderContext& ctx)
459{
460 if (!ctx.swapchain) {
461 return;
462 }
463
464 auto extent = ctx.swapchain->get_extent();
465
466 if (ctx.depth_image && ctx.depth_image->is_initialized()
467 && ctx.depth_image->get_width() == extent.width
468 && ctx.depth_image->get_height() == extent.height) {
469 return;
470 }
471
472 auto device = m_context.get_device();
473
474 if (ctx.depth_image && ctx.depth_image->is_initialized()) {
475 device.waitIdle();
476 const auto& res = ctx.depth_image->get_image_resources();
477 if (res.image_view) {
478 device.destroyImageView(res.image_view);
479 }
480 if (res.memory) {
481 device.freeMemory(res.memory);
482 }
483 if (res.image) {
484 device.destroyImage(res.image);
485 }
486 ctx.depth_image.reset();
487 }
488
489 ctx.depth_image = std::make_shared<VKImage>(
490 extent.width, extent.height, 1,
491 vk::Format::eD32Sfloat,
492 VKImage::Usage::DEPTH_STENCIL,
493 VKImage::Type::TYPE_2D,
494 1, 1,
496
497 m_resource_manager->initialize_image(ctx.depth_image);
498
499 if (!ctx.depth_image->is_initialized()) {
501 "Failed to create depth image for window '{}'",
502 ctx.window->get_create_info().title);
503 ctx.depth_image.reset();
504 return;
505 }
506
507 m_resource_manager->transition_image_layout(
508 ctx.depth_image->get_image(),
509 vk::ImageLayout::eUndefined,
510 vk::ImageLayout::eDepthStencilAttachmentOptimal,
511 1, 1,
512 vk::ImageAspectFlagBits::eDepth);
513 ctx.depth_image->set_current_layout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
514
516 "Created depth image for window '{}': {}x{}, D32_SFLOAT",
517 ctx.window->get_create_info().title,
518 extent.width, extent.height);
519}
520
521void BackendWindowHandler::render_window(const std::shared_ptr<Window>& window)
522{
523 auto ctx = find_window_context(window);
524 if (!ctx) {
526 "Window '{}' not registered for rendering",
527 window->get_create_info().title);
528 return;
529 }
530
531 if (ctx->window && ctx->window->get_rendering_buffers().empty()) {
532 render_empty_window(*ctx);
533 }
534}
535
536void BackendWindowHandler::render_all_windows()
537{
539 "render_all_windows() is not implemented in BackendWindowHandler. Dynamic rendering is expected to be handled externally.");
540}
541
542void BackendWindowHandler::render_empty_window(WindowRenderContext& ctx)
543{
544 auto window = ctx.window;
545
546 if (!window || !window->get_state().is_visible || !window->get_rendering_buffers().empty()) {
547 return;
548 }
549
550 try {
551 auto device = m_context.get_device();
552
553 size_t frame_index = ctx.current_frame;
554 auto& in_flight = ctx.in_flight[frame_index];
555 auto& image_available = ctx.image_available[frame_index];
556 auto& render_finished = ctx.render_finished[frame_index];
557
558 auto cmd_buffer = ctx.clear_command_buffers[frame_index];
559 if (!cmd_buffer) {
561 "Clear command buffer not allocated for window '{}'",
562 window->get_create_info().title);
563 return;
564 }
565 cmd_buffer.reset({});
566
567 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
568 return;
569 }
570
571 auto image_index_opt = ctx.swapchain->acquire_next_image(image_available);
572 if (!image_index_opt.has_value()) {
573 ctx.needs_recreation = true;
574 return;
575 }
576 ctx.current_image_index = image_index_opt.value();
577
578 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
579 return;
580 }
581
582 vk::CommandBufferBeginInfo begin_info {};
583 begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
584 cmd_buffer.begin(begin_info);
585
586 const auto& image_views = ctx.swapchain->get_image_views();
587 auto extent = ctx.swapchain->get_extent();
588
589 vk::RenderingAttachmentInfo color_attachment {};
590 color_attachment.imageView = image_views[ctx.current_image_index];
591 color_attachment.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
592 color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
593 color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
594 color_attachment.clearValue.color = vk::ClearColorValue(
595 window->get_create_info().clear_color);
596
597 vk::RenderingInfo rendering_info {};
598 rendering_info.renderArea = vk::Rect2D { { 0, 0 }, extent };
599 rendering_info.layerCount = 1;
600 rendering_info.colorAttachmentCount = 1;
601 rendering_info.pColorAttachments = &color_attachment;
602
603 vk::ImageMemoryBarrier barrier {};
604 barrier.oldLayout = vk::ImageLayout::eUndefined;
605 barrier.newLayout = vk::ImageLayout::eColorAttachmentOptimal;
606 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
607 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
608 barrier.image = ctx.swapchain->get_images()[ctx.current_image_index];
609 barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
610 barrier.subresourceRange.levelCount = 1;
611 barrier.subresourceRange.layerCount = 1;
612 barrier.srcAccessMask = {};
613 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
614
615 cmd_buffer.pipelineBarrier(
616 vk::PipelineStageFlagBits::eTopOfPipe,
617 vk::PipelineStageFlagBits::eColorAttachmentOutput,
618 {}, {}, {}, barrier);
619
620 cmd_buffer.beginRendering(rendering_info);
621 cmd_buffer.endRendering();
622
623 barrier.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
624 barrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
625 barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
626 barrier.dstAccessMask = {};
627
628 cmd_buffer.pipelineBarrier(
629 vk::PipelineStageFlagBits::eColorAttachmentOutput,
630 vk::PipelineStageFlagBits::eBottomOfPipe,
631 {}, {}, {}, barrier);
632
633 cmd_buffer.end();
634
635 submit_and_present(window, cmd_buffer);
636
637 ctx.current_frame = (ctx.current_frame + 1) % ctx.in_flight.size();
638
639 } catch (const std::exception& e) {
641 "Failed to render empty window '{}': {}",
642 window->get_create_info().title, e.what());
643 }
644}
645
646void BackendWindowHandler::submit_and_present(
647 const std::shared_ptr<Window>& window,
648 const vk::CommandBuffer& command_buffer)
649{
650 auto* ctx = find_window_context(window);
651 if (!ctx) {
653 "Window not registered for submit_and_present");
654 return;
655 }
656
657 auto graphics_queue = m_context.get_graphics_queue();
658
659 size_t frame_index = ctx->current_frame;
660 auto& in_flight = ctx->in_flight[frame_index];
661 auto& image_available = ctx->image_available[frame_index];
662 auto& render_finished = ctx->render_finished[frame_index];
663
664 vk::SubmitInfo submit_info {};
665 vk::PipelineStageFlags wait_stages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
666 submit_info.waitSemaphoreCount = 1;
667 submit_info.pWaitSemaphores = &image_available;
668 submit_info.pWaitDstStageMask = wait_stages;
669 submit_info.commandBufferCount = 1;
670 submit_info.pCommandBuffers = &command_buffer;
671 submit_info.signalSemaphoreCount = 1;
672 submit_info.pSignalSemaphores = &render_finished;
673
674 try {
675 auto result = graphics_queue.submit(1, &submit_info, in_flight);
676 } catch (const vk::SystemError& e) {
678 "Failed to submit primary command buffer: {}", e.what());
679 return;
680 }
681
682 bool present_success = ctx->swapchain->present(
683 ctx->current_image_index, render_finished, graphics_queue);
684
685 if (!present_success) {
686 ctx->needs_recreation = true;
687 }
688
689 ctx->current_frame = (frame_index + 1) % ctx->in_flight.size();
690
692 "Window '{}': frame submitted and presented",
693 window->get_create_info().title);
694}
695
696bool BackendWindowHandler::is_window_registered(const std::shared_ptr<Window>& window) const
697{
698 return find_window_context(window) != nullptr;
699}
700
701void BackendWindowHandler::unregister_window(const std::shared_ptr<Window>& window)
702{
703 auto it = m_window_contexts.begin();
704 while (it != m_window_contexts.end()) {
705 if (it->window == window) {
706 it->cleanup(m_context);
708 "Unregistered window '{}'", it->window->get_create_info().title);
709 it = m_window_contexts.erase(it);
710 return;
711 }
712 ++it;
713 }
714}
715
716void BackendWindowHandler::handle_window_resize()
717{
718 for (auto& context : m_window_contexts) {
719 if (context.needs_recreation) {
720 recreate_swapchain_for_context(context);
721 }
722 }
723}
724
725void BackendWindowHandler::cleanup()
726{
727 for (auto& config : m_window_contexts) {
728 config.cleanup(m_context);
729 }
730 m_window_contexts.clear();
731}
732
733}
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
void download_image_data(std::shared_ptr< VKImage > image, void *data, size_t size, vk::ImageLayout restore_layout=vk::ImageLayout::eShaderReadOnlyOptimal, vk::PipelineStageFlags restore_stage=vk::PipelineStageFlagBits::eFragmentShader)
Download data from an image into a caller-supplied buffer.
void ensure_depth_image(WindowRenderContext &ctx)
Ensure depth image exists at current swapchain extent.
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
@ TEXTURE_2D
Sampled texture (shader read)
@ 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.
@ IMAGE_COLOR
2D RGB/RGBA image
@ IMAGE_2D
2D image (grayscale or single channel)
Vulkan image resource handles.
Definition VKImage.hpp:15
Event data for window and input events.
std::vector< vk::Semaphore > image_available
std::unique_ptr< VKSwapchain > swapchain
std::vector< vk::Semaphore > render_finished
std::vector< vk::CommandBuffer > clear_command_buffers