Compare commits

...

13 commits

Author SHA1 Message Date
sandboxgamedev123
5a0bdcd021
Merge c9d8282663 into a9e64edf73 2024-06-17 18:13:17 +09:00
BearOso
a9e64edf73 Vulkan/Win32: Fix compile error. 2024-06-13 16:50:12 -05:00
BearOso
008cbcd1a1 Vulkan: Simplify set_vsync. Remove relaxed fifo.
It looks like relaxed fifo tears when refresh rate doesn't
match because it always misses a refresh interval.
2024-06-13 16:17:36 -05:00
BearOso
3980a9d6d4 Vulkan: Fix inverted logic. 2024-06-13 15:56:04 -05:00
BearOso
5c78493f4e Vulkan: Add device wait back to swapchain recreation. 2024-06-13 14:39:36 -05:00
BearOso
33e40a8f16 Vulkan: Refactor present modes. Add relaxed. 2024-06-13 11:37:00 -05:00
BearOso
2e25b70cf0 Vulkan: Associate new fence with swapchain image, not frame resources. 2024-06-13 11:16:36 -05:00
BearOso
5949bbab97 Vulkan: Utilize VK_EXT_swapchain_maintenance1.
This is core in Vulkan 1.1.

We can now change vsync state without a new swapchain.

A fence is signaled when image is on screen, so we can possibly
be a little more precise with timing and avoid a whole device wait.
2024-06-12 16:54:13 -05:00
BearOso
99990af31e Vulkan: Don't use mailbox for vsync case. 2024-06-11 18:04:35 -05:00
BearOso
c02ee4373e Vulkan: Refactor device acquisition for cleanness. 2024-06-11 16:07:38 -05:00
BearOso
4f1a2d9c29 Vulkan: Remove exception handler/old swapchain use.
Exceptions are now turned off anyway.
2024-06-11 16:06:30 -05:00
sandboxgamedev123
c9d8282663
Add files via upload 2024-06-01 09:33:57 -04:00
sandboxgamedev123
32f817bfb9
Rename makeappimage.sh to makeappimage_64-bit.sh 2024-06-01 08:43:57 -04:00
8 changed files with 199 additions and 109 deletions

View file

@ -0,0 +1,31 @@
#!/usr/bin/env bash
if [ ! -f appimagetool-i686.AppImage ]; then
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-i686.AppImage
chmod +x appimagetool-i686.AppImage
fi
if [ ! -f linuxdeploy-i386.AppImage ]; then
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-i386.AppImage
chmod +x linuxdeploy-i386.AppImage
fi
DESTDIR=AppDir ninja install
./linuxdeploy-i386.AppImage --appimage-extract-and-run --appdir=AppDir \
--exclude-library="libX*" \
--exclude-library="libglib*" \
--exclude-library="libgobject*" \
--exclude-library="libgdk_pixbuf*" \
--exclude-library="libwayland*" \
--exclude-library="libgmodule*" \
--exclude-library="libgio*" \
--exclude-library="libxcb*" \
--exclude-library="libxkbcommon*" \
--exclude-library="libdb*"
rm AppDir/snes9x.png
pushd AppDir
ln -s usr/share/icons/hicolor/256x256/apps/snes9x.png
chmod +x AppRun
popd
./appimagetool-i686.AppImage --appimage-extract-and-run AppDir

View file

@ -222,6 +222,7 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int
throttle.wait_for_frame_and_rebase_time();
}
context->swapchain->set_vsync(gui_config->sync_to_vblank);
context->swapchain->swap();
if (gui_config->reduce_input_lag)

View file

@ -178,9 +178,6 @@ void EmuCanvasVulkan::draw()
if (!window->isVisible())
return;
if (context->swapchain->set_vsync(config->enable_vsync))
context->recreate_swapchain();
if (S9xImGuiDraw(width() * devicePixelRatioF(), height() * devicePixelRatioF()))
{
auto draw_data = ImGui::GetDrawData();
@ -205,10 +202,11 @@ void EmuCanvasVulkan::draw()
if (retval)
{
throttle();
context->swapchain->set_vsync(config->enable_vsync);
context->swapchain->swap();
if (config->reduce_input_lag)
{
context->wait_idle();
context->swapchain->wait_on_frames();
}
}
}

View file

