MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ setup_backend_service()

void MayaFlux::Core::BackendWindowHandler::setup_backend_service ( const std::shared_ptr< Registry::Service::DisplayService > &  display_service)

Definition at line 138 of file BackendWindowHandler.cpp.

139{
140 display_service->submit_and_present = [this](
141 const std::shared_ptr<void>& window_ptr,
142 uint64_t primary_cmd_bits) {
143 auto window = std::static_pointer_cast<Window>(window_ptr);
144 vk::CommandBuffer primary_cmd = *reinterpret_cast<vk::CommandBuffer*>(&primary_cmd_bits);
145
146 this->submit_and_present(window, primary_cmd);
147 };
148
149 display_service->wait_idle = [this]() {
150 m_context.get_device().waitIdle();
151 };
152
153 display_service->resize_surface = [this](const std::shared_ptr<void>& window_ptr, uint32_t width, uint32_t height) {
154 auto window = std::static_pointer_cast<Window>(window_ptr);
155 window->set_size(width, height);
156 for (auto& ctx : m_window_contexts) {
157 if (ctx.window == window) {
158 ctx.needs_recreation = true;
159 break;
160 }
161 }
162 };
163
164 display_service->get_swapchain_image_count = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
165 auto window = std::static_pointer_cast<Window>(window_ptr);
166 for (const auto& ctx : m_window_contexts) {
167 if (ctx.window == window) {
168 return static_cast<uint32_t>(ctx.swapchain->get_image_count());
169 }
170 }
171 return 0;
172 };
173
174 display_service->get_submitted_frame_count = [this](const std::shared_ptr<void>& window_ptr) -> uint64_t {
175 const auto* ctx = find_window_context(std::static_pointer_cast<Window>(window_ptr));
176 return ctx ? ctx->submitted_frames : 0;
177 };
178
179 display_service->get_completed_frame_count = [this](const std::shared_ptr<void>& window_ptr) -> uint64_t {
180 const auto* ctx = find_window_context(std::static_pointer_cast<Window>(window_ptr));
181 return ctx ? completed_frame_count(*ctx) : 0;
182 };
183
184 display_service->get_swapchain_format = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
185 auto window = std::static_pointer_cast<Window>(window_ptr);
186 for (const auto& ctx : m_window_contexts) {
187 if (ctx.window == window) {
188 return static_cast<int>(ctx.swapchain->get_image_format());
189 }
190 }
191 return 0;
192 };
193
194 display_service->get_swapchain_extent = [this](
195 const std::shared_ptr<void>& window_ptr,
196 uint32_t& out_width,
197 uint32_t& out_height) {
198 auto window = std::static_pointer_cast<Window>(window_ptr);
199 auto* context = find_window_context(window);
200
201 if (context && context->swapchain) {
202 auto extent = context->swapchain->get_extent();
203 out_width = extent.width;
204 out_height = extent.height;
205 } else {
206 out_width = 0;
207 out_height = 0;
208 }
209 };
210
211 display_service->acquire_next_swapchain_image = [this](const std::shared_ptr<void>& window_ptr) -> uint64_t {
212 auto window = std::static_pointer_cast<Window>(window_ptr);
213 auto* ctx = find_window_context(window);
214 if (!ctx) {
216 "Window '{}' not registered for swapchain acquisition",
217 window->get_create_info().title);
218 return 0;
219 }
220
221 auto device = m_context.get_device();
222 size_t frame_index = ctx->current_frame;
223 auto& in_flight = ctx->in_flight[frame_index];
224 auto& image_available = ctx->image_available[frame_index];
225
226 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
228 "Fence timeout during swapchain acquisition for window '{}'",
229 window->get_create_info().title);
230 return 0;
231 }
232
233 auto image_index_opt = ctx->swapchain->acquire_next_image(image_available);
234 if (!image_index_opt.has_value()) {
235 ctx->needs_recreation = true;
236 return 0;
237 }
238
239 ctx->current_image_index = image_index_opt.value();
240
241 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
243 "Failed to reset fence for window '{}'",
244 window->get_create_info().title);
245 return 0;
246 }
247
248 const auto& images = ctx->swapchain->get_images();
249 VkImage raw = images[ctx->current_image_index];
250 return reinterpret_cast<uint64_t>(raw);
251 };
252
253 display_service->get_current_image_view = [this](const std::shared_ptr<void>& window_ptr) -> void* {
254 auto window = std::static_pointer_cast<Window>(window_ptr);
255 auto* context = find_window_context(window);
256
257 if (!context || !context->swapchain) {
258 return nullptr;
259 }
260
261 const auto& image_views = context->swapchain->get_image_views();
262 if (context->current_image_index >= image_views.size()) {
264 "Invalid current_image_index {} for window '{}' (swapchain has {} images)",
265 context->current_image_index,
266 window->get_create_info().title,
267 image_views.size());
268 return nullptr;
269 }
270
271 static thread_local vk::ImageView view;
272 view = image_views[context->current_image_index];
273 return static_cast<void*>(&view);
274 };
275
276 display_service->get_current_swapchain_image = [this](const std::shared_ptr<void>& window_ptr) -> uint64_t {
277 auto window = std::static_pointer_cast<Window>(window_ptr);
278 auto* ctx = find_window_context(window);
279 if (!ctx || !ctx->swapchain)
280 return 0;
281
282 const auto& images = ctx->swapchain->get_images();
283 if (ctx->current_image_index >= images.size())
284 return 0;
285
286 return reinterpret_cast<uint64_t>(static_cast<VkImage>(images[ctx->current_image_index]));
287 };
288
289 display_service->readback_swapchain_region = [this](
290 const std::shared_ptr<void>& window_ptr,
291 void* dst,
292 uint32_t /*x_offset*/,
293 uint32_t /*y_offset*/,
294 uint32_t pixel_width,
295 uint32_t pixel_height,
296 size_t byte_count) -> bool {
297 auto window = std::static_pointer_cast<Window>(window_ptr);
298 auto* ctx = find_window_context(window);
299
300 if (!ctx || !ctx->swapchain) {
302 "readback_swapchain_region: window '{}' has no swapchain",
303 window->get_create_info().title);
304 return false;
305 }
306
307 if (!m_resource_manager) {
309 "readback_swapchain_region: resource manager not set");
310 return false;
311 }
312
313 const auto& images = ctx->swapchain->get_images();
314 if (ctx->current_image_index >= images.size()) {
316 "readback_swapchain_region: invalid image index {} for '{}'",
317 ctx->current_image_index, window->get_create_info().title);
318 return false;
319 }
320
321 auto proxy = std::make_shared<VKImage>(
322 pixel_width, pixel_height, 1U,
323 ctx->swapchain->get_image_format(),
326 1U, 1U,
328
329 VKImageResources res {};
330 res.image = images[ctx->current_image_index];
331 proxy->set_image_resources(res);
332 proxy->set_current_layout(vk::ImageLayout::ePresentSrcKHR);
333
335 proxy,
336 dst,
337 byte_count,
338 nullptr,
339 false,
340 vk::ImageLayout::ePresentSrcKHR,
341 vk::PipelineStageFlagBits::eBottomOfPipe);
342
343 return true;
344 };
345
346 display_service->ensure_depth_attachment = [this](const std::shared_ptr<void>& window_ptr) {
347 auto window = std::static_pointer_cast<Window>(window_ptr);
348 auto* ctx = find_window_context(window);
349 if (!ctx) {
351 "ensure_depth_attachment: window '{}' not registered",
352 window->get_create_info().title);
353 return;
354 }
355 ensure_depth_image(*ctx);
356 };
357
358 display_service->get_depth_image_view = [this](const std::shared_ptr<void>& window_ptr) -> void* {
359 auto window = std::static_pointer_cast<Window>(window_ptr);
360 auto* ctx = find_window_context(window);
361 if (!ctx || !ctx->depth_image || !ctx->depth_image->is_initialized()) {
362 return nullptr;
363 }
364 static thread_local vk::ImageView view;
365 view = ctx->depth_image->get_image_view();
366 return static_cast<void*>(&view);
367 };
368
369 display_service->get_depth_format = [this](const std::shared_ptr<void>& window_ptr) -> uint32_t {
370 auto window = std::static_pointer_cast<Window>(window_ptr);
371 auto* ctx = find_window_context(window);
372 if (!ctx || !ctx->depth_image || !ctx->depth_image->is_initialized()) {
373 return static_cast<uint32_t>(vk::Format::eUndefined);
374 }
375 return static_cast<uint32_t>(ctx->depth_image->get_format());
376 };
377
378 // ======================================================================
379 // get_last_frame – hazard‑pointer protected read + copy
380 // ======================================================================
381 display_service->get_last_frame = [this](const std::shared_ptr<void>& window_ptr)
382 -> std::shared_ptr<std::vector<uint8_t>> {
383 auto* ctx = find_window_context(std::static_pointer_cast<Window>(window_ptr));
384 if (!ctx || !ctx->capture)
385 return nullptr;
386
387#ifdef MAYAFLUX_PLATFORM_MACOS
388 auto& state = *ctx->capture;
389
390 size_t slot = CaptureState::LAST_FRAME_MAX_READERS;
391 for (size_t i = 0; i < CaptureState::LAST_FRAME_MAX_READERS; ++i) {
392 bool expected = false;
393 if (state.last_frame_slot_active[i].compare_exchange_strong(expected, true, std::memory_order_acquire)) {
394 slot = i;
395 break;
396 }
397 }
398 if (slot == CaptureState::LAST_FRAME_MAX_READERS)
399 return nullptr;
400
401 const std::vector<uint8_t>* raw;
402 do {
403 raw = state.last_frame.load(std::memory_order_acquire);
404 state.last_frame_hazard_ptrs[slot].store(raw, std::memory_order_release);
405 } while (raw != state.last_frame.load(std::memory_order_acquire));
406
407 if (!raw) {
408 state.last_frame_hazard_ptrs[slot].store(nullptr, std::memory_order_release);
409 state.last_frame_slot_active[slot].store(false, std::memory_order_release);
410 return nullptr;
411 }
412
413 return std::shared_ptr<std::vector<uint8_t>>(
414 const_cast<std::vector<uint8_t>*>(raw),
415 [&state, slot](std::vector<uint8_t>*) {
416 state.last_frame_hazard_ptrs[slot].store(nullptr, std::memory_order_release);
417 state.last_frame_slot_active[slot].store(false, std::memory_order_release);
418 });
419#else
420 return ctx->capture->last_frame.load(std::memory_order_acquire);
421#endif
422 };
423
424 // ======================================================================
425 // register_frame_observer – hazard‑pointer update of observer map
426 // ======================================================================
427 display_service->register_frame_observer = [this](
428 const std::shared_ptr<void>& window_ptr,
429 const std::function<void(
430 const std::shared_ptr<std::vector<uint8_t>>&,
431 uint32_t, uint32_t, uint32_t)>&
432 cb) -> uint32_t {
433 auto* ctx = find_window_context(std::static_pointer_cast<Window>(window_ptr));
434 if (!ctx) {
436 "register_frame_observer: window not registered with graphics backend");
437 return 0;
438 }
439 if (!ctx->capture) {
441 }
442 if (!ctx->capture) {
444 "register_frame_observer: capture is not enabled for '{}'",
445 ctx->window->get_create_info().title);
446 return 0;
447 }
448
449 auto& state = *ctx->capture;
450 uint32_t id = state.next_observer_id.fetch_add(1, std::memory_order_relaxed);
451
452#ifdef MAYAFLUX_PLATFORM_MACOS
453 size_t obs_slot = CaptureState::OBSERVERS_MAX_READERS;
454 for (size_t i = 0; i < CaptureState::OBSERVERS_MAX_READERS; ++i) {
455 bool expected = false;
456 if (state.observers_slot_active[i].compare_exchange_strong(expected, true, std::memory_order_acquire)) {
457 obs_slot = i;
458 break;
459 }
460 }
461 if (obs_slot == CaptureState::OBSERVERS_MAX_READERS)
462 return 0;
463
465 CaptureState::ObserverMap* new_map = nullptr;
466
467 do {
468 if (new_map)
469 delete new_map;
470
471 do {
472 current = state.observers.load(std::memory_order_acquire);
473 state.observers_hazard_ptrs[obs_slot].store(current, std::memory_order_release);
474 } while (current != state.observers.load(std::memory_order_acquire));
475
476 new_map = new CaptureState::ObserverMap(current ? *current : CaptureState::ObserverMap {});
477 (*new_map)[id] = cb;
478
479 } while (!state.observers.compare_exchange_weak(current, new_map, std::memory_order_acq_rel, std::memory_order_acquire));
480
481 state.observers_hazard_ptrs[obs_slot].store(nullptr, std::memory_order_release);
482 state.observers_slot_active[obs_slot].store(false, std::memory_order_release);
483
484 if (current)
485 state.retire_observers(current);
486#else
487 auto current = state.observers.load(std::memory_order_acquire);
488 std::shared_ptr<CaptureState::ObserverMap> next;
489 do {
490 next = std::make_shared<CaptureState::ObserverMap>(*current);
491 (*next)[id] = cb;
492 } while (!state.observers.compare_exchange_weak(
493 current, next,
494 std::memory_order_release, std::memory_order_acquire));
495#endif
496 return id;
497 };
498
499 // ======================================================================
500 // unregister_frame_observer – hazard‑pointer update
501 // ======================================================================
502 display_service->unregister_frame_observer = [this](
503 const std::shared_ptr<void>& window_ptr, uint32_t id) {
504 auto* ctx = find_window_context(std::static_pointer_cast<Window>(window_ptr));
505 if (!ctx) {
507 "unregister_frame_observer: window not registered with graphics backend");
508 return;
509 }
510 if (!ctx->capture) {
512 "unregister_frame_observer: capture not active for '{}'",
513 ctx->window->get_create_info().title);
514 return;
515 }
516
517 auto& state = *ctx->capture;
518
519#ifdef MAYAFLUX_PLATFORM_MACOS
520 size_t obs_slot = CaptureState::OBSERVERS_MAX_READERS;
521 for (size_t i = 0; i < CaptureState::OBSERVERS_MAX_READERS; ++i) {
522 bool expected = false;
523 if (state.observers_slot_active[i].compare_exchange_strong(expected, true, std::memory_order_acquire)) {
524 obs_slot = i;
525 break;
526 }
527 }
528 if (obs_slot == CaptureState::OBSERVERS_MAX_READERS)
529 return;
530
532 CaptureState::ObserverMap* new_map = nullptr;
533
534 do {
535 if (new_map)
536 delete new_map;
537
538 do {
539 current = state.observers.load(std::memory_order_acquire);
540 state.observers_hazard_ptrs[obs_slot].store(current, std::memory_order_release);
541 } while (current != state.observers.load(std::memory_order_acquire));
542
543 new_map = new CaptureState::ObserverMap(current ? *current : CaptureState::ObserverMap {});
544 new_map->erase(id);
545
546 } while (!state.observers.compare_exchange_weak(current, new_map, std::memory_order_acq_rel, std::memory_order_acquire));
547
548 state.observers_hazard_ptrs[obs_slot].store(nullptr, std::memory_order_release);
549 state.observers_slot_active[obs_slot].store(false, std::memory_order_release);
550
551 if (current)
552 state.retire_observers(current);
553#else
554 auto current = state.observers.load(std::memory_order_acquire);
555 std::shared_ptr<CaptureState::ObserverMap> next;
556 do {
557 next = std::make_shared<CaptureState::ObserverMap>(*current);
558 next->erase(id);
559 } while (!state.observers.compare_exchange_weak(
560 current, next,
561 std::memory_order_release, std::memory_order_acquire));
562#endif
563 };
564}
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
glm::vec2 current
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
uint32_t * dst
vk::PhysicalDevice device
Definition VKDevice.cpp:121
uint32_t width
uint32_t height
void download_image_data(std::shared_ptr< VKImage > image, void *data, size_t size, const std::shared_ptr< Buffers::VKBuffer > &staging=nullptr, bool deferred=false, vk::ImageLayout restore_layout=vk::ImageLayout::eShaderReadOnlyOptimal, vk::PipelineStageFlags restore_stage=vk::PipelineStageFlagBits::eFragmentShader)
Download image data to a host pointer.
void ensure_depth_image(WindowRenderContext &ctx)
Ensure depth image exists at current swapchain extent.
uint64_t completed_frame_count(const WindowRenderContext &ctx) const
Submitted frames of a window whose GPU work has finished.
std::vector< WindowRenderContext > m_window_contexts
void submit_and_present(const std::shared_ptr< Window > &window, const vk::CommandBuffer &command_buffer)
WindowRenderContext * find_window_context(const std::shared_ptr< Window > &window)
void ensure_capture_state(WindowRenderContext &ctx)
Ensure capture state is initialized for a window context.
vk::Device get_device() const
Get logical device.
Definition VKContext.hpp:49
@ TEXTURE_2D
Sampled texture (shader read)
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ GraphicsCallback
Graphics/visual rendering callback - frame-rate real-time.
@ Core
Core engine, backend, subsystems.
@ IMAGE_COLOR
2D RGB/RGBA image
std::unordered_map< uint32_t, FrameObserver > ObserverMap

References completed_frame_count(), MayaFlux::Journal::Core, current, device, MayaFlux::Core::BackendResourceManager::download_image_data(), dst, ensure_capture_state(), ensure_depth_image(), find_window_context(), MayaFlux::Core::VKContext::get_device(), MayaFlux::Journal::GraphicsBackend, MayaFlux::Journal::GraphicsCallback, height, MayaFlux::Core::VKImageResources::image, MayaFlux::Kakshya::IMAGE_COLOR, m_context, m_resource_manager, m_window_contexts, MF_RT_ERROR, MF_RT_WARN, MF_WARN, submit_and_present(), MayaFlux::Core::VKImage::TEXTURE_2D, MayaFlux::Core::VKImage::TYPE_2D, width, and window.

+ Here is the call graph for this function: