EchoMap 2026-07-25 6d3977c
An experimental cross-platform digital signal processing application for sound-source localisation.
Loading...
Searching...
No Matches
echomap::EchoMap Class Referenceabstract

The EchoMap maintains state for the application including WebGPU and Dear ImGui context, encapsulating initialisation, game loop, interaction, and clean-up. More...

#include <EchoMap.hpp>

Inheritance diagram for echomap::EchoMap:
[legend]

Public Member Functions

 EchoMap ()
 Initialise a EchoMap application instance.
virtual void run_event_loop ()=0
 Runs the platform-dependent event loop to manage and propagate interaction with the EchoMap application.
virtual ~EchoMap () noexcept
 Clean up all persistent state registered by the application instance.
void change_active_project (std::unique_ptr< Project > new_project) noexcept
void notify (const Notification &notification)
 Submit a new Notification to the application queue.
void increment_forced_frames (unsigned int count=4) noexcept
 Indicate to the renderer that the following frames should always be rendered, regardless of whether there are any new events to process.
 EchoMap (const EchoMap &)=delete
EchoMapoperator= (const EchoMap &)=delete
 EchoMap (EchoMap &&)=delete
EchoMapoperator= (EchoMap &&)=delete

Protected Member Functions

auto make_common_notification_visitors ()
 Produce an overload set for std::visit for all platform-independent Notification objects.
virtual void visit_notification (Notification &notification)=0
 Uses std::visit on the given notification to invoke the corresponding handler.
void render () noexcept
 Perform a render cycle on the configured Surface and Device.
void setup_subscriptions ()
 Configure the core signals for the application instance.
wgpu::Future request_adapter () noexcept
 Produce a WebGPU Future for requesting an Adapter.
wgpu::Future request_device () noexcept
 Produce a WebGPU Future for requesting an accelerator device.
void setup_imgui ()
 Create a context for Dear ImGui and ImPlot, and configure the plain GLFW and WebGPU backends.
void setup_dockspace ()
bool handle_window_resize () noexcept
 Check if the window has been resized compared with the stored dimensions, updating member variables and reconfiguring the WebGPU surface if necessary.
void process_notifications ()
 Handle any unconsumed Notification objects from the queue.
void process_worker_results ()
 Handle any unconsumed events from the Worker.
void raise_error (std::string_view message)
void raise_error (std::string_view message, const std::runtime_error &exception)
void handle_notification (const AddChannelMappingNotification &notification) const
void handle_notification (const ModifySensorColourNotification &notification) const
void handle_notification (const ModifySensorPositionNotification &notification) const
void handle_notification (const ProjectSelectionCompleteNotification &notification)
void handle_notification (const ClearErrorNotification &notification)
virtual void handle_result (LoadProjectResult &&result)
virtual void handle_result (LoadSignalFileResult &&result)

Static Protected Member Functions

static GLFWwindow * create_window (int width, int height)
 Create a new GLFW window of the specified dimensions from a static context.
static void configure_surface (const wgpu::Surface &surface, const wgpu::Device &device, const wgpu::SurfaceCapabilities &capabilities, std::uint32_t viewport_width, std::uint32_t viewport_height) noexcept
 Configure a WebGPU Surface from a static context given metadata and Adapter capabilities.

Protected Attributes

std::uint32_t viewport_width = 1024
std::uint32_t viewport_height = 1024
wgpu::Instance instance
wgpu::Adapter adapter
wgpu::Device device
wgpu::Surface surface
wgpu::SurfaceCapabilities surface_capabilities
GLFWwindow * window = nullptr
Worker worker
 Multi-threaded worker for scheduling heavy computation tasks.
WorkerResultDespatcher despatcher
 Despatcher to manage Worker result channels.
std::vector< sigc::scoped_connection > connections
 RAII lifetime manager for signal connections.
std::vector< std::unique_ptr< IProjectPanel > > panels
 Individual display components.
std::optional< ErrorModalerror_modal
 Persistent panel to indicate errors over all other panels.
std::vector< Notificationnotification_queue
std::unique_ptr< Projectproject
 Owning container for the active Project.
std::unique_ptr< IPanelactive_modal
 The current active non-ErrorModal modal panel.
ImGuiID dockspace_id
bool dockspace_configured = false
unsigned int forced_frames = 0

Static Protected Attributes

static constexpr auto operation_timeout = std::numeric_limits<std::uint64_t>::max()

Detailed Description

The EchoMap maintains state for the application including WebGPU and Dear ImGui context, encapsulating initialisation, game loop, interaction, and clean-up.

Definition at line 34 of file EchoMap.hpp.

Constructor & Destructor Documentation

◆ EchoMap()

echomap::EchoMap::EchoMap ( )

Initialise a EchoMap application instance.

Initialisation is a computationally substantial task. Context from all managed frameworks must be initialised (GLFW, WebGPU/Dawn, and Dear ImGui) and their components registered. Once the constructor has completed, the game loop can begin with run_event_loop.

Exceptions
ConfigurationErrorSome part of initialisation, described in the exception message, did not succeed.

Definition at line 41 of file EchoMap.cpp.

41 :
42 window(create_window(
43 static_cast<int>(viewport_width),
44 static_cast<int>(viewport_height)
45 )),
46 worker{[] {
47#if !defined(__EMSCRIPTEN__) || defined(__DOXYGEN__)
48 glfwPostEmptyEvent();
49#endif
50 }},
51 dockspace_id(ImHashStr("MainDockSpace"))
52{
54
55 static constexpr auto timed_wait_any = wgpu::InstanceFeatureName::TimedWaitAny;
56 constexpr wgpu::InstanceDescriptor instance_desc{.requiredFeatureCount = 1, .requiredFeatures = &timed_wait_any};
57
58 instance = wgpu::CreateInstance(&instance_desc);
59
60 int actual_width = 0;
61 int actual_height = 0;
62 glfwGetFramebufferSize(window, &actual_width, &actual_height);
63 assert(actual_width >= 0);
64 assert(actual_height >= 0);
65 viewport_width = static_cast<std::uint32_t>(actual_width);
66 viewport_height = static_cast<std::uint32_t>(actual_height);
67
68 surface = SurfaceFactory::create_surface(instance, window);
69
70 if (const auto adapter_wait = instance.WaitAny(request_adapter(), operation_timeout);
71 adapter_wait != wgpu::WaitStatus::Success || !adapter)
72 throw ConfigurationError("WebGPU adapter request did not complete");
73
74 if (const auto device_wait = instance.WaitAny(request_device(), operation_timeout);
75 device_wait != wgpu::WaitStatus::Success || !device)
76 throw ConfigurationError("WebGPU device request did not complete");
77
78 surface.GetCapabilities(adapter, &surface_capabilities);
79 configure_surface(surface, device, surface_capabilities, viewport_width, viewport_height);
80
82
89}
void setup_subscriptions()
Configure the core signals for the application instance.
Definition EchoMap.cpp:166
wgpu::Future request_device() noexcept
Produce a WebGPU Future for requesting an accelerator device.
Definition EchoMap.cpp:208
static void configure_surface(const wgpu::Surface &surface, const wgpu::Device &device, const wgpu::SurfaceCapabilities &capabilities, std::uint32_t viewport_width, std::uint32_t viewport_height) noexcept
Configure a WebGPU Surface from a static context given metadata and Adapter capabilities.
Definition EchoMap.cpp:140
static GLFWwindow * create_window(int width, int height)
Create a new GLFW window of the specified dimensions from a static context.
Definition EchoMap.cpp:118
std::vector< std::unique_ptr< IProjectPanel > > panels
Individual display components.
Definition EchoMap.hpp:242
void setup_imgui()
Create a context for Dear ImGui and ImPlot, and configure the plain GLFW and WebGPU backends.
Definition EchoMap.cpp:336
wgpu::Future request_adapter() noexcept
Produce a WebGPU Future for requesting an Adapter.
Definition EchoMap.cpp:186
Worker worker
Multi-threaded worker for scheduling heavy computation tasks.
Definition EchoMap.hpp:238
WorkerResultDespatcher despatcher
Despatcher to manage Worker result channels.
Definition EchoMap.hpp:239
static wgpu::Surface create_surface(const wgpu::Instance &instance, GLFWwindow *window)
Creates and binds a Surface to the given GLFW window.
T make_unique(T... args)

◆ ~EchoMap()

echomap::EchoMap::~EchoMap ( )
virtualnoexcept

Clean up all persistent state registered by the application instance.

Definition at line 91 of file EchoMap.cpp.

92{
93 if (ImGui::GetCurrentContext() != nullptr) {
94 ImGui_ImplWGPU_Shutdown();
95 ImGui_ImplGlfw_Shutdown();
96 ImPlot3D::DestroyContext();
97 ImPlot::DestroyContext();
98 ImGui::DestroyContext();
99 }
100
101 if (surface) {
102 surface.Unconfigure();
103 surface = nullptr;
104 }
105
106 device = nullptr;
107 adapter = nullptr;
108 instance = nullptr;
109
110 if (window != nullptr) {
111 glfwDestroyWindow(window);
112 window = nullptr;
113 }
114
115 glfwTerminate();
116}

Member Function Documentation

◆ change_active_project()

void echomap::EchoMap::change_active_project ( std::unique_ptr< Project > new_project)
noexcept

Definition at line 589 of file EchoMap.cpp.