@ -183,69 +183,88 @@ bool Context::init_command_pool()
return true;
}
bool Context::init_device(int preferred_device)
static bool find_extension(std::vector<vk::ExtensionProperties> &props, const char *extension_string)
{
const char *required_extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
// VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME
return std::find_if(props.begin(),
props.end(),
[&](vk::ExtensionProperties &ext) {
return (std::string(ext.extensionName.data()) == extension_string);
}) != props.end();
};
auto check_extensions = [&](vk::PhysicalDevice &device) -> bool {
auto [retval, props] = device.enumerateDeviceExtensionProperties();
for (const auto &extension : required_extensions) {
auto found = std::find_if(
props.begin(), props.end(), [&](vk::ExtensionProperties &ext) {
return (std::string(ext.extensionName.data()) == extension);
});
return found != props.end();
static uint32_t find_graphics_queue(vk::PhysicalDevice &device)
{
auto queue_props = device.getQueueFamilyProperties();
for (size_t i = 0; i < queue_props.size(); i++)
{
if (queue_props[i].queueFlags & vk::QueueFlagBits::eGraphics)
{
return i;
}
}
return UINT32_MAX;
}
static bool check_extensions(std::vector<const char *> &required_extensions, vk::PhysicalDevice &device)
{
auto props = device.enumerateDeviceExtensionProperties().value;
for (const auto &extension : required_extensions)
{
if (!find_extension(props, extension))
return false;
}
return true;
};
bool Context::init_device(int preferred_device)
{
std::vector<const char *> required_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
auto device_list = instance->enumeratePhysicalDevices().value;
physical_device = nullptr;
if (preferred_device > -1 &&
(size_t)preferred_device < device_list.size() &&
check_extensions(device_list[preferred_device]))
check_extensions(required_extensions, device_list[preferred_device]))
{
physical_device = device_list[preferred_device];
}
else
if (physical_device == nullptr)
{
for (auto &device : device_list)
if (check_extensions(device))
{
if (check_extensions(required_extensions, device))
{
physical_device = device;
break;
}
}
}
auto extension_properties = physical_device.enumerateDeviceExtensionProperties().value;
physical_device.getProperties(&physical_device_props);
graphics_queue_family_index = UINT32_MAX;
auto queue_props = physical_device.getQueueFamilyProperties();
for (size_t i = 0; i < queue_props.size(); i++)
{
if (queue_props[i].queueFlags & vk::QueueFlagBits::eGraphics)
{
graphics_queue_family_index = i;
break;
}
}
graphics_queue_family_index = find_graphics_queue(physical_device);
if (graphics_queue_family_index == UINT32_MAX)
return false;
vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, 1);
vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions, {});
std::vector<float> priorities = { 1.0f };
vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, priorities);
vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions);
device = physical_device.createDevice(dci).value;
queue = device.getQueue(graphics_queue_family_index, 0);
auto surface_formats = physical_device.getSurfaceFormatsKHR(surface.get()).value;
auto format = std::find_if(surface_formats.begin(), surface_formats.end(), [](vk::SurfaceFormatKHR &f) {
if (std::find_if(surface_formats.begin(),
surface_formats.end(),
[](vk::SurfaceFormatKHR &f) {
return (f.format == vk::Format::eB8G8R8A8Unorm);
});
if (format == surface_formats.end())
}) == surface_formats.end())
return false;
return true;

View file

