diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 017121c..dde2e3d 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -299,7 +299,7 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height wl_surface_commit(surface); - zxdg_toplevel_decoration_v1* decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(Device::manager, xdgToplevel); + decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(Device::manager, xdgToplevel); zxdg_toplevel_decoration_v1_set_mode(decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); wpViewport = wp_viewporter_get_viewport(Device::wpViewporter, surface); @@ -515,6 +515,52 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height currentMousePos = {0,0}; } +#ifndef CRAFTER_GRAPHICS_WINDOW_DOM +Window::~Window() { + // Nothing may still run against the semaphores, fences or images below. + if (Device::device != VK_NULL_HANDLE) vkDeviceWaitIdle(Device::device); + if (Device::device != VK_NULL_HANDLE) { + vkFreeCommandBuffers(Device::device, Device::commandPool, numFrames, drawCmdBuffers); + for (std::uint8_t i = 0; i < numFrames; i++) { + if (renderFinishedSemaphores[i]) vkDestroySemaphore(Device::device, renderFinishedSemaphores[i], nullptr); + if (waitFences[i]) vkDestroyFence(Device::device, waitFences[i], nullptr); + } + for (std::uint8_t i = 0; i < numAcquireSemaphores; i++) { + if (imageAcquiredSemaphores[i]) vkDestroySemaphore(Device::device, imageAcquiredSemaphores[i], nullptr); + } + if (swapChain != VK_NULL_HANDLE) vkDestroySwapchainKHR(Device::device, swapChain, nullptr); + swapChain = VK_NULL_HANDLE; + } + if (vulkanSurface != VK_NULL_HANDLE) vkDestroySurfaceKHR(Device::instance, vulkanSurface, nullptr); + vulkanSurface = VK_NULL_HANDLE; + + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + SetDefaultCursor(); + // `cb` is not destroyed here: wl_surface_frame_done frees the callback it + // was called with and re-arms through its own local, so the member may + // already name a freed one. + if (wpViewport) wp_viewport_destroy(wpViewport); + if (wp_scale) { + wp_fractional_scale_v1_destroy(wp_scale); + wp_scale = nullptr; + } + if (decoration) zxdg_toplevel_decoration_v1_destroy(decoration); + if (xdgToplevel) xdg_toplevel_destroy(xdgToplevel); + if (xdgSurface) xdg_surface_destroy(xdgSurface); + if (surface) wl_surface_destroy(surface); + wpViewport = nullptr; + decoration = nullptr; + xdgToplevel = nullptr; + xdgSurface = nullptr; + surface = nullptr; + wl_display_flush(Device::display); + std::erase(Device::windows, this); + if (Device::focusedWindow == this) Device::focusedWindow = nullptr; + if (Device::keyboardWindow == this) Device::keyboardWindow = nullptr; + #endif +} +#endif + void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) { // Skip degenerate resizes. Win32 minimised windows give (0, 0); Wayland // sometimes echoes the current size in a configure. diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 37e97f4..c9f7155 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -110,16 +110,19 @@ export namespace Crafter { Window(Window&) = delete; Window(Window&&) = delete; Window& operator=(const Window&) = delete; - #ifdef CRAFTER_GRAPHICS_WINDOW_DOM // DOM mode keeps a process-global pointer to the live Window so // the JS bridge can deliver document/window events back. If the // Window is destroyed (e.g. it was stack-allocated in main and // main returned), the global must be cleared so subsequent // browser callbacks become no-ops instead of dereferencing - // freed memory. Native builds have nothing to clean up at the - // Window level (Vulkan resources teardown is the user's job). + // freed memory. Native builds release what the constructor made: + // after a device wait, the command buffers, semaphores and fences, + // the swapchain and the Vulkan surface, then (Wayland) the cursor, + // viewport, fractional scale, decoration, xdg toplevel, xdg surface + // and wl_surface, and the window leaves Device::windows and the + // focus pointers — so a window can be closed and another created, + // which a resident app does on every hide and show. ~Window(); - #endif // Enter the platform event loop and never return. In DOM mode this // hands the loop to requestAnimationFrame and _Exit(0)s so the wasm @@ -191,6 +194,7 @@ export namespace Crafter { std::int32_t pendingLogicalWidth = 0; std::int32_t pendingLogicalHeight = 0; xdg_toplevel* xdgToplevel = nullptr; + zxdg_toplevel_decoration_v1* decoration = nullptr; wp_viewport* wpViewport = nullptr; wl_surface* surface = nullptr; xdg_surface* xdgSurface = nullptr;