592{
593 if (new_project == nullptr)
594 LOG_DEBUG("Clearing the active project.");
595 else
596 LOG_F_DEBUG("Changing active project to {}.", new_project->get_name());
597
598 for (const auto& panel : panels)
599 panel->change_active_project(new_project.get());
600
601 project = std::move(new_project);
602}
#define LOG_F_DEBUG(msg,...)
Conditionally logs a formatted debug-level message using echomap::Logger::log_f.
Definition Logger.hpp:94
#define LOG_DEBUG(msg,...)
Conditionally logs an unformatted debug-level message using echomap::Logger::log.
Definition Logger.hpp:141
std::unique_ptr< Project > project
Owning container for the active Project.
Definition EchoMap.hpp:245
T get(T... args)

◆ configure_surface()

void echomap::EchoMap::configure_surface ( const wgpu::Surface & surface,
const wgpu::Device & device,
const wgpu::SurfaceCapabilities & capabilities,
std::uint32_t viewport_width,
std::uint32_t viewport_height )
staticprotectednoexcept

Configure a WebGPU Surface from a static context given metadata and Adapter capabilities.

Note
There is no way to check if the given Surface is already configured from the public WebGPU.h API. Moreover, attempting to Unconfigure an unconfigured Surface will assert. Therefore, callers must ensure that the given Surface is in an unconfigured state prior to invoking this function, as it cannot sanity-check the state of the Surface.
Parameters
surfaceThe Surface to configure.
deviceThe WebGPU Device on which the Surface will be displayed.
capabilitiesCapabilities of the WebGPU Adapter and Instance.
viewport_widthInitial width of the Surface viewport, in pixels.
viewport_heightInitial height of the Surface viewport, in pixels.

Definition at line 140 of file EchoMap.cpp.

147{
148 const wgpu::SurfaceConfiguration config{
149 .device = device,
150 // ReSharper disable once CppDFAConstantConditions
151 // ReSharper disable once CppDFAUnreachableCode
152 .format = capabilities.formats != nullptr ? *capabilities.formats : wgpu::TextureFormat::RGBA8Snorm,
153 .usage = wgpu::TextureUsage::RenderAttachment,
154 .width = viewport_width,
155 .height = viewport_height,
156 // ReSharper disable once CppDFAConstantConditions
157 // ReSharper disable once CppDFAUnreachableCode
158 .alphaMode =
159 capabilities.alphaModes != nullptr ? *capabilities.alphaModes : wgpu::CompositeAlphaMode::Opaque,
160 .presentMode = wgpu::PresentMode::Fifo,
161 };
162
163 surface.Configure(&config);
164}

◆ create_window()

GLFWwindow * echomap::EchoMap::create_window ( int width,
int height )
staticprotected

Create a new GLFW window of the specified dimensions from a static context.

Parameters
widthInitial width of the window, in pixels.
heightInitial height of the window, in pixels.
Returns
A mutable pointer to the created window, which must be explicitly deleted following use.
Exceptions
ConfigurationErrorA GLFW initialisation step failed.

Definition at line 118 of file EchoMap.cpp.

124{
125 if (glfwInit() == 0)
126 throw ConfigurationError("glfwInit failed");
127
128 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
129
130 auto* const window = glfwCreateWindow(width, height, "EchoMap", nullptr, nullptr);
131
132 if (window == nullptr) {
133 glfwTerminate();
134 throw ConfigurationError("glfwCreateWindow failed");
135 }
136
137 return window;
138}

◆ handle_notification() [1/5]

void echomap::EchoMap::handle_notification ( const AddChannelMappingNotification & notification) const
protected

Definition at line 521 of file EchoMap.cpp.

524{
525 notification.verify_project(project.get());
526 project->add_association(notification.signal_id, notification.sensor_id);
527}

◆ handle_notification() [2/5]

void echomap::EchoMap::handle_notification ( const ClearErrorNotification & notification)
protected

Definition at line 555 of file EchoMap.cpp.

558{
559 std::ignore = notification;
560 error_modal.reset();
561}
std::optional< ErrorModal > error_modal
Persistent panel to indicate errors over all other panels.
Definition EchoMap.hpp:243

◆ handle_notification() [3/5]

void echomap::EchoMap::handle_notification ( const ModifySensorColourNotification & notification) const
protected

Definition at line 529 of file EchoMap.cpp.

532{
533 notification.verify_project(project.get());
534 project->get_mutable_sensor(notification.sensor_id).set_colour(notification.colour);
535}

◆ handle_notification() [4/5]

void echomap::EchoMap::handle_notification ( const ModifySensorPositionNotification & notification) const
protected

Definition at line 537 of file EchoMap.cpp.

540{
541 notification.verify_project(project.get());
542 project->get_mutable_sensor(notification.sensor_id).set_position(notification.position);
543}

◆ handle_notification() [5/5]

void echomap::EchoMap::handle_notification ( const ProjectSelectionCompleteNotification & notification)
protected