@ -1,5 +1,4 @@
#include "vulkan_swapchain.hpp"
#include <thread>
namespace Vulkan
{
@ -19,13 +18,9 @@ Swapchain::~Swapchain()
{
}
bool Swapchain::set_vsync(bool new_setting)
void Swapchain::set_vsync(bool new_setting)
{
if (new_setting == vsync)
return false;
vsync = new_setting;
return true;
}
void Swapchain::on_render_pass_end(std::function<void ()> function)
@ -79,14 +74,41 @@ void Swapchain::create_render_pass()
}
bool Swapchain::recreate(int new_width, int new_height)
{
if (swapchain_object)
{
device.waitIdle();
wait_on_frames();
}
return create(num_swapchain_images, new_width, new_height);
}
vk::Image Swapchain::get_image()
{
return imageviewfbs[current_swapchain_image].image;
return image_data[current_swapchain_image].image;
}
template<typename T>
static bool vector_find(std::vector<T> haystack, T&& needle)
{
for (auto &elem : haystack)
if (elem == needle)
return true;
return false;
}
vk::PresentModeKHR Swapchain::get_present_mode() {
auto present_mode = vk::PresentModeKHR::eFifo;
if (!vsync) {
if (supports_mailbox)
present_mode = vk::PresentModeKHR::eMailbox;
if (supports_immediate)
present_mode = vk::PresentModeKHR::eImmediate;
}
return present_mode;
}
bool Swapchain::check_and_resize(int width, int height)
@ -115,7 +137,7 @@ bool Swapchain::check_and_resize(int width, int height)
bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, int new_height)
{
frames.clear();
imageviewfbs.clear();
image_data.clear();
auto surface_capabilities = physical_device.getSurfaceCapabilitiesKHR(surface).value;
@ -164,11 +186,12 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
extents.height = surface_capabilities.minImageExtent.height;
auto present_modes = physical_device.getSurfacePresentModesKHR(surface).value;
auto tearing_present_mode = vk::PresentModeKHR::eFifo;
if (std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eImmediate) != present_modes.end())
tearing_present_mode = vk::PresentModeKHR::eImmediate;
if (std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eMailbox) != present_modes.end())
tearing_present_mode = vk::PresentModeKHR::eMailbox;
supports_mailbox = vector_find(present_modes, vk::PresentModeKHR::eMailbox);
supports_immediate = vector_find(present_modes, vk::PresentModeKHR::eImmediate);
supports_relaxed = vector_find(present_modes, vk::PresentModeKHR::eFifoRelaxed);
auto swapchain_maintenance_info = vk::SwapchainPresentModesCreateInfoEXT{}
.setPresentModes(present_modes);
auto swapchain_create_info = vk::SwapchainCreateInfoKHR{}
.setMinImageCount(num_swapchain_images)
@ -179,33 +202,38 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc)
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
.setClipped(true)
.setPresentMode(vsync ? vk::PresentModeKHR::eFifo : tearing_present_mode)
.setPresentMode(get_present_mode())
.setSurface(surface)
.setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity)
.setImageArrayLayers(1)
.setQueueFamilyIndices(graphics_queue_index);
.setQueueFamilyIndices(graphics_queue_index)
.setPNext(&swapchain_maintenance_info);
if (swapchain_object)
swapchain_create_info.setOldSwapchain(swapchain_object.get());
try {
swapchain_object = device.createSwapchainKHRUnique(swapchain_create_info).value;
} catch (std::exception &e) {
swapchain_object.reset();
auto resval = device.createSwapchainKHRUnique(swapchain_create_info);
if (resval.result != vk::Result::eSuccess && resval.result != vk::Result::eSuboptimalKHR)
{
swapchain_object.reset();
return false;
}
swapchain_object = std::move(resval.value);
create_resources();
return true;
}
if (!swapchain_object)
return false;
bool Swapchain::create_resources()
{
auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get()).value;
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, swapchain_images.size());
if (swapchain_images.size() > num_swapchain_images)
num_swapchain_images = swapchain_images.size();
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, num_swapchain_images);
auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info).value;
if (imageviewfbs.size() > num_swapchain_images)
num_swapchain_images = imageviewfbs.size();
frames.resize(num_swapchain_images);
imageviewfbs.resize(num_swapchain_images);
image_data.resize(num_swapchain_images);
vk::FenceCreateInfo fence_create_info(vk::FenceCreateFlagBits::eSignaled);
@ -218,12 +246,11 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
frame.acquire = device.createSemaphoreUnique({}).value;
frame.complete = device.createSemaphoreUnique({}).value;
}
current_frame = 0;
for (unsigned int i = 0; i < num_swapchain_images; i++)
{
// Create resources associated with swapchain images
auto &image = imageviewfbs[i];
auto &image = image_data[i];
image.image = swapchain_images[i];
auto image_view_create_info = vk::ImageViewCreateInfo{}
.setImage(swapchain_images[i])
@ -240,11 +267,12 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
.setLayers(1)
.setRenderPass(render_pass.get());
image.framebuffer = device.createFramebufferUnique(framebuffer_create_info).value;
image.fence = device.createFenceUnique(fence_create_info).value;
}
device.waitIdle();
current_swapchain_image = 0;
current_frame = 0;
return true;
}
@ -259,7 +287,7 @@ bool Swapchain::begin_frame()
auto &frame = frames[current_frame];
auto result = device.waitForFences(frame.fence.get(), true, 33333333);
auto result = device.waitForFences({ frame.fence.get() }, true, 33333333);
if (result != vk::Result::eSuccess)
{
printf("Timed out waiting for fence.\n");
@ -320,8 +348,17 @@ bool Swapchain::swap()
.setSwapchains(swapchain_object.get())
.setImageIndices(current_swapchain_image);
vk::Result result = vk::Result::eSuccess;
result = queue.presentKHR(present_info);
vk::SwapchainPresentModeInfoEXT present_mode_info;
auto present_mode = get_present_mode();
present_mode_info.setPresentModes(present_mode);
present_info.setPNext(&present_mode_info);
auto &present_fence = image_data[current_swapchain_image].fence.get();
device.resetFences(present_fence);
vk::SwapchainPresentFenceInfoEXT present_fence_info(present_fence);
present_mode_info.setPNext(&present_fence_info);
vk::Result result = queue.presentKHR(present_info);
if (result == vk::Result::eErrorOutOfDateKHR)
{
// NVIDIA binary drivers will set OutOfDate between acquire and
@ -343,7 +380,7 @@ bool Swapchain::end_frame()
vk::Framebuffer Swapchain::get_framebuffer()
{
return imageviewfbs[current_swapchain_image].framebuffer.get();
return image_data[current_swapchain_image].framebuffer.get();
}
vk::CommandBuffer &Swapchain::get_cmd()
@ -360,7 +397,7 @@ void Swapchain::begin_render_pass()
auto render_pass_begin_info = vk::RenderPassBeginInfo{}
.setRenderPass(render_pass.get())
.setFramebuffer(imageviewfbs[current_swapchain_image].framebuffer.get())
.setFramebuffer(image_data[current_swapchain_image].framebuffer.get())
.setRenderArea(vk::Rect2D({}, extents))
.setClearValues(value);
get_cmd().beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
@ -379,10 +416,16 @@ void Swapchain::end_render_pass()
bool Swapchain::wait_on_frame(int frame_num)
{
auto result = device.waitForFences(frames[frame_num].fence.get(), true, 33000000);
auto result = device.waitForFences({ image_data[frame_num].fence.get() }, true, 33000000);
return (result == vk::Result::eSuccess);
}
void Swapchain::wait_on_frames()
{
for (auto i = 0; i < image_data.size(); i++)
wait_on_frame(i);
}
vk::Extent2D Swapchain::get_extents()
{
return extents;

View file

@ -17,6 +17,7 @@ class Swapchain
~Swapchain();
bool create(unsigned int num_frames, int width = -1, int height = -1);
bool recreate(int width = -1, int height = -1);
bool create_resources();
bool check_and_resize(int width = -1, int height = -1);
bool begin_frame();
void begin_render_pass();
@ -25,10 +26,11 @@ class Swapchain
bool end_frame();
void end_frame_without_swap();
bool swap();
// Returns true if vsync setting was changed, false if it was the same
bool set_vsync(bool on);
void wait_on_frames();
void set_vsync(bool on);
void on_render_pass_end(std::function<void()> function);
int get_num_frames() { return num_swapchain_images; }
vk::PresentModeKHR get_present_mode();
vk::Image get_image();
vk::Framebuffer get_framebuffer();
@ -48,9 +50,10 @@ class Swapchain
vk::UniqueCommandBuffer command_buffer;
};
struct ImageViewFB
struct ImageData
{
vk::Image image;
vk::UniqueFence fence;
vk::UniqueImageView image_view;
vk::UniqueFramebuffer framebuffer;
};
@ -64,8 +67,11 @@ class Swapchain
unsigned int current_swapchain_image = 0;
unsigned int num_swapchain_images = 0;
bool vsync = true;
bool supports_immediate = false;
bool supports_mailbox = false;
bool supports_relaxed = false;
std::vector<Frame> frames;
std::vector<ImageViewFB> imageviewfbs;
std::vector<ImageData> image_data;
vk::Device device;
vk::SurfaceKHR surface;

View file

@ -70,6 +70,8 @@ bool CVulkan::Initialize(HWND hWnd)
return false;
}
context->swapchain->set_vsync(GUI.Vsync);
if (!Settings.AutoDisplayMessages)
{
Settings.DisplayIndicators = true;
@ -91,11 +93,6 @@ bool CVulkan::Initialize(HWND hWnd)
}
}
if (context->swapchain->set_vsync(GUI.Vsync))
{
context->recreate_swapchain();
}
simple_output = std::make_unique<Vulkan::SimpleOutput>(context.get(), vk::Format::eR5G6B5UnormPack16);
return true;
@ -181,9 +178,7 @@ bool CVulkan::ChangeRenderSize(unsigned int newWidth, unsigned int newHeight)
if (!context)
return false;
bool vsync_changed = context->swapchain->set_vsync(GUI.Vsync);
if (newWidth != current_width || newHeight != current_height || vsync_changed)
if (newWidth != current_width || newHeight != current_height)
{
context->recreate_swapchain(newWidth, newHeight);
context->wait_idle();
@ -218,10 +213,7 @@ bool CVulkan::ApplyDisplayChanges(void)
current_shadername = shadername;
}
if (context->swapchain->set_vsync(GUI.Vsync))
{
context->recreate_swapchain();
}
context->swapchain->set_vsync(GUI.Vsync);
return true;
}