Vulkan: Wayland Windowing

Wayland WSI would crash the system when used. The reason is that the
wayland vulkan WSI doesn't provide windowing support. Vulkan gets full access
to the desktop of the OS and it is the responsibilty of the application to
do the right thing.

For OpenGL Wayland proved basic windowing support using `wayland-egl.h`.
Which essentially is a tiny wrapper that keeps track of the window position and
size.

This PR changes a few things to make the Wayland surface usable:

- Do not load debug extensions when blender isn't started with
  `--debug-gpu`.
- Recreate swapchain images when surface size changes.

Pull Request: https://projects.blender.org/blender/blender/pulls/113007
This commit is contained in:
Jeroen Bakker 2023-10-06 14:45:09 +02:00
parent 44882ceb7b
commit 9a654c04de
7 changed files with 162 additions and 50 deletions

View File

@ -400,6 +400,7 @@ GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual,
/* Wayland */
wl_surface *wayland_surface,
wl_display *wayland_display,
const GHOST_ContextVK_WindowInfo *wayland_window_info,
#endif
int contextMajorVersion,
int contextMinorVersion,
@ -417,6 +418,7 @@ GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual,
/* Wayland */
m_wayland_surface(wayland_surface),
m_wayland_display(wayland_display),
m_wayland_window_info(wayland_window_info),
#endif
m_context_major_version(contextMajorVersion),
m_context_minor_version(contextMinorVersion),
@ -476,6 +478,25 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers()
return GHOST_kFailure;
}
#ifdef WITH_GHOST_WAYLAND
/* Wayland doesn't provide a WSI with windowing capabilities, therefore cannot detect whether the
* swap-chain needs to be recreated. But as a side effect we can recreate the swap chain before
* presenting. */
if (m_wayland_window_info) {
const bool recreate_swapchain =
((m_wayland_window_info->size[0] !=
std::max(m_render_extent.width, m_render_extent_min.width)) ||
(m_wayland_window_info->size[1] !=
std::max(m_render_extent.height, m_render_extent_min.height)));
if (recreate_swapchain) {
/* Swap-chain is out of date. Recreate swap-chain. */
destroySwapchain();
createSwapchain();
}
}
#endif
assert(vulkan_device.has_value() && vulkan_device->device != VK_NULL_HANDLE);
VkDevice device = vulkan_device->device;
vkAcquireNextImageKHR(device, m_swapchain, UINT64_MAX, VK_NULL_HANDLE, m_fence, &m_currentImage);
@ -795,11 +816,29 @@ GHOST_TSuccess GHOST_ContextVK::createSwapchain()
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, m_surface, &capabilities);
m_render_extent = capabilities.currentExtent;
m_render_extent_min = capabilities.minImageExtent;
if (m_render_extent.width == UINT32_MAX) {
/* Window Manager is going to set the surface size based on the given size.
* Choose something between minImageExtent and maxImageExtent. */
m_render_extent.width = 1280;
m_render_extent.height = 720;
int width = 0;
int height = 0;
#ifdef WITH_GHOST_WAYLAND
/* Wayland doesn't provide a windowing API via WSI. */
if (m_wayland_window_info) {
width = m_wayland_window_info->size[0];
height = m_wayland_window_info->size[1];
}
#endif
if (width == 0 || height == 0) {
width = 1280;
height = 720;
}
m_render_extent.width = width;
m_render_extent.height = height;
if (capabilities.minImageExtent.width > m_render_extent.width) {
m_render_extent.width = capabilities.minImageExtent.width;
}

View File

@ -42,6 +42,10 @@ typedef enum {
#endif
} GHOST_TVulkanPlatformType;
struct GHOST_ContextVK_WindowInfo {
int size[2];
};
class GHOST_ContextVK : public GHOST_Context {
public:
/**
@ -61,6 +65,7 @@ class GHOST_ContextVK : public GHOST_Context {
/* Wayland */
wl_surface *wayland_surface,
wl_display *wayland_display,
const GHOST_ContextVK_WindowInfo *wayland_window_info,
#endif
int contextMajorVersion,
int contextMinorVersion,
@ -151,6 +156,7 @@ class GHOST_ContextVK : public GHOST_Context {
/* Wayland */
wl_surface *m_wayland_surface;
wl_display *m_wayland_display;
const GHOST_ContextVK_WindowInfo *m_wayland_window_info;
#endif
const int m_context_major_version;
@ -169,6 +175,7 @@ class GHOST_ContextVK : public GHOST_Context {
std::vector<VkImage> m_swapchain_images;
VkExtent2D m_render_extent;
VkExtent2D m_render_extent_min;
VkSurfaceFormatKHR m_surface_format;
VkFence m_fence;

View File

@ -6337,9 +6337,7 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GPUSettings gp
std::lock_guard lock_server_guard{*server_mutex};
#endif
#ifdef WITH_VULKAN_BACKEND
const bool debug_context = (gpuSettings.flags & GHOST_gpuDebugContext) != 0;
#endif
switch (gpuSettings.context_type) {
@ -6354,6 +6352,7 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GPUSettings gp
nullptr,
wl_surface,
display_->wl.display,
nullptr,
1,
2,
debug_context);
@ -6379,16 +6378,18 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GPUSettings gp
for (int minor = 6; minor >= 3; --minor) {
/* Caller must lock `system->server_mutex`. */
GHOST_Context *context = new GHOST_ContextEGL(this,
false,
EGLNativeWindowType(egl_window),
EGLNativeDisplayType(display_->wl.display),
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
4,
minor,
GHOST_OPENGL_EGL_CONTEXT_FLAGS,
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
EGL_OPENGL_API);
GHOST_Context *context = new GHOST_ContextEGL(
this,
false,
EGLNativeWindowType(egl_window),
EGLNativeDisplayType(display_->wl.display),
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
4,
minor,
GHOST_OPENGL_EGL_CONTEXT_FLAGS |
(debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
EGL_OPENGL_API);
if (context->initializeDrawingContext()) {
wl_surface_set_user_data(wl_surface, egl_window);
@ -6480,7 +6481,8 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
gpuSettings.context_type,
is_dialog,
((gpuSettings.flags & GHOST_gpuStereoVisual) != 0),
exclusive);
exclusive,
(gpuSettings.flags & GHOST_gpuDebugContext) != 0);
if (window) {
if (window->getValid()) {

View File

@ -358,8 +358,16 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GPUSettings gpuSet
switch (gpuSettings.context_type) {
#ifdef WITH_VULKAN_BACKEND
case GHOST_kDrawingContextTypeVulkan: {
GHOST_Context *context = new GHOST_ContextVK(
false, GHOST_kVulkanPlatformX11, 0, m_display, nullptr, nullptr, 1, 2, debug_context);
GHOST_Context *context = new GHOST_ContextVK(false,
GHOST_kVulkanPlatformX11,
0,
m_display,
nullptr,
nullptr,
nullptr,
1,
2,
debug_context);
if (context->initializeDrawingContext()) {
return context;
}

View File

@ -24,10 +24,12 @@
#include <wayland-client-protocol.h>
#ifdef WITH_GHOST_WAYLAND_DYNLOAD
# include <wayland_dynload_egl.h>
#ifdef WITH_OPENGL_BACKEND
# ifdef WITH_GHOST_WAYLAND_DYNLOAD
# include <wayland_dynload_egl.h>
# endif
# include <wayland-egl.h>
#endif
#include <wayland-egl.h>
#include <algorithm> /* For `std::find`. */
@ -221,8 +223,6 @@ struct GWL_Window {
/** Wayland core types. */
struct {
wl_surface *surface = nullptr;
wl_egl_window *egl_window = nullptr;
} wl;
/** Wayland native types. */
@ -242,8 +242,19 @@ struct GWL_Window {
xdg_activation_token_v1 *activation_token = nullptr;
} xdg;
struct {
#ifdef WITH_OPENGL_BACKEND
wl_egl_window *egl_window = nullptr;
#endif
#ifdef WITH_VULKAN_BACKEND
GHOST_ContextVK_WindowInfo *vulkan_window_info = nullptr;
#endif
} backend;
GHOST_WindowWayland *ghost_window = nullptr;
GHOST_SystemWayland *ghost_system = nullptr;
GHOST_TDrawingContextType ghost_context_type = GHOST_kDrawingContextTypeNone;
/**
* Outputs on which the window is currently shown on.
*
@ -288,6 +299,21 @@ struct GWL_Window {
#endif /* USE_EVENT_BACKGROUND_THREAD */
};
static void gwl_window_resize_for_backend(GWL_Window *win, const int32_t size[2])
{
#ifdef WITH_OPENGL_BACKEND
if (win->ghost_context_type == GHOST_kDrawingContextTypeOpenGL) {
wl_egl_window_resize(win->backend.egl_window, UNPACK2(size), 0, 0);
}
#endif
#ifdef WITH_VULKAN_BACKEND
if (win->ghost_context_type == GHOST_kDrawingContextTypeVulkan) {
win->backend.vulkan_window_info->size[0] = size[0];
win->backend.vulkan_window_info->size[1] = size[1];
}
#endif
}
static void gwl_window_title_set(GWL_Window *win, const char *title)
{
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
@ -648,7 +674,7 @@ static void gwl_window_frame_pending_fractional_scale_set(GWL_Window *win,
static void gwl_window_frame_pending_size_set(GWL_Window *win,
bool *r_surface_needs_commit,
bool *r_surface_needs_egl_resize,
bool *r_surface_needs_resize_for_backend,
bool *r_surface_needs_buffer_scale)
{
if (win->frame_pending.size[0] == 0 || win->frame_pending.size[1] == 0) {
@ -668,11 +694,11 @@ static void gwl_window_frame_pending_size_set(GWL_Window *win,
gwl_window_viewport_size_update(win);
}
if (r_surface_needs_egl_resize) {
*r_surface_needs_egl_resize = true;
if (r_surface_needs_resize_for_backend) {
*r_surface_needs_resize_for_backend = true;
}
else {
wl_egl_window_resize(win->wl.egl_window, UNPACK2(win->frame.size), 0, 0);
gwl_window_resize_for_backend(win, win->frame.size);
}
win->ghost_window->notify_size();
@ -741,15 +767,17 @@ static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
const bool dpi_changed = win->frame_pending.fractional_scale != win->frame.fractional_scale;
bool surface_needs_commit = false;
bool surface_needs_egl_resize = false;
bool surface_needs_resize_for_backend = false;
bool surface_needs_buffer_scale = false;
if (win->frame_pending.size[0] != 0 && win->frame_pending.size[1] != 0) {
if ((win->frame.size[0] != win->frame_pending.size[0]) ||
(win->frame.size[1] != win->frame_pending.size[1]))
{
gwl_window_frame_pending_size_set(
win, &surface_needs_commit, &surface_needs_egl_resize, &surface_needs_buffer_scale);
gwl_window_frame_pending_size_set(win,
&surface_needs_commit,
&surface_needs_resize_for_backend,
&surface_needs_buffer_scale);
}
}
@ -764,8 +792,8 @@ static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
}
}
if (surface_needs_egl_resize) {
wl_egl_window_resize(win->wl.egl_window, UNPACK2(win->frame.size), 0, 0);
if (surface_needs_resize_for_backend) {
gwl_window_resize_for_backend(win, win->frame.size);
}
if (surface_needs_buffer_scale) {
@ -1337,10 +1365,12 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
const GHOST_TDrawingContextType type,
const bool is_dialog,
const bool stereoVisual,
const bool exclusive)
const bool exclusive,
const bool is_debug)
: GHOST_Window(width, height, state, stereoVisual, exclusive),
system_(system),
window_(new GWL_Window)
window_(new GWL_Window),
is_debug_context_(is_debug)
{
#ifdef USE_EVENT_BACKGROUND_THREAD
std::lock_guard lock_server_guard{*system->server_mutex};
@ -1348,6 +1378,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
window_->ghost_window = this;
window_->ghost_system = system;
window_->ghost_context_type = type;
/* NOTE(@ideasman42): The scale set here to avoid flickering on startup.
* When all monitors use the same scale (which is quite common) there aren't any problems.
@ -1387,8 +1418,19 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
wl_surface_add_listener(window_->wl.surface, &wl_surface_listener, window_);
window_->wl.egl_window = wl_egl_window_create(
window_->wl.surface, int(window_->frame.size[0]), int(window_->frame.size[1]));
#ifdef WITH_OPENGL_BACKEND
if (type == GHOST_kDrawingContextTypeOpenGL) {
window_->backend.egl_window = wl_egl_window_create(
window_->wl.surface, int(window_->frame.size[0]), int(window_->frame.size[1]));
}
#endif
#ifdef WITH_VULKAN_BACKEND
if (type == GHOST_kDrawingContextTypeVulkan) {
window_->backend.vulkan_window_info = new GHOST_ContextVK_WindowInfo;
window_->backend.vulkan_window_info->size[0] = window_->frame.size[0];
window_->backend.vulkan_window_info->size[1] = window_->frame.size[1];
}
#endif
wp_fractional_scale_manager_v1 *fractional_scale_manager =
system->wp_fractional_scale_manager_get();
@ -1501,9 +1543,9 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
gwl_window_state_set(window_, state);
}
/* EGL context. */
/* Drawing context. */
if (setDrawingContextType(type) == GHOST_kFailure) {
GHOST_PRINT("Failed to create EGL context" << std::endl);
GHOST_PRINT("Failed to create drawing context" << std::endl);
}
/* Set swap interval to 0 to prevent blocking. */
@ -1669,7 +1711,16 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
releaseNativeHandles();
wl_egl_window_destroy(window_->wl.egl_window);
#ifdef WITH_OPENGL_BACKEND
if (window_->ghost_context_type == GHOST_kDrawingContextTypeOpenGL) {
wl_egl_window_destroy(window_->backend.egl_window);
}
#endif
#ifdef WITH_VULKAN_BACKEND
if (window_->ghost_context_type == GHOST_kDrawingContextTypeVulkan) {
delete window_->backend.vulkan_window_info;
}
#endif
if (window_->xdg.activation_token) {
xdg_activation_token_v1_destroy(window_->xdg.activation_token);
@ -1834,15 +1885,16 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
#ifdef WITH_VULKAN_BACKEND
case GHOST_kDrawingContextTypeVulkan: {
GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual,
GHOST_kVulkanPlatformWayland,
0,
nullptr,
window_->wl.surface,
system_->wl_display_get(),
1,
2,
true);
GHOST_ContextVK *context = new GHOST_ContextVK(m_wantStereoVisual,
GHOST_kVulkanPlatformWayland,
0,
nullptr,
window_->wl.surface,
system_->wl_display_get(),
window_->backend.vulkan_window_info,
1,
2,
is_debug_context_);
if (context->initializeDrawingContext()) {
return context;
}
@ -1857,12 +1909,13 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
GHOST_Context *context = new GHOST_ContextEGL(
system_,
m_wantStereoVisual,
EGLNativeWindowType(window_->wl.egl_window),
EGLNativeWindowType(window_->backend.egl_window),
EGLNativeDisplayType(system_->wl_display_get()),
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
4,
minor,
GHOST_OPENGL_EGL_CONTEXT_FLAGS,
GHOST_OPENGL_EGL_CONTEXT_FLAGS |
(is_debug_context_ ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
EGL_OPENGL_API);

View File

@ -74,7 +74,8 @@ class GHOST_WindowWayland : public GHOST_Window {
GHOST_TDrawingContextType type,
const bool is_dialog,
const bool stereoVisual,
const bool exclusive);
const bool exclusive,
const bool is_debug);
~GHOST_WindowWayland() override;
@ -189,6 +190,7 @@ class GHOST_WindowWayland : public GHOST_Window {
private:
GHOST_SystemWayland *system_;
struct GWL_Window *window_;
bool is_debug_context_;
/**
* \param type: The type of rendering context create.

View File

@ -1185,6 +1185,7 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
m_display,
nullptr,
nullptr,
nullptr,
1,
2,
m_is_debug_context);