Definition at line 545 of file EchoMap.cpp.

548{
549 active_modal.reset();
550
551 if (notification.path.has_value())
552 worker.submit(std::make_unique<LoadProjectTask>(*notification.path, &worker));
553}
std::unique_ptr< IPanel > active_modal
The current active non-ErrorModal modal panel.
Definition EchoMap.hpp:246

◆ handle_result() [1/2]

void echomap::EchoMap::handle_result ( LoadProjectResult && result)
protectedvirtual

Definition at line 563 of file EchoMap.cpp.

566{
567 if (active_modal != nullptr) {
568 LOG_WARN("Ignoring request to change active Project since there is an active modal.");
569 return;
570 }
571
572 change_active_project(std::move(std::move(result).take_project()));
573}
#define LOG_WARN(msg)
Logs an unformatted warning-level message using echomap::Logger::log.
Definition Logger.hpp:162

◆ handle_result() [2/2]

void echomap::EchoMap::handle_result ( LoadSignalFileResult && result)
protectedvirtual

Definition at line 575 of file EchoMap.cpp.

578{
579 if (project == nullptr || result.get_project_id() != project->get_id())
581 "Dropping LoadSignalFileResult, which was intended for the unavailable Project with ID {}.",
582 result.get_project_id()
583 );
584 else
585 for (auto&& signals = std::move(result).take_signals(); auto signal : signals | std::views::as_rvalue)
586 project->add_signal(std::move(signal));
587}
#define LOG_F_WARN(msg,...)
Logs a formatted warning-level message using echomap::Logger::log_f.
Definition Logger.hpp:115
T signal(T... args)

◆ handle_window_resize()

bool echomap::EchoMap::handle_window_resize ( )
protectednoexcept

Check if the window has been resized compared with the stored dimensions, updating member variables and reconfiguring the WebGPU surface if necessary.

Returns
Is the window visible?

Definition at line 439 of file EchoMap.cpp.

440{
441 int fb_width = 0;
442 int fb_height = 0;
443 glfwGetFramebufferSize(window, &fb_width, &fb_height);
444
445 if (fb_width <= 0 || fb_height <= 0)
446 // The window is too small or collapsed.
447 return false;
448
449 const auto new_width = static_cast<std::uint32_t>(fb_width);
450 // ReSharper disable once CppTooWideScopeInitStatement
451 const auto new_height = static_cast<std::uint32_t>(fb_height);
452
453 if (new_width != viewport_width || new_height != viewport_height) {
454 // The window has been resized, so update the WebGPU surface.
455
456 viewport_width = new_width;
457 viewport_height = new_height;
458
459 surface.Unconfigure();
460 configure_surface(surface, device, surface_capabilities, viewport_width, viewport_height);
461
462 // The window size has changed and the surface re-configured accordingly.
463 return true;
464 }
465
466 // The window size is unchanged.
467 return true;
468}

◆ increment_forced_frames()

void echomap::EchoMap::increment_forced_frames ( unsigned int count = 4)
noexcept

Indicate to the renderer that the following frames should always be rendered, regardless of whether there are any new events to process.

The forced frame count stacks (as in a source) until the renderer drains it to zero (as in a sink). By default, we force four frames since most Dear ImGui components can fully render a four-frame cycle.

Parameters
countThe number of frames to force.

Definition at line 622 of file EchoMap.cpp.

625{
626 forced_frames += count;
627}
T count(T... args)

◆ make_common_notification_visitors()

auto echomap::EchoMap::make_common_notification_visitors ( )
inlineprotected

Produce an overload set for std::visit for all platform-independent Notification objects.

Returns
The overload set.

Definition at line 94 of file EchoMap.hpp.

95 {
96 // clang-format off
97 return variant_helpers::Overloaded{
98 [this](const AddChannelMappingNotification& n) { handle_notification(n); },
99 [this](const ModifySensorColourNotification& n) { handle_notification(n); },
100 [this](const ModifySensorPositionNotification& n) { handle_notification(n); },
101 [this](const ProjectSelectionCompleteNotification& n) { handle_notification(n); },
102 [this](const ClearErrorNotification& n) { handle_notification(n); },
103 };
104 // clang-format on
105 }

◆ notify()

void echomap::EchoMap::notify ( const Notification & notification)

Submit a new Notification to the application queue.

Notifications are processed at the beginning of render cycles in a first-come first-served ordering.

Parameters
notificationThe Notification to schedule.

Definition at line 604 of file EchoMap.cpp.

607{
608 notification_queue.emplace_back(notification);
609
610 /*
611 * The address is just a "hint" (as opposed to an ID) because the queue might be re-allocated. It's a best-guess
612 * effort to quickly discriminate o notification without adding bloat to their structures.
613 */
615 "Scheduling {} with hint {} at position {}.",
616 NotificationNames::indexed_names[notification_queue.back().index()],
617 static_cast<void*>(&notification_queue.back()),
618 notification_queue.size() - 1
619 );
620}
static const std::array< std::string_view, std::variant_size_v< Notification > > indexed_names
Aggregated names by the index of the alternative.

