Skip to main content

GLFWPlatformManager.ixx File

GLFW-backed platform manager handling runtime init, window lifecycle, and frame service commands. More...

Included Headers

#include <glad/gl.h> #include <GLFW/glfw3.h> #include <cassert> #include <ostream> #include <ranges> #include <vector> #include <helios.engine.platform.window.concepts.IsWindowHandle> #include <helios.engine.rendering.common.concepts.CanInitializeRenderBackend> #include <helios.engine.runtime.messaging.command> #include <helios.glfw.types> #include <helios.engine.platform.window.commands> #include <helios.engine.platform.window.types> #include <helios.engine.platform.environment.components> #include <helios.engine.platform.environment.commands> #include <helios.engine.runtime.world.UpdateContext> #include <helios.engine.runtime.concepts> #include <helios.glfw.components> #include <helios.engine.core.types> #include <helios.engine.platform.window.components> #include <helios.engine.runtime.enginestate.types> #include <helios.engine.rendering.common.concepts.CanProvideWindowHints> #include <helios.engine.spatial.components> #include <helios.engine.rendering.renderTarget> #include <helios.engine.runtime.messaging.command.concepts.IsPlatformCommandBuffer> #include <helios.engine.platform.environment.types> #include <helios.engine.runtime.world.tags.ManagerRole> #include <helios.engine.runtime.messaging.command.CommandHandlerRegistry> #include <helios.engine.runtime.world.Session> #include <helios.engine.util.log> #include <helios.engine.runtime.messaging.command.CommandBufferRegistry> #include <helios.engine.state.commands> #include <helios.engine.platform.lifecycle.commands> #include <helios.engine.state.types> #include <helios.engine.runtime.world.EngineWorld>

Namespaces Index

namespacehelios
namespaceglfw
namespacetypes
namespacecomponents
namespacecomponents
namespacetags
namespacecommands
namespacecommands
namespacetypes
namespacecomponents
namespacecommands
namespacetypes
namespacecomponents
namespacecommands
namespacetypes
namespaceconcepts
namespacetypes
namespacecommand
namespacetypes

Classes Index

classGLFWPlatformManager<TRenderPlatform, THandle, TStateCommandBuffer, TPlatformCommandBuffer>

Concrete manager integrating GLFW with helios runtime/window command flow. More...

Macro Definitions Index

#defineHELIOS_LOG_SCOPE   "helios::glfw::GLFWPlatformManager"

Description

GLFW-backed platform manager handling runtime init, window lifecycle, and frame service commands.

Macro Definitions

HELIOS_LOG_SCOPE

#define HELIOS_LOG_SCOPE   "helios::glfw::GLFWPlatformManager"

Definition at line 80 of file GLFWPlatformManager.ixx.

80#define HELIOS_LOG_SCOPE "helios::glfw::GLFWPlatformManager"

File Listing

The file content with the documentation metadata removed is:

