MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BackendWindowHandler.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "vulkan/vulkan.hpp"
4
6struct DisplayService;
7}
8
9namespace MayaFlux::Core {
10
11class VKContext;
12class VKImage;
13class VKSwapchain;
14class VKCommandManager;
15class Window;
16class BackendResourceManager;
17
19 vk::Image image {};
20 vk::DeviceMemory mem {};
21 vk::Extent2D extent {};
22 vk::CommandBuffer cmd {};
23 vk::Fence fence {};
24 std::atomic<bool> pending { false };
25};
26
28#ifdef MAYAFLUX_PLATFORM_MACOS
29 std::atomic<const std::vector<uint8_t>*> last_frame { nullptr };
30
31 static constexpr size_t LAST_FRAME_MAX_READERS = 32;
32 mutable std::array<std::atomic<const std::vector<uint8_t>*>, LAST_FRAME_MAX_READERS> last_frame_hazard_ptrs {};
33 mutable std::array<std::atomic<bool>, LAST_FRAME_MAX_READERS> last_frame_slot_active { false };
34
35 void retire_last_frame(const std::vector<uint8_t>* ptr);
36#else
37 std::atomic<std::shared_ptr<std::vector<uint8_t>>> last_frame;
38#endif
39
40 std::vector<std::unique_ptr<CaptureSlot>> slots;
41 size_t slot_index {};
42 vk::Format format {};
43 uint32_t bpp {};
44 std::thread readback_thread;
45 std::atomic<bool> readback_running { false };
46
47 using FrameObserver = std::function<void(
48 const std::shared_ptr<std::vector<uint8_t>>&,
49 uint32_t, uint32_t, uint32_t)>;
50 using ObserverMap = std::unordered_map<uint32_t, FrameObserver>;
51
52 std::atomic<uint32_t> next_observer_id { 1 };
53
54#ifdef MAYAFLUX_PLATFORM_MACOS
55 std::atomic<const ObserverMap*> observers { nullptr };
56
57 static constexpr size_t OBSERVERS_MAX_READERS = 32;
58 mutable std::array<std::atomic<const ObserverMap*>, OBSERVERS_MAX_READERS> observers_hazard_ptrs {};
59 mutable std::array<std::atomic<bool>, OBSERVERS_MAX_READERS> observers_slot_active { false };
60
61 void retire_observers(const ObserverMap* ptr);
62#else
63 std::atomic<std::shared_ptr<ObserverMap>> observers {
64 std::make_shared<ObserverMap>()
65 };
66#endif
67
69 {
70 readback_running.store(false, std::memory_order_release);
71 if (readback_thread.joinable())
72 readback_thread.join();
73
74#ifdef MAYAFLUX_PLATFORM_MACOS
75 auto* old_frame = last_frame.exchange(nullptr);
76 if (old_frame)
77 retire_last_frame(old_frame);
78
79 auto* old_obs = observers.exchange(nullptr);
80 if (old_obs)
81 retire_observers(old_obs);
82#endif
83 }
84};
85
87 std::shared_ptr<Window> window;
88 vk::SurfaceKHR surface;
89 std::unique_ptr<VKSwapchain> swapchain;
90
91 std::vector<vk::Semaphore> image_available;
92 std::vector<vk::Semaphore> render_finished;
93 std::vector<vk::Fence> in_flight;
94 std::vector<uint64_t> slot_submission;
95 uint64_t submitted_frames {};
96
97 std::vector<vk::CommandBuffer> clear_command_buffers;
98 std::shared_ptr<VKImage> depth_image;
99
101 size_t current_frame {};
103
104 std::unique_ptr<CaptureState> capture;
105
108
113
114 void cleanup(VKContext& context);
115};
116
117class MAYAFLUX_API BackendWindowHandler {
118public:
119 BackendWindowHandler(VKContext& context, VKCommandManager& command_manager);
121
122 void setup_backend_service(const std::shared_ptr<Registry::Service::DisplayService>& display_service);
123
126
128 BackendWindowHandler& operator=(BackendWindowHandler&&) noexcept = default;
129
130 // ========================================================================
131 // Window management
132 // ========================================================================
133 bool register_window(const std::shared_ptr<Window>& window);
134 void unregister_window(const std::shared_ptr<Window>& window);
135 [[nodiscard]] bool is_window_registered(const std::shared_ptr<Window>& window) const;
136
137 // ========================================================================
138 // Rendering
139 // ========================================================================
140 void render_window(const std::shared_ptr<Window>& window);
141 void render_all_windows();
142 void handle_window_resize();
143
144 void submit_and_present(
145 const std::shared_ptr<Window>& window,
146 const vk::CommandBuffer& command_buffer);
147
148 // ========================================================================
149 // Access control
150 // ========================================================================
151 WindowRenderContext* find_window_context(const std::shared_ptr<Window>& window);
152 [[nodiscard]] const WindowRenderContext* find_window_context(const std::shared_ptr<Window>& window) const;
153
154 // Information
155 [[nodiscard]] uint32_t get_swapchain_image_count(const std::shared_ptr<Window>& window) const;
156
157 // Cleanup
158 void cleanup();
159
160 void set_resource_manager(BackendResourceManager* resource_manager) { m_resource_manager = resource_manager; }
161
162private:
165 std::vector<WindowRenderContext> m_window_contexts;
166
167 /**
168 * @brief Create synchronization objects for a window's swapchain
169 * @param config Window swapchain configuration to populate
170 * @return True if creation succeeded
171 */
172 bool create_sync_objects(WindowRenderContext& config);
173
174 /**
175 * @brief Submitted frames of a window whose GPU work has finished
176 * @param ctx Window render context
177 *
178 * Never blocks. A slot reset for acquisition but not yet resubmitted holds
179 * the id of its previous, finished submission, so the result can only
180 * understate completion.
181 */
182 [[nodiscard]] uint64_t completed_frame_count(const WindowRenderContext& ctx) const;
183
184 /**
185 * @brief Recreate the swapchain and related resources for a window
186 * @param context Window render context
187 */
188 void recreate_swapchain_for_context(WindowRenderContext& context);
189
190 /**
191 * @brief Internal logic to recreate swapchain and related resources
192 * @param context Window render context
193 * @return True if recreation succeeded
194 */
195 bool recreate_swapchain_internal(WindowRenderContext& context);
196
197 /**
198 * @brief Ensure depth image exists at current swapchain extent
199 * @param ctx Window context to create depth image for
200 *
201 * Creates or recreates a D32_SFLOAT depth image matching the
202 * swapchain extent. No-op if depth image already matches.
203 */
204 void ensure_depth_image(WindowRenderContext& ctx);
205
206 /**
207 * @brief Render empty windows with clear color
208 * @param ctx Window render context
209 *
210 * For windows that are registered for processing but have no buffers attached,
211 * this performs a minimal clear pass so the window is visible and responsive
212 * to input events.
213 */
214 void render_empty_window(WindowRenderContext& ctx);
215
216 /**
217 * @brief Ensure capture state is initialized for a window context
218 * @param ctx Window render context to initialize capture state for
219 *
220 * If the context does not already have capture state and the associated window has
221 * capture enabled, this initializes the capture state including allocating command buffers
222 * and synchronization objects for readback.
223 */
224 void ensure_capture_state(WindowRenderContext& ctx);
225
226 /**
227 * @brief Capture the current frame from a window's swapchain
228 * @param ctx Window render context to capture from
229 *
230 * This initiates an asynchronous readback of the current swapchain image into
231 * CPU-accessible memory. The result is stored in the capture state for retrieval
232 * via the display service.
233 */
234 void capture_frame(WindowRenderContext& ctx);
235
236 /**
237 * @brief Thread function to perform asynchronous readback of captured frames
238 * @param state Capture state containing pending readbacks
239 * @param dev Vulkan device to use for readback operations
240 *
241 * This thread continuously checks for pending capture slots and performs the necessary
242 * Vulkan commands to read back the image data into CPU memory. The last captured frame
243 * is stored atomically for retrieval by the display service.
244 */
245 void start_readback_thread(CaptureState& state, vk::Device dev);
246
247 BackendResourceManager* m_resource_manager {};
248};
249
250}
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
const uint8_t * ptr
Manages Vulkan resources (buffers, images, samplers) for the graphics backend.
BackendWindowHandler(BackendWindowHandler &&) noexcept=default
BackendWindowHandler & operator=(const BackendWindowHandler &)=delete
std::vector< WindowRenderContext > m_window_contexts
BackendWindowHandler(const BackendWindowHandler &)=delete
Manages Vulkan command pools and command buffers.
High-level wrapper for Vulkan instance and device.
Definition VKContext.hpp:16
Platform-agnostic window wrapper.
Definition Window.hpp:22
std::vector< std::unique_ptr< CaptureSlot > > slots
std::function< void(const std::shared_ptr< std::vector< uint8_t > > &, uint32_t, uint32_t, uint32_t)> FrameObserver
std::atomic< std::shared_ptr< ObserverMap > > observers
std::atomic< std::shared_ptr< std::vector< uint8_t > > > last_frame
std::unordered_map< uint32_t, FrameObserver > ObserverMap
std::atomic< uint32_t > next_observer_id
std::vector< vk::Semaphore > image_available
std::unique_ptr< CaptureState > capture
std::unique_ptr< VKSwapchain > swapchain
WindowRenderContext(WindowRenderContext &&)=default
std::vector< vk::Semaphore > render_finished
std::vector< vk::CommandBuffer > clear_command_buffers
WindowRenderContext & operator=(WindowRenderContext &&)=default
WindowRenderContext(const WindowRenderContext &)=delete
WindowRenderContext & operator=(const WindowRenderContext &)=delete