◆ process_notifications()

void echomap::EchoMap::process_notifications ( )
protected

Handle any unconsumed Notification objects from the queue.

Definition at line 470 of file EchoMap.cpp.

471{
472 while (!notification_queue.empty()) {
473 auto& notification = notification_queue.back();
474 const auto type_name = NotificationNames::indexed_names[notification.index()];
475 auto* const hint = static_cast<void*>(&notification);
476
477 LOG_F_DEBUG("Consuming {} with hint {}.", type_name, hint);
478
479 try {
480 visit_notification(notification);
481 } catch (const IgnoredWarning& warning) {
482 LOG_F_WARN("{} with hint {} was dropped: {}", type_name, hint, warning.what());
483 } catch (const std::exception& exception) {
484 raise_error(exception.what());
485 LOG_F_ERROR("{} with hint {} was responsible for error: {}", type_name, hint, exception.what());
486 }
487
488 notification_queue.pop_back();
489 }
490}
#define LOG_F_ERROR(msg,...)
Logs a formatted error-level message using echomap::Logger::log_f.
Definition Logger.hpp:125
virtual void visit_notification(Notification &notification)=0
Uses std::visit on the given notification to invoke the corresponding handler.
T what(T... args)

◆ process_worker_results()

void echomap::EchoMap::process_worker_results ( )
protected

Handle any unconsumed events from the Worker.

Definition at line 492 of file EchoMap.cpp.

493{
494 while (auto result = worker.try_get_result())
495 try {
496 despatcher.publish(std::move(*result));
497 } catch (const std::exception& exception) {
498 Logger::log(Logger::Level::Error, exception.what(), std::source_location::current());
499 }
500}
static void log(Level level, std::string_view message, std::source_location location=std::source_location::current())
Log an unformatted message to the backend.
Definition Logger.cpp:22
T current(T... args)

◆ raise_error() [1/2]

void echomap::EchoMap::raise_error ( std::string_view message)
protected

Definition at line 502 of file EchoMap.cpp.

505{
506 error_modal.emplace(message, [this] {
507 notify(ClearErrorNotification{});
508 });
509}
void notify(const Notification &notification)
Submit a new Notification to the application queue.
Definition EchoMap.cpp:604

◆ raise_error() [2/2]

void echomap::EchoMap::raise_error ( std::string_view message,
const std::runtime_error & exception )
protected

Definition at line 511 of file EchoMap.cpp.

515{
516 error_modal.emplace(message, exception, [this] {
517 notify(ClearErrorNotification{});
518 });
519}

◆ render()

void echomap::EchoMap::render ( )
protectednoexcept

Perform a render cycle on the configured Surface and Device.

A single render cycle requests all panels to render their state to the Surface, and provides an opportunity to submit any work to the GPU. Events are also received from GLFW and processed as required.

Definition at line 256 of file EchoMap.cpp.

257{
258 // Process any events that have arrived since the last cycle.
261
262 // Handle system/graphics changes.
264 return;
265
266 wgpu::SurfaceTexture surface_texture{};
267 surface.GetCurrentTexture(&surface_texture);
268
269 switch (surface_texture.status) {
270 case wgpu::SurfaceGetCurrentTextureStatus::Error:
271 case wgpu::SurfaceGetCurrentTextureStatus::Lost:
272 case wgpu::SurfaceGetCurrentTextureStatus::Outdated:
273 surface.Unconfigure();
274 configure_surface(surface, device, surface_capabilities, viewport_width, viewport_height);
275 return;
276
277 case wgpu::SurfaceGetCurrentTextureStatus::Timeout:
278 return;
279
280 case wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal:
281 case wgpu::SurfaceGetCurrentTextureStatus::SuccessSuboptimal:
282 break;
283 }
284
285 const wgpu::TextureView surface_view = surface_texture.texture.CreateView();
286
287 ImGui_ImplWGPU_NewFrame();
288 ImGui_ImplGlfw_NewFrame();
289 ImGui::NewFrame();
290
291 setup_dockspace();
292
293 // Draw the panels and express any applicable error state.
294 for (const auto& panel : panels)
295 panel->draw();
296
297 if (active_modal != nullptr)
298 active_modal->draw();
299
300 if (error_modal.has_value())
301 error_modal->draw();
302
303 ImGui::Render();
304
305 // Set up a command encoder for the render and allow Dear ImGui panels to provide work.
306 const wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
307
308 // TODO no panels use GPU yet.
309
310 // Provide the framebuffer to the WebGPU driver.
311 wgpu::RenderPassColorAttachment const attachment{
312 .view = surface_view,
313 .loadOp = wgpu::LoadOp::Clear,
314 .storeOp = wgpu::StoreOp::Store,
315 .clearValue = {.r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0},
316 };
317
318 const wgpu::RenderPassDescriptor pass_descriptor{.colorAttachmentCount = 1, .colorAttachments = &attachment};
319 const wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&pass_descriptor);
320
321 ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass.Get());
322 pass.End();
323
324 const wgpu::CommandBuffer commands = encoder.Finish();
325
326 // Submit batched work to the GPU.
327 device.GetQueue().Submit(1, &commands);
328
329#if !defined(__EMSCRIPTEN__) || defined(__DOXYGEN__)
330 // ReSharper disable once CppExpressionWithoutSideEffects
331 surface.Present();
332#endif
333}
void process_notifications()
Handle any unconsumed Notification objects from the queue.
Definition EchoMap.cpp:470
bool handle_window_resize() noexcept
Check if the window has been resized compared with the stored dimensions, updating member variables a...
Definition EchoMap.cpp:439
void process_worker_results()
Handle any unconsumed events from the Worker.
Definition EchoMap.cpp:492