1
5module;
6
7#include <glad/gl.h>
8#include <GLFW/glfw3.h>
9#include <cassert>
10#include <ostream>
11#include <ranges>
12#include <vector>
13
14export module helios.glfw.GLFWPlatformManager;
15
16import helios.engine.runtime.world.UpdateContext;
17import helios.engine.runtime.world.Session;
18
19import helios.engine.util.log;
20import helios.engine.core.types;
21
22import helios.engine.runtime.enginestate.types;
23
24import helios.engine.spatial.components;
25
26import helios.engine.rendering.renderTarget;
27
28import helios.engine.runtime.messaging.command.concepts.IsPlatformCommandBuffer;
29import helios.engine.runtime.messaging.command.CommandHandlerRegistry;
30import helios.engine.runtime.messaging.command.CommandBufferRegistry;
31
32import helios.engine.state.commands;
33import helios.engine.state.types;
34
35import helios.engine.runtime.world.EngineWorld;
36import helios.engine.runtime.world.tags.ManagerRole;
37
38import helios.engine.platform.environment.commands;
39import helios.engine.platform.lifecycle.commands;
40import helios.engine.platform.environment.components;
41import helios.engine.platform.environment.types;
42
43import helios.engine.platform.window.commands;
44import helios.engine.platform.window.components;
45import helios.engine.platform.window.types;
46
47import helios.glfw.components;
48import helios.glfw.types;
49
50import helios.engine.rendering.common.concepts.CanProvideWindowHints;
51import helios.engine.rendering.common.concepts.CanInitializeRenderBackend;
52
53import helios.engine.runtime.concepts;
54import helios.engine.runtime.messaging.command;
55import helios.engine.platform.window.concepts.IsWindowHandle;
56
57using namespace helios::engine::rendering::renderTarget::types;
58using namespace helios::engine::rendering::renderTarget::components;
59using namespace helios::engine::spatial::components;
60using namespace helios::engine::runtime::world::tags;
61using namespace helios::engine::platform::environment::commands;
62using namespace helios::engine::platform::lifecycle::commands;
63using namespace helios::engine::platform::environment::types;
64using namespace helios::engine::platform::environment::components;
65using namespace helios::engine::platform::window::commands;
66using namespace helios::engine::platform::window::types;
67using namespace helios::engine::platform::window::components;
68using namespace helios::glfw::components;
69using namespace helios::glfw::types;
70using namespace helios::engine::state::commands;
71using namespace helios::engine::state::types;
72using namespace helios::engine::runtime::messaging::command::concepts;
73using namespace helios::engine::rendering::common::concepts;
74using namespace helios::engine::core::types;
75using namespace helios::engine::runtime::messaging::command;
76using namespace helios::engine::runtime::world;
77using namespace helios::engine::platform::window::concepts;
78using namespace helios::engine::runtime::enginestate::types;
79
80#define HELIOS_LOG_SCOPE "helios::glfw::GLFWPlatformManager"
81export namespace helios::glfw {
82
93 template<typename TRenderPlatform, typename THandle, typename TStateCommandBuffer = NullCommandBuffer, typename TPlatformCommandBuffer = NullCommandBuffer>
94 requires IsWindowHandle<THandle>
95 && IsCommandBufferLike<TStateCommandBuffer>
96 && IsPlatformCommandBuffer<TPlatformCommandBuffer>
97 && CanInitializeRenderBackend<TRenderPlatform>
98 && CanProvideWindowHints<TRenderPlatform>
100
101 std::vector<WindowResizeCommand<THandle>> pendingResizeCommands_;
102
103 std::vector<WindowCreateCommand<THandle>> windowCreateCommands_;
104
105 std::vector<SwapBuffersCommand<THandle>> pendingBufferSwaps_;
106
107 std::vector<WindowCloseCommand<THandle>> pendingCloseCommands_;
108
109 std::vector<THandle> currentContexts_;
110
111 bool shouldInit_ = false;
112
113 bool shouldShutdown_ = false;
114
115 bool pollEvents_ = false;
116
117 bool initialized_ = false;
118
119 TRenderPlatform& renderPlatform_;
120
121 inline static const helios::engine::util::log::Logger& logger_ = helios::engine::util::log::LogManager::loggerForScope(
123
124 PlatformWorld* platformWorld_;
125
131 bool initPlatform(UpdateContext& updateContext) noexcept {
132
133 if (!shouldInit_ || initialized_) {
134 return false;
135 }
136
137 if (glfwInit() == GLFW_FALSE) {
138 assert(false && "Failed to initialize glfw");
139 }
140
141 renderPlatform_.provideWindowHints();
142
143 assert(updateContext.session().state<EngineState>() == EngineState::Booting &&
144 "Expected EngineState to be Booting during platform initialization");
145
146 initialized_ = updateContext.session().initialize() &&
147 updateContext.runtimeEnvironment().initialize();
148
149 shouldInit_ = false;
150
151 return initialized_;
152 }
153
154
163 bool createWindow(UpdateContext& updateContext, const WindowCreateCommand<THandle>& cmd) noexcept {
164
165 auto window = updateContext.find(cmd.windowHandle);
166
167 if (!window) {
168 return false;
169 }
170
171 auto& cfg = cmd.windowConfig;
172
173 auto* nativeHandle = glfwCreateWindow(
174 cfg.size[0],
175 cfg.size[1],
176 cfg.title.c_str(),
177 nullptr,
178 nullptr
179 );
180
181 assert(nativeHandle && "Failed to create GLFW window");
182
183 if (cfg.aspectRatioNumer > 0 && cfg.aspectRatioDenom > 0) {
184 glfwSetWindowAspectRatio(
185 nativeHandle,
186 cfg.aspectRatioNumer,
187 cfg.aspectRatioDenom
188 );
189 }
190
191 assert(window->template has<WindowCreateRequestComponent<THandle>>() && "Expected entity to have WindowCreateRequestComponent");
192 window->template remove<WindowCreateRequestComponent<THandle>>();
193 assert(!window->template has<WindowCreateRequestComponent<THandle>>() && "Expected entity to not have WindowCreateRequestComponent");
194 assert(!window->template has<WindowComponent<THandle>>() && "Expected entity to not have WindowComponent");
195 window->template add<WindowComponent<THandle>>(
196 std::move(cfg.title),
197 cfg.aspectRatioNumer,
198 cfg.aspectRatioDenom
199 );
200 window->template add<
201 Size2DComponent<THandle>
202 >(WindowSize(cfg.size));
203 window->template add<GLFWWindowHandleComponent<THandle>>(nativeHandle);
204
205 removeCurrentContext(updateContext);
206
207 glfwMakeContextCurrent(nativeHandle);
208
209 window->template add<CurrentContextComponent<THandle>>();
210
211 window->template add<WindowShownComponent<THandle>>();
212 window->template add<GLFWWindowUserPointerComponent<THandle, TPlatformCommandBuffer>>(
214 cmd.windowHandle, commandBufferRegistry_->template item<TPlatformCommandBuffer>()
215 ));
216
217 installResizeListener(cmd.windowHandle);
218
219
220 int renderTargetWidth = 0;
221 int renderTargetHeight = 0;
222 int windowWidth = 0;
223 int windowHeight = 0;
224
225 glfwGetFramebufferSize(nativeHandle, &renderTargetWidth, &renderTargetHeight);
226 glfwGetWindowSize(nativeHandle, &windowWidth, &windowHeight);
227
228 commandBufferRegistry_->template item<TPlatformCommandBuffer>()
229 ->template add<WindowResizeCommand<THandle>>(
230 cmd.windowHandle,
231 WindowSize(windowWidth, windowHeight),
232 RenderTargetSize(renderTargetWidth, renderTargetHeight));
233
234 return true;
235 }
236
242 void removeCurrentContext(UpdateContext& updateContext) {
243 // remove any currentcontext component
244 currentContexts_.clear();
245 for (auto [window, cc]: updateContext.template view<THandle, CurrentContextComponent<THandle>>()) {
246 currentContexts_.push_back(window.handle());
247 }
248 for (auto& handle : currentContexts_) {
249 auto go = updateContext.find<THandle> (handle);
250 if (go) {
251 go->template remove<CurrentContextComponent<THandle>>();
252 }
253 }
254 }
255
261 void installResizeListener(THandle handle) noexcept {
262
263 auto entity = platformWorld_->findEntity<THandle>(handle);
264
265 if (!entity) {
266 logger_.warn("Entity was not found");
267 return;
268 }
269
270 const auto* glfw = entity->template get<GLFWWindowHandleComponent<THandle>>();
271 if (!glfw) {
272 logger_.error("Entity does not have GLFWWindowHandleComponent");
273 assert(false && "Entity does not have GLFWWindowHandleComponent");
274 return;
275 }
276
277 auto* wuptrComponent = entity->template get<GLFWWindowUserPointerComponent<THandle, TPlatformCommandBuffer>>();
278 if (!wuptrComponent) {
279 logger_.error("Entity does not have GLFWWindowUserPointerComponent");
280 assert(false && "Entity does not have GLFWWindowUserPointerComponent");
281 return;
282 }
283 auto* wuptr = &wuptrComponent->userPointer;
284
285 glfwSetWindowUserPointer(glfw->handle, static_cast<void*>(wuptr));
286
287 glfwSetFramebufferSizeCallback(
288 glfw->handle,
289 [] (GLFWwindow* nativeHandle, const int width, const int height) {
290 const auto* ptr = static_cast<GLFWWindowUserPointer<THandle, TPlatformCommandBuffer>*>(glfwGetWindowUserPointer(nativeHandle));
291
292 if (ptr && ptr->platformCommandBuffer) {
293
294 int windowWidth = 0;
295 int windowHeight = 0;
296
297 glfwGetWindowSize(nativeHandle, &windowWidth, &windowHeight);
298 ptr->platformCommandBuffer->template add<WindowResizeCommand<THandle>>(
299 ptr->windowHandle,
300 WindowSize(windowWidth, windowHeight),
301 RenderTargetSize(width, height)
302 );
303 }
304 });
305 }
306
307
314 void swapBuffer(UpdateContext& updateContext, const SwapBuffersCommand<THandle>& cmd) noexcept {
315
316 const auto entity = updateContext.find(cmd.windowHandle);
317
318 if (!entity) {
319 logger_.warn("Entity was not found");
320 return;
321 }
322
323 const auto* glfw = entity->template get<GLFWWindowHandleComponent<THandle>>();
324
325 if (!glfw) {
326 logger_.error("Entity does not have GLFWWindowHandleComponent");
327 assert(false && "Entity does not have GLFWWindowHandleComponent");
328 return;
329 }
330
331 assert((updateContext.session().state<EngineState>() != EngineState::Booting) &&
332 "GLFWSwapBuffersSystem should not be running during boot");
333 assert(glfw->handle && "GLFWWindowComponent has no native handle");
334 glfwSwapBuffers(glfw->handle);
335 }
336
337
345 bool createWindows(UpdateContext& updateContext) noexcept {
346 if (windowCreateCommands_.empty()) {
347 return false;
348 }
349 for (const auto& windowCreateCommand : windowCreateCommands_) {
350 const bool isContextAvailable = createWindow(updateContext, windowCreateCommand);
351 if (!isContextAvailable) {
352 logger_.error("Failed to create window");
353 assert(false && "Failed to create window");
354 }
355 }
356
357 windowCreateCommands_.clear();
358 return true;
359 }
360
370 void resizeWindows(UpdateContext& updateContext) noexcept {
371
372 if (pendingResizeCommands_.empty()) {
373 return;
374 }
375
376 for (const auto& [windowHandle, windowSize, renderTargetSize]: pendingResizeCommands_) {
377
378 if (!windowHandle.isValid()) {
379 continue;
380 }
381
382 if (auto entity = updateContext.find(windowHandle)) {
383
384 if (auto* wsc = entity->template get<Size2DComponent<THandle>>()) {
385 wsc->setValue(windowSize);
386 }
387 if (auto* fbc = entity->template get<RenderTargetBindingComponent<THandle>>()) {
388 auto renderTargetHandle = fbc->targetHandle();
389 auto renderTarget = updateContext.find<RenderTargetHandle>(renderTargetHandle);
390 auto fsc = renderTarget->template get<Size2DComponent<RenderTargetHandle>>();
391
392 logger_.info("Setting renderTarget size to {0},{1}", renderTargetSize[0], renderTargetSize[1]);
393 fsc->setValue(renderTargetSize);
394 }
395 }
396
397 }
398
399 pendingResizeCommands_.clear();
400 }
401
407 void swapBuffers(UpdateContext& updateContext) noexcept {
408 if (pendingBufferSwaps_.empty()) {
409 return;
410 }
411 for (const auto& swapBufferCommand : pendingBufferSwaps_) {
412 swapBuffer(updateContext, swapBufferCommand);
413 }
414 pendingBufferSwaps_.clear();
415 }
416
422 void pollEvents(UpdateContext& updateContext) noexcept {
423 if (!pollEvents_) {
424 return;
425 }
426
427 glfwPollEvents();
428 pollEvents_ = false;
429 }
430
436 void closeWindows(UpdateContext& updateContext) noexcept {
437 if (pendingCloseCommands_.empty()) {
438 return;
439 }
440
441 for (const auto& cmd : pendingCloseCommands_) {
442 auto entity = updateContext.find(cmd.windowHandle);
443
444 if (!entity) {
445 logger_.warn("Entity was not found");
446 continue;
447 }
448
449 const auto* glfw = entity->template get<GLFWWindowHandleComponent<THandle>>();
450 if (!glfw) {
451 logger_.warn("Entity does not have GLFWWindowHandleComponent");
452 continue;
453 }
454
455 glfwDestroyWindow(glfw->handle);
456 bool destroyed = platformWorld_->destroy<THandle>(cmd.windowHandle);
457 assert(destroyed && "Failed to destroy entity");
458 }
459
460 pendingCloseCommands_.clear();
461 }
462
463
469 void shutdown(UpdateContext& updateContext) noexcept {
470
471 glfwTerminate();
472
473 commandBufferRegistry_->template item<TStateCommandBuffer>()->template add<StateCommand<EngineState>>(
474 StateTransitionRequest<EngineState>(
475 updateContext.session().state<EngineState>(),
476 EngineStateTransitionId::ShutdownRequest
477 )
478 );
479
480
481 }
482
483 CommandBufferRegistry* commandBufferRegistry_ = nullptr;
484
485 public:
486
487
491 using EngineRoleTag = ManagerRole;
492
494 TRenderPlatform& renderPlatform,
495 PlatformWorld& platformWorld,
496 CommandBufferRegistry& commandBufferRegistry)
497 : renderPlatform_(renderPlatform),
498 platformWorld_(&platformWorld),
499 commandBufferRegistry_(&commandBufferRegistry) {};
500
501
507 void flush(UpdateContext& updateContext) noexcept {
508
509 if (shouldShutdown_) {
510 shutdown(updateContext);
511 return;
512 }
513
514 if (initPlatform(updateContext)) {
515 commandBufferRegistry_->template item<TStateCommandBuffer>()->template add<StateCommand<EngineState>>(
516 StateTransitionRequest<EngineState>(
517 updateContext.session().state<EngineState>(),
518 EngineStateTransitionId::BootRequest
519 )
520 );
521 }
522 pollEvents(updateContext);
523 const bool isContextAvailable = createWindows(updateContext);
524
525 if (!renderPlatform_.isInitialized() && isContextAvailable) {
526 if (renderPlatform_.init()) {
527 updateContext.runtimeEnvironment().setGPUReady();
528 }
529 }
530
531 resizeWindows(updateContext);
532 closeWindows(updateContext);
533 swapBuffers(updateContext);
534 }
535
543 bool submit(const PollEventsCommand& command) noexcept {
544 pollEvents_ = true;
545 return true;
546 }
547
555 bool submit(const PlatformInitCommand& command) noexcept {
556 assert(!initialized_ && "Application was already initialized.");
557 shouldInit_ = true;
558 return true;
559 }
560
568 bool submit(const WindowCreateCommand<THandle>& command) noexcept {
569 windowCreateCommands_.push_back(command);
570 return true;
571 }
572
580 bool submit(const SwapBuffersCommand<THandle>& command) noexcept {
581 pendingBufferSwaps_.push_back(command);
582 return true;
583 }
584
592 bool submit(const WindowResizeCommand<THandle>& command) noexcept {
593 const auto idx = command.windowHandle.entityId;
594
595 if (pendingResizeCommands_.size() <= idx) {
596 pendingResizeCommands_.resize(idx + 1);
597 }
598
599 pendingResizeCommands_[idx] = command;
600 return true;
601 }
602
610 bool submit(const WindowCloseCommand<THandle>& command) noexcept {
611 pendingCloseCommands_.push_back(command);
612 return true;
613 }
614
622 bool submit(const ShutdownCommand& command) noexcept {
623 shouldShutdown_ = true;
624 return true;
625 }
626
632 void init(CommandHandlerRegistry& commandHandlerRegistry) noexcept {
633
634 commandHandlerRegistry.handleCommands<
635 WindowCreateCommand<THandle>,
636 PlatformInitCommand,
637 WindowResizeCommand<THandle>,
638 SwapBuffersCommand<THandle>,
639 PollEventsCommand,
640 WindowCloseCommand<THandle>,
641 ShutdownCommand
642 >(*this);
643 }
644 };
645
646
647}

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.9.8.