Wayland compatibility with Vulkan.

This commit is contained in:
BearOso 2023-01-24 17:33:46 -06:00
parent fa03a2c5b2
commit e4768f9b80
13 changed files with 293 additions and 63 deletions

View File

@ -125,6 +125,8 @@ if(USE_WAYLAND)
list(APPEND DEFINES "USE_WAYLAND")
list(APPEND SOURCES src/gtk_wayland_egl_context.cpp
src/gtk_wayland_egl_context.h
src/gtk_wayland_surface.cpp
src/gtk_wayland_surface.h
src/wayland-idle-inhibit-unstable-v1.c)
list(APPEND ARGS ${WAYLAND_CFLAGS})
list(APPEND LIBS ${WAYLAND_LIBRARIES})
@ -365,21 +367,6 @@ add_executable(slang_test ../vulkan/slang_helpers.cpp
../vulkan/slang_preset_test.cpp
../conffile.cpp
../stream.cpp)
#add_executable(vulkan_test ../vulkan/slang_helpers.cpp
# ../vulkan/slang_helpers.hpp
# ../vulkan/slang_shader.cpp
# ../vulkan/slang_shader.hpp
# ../vulkan/slang_preset.cpp
# ../vulkan/slang_preset.hpp
# ../vulkan/vulkan_hpp_storage.cpp
# ../vulkan/test.cpp
# ../vulkan/vk2d.cpp
# ../conffile.cpp
# ../stream.cpp)
#target_include_directories(vulkan_test PRIVATE ${INCLUDES})
#target_compile_options(vulkan_test PRIVATE ${ARGS})
#target_compile_definitions(vulkan_test PRIVATE ${DEFINES} "VULKAN_HPP_NO_EXCEPTIONS" "VULKAN_HPP_ASSERT_ON_RESULT=")
#target_link_libraries(vulkan_test PRIVATE ${LIBS})
target_include_directories(slang_test PRIVATE ${INCLUDES})
target_compile_options(slang_test PRIVATE ${ARGS})

View File

@ -900,7 +900,8 @@ static void S9xInitDriver()
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default()))
{
gui_config->display_driver = "opengl";
if (gui_config->display_driver != "vulkan")
gui_config->display_driver = "opengl";
}
#endif

View File

@ -187,7 +187,13 @@ void S9xVulkanDisplayDriver::refresh(int width, int height)
if (new_width != current_width || new_height != current_height || vsync_changed)
{
context->recreate_swapchain();
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
{
wayland_surface->resize();
}
#endif
context->recreate_swapchain(new_width, new_height);
context->wait_idle();
current_width = new_width;
current_height = new_height;
@ -200,11 +206,27 @@ int S9xVulkanDisplayDriver::init()
{
current_width = drawing_area->get_width() * drawing_area->get_scale_factor();
current_height = drawing_area->get_height() * drawing_area->get_scale_factor();
display = gdk_x11_display_get_xdisplay(drawing_area->get_display()->gobj());
xid = gdk_x11_window_get_xid(drawing_area->get_window()->gobj());
context = std::make_unique<Vulkan::Context>();
context->init(display, xid);
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
{
wayland_surface = std::make_unique<WaylandSurface>();
if (!wayland_surface->attach(GTK_WIDGET(drawing_area->gobj())))
{
return -1;
}
context->init_wayland(wayland_surface->display, wayland_surface->child, current_width, current_height);
}
#endif
if (GDK_IS_X11_WINDOW(drawing_area->get_window()->gobj()))
{
display = gdk_x11_display_get_xdisplay(drawing_area->get_display()->gobj());
xid = gdk_x11_window_get_xid(drawing_area->get_window()->gobj());
context->init_Xlib(display, xid);
}
swapchain = context->swapchain.get();
device = context->device;

View File

@ -12,6 +12,10 @@
#include "../../vulkan/slang_preset.hpp"
#include "../../vulkan/vulkan_shader_chain.hpp"
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include "gtk_wayland_surface.h"
#endif
class S9xVulkanDisplayDriver : public S9xDisplayDriver
{
public:
@ -40,6 +44,10 @@ class S9xVulkanDisplayDriver : public S9xDisplayDriver
int current_width;
int current_height;
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
std::unique_ptr<WaylandSurface> wayland_surface;
#endif
void create_pipeline();
vk::UniqueDescriptorSetLayout descriptor_set_layout;
vk::UniquePipelineLayout pipeline_layout;

View File

@ -0,0 +1,119 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "gtk_s9x.h"
#include "gtk_wayland_surface.h"
#include "wayland-idle-inhibit-unstable-v1.h"
static void wl_global(void *data,
struct wl_registry *wl_registry,
uint32_t name,
const char *interface,
uint32_t version)
{
auto wl = (WaylandSurface *)data;
if (!strcmp(interface, "wl_compositor"))
wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3);
else if (!strcmp(interface, "wl_subcompositor"))
wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
else if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name))
wl->idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
}
static void wl_global_remove(void *data,
struct wl_registry *wl_registry,
uint32_t name)
{
}
static const struct wl_registry_listener wl_registry_listener = {
wl_global,
wl_global_remove
};
WaylandSurface::WaylandSurface()
{
display = nullptr;
registry = nullptr;
compositor = nullptr;
subcompositor = nullptr;
parent = nullptr;
child = nullptr;
region = nullptr;
subsurface = nullptr;
idle_inhibit_manager = nullptr;
idle_inhibitor = nullptr;
}
WaylandSurface::~WaylandSurface()
{
if (idle_inhibitor)
zwp_idle_inhibitor_v1_destroy(idle_inhibitor);
if (idle_inhibit_manager)
zwp_idle_inhibit_manager_v1_destroy(idle_inhibit_manager);
if (subsurface)
wl_subsurface_destroy(subsurface);
if (region)
wl_region_destroy(region);
if (child)
wl_surface_destroy(child);
}
bool WaylandSurface::attach(GtkWidget *widget)
{
GdkWindow *window = gtk_widget_get_window(widget);
if (!GDK_IS_WAYLAND_WINDOW(window))
return false;
gdk_window = window;
gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
display = gdk_wayland_display_get_wl_display(gdk_window_get_display(gdk_window));
parent = gdk_wayland_window_get_wl_surface(gdk_window);
registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &wl_registry_listener, this);
wl_display_roundtrip(display);
if (!compositor || !subcompositor)
return false;
child = wl_compositor_create_surface(compositor);
region = wl_compositor_create_region(compositor);
subsurface = wl_subcompositor_get_subsurface(subcompositor, child, parent);
wl_surface_set_input_region(child, region);
wl_subsurface_set_desync(subsurface);
wl_subsurface_set_position(subsurface, x, y);
if (idle_inhibit_manager && gui_config->prevent_screensaver)
{
printf("Inhibiting screensaver.\n");
zwp_idle_inhibit_manager_v1_create_inhibitor(idle_inhibit_manager, child);
}
auto scale = gdk_window_get_scale_factor(window);
wl_surface_set_buffer_scale(child, scale);
return true;
}
void WaylandSurface::resize()
{
gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
auto scale = gdk_window_get_scale_factor(gdk_window);
wl_subsurface_set_position(subsurface, x, y);
wl_surface_set_buffer_scale(child, scale);
}

View File

@ -0,0 +1,38 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#pragma once
#include "gtk_compat.h"
class WaylandSurface
{
public:
WaylandSurface();
~WaylandSurface();
bool attach(GtkWidget *widget);
void resize();
GdkWindow *gdk_window;
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_subcompositor *subcompositor;
struct wl_surface *parent;
struct wl_surface *child;
struct wl_subsurface *subsurface;
struct wl_region *region;
int x;
int y;
int width;
int height;
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager;
struct zwp_idle_inhibitor_v1 *idle_inhibitor;
};

View File

@ -37,22 +37,52 @@ Context::~Context()
device.destroy();
}
bool Context::init(Display *dpy, Window xid, int preferred_device)
static vk::UniqueInstance create_instance_preamble(const char *wsi_extension)
{
if (instance)
return false;
xlib_display = dpy;
xlib_window = xid;
std::vector<const char *> extensions = { VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME };
std::vector<const char *> extensions = { wsi_extension, VK_KHR_SURFACE_EXTENSION_NAME };
vk::ApplicationInfo application_info({}, {}, {}, {}, VK_API_VERSION_1_0);
vk::InstanceCreateInfo instance_create_info({}, &application_info, {}, extensions);
instance = vk::createInstanceUnique(instance_create_info);
auto instance = vk::createInstanceUnique(instance_create_info);
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance.get());
surface = instance->createXlibSurfaceKHRUnique({ {}, xlib_display, xlib_window });
return instance;
}
#ifdef VK_USE_PLATFORM_XLIB_KHR
bool Context::init_Xlib(Display *dpy, Window xid, int preferred_device)
{
if (instance)
return false;
instance = create_instance_preamble(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
surface = instance->createXlibSurfaceKHRUnique({ {}, dpy, xid });
return init(preferred_device);
}
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
bool Context::init_wayland(wl_display *dpy, wl_surface *parent, int initial_width, int initial_height, int preferred_device)
{
if (instance)
return false;
instance = create_instance_preamble(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
auto wayland_surface_create_info = vk::WaylandSurfaceCreateInfoKHR{}
.setSurface(parent)
.setDisplay(dpy);
surface = instance->createWaylandSurfaceKHRUnique(wayland_surface_create_info);
init_device(preferred_device);
init_vma();
init_command_pool();
init_descriptor_pool();
create_swapchain(initial_width, initial_height);
device.waitIdle();
return true;
}
#endif
bool Context::init(int preferred_device)
{
init_device(preferred_device);
init_vma();
@ -159,16 +189,16 @@ bool Context::init_vma()
return true;
}
bool Context::create_swapchain()
bool Context::create_swapchain(int width, int height)
{
wait_idle();
swapchain = std::make_unique<Swapchain>(device, physical_device, queue, surface.get(), command_pool.get());
return swapchain->create(3);
return swapchain->create(3, width, height);
}
bool Context::recreate_swapchain()
bool Context::recreate_swapchain(int width, int height)
{
return swapchain->recreate();
return swapchain->recreate(width, height);
}
void Context::wait_idle()

View File

@ -1,6 +1,9 @@
#pragma once
#include "X11/Xlib.h"
//"VK_USE_PLATFORM_WAYLAND_KHR"
#ifdef VK_USE_PLATFORM_XLIB_KHR
#include <X11/Xlib.h>
#endif
#include "vk_mem_alloc.hpp"
#include "vulkan/vulkan.hpp"
#include "vulkan_swapchain.hpp"
@ -15,9 +18,15 @@ class Context
public:
Context();
~Context();
bool init(Display *dpy, Window xid, int preferred_device = 0);
bool create_swapchain();
bool recreate_swapchain();
#ifdef VK_USE_PLATFORM_XLIB_KHR
bool init_Xlib(Display *dpy, Window xid, int preferred_device = 0);
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
bool init_wayland(wl_display *dpy, wl_surface *parent, int width, int height, int preferred_device = 0);
#endif
bool init(int preferred_device = 0);
bool create_swapchain(int width = -1, int height = -1);
bool recreate_swapchain(int width = -1, int height = -1);
void wait_idle();
vk::CommandBuffer begin_cmd_buffer();
void end_cmd_buffer();
@ -38,8 +47,10 @@ class Context
vk::UniquePipeline create_generic_pipeline();
#ifdef VK_USE_PLATFORM_XLIB_KHR
Display *xlib_display;
Window xlib_window;
#endif
vk::UniqueInstance instance;
vk::PhysicalDevice physical_device;

View File

@ -235,17 +235,18 @@ bool ShaderChain::load_shader_preset(std::string filename)
if (p.samplers.size() > 0)
num_samplers += p.samplers.size();
}
std::array<vk::DescriptorPoolSize, 2> descriptor_pool_sizes;
descriptor_pool_sizes[0]
.setType(vk::DescriptorType::eUniformBuffer)
.setDescriptorCount(num_ubos * 3);
.setDescriptorCount(num_ubos * context->swapchain->get_num_frames());
descriptor_pool_sizes[1]
.setType(vk::DescriptorType::eCombinedImageSampler)
.setDescriptorCount(num_samplers * 3);
.setDescriptorCount(num_samplers * context->swapchain->get_num_frames());
auto descriptor_pool_create_info = vk::DescriptorPoolCreateInfo{}
.setPoolSizes(descriptor_pool_sizes)
.setMaxSets(pipelines.size() * 3)
.setMaxSets(pipelines.size() * context->swapchain->get_num_frames())
.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet);
descriptor_pool = context->device.createDescriptorPoolUnique(descriptor_pool_create_info);
@ -393,7 +394,6 @@ void ShaderChain::do_frame(uint8_t *data, int width, int height, int stride, vk:
auto cmd = context->swapchain->get_cmd();
current_frame_index = context->swapchain->get_current_frame();
update_and_propagate_sizes(width, height, viewport_width, viewport_height);
update_framebuffers(cmd, current_frame_index);

View File

@ -33,7 +33,8 @@ class SlangPipeline
{
vk::UniqueDescriptorSet descriptor_set;
PipelineImage image;
} frame[3];
vk::UniqueFence fence;
} frame[4];
vk::Buffer uniform_buffer;
vma::Allocation uniform_buffer_allocation;

View File

@ -72,11 +72,11 @@ void Swapchain::create_render_pass()
render_pass = device.createRenderPassUnique(render_pass_create_info);
}
bool Swapchain::recreate()
bool Swapchain::recreate(int new_width, int new_height)
{
device.waitIdle();
return create(num_frames);
return create(num_frames, new_width, new_height);
}
vk::Image Swapchain::get_image()
@ -84,7 +84,7 @@ vk::Image Swapchain::get_image()
return imageviewfbs[current_swapchain_image].image;
}
bool Swapchain::create(unsigned int desired_num_frames)
bool Swapchain::create(unsigned int desired_num_frames, int new_width, int new_height)
{
frames.clear();
imageviewfbs.clear();
@ -98,10 +98,17 @@ bool Swapchain::create(unsigned int desired_num_frames)
extents = surface_capabilities.currentExtent;
if (extents.width <= 1 || extents.height <= 1)
if (new_width > 0 && new_height > 0)
{
// No buffer is allocated for surface yet
extents.width = new_width;
extents.height = new_height;
}
else if (extents.width < 1 || extents.height < 1)
{
// Surface is likely hidden
printf("Extents too small.\n");
swapchain_object.reset();
return false;
}
@ -118,12 +125,14 @@ bool Swapchain::create(unsigned int desired_num_frames)
.setSurface(surface)
.setImageArrayLayers(1);
if (swapchain)
swapchain_create_info.setOldSwapchain(swapchain.get());
if (swapchain_object)
swapchain_create_info.setOldSwapchain(swapchain_object.get());
swapchain = device.createSwapchainKHRUnique(swapchain_create_info);
swapchain_object = device.createSwapchainKHRUnique(swapchain_create_info);
if (!swapchain_object)
return false;
auto swapchain_images = device.getSwapchainImagesKHR(swapchain.get());
auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get());
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, num_frames);
auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info);
@ -168,6 +177,8 @@ bool Swapchain::create(unsigned int desired_num_frames)
image.framebuffer = device.createFramebufferUnique(framebuffer_create_info);
}
device.waitIdle();
current_swapchain_image = 0;
return true;
@ -175,7 +186,7 @@ bool Swapchain::create(unsigned int desired_num_frames)
bool Swapchain::begin_frame()
{
if (extents.width < 1 || extents.height < 1)
if (!swapchain_object || extents.width < 1 || extents.height < 1)
{
printf ("Extents too small\n");
return false;
@ -190,17 +201,18 @@ bool Swapchain::begin_frame()
return false;
}
auto result_value = device.acquireNextImageKHR(swapchain.get(), 33000000, frame.acquire.get());
auto result_value = device.acquireNextImageKHR(swapchain_object.get(), 33000000, frame.acquire.get());
if (result_value.result == vk::Result::eErrorOutOfDateKHR ||
result_value.result == vk::Result::eSuboptimalKHR)
{
printf("Out of date\n");
recreate();
return begin_frame();
}
if (result_value.result != vk::Result::eSuccess)
{
printf ("Random failure %d\n", result_value.result);
printf("Timeout waiting for frame. Running too slow.\n");
return false;
}
@ -213,7 +225,7 @@ bool Swapchain::begin_frame()
return true;
}
bool Swapchain::end_frame()
bool Swapchain::end_frame(vk::Fence extra_fence)
{
auto &frame = frames[current_frame];
frame.command_buffer->end();
@ -225,11 +237,13 @@ bool Swapchain::end_frame()
frame.command_buffer.get(),
frame.complete.get());
if (extra_fence)
queue.submit(submit_info, extra_fence);
queue.submit(submit_info, frame.fence.get());
auto present_info = vk::PresentInfoKHR{}
.setWaitSemaphores(frames[current_frame].complete.get())
.setSwapchains(swapchain.get())
.setSwapchains(swapchain_object.get())
.setImageIndices(current_swapchain_image);
auto result = queue.presentKHR(present_info);

View File

@ -16,13 +16,13 @@ class Swapchain
vk::SurfaceKHR surface,
vk::CommandPool command_pool);
~Swapchain();
bool create(unsigned int num_frames);
bool recreate();
bool create(unsigned int num_frames, int width = -1, int height = -1);
bool recreate(int width = -1, int height = -1);
bool begin_frame();
void begin_render_pass();
void end_render_pass();
bool wait_on_frame(int frame_num);
bool end_frame();
bool end_frame(vk::Fence extra_fence = nullptr);
// Returns true if vsync setting was changed, false if it was the same
bool set_vsync(bool on);
@ -53,7 +53,7 @@ class Swapchain
vk::UniqueFramebuffer framebuffer;
};
vk::UniqueSwapchainKHR swapchain;
vk::UniqueSwapchainKHR swapchain_object;
vk::Extent2D extents;
vk::UniqueRenderPass render_pass;

View File

@ -214,7 +214,6 @@ void Texture::from_buffer(uint8_t *buffer, int width, int height, int byte_strid
vk::SubmitInfo si{};
si.setCommandBuffers(cmd.get());
queue.submit(si);
queue.waitIdle();
}
void Texture::create(int width, int height, vk::Format fmt, vk::SamplerAddressMode wrap_mode, bool linear, bool mipmap)