◆ request_adapter()

wgpu::Future echomap::EchoMap::request_adapter ( )
protectednoexcept

Produce a WebGPU Future for requesting an Adapter.

Returns
A Future to request an Adapter that is suitable for the Surface member from the WebGPU driver.

Definition at line 186 of file EchoMap.cpp.

187{
188 const wgpu::RequestAdapterOptions options{.compatibleSurface = surface};
189
190 return instance.RequestAdapter(
191 &options,
192 wgpu::CallbackMode::WaitAnyOnly,
193 [this](const wgpu::RequestAdapterStatus status, wgpu::Adapter new_adapter, const wgpu::StringView message) {
194 if (status != wgpu::RequestAdapterStatus::Success)
196 Logger::Level::Error,
198 "WebGPU adapter request failed {}: {}.",
199 std::to_underlying(status),
200 std::string_view(message)
201 );
202 else
203 adapter = std::move(new_adapter);
204 }
205 );
206}
static void log_f(const Level level, const std::source_location location, std::format_string< Args... > fmt, Args &&... args)
Log a formatted message to the backend with a specific source location.
Definition Logger.hpp:61

◆ request_device()

wgpu::Future echomap::EchoMap::request_device ( )
protectednoexcept

Produce a WebGPU Future for requesting an accelerator device.

Returns
A Future to request a Device from the WebGPU driver.

Definition at line 208 of file EchoMap.cpp.

209{
210 wgpu::DeviceDescriptor desc{};
211 desc.SetDeviceLostCallback(
212 wgpu::CallbackMode::AllowSpontaneous,
213 [](const wgpu::Device&, const wgpu::DeviceLostReason reason, const wgpu::StringView message) {
214 if (reason == wgpu::DeviceLostReason::Destroyed)
215 return;
216
218 Logger::Level::Warning,
220 "Device lost because of reason {}: {}.",
221 std::to_underlying(reason),
222 std::string_view(message)
223 );
224 }
225 );
226
227 desc.SetUncapturedErrorCallback(
228 [](const wgpu::Device&, const wgpu::ErrorType error_type, const wgpu::StringView message) {
230 Logger::Level::Error,
232 "WebGPU error {}: {}",
233 std::to_underlying(error_type),
234 std::string_view(message)
235 );
236 }
237 );
238
239 return adapter.RequestDevice(
240 &desc,
241 wgpu::CallbackMode::WaitAnyOnly,
242 [this](const wgpu::RequestDeviceStatus status, wgpu::Device new_device, const wgpu::StringView message) {
243 if (status != wgpu::RequestDeviceStatus::Success)
245 Logger::Level::Error,
247 "WebGPU device request failed: {}",
248 std::string_view(message)
249 );
250 else
251 device = std::move(new_device);
252 }
253 );
254}

◆ run_event_loop()

virtual void echomap::EchoMap::run_event_loop ( )
pure virtual

Runs the platform-dependent event loop to manage and propagate interaction with the EchoMap application.

This function returns only once GLFW indicates that the window should close. Following closure, the event loop could be re-run, or the application could clean up by calling the destructor.

Implemented in echomap::EchoMapNative, and echomap::EchoMapWeb.

◆ setup_dockspace()

void echomap::EchoMap::setup_dockspace ( )
protected

Definition at line 382 of file EchoMap.cpp.

