snes9x/gtk/src/gtk_display_driver_vulkan.cpp
BearOso f0001ab428 Wayland: Reorder resizing operations
Ensure a buffer is in place before calling viewporter.
2024-01-09 10:31:49 -06:00

255 lines
7.9 KiB
C++

/*****************************************************************************\
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 "gtk_compat.h"
#include "gtk_display.h"
#include "gtk_display_driver_vulkan.h"
#include "gtk_shader_parameters.h"
#include "snes9x.h"
#include "gfx.h"
#include "fmt/format.h"
#include "snes9x_imgui.h"
#include "external/imgui/imgui_impl_vulkan.h"
#ifdef GDK_WINDOWING_WAYLAND
static WaylandSurface::Metrics get_metrics(Gtk::DrawingArea &w)
{
int x, y, width, height;
w.get_window()->get_geometry(x, y, width, height);
return { x, y, width, height, w.get_window()->get_scale_factor() };
}
#endif
S9xVulkanDisplayDriver::S9xVulkanDisplayDriver(Snes9xWindow *_window, Snes9xConfig *_config)
{
window = _window;
config = _config;
drawing_area = window->drawing_area;
gdk_window = nullptr;
gdk_display = nullptr;
context.reset();
}
S9xVulkanDisplayDriver::~S9xVulkanDisplayDriver()
{
}
bool S9xVulkanDisplayDriver::init_imgui()
{
auto defaults = S9xImGuiGetDefaults();
defaults.font_size = gui_config->osd_size;
defaults.spacing = defaults.font_size / 2.4;
S9xImGuiInit(&defaults);
ImGui_ImplVulkan_LoadFunctions([](const char *function, void *instance) {
return VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr(*((VkInstance *)instance), function);
}, &context->instance.get());
vk::DescriptorPoolSize pool_sizes[] =
{
{ vk::DescriptorType::eCombinedImageSampler, 1000 },
{ vk::DescriptorType::eUniformBuffer, 1000 }
};
auto descriptor_pool_create_info = vk::DescriptorPoolCreateInfo{}
.setPoolSizes(pool_sizes)
.setMaxSets(1000)
.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet);
imgui_descriptor_pool = device.createDescriptorPoolUnique(descriptor_pool_create_info);
ImGui_ImplVulkan_InitInfo init_info{};
init_info.Instance = context->instance.get();
init_info.PhysicalDevice = context->physical_device;
init_info.Device = context->device;;
init_info.QueueFamily = context->graphics_queue_family_index;
init_info.Queue = context->queue;
init_info.DescriptorPool = imgui_descriptor_pool.get();
init_info.Subpass = 0;
init_info.MinImageCount = context->swapchain->get_num_frames();
init_info.ImageCount = context->swapchain->get_num_frames();
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
ImGui_ImplVulkan_Init(&init_info, context->swapchain->get_render_pass());
auto cmd = context->begin_cmd_buffer();
ImGui_ImplVulkan_CreateFontsTexture(cmd);
context->end_cmd_buffer();
context->wait_idle();
return true;
}
void S9xVulkanDisplayDriver::refresh()
{
if (!context)
return;
context->swapchain->set_vsync(gui_config->sync_to_vblank);
int new_width, new_height;
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
std::tie(new_width, new_height) = wayland_surface->get_size_for_metrics(get_metrics(*drawing_area));
else
#endif
{
new_width = drawing_area->get_width() * drawing_area->get_scale_factor();
new_height = drawing_area->get_height() * drawing_area->get_scale_factor();
}
if (new_width != current_width || new_height != current_height)
{
context->recreate_swapchain(new_width, new_height);
context->wait_idle();
current_width = new_width;
current_height = new_height;
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
wayland_surface->resize(get_metrics(*drawing_area));
#endif
}
}
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();
context = std::make_unique<Vulkan::Context>();
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj()))
{
wayland_surface = std::make_unique<WaylandSurface>();
wl_surface *surface = gdk_wayland_window_get_wl_surface(drawing_area->get_window()->gobj());
wl_display *display = gdk_wayland_display_get_wl_display(drawing_area->get_display()->gobj());
if (!wayland_surface->attach(display, surface, get_metrics(*drawing_area)))
return -1;
if (!context->init_wayland(wayland_surface->display, wayland_surface->child, current_width, current_height))
return -1;
}
#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());
if (!context->init_Xlib(display, xid))
return -1;
}
device = context->device;
init_imgui();
if (!gui_config->shader_filename.empty() && gui_config->use_shaders)
{
shaderchain = std::make_unique<Vulkan::ShaderChain>(context.get());
setlocale(LC_NUMERIC, "C");
if (!shaderchain->load_shader_preset(gui_config->shader_filename))
{
fmt::print("Couldn't load shader preset file\n");
shaderchain = nullptr;
setlocale(LC_NUMERIC, "");
}
else
{
window->enable_widget("shader_parameters_item", true);
setlocale(LC_NUMERIC, "");
return 0;
}
}
simple_output = std::make_unique<Vulkan::SimpleOutput>(context.get(), vk::Format::eR5G6B5UnormPack16);
return 0;
}
void S9xVulkanDisplayDriver::deinit()
{
if (!context)
return;
if (shaderchain)
gtk_shader_parameters_dialog_close();
context->wait_idle();
if (ImGui::GetCurrentContext())
{
ImGui_ImplVulkan_Shutdown();
ImGui::DestroyContext();
}
}
void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int stride_in_pixels)
{
if (!context)
return;
if (S9xImGuiDraw(current_width, current_height))
{
ImDrawData *draw_data = ImGui::GetDrawData();
context->swapchain->on_render_pass_end([&, draw_data] {
ImGui_ImplVulkan_RenderDrawData(draw_data, context->swapchain->get_cmd());
});
}
auto viewport = S9xApplyAspect(width, height, current_width, current_height);
bool retval = false;
if (shaderchain)
{
retval = shaderchain->do_frame_without_swap((uint8_t *)buffer, width, height, stride_in_pixels << 1, vk::Format::eR5G6B5UnormPack16, viewport.x, viewport.y, viewport.w, viewport.h);
}
else if (simple_output)
{
simple_output->set_filter(Settings.BilinearFilter);
retval = simple_output->do_frame_without_swap((uint8_t *)buffer, width, height, stride_in_pixels << 1, viewport.x, viewport.y, viewport.w, viewport.h);
}
if (retval)
{
if (Settings.SkipFrames == THROTTLE_TIMER || Settings.SkipFrames == THROTTLE_TIMER_FRAMESKIP)
{
throttle.set_frame_rate(Settings.PAL ? PAL_PROGRESSIVE_FRAME_RATE : NTSC_PROGRESSIVE_FRAME_RATE);
throttle.wait_for_frame_and_rebase_time();
}
context->swapchain->swap();
if (gui_config->reduce_input_lag)
context->wait_idle();
}
}
int S9xVulkanDisplayDriver::query_availability()
{
return 0;
}
void *S9xVulkanDisplayDriver::get_parameters()
{
if (shaderchain)
return &shaderchain->preset->parameters;
return nullptr;
}
void S9xVulkanDisplayDriver::save(const char *filename)
{
setlocale(LC_NUMERIC, "C");
if (shaderchain)
shaderchain->preset->save_to_file(filename);
setlocale(LC_NUMERIC, "");
}
bool S9xVulkanDisplayDriver::is_ready()
{
return true;
}