383{
384 const auto* const viewport = ImGui::GetMainViewport();
385
386 if (!dockspace_configured) {
387 dockspace_configured = true;
388
389 if (ImGui::DockBuilderGetNode(dockspace_id) == nullptr) {
390 // Root dockspace node.
391 ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
392 ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
393
394 // Left (narrow project explorer pane) and main workspace.
395 ImGuiID dock_id_left = 0;
396 ImGuiID dock_id_main = 0;
397 ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, .15f, &dock_id_left, &dock_id_main);
398 ImGui::DockBuilderDockWindow(ProjectPanel::get_imgui_stable_name(), dock_id_left);
399
400 // Upper/lower
401 ImGuiID dock_id_main_upper = 0;
402 ImGuiID dock_id_main_lower = 0;
403 ImGui::DockBuilderSplitNode(dock_id_main, ImGuiDir_Up, .33f, &dock_id_main_upper, &dock_id_main_lower);
404
405 // Upper left/right
406 ImGuiID dock_id_main_upper_left = 0;
407 ImGuiID dock_id_main_upper_right = 0;
408 ImGui::DockBuilderSplitNode(
409 dock_id_main_upper,
410 ImGuiDir_Left,
411 .5f,
412 &dock_id_main_upper_left,
413 &dock_id_main_upper_right
414 );
415 ImGui::DockBuilderDockWindow(ChannelMappingPanel::get_imgui_stable_name(), dock_id_main_upper_left);
416 ImGui::DockBuilderDockWindow(SensorGeometryPanel::get_imgui_stable_name(), dock_id_main_upper_right);
417
418 // Lower left/right
419 ImGuiID dock_id_main_lower_left = 0;
420 ImGuiID dock_id_main_lower_right = 0;
421 ImGui::DockBuilderSplitNode(
422 dock_id_main_lower,
423 ImGuiDir_Left,
424 .5f,
425 &dock_id_main_lower_left,
426 &dock_id_main_lower_right
427 );
428 ImGui::DockBuilderDockWindow(SignalWaveformPanel::get_imgui_stable_name(), dock_id_main_lower_left);
429 ImGui::DockBuilderDockWindow(SignalDFTPanel::get_imgui_stable_name(), dock_id_main_lower_right);
430
431 ImGui::DockBuilderFinish(dockspace_id);
432 }
433 }
434
435 ImGui::DockSpaceOverViewport(dockspace_id, viewport, ImGuiDockNodeFlags_PassthruCentralNode);
436}

◆ setup_imgui()

void echomap::EchoMap::setup_imgui ( )
protected

Create a context for Dear ImGui and ImPlot, and configure the plain GLFW and WebGPU backends.

Exceptions
ConfigurationErrorA Dear ImGui backend could not be initialised.

Definition at line 336 of file EchoMap.cpp.

337{
338 if (!device)
339 throw ConfigurationError("Cannot initialise ImGui: WebGPU device is null");
340
341 // Bring up the Dear ImGui context and initialise the GLFW backend.
342 IMGUI_CHECKVERSION();
343 ImGui::CreateContext();
344 ImPlot::CreateContext();
345 ImPlot3D::CreateContext();
346
347 auto& io = ImGui::GetIO();
348 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // NOLINT(*-signed-bitwise) - Required by ImGui API.
349 io.Fonts->AddFontFromMemoryCompressedTTF(
350 // ReSharper disable once CppRedundantCastExpression
351 static_cast<const void*>(data::RobotoMedium_compressed_data),
352 std::size(data::RobotoMedium_compressed_data),
353 14
354 );
355 ImGui::StyleColorsLight();
356
357 if (!ImGui_ImplGlfw_InitForOther(window, true))
358 throw ConfigurationError("ImGui_ImplGlfw_InitForOther failed");
359
360 // Configure the WebGPU backend for Dear ImGui.
361 ImGui_ImplWGPU_InitInfo init_info{};
362 init_info.Device = device.Get();
363 // ReSharper disable once CppDFAConstantConditions
364 // ReSharper disable once CppDFAUnreachableCode
365 init_info.RenderTargetFormat = static_cast<WGPUTextureFormat>(std::to_underlying(
366 (surface_capabilities.formats != nullptr) ? *surface_capabilities.formats : wgpu::TextureFormat::RGBA8Snorm
367 ));
368
369 if (!ImGui_ImplWGPU_Init(&init_info))
370 throw ConfigurationError("ImGui_ImplWGPU_Init failed");
371
372#if defined(__EMSCRIPTEN__) || defined(__DOXYGEN__)
373 /*
374 * If we're targeting WebAssembly, the window dimensions reported by GLFW should match the size of the canvas
375 * identified by the CSS selector <code>#canvas</code>. Dear ImGui provides the helper
376 * ImGui_ImplGlfw_InstallEmscriptenCallbacks to interface with the DOM and trigger re-draws as needed.
377 */
378 ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
379#endif
380}
T size(T... args)

◆ setup_subscriptions()

void echomap::EchoMap::setup_subscriptions ( )
protected

Configure the core signals for the application instance.

This should be invoked during construction prior to any IPanel invocations as it takes the exclusive consumer role for several critical message classes.

Definition at line 166 of file EchoMap.cpp.

167{
168 // NOLINTBEGIN(*-redundant-casting) - False positive; casts are required for libsigcpp to resolve overloads.
169 connections.emplace_back(
170 despatcher.load_project_finished_channel.nominate_consumer(
171 sigc::mem_fun(*this, static_cast<void (EchoMap::*)(LoadProjectResult&&)>(&EchoMap::handle_result))
172 ));
173
174 connections.emplace_back(
175 despatcher.load_signal_file_channel.nominate_consumer(
176 sigc::mem_fun(*this, static_cast<void (EchoMap::*)(LoadSignalFileResult&&)>(&EchoMap::handle_result))
177 ));
178 // NOLINTEND(*-redundant-casting)
179
180 connections.emplace_back(despatcher.error_channel.observe([this](const ErrorResult& error) {
181 raise_error(error.what());
182 LOG_F_ERROR("Error modal raised due to error: {}", error.what());
183 }));
184}
std::vector< sigc::scoped_connection > connections
RAII lifetime manager for signal connections.
Definition EchoMap.hpp:240
EchoMap()
Initialise a EchoMap application instance.
Definition EchoMap.cpp:41

◆ visit_notification()

virtual void echomap::EchoMap::visit_notification ( Notification & notification)
protectedpure virtual

Uses std::visit on the given notification to invoke the corresponding handler.

This function is virtual, since the overload set can be platform-dependent in addition to the base handlers provided by make_common_notification_visitors.

Parameters
notificationThe notification to visit.

Implemented in echomap::EchoMapNative, and echomap::EchoMapWeb.

Member Data Documentation

◆ active_modal

std::unique_ptr<IPanel> echomap::EchoMap::active_modal
protected

The current active non-ErrorModal modal panel.

Definition at line 246 of file EchoMap.hpp.

◆ adapter

wgpu::Adapter echomap::EchoMap::adapter
protected

Definition at line 232 of file EchoMap.hpp.

◆ connections

std::vector<sigc::scoped_connection> echomap::EchoMap::connections
protected

RAII lifetime manager for signal connections.

Definition at line 240 of file EchoMap.hpp.

◆ despatcher

WorkerResultDespatcher echomap::EchoMap::despatcher
protected

Despatcher to manage Worker result channels.

Definition at line 239 of file EchoMap.hpp.

◆ device

wgpu::Device echomap::EchoMap::device
protected

Definition at line 233 of file EchoMap.hpp.

◆ dockspace_configured

bool echomap::EchoMap::dockspace_configured = false
protected

Definition at line 249 of file EchoMap.hpp.

◆ dockspace_id

ImGuiID echomap::EchoMap::dockspace_id
protected

Definition at line 248 of file EchoMap.hpp.

◆ error_modal

std::optional<ErrorModal> echomap::EchoMap::error_modal
protected

Persistent panel to indicate errors over all other panels.

Definition at line 243 of file EchoMap.hpp.

◆ forced_frames

unsigned int echomap::EchoMap::forced_frames = 0
protected

Definition at line 250 of file EchoMap.hpp.

◆ instance

wgpu::Instance echomap::EchoMap::instance
protected

Definition at line 231 of file EchoMap.hpp.

◆ notification_queue

std::vector<Notification> echomap::EchoMap::notification_queue
protected

Definition at line 244 of file EchoMap.hpp.

◆ operation_timeout

auto echomap::EchoMap::operation_timeout = std::numeric_limits<std::uint64_t>::max()
staticconstexprprotected

Definition at line 125 of file EchoMap.hpp.

◆ panels

std::vector<std::unique_ptr<IProjectPanel> > echomap::EchoMap::panels
protected

Individual display components.

Definition at line 242 of file EchoMap.hpp.

◆ project

std::unique_ptr<Project> echomap::EchoMap::project
protected

Owning container for the active Project.

Definition at line 245 of file EchoMap.hpp.

◆ surface

wgpu::Surface echomap::EchoMap::surface
protected

Definition at line 234 of file EchoMap.hpp.

◆ surface_capabilities

wgpu::SurfaceCapabilities echomap::EchoMap::surface_capabilities
protected

Definition at line 235 of file EchoMap.hpp.

◆ viewport_height

std::uint32_t echomap::EchoMap::viewport_height = 1024
protected

Definition at line 229 of file EchoMap.hpp.

◆ viewport_width

std::uint32_t echomap::EchoMap::viewport_width = 1024
protected

Definition at line 228 of file EchoMap.hpp.

◆ window

GLFWwindow* echomap::EchoMap::window = nullptr
protected

Definition at line 236 of file EchoMap.hpp.

◆ worker

Worker echomap::EchoMap::worker
protected

Multi-threaded worker for scheduling heavy computation tasks.

Definition at line 238 of file EchoMap.hpp.


The documentation for this class was generated from the following files: