Skip to content

Commit

Permalink
Allow gamescope to work on devices that don't support VK_KHR_present_…
Browse files Browse the repository at this point in the history
…id and/or VK_KHR_present_wait
  • Loading branch information
sharkautarch committed Feb 3, 2024
1 parent bba5f0d commit e072e39
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 42 deletions.
35 changes: 27 additions & 8 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ bool BIsSDLSession( void )
}


static bool initOutput(int preferredWidth, int preferredHeight, int preferredRefresh);
static bool initOutput(int preferredWidth, int preferredHeight, int preferredRefresh, bool bSupportsPresentWait);
static void steamCompMgrThreadRun(int argc, char **argv);

static std::string build_optstring(const struct option *options)
Expand Down Expand Up @@ -735,25 +735,44 @@ int main(int argc, char **argv)
g_pOriginalDisplay = getenv("DISPLAY");
g_pOriginalWaylandDisplay = getenv("WAYLAND_DISPLAY");
g_bIsNested = g_pOriginalDisplay != NULL || g_pOriginalWaylandDisplay != NULL;


const bool bSupportsPresentWait = checkForPresentWaitExt();

if ( BIsSDLSession() && g_pOriginalWaylandDisplay != NULL )
{
if (CheckWaylandPresentationTime())
const bool bCompositor_supports_present_wait = CheckWaylandPresentationTime();
if (bCompositor_supports_present_wait && bSupportsPresentWait)
{
// Default to SDL_VIDEODRIVER wayland under Wayland and force enable vk_khr_present_wait
// (not enabled by default in Mesa because instance does not know if Wayland
// compositor supports wp_presentation, but we can check that ourselves.)
setenv("vk_khr_present_wait", "true", 0);
setenv("SDL_VIDEODRIVER", "wayland", 0);
}
else
else if (bSupportsPresentWait)
{
fprintf(stderr,
"Your Wayland compositor does NOT support wp_presentation/presentation-time which is required for VK_KHR_present_wait and VK_KHR_present_id.\n"
"Please complain to your compositor vendor for support. Falling back to X11 window with less accurate present wait.\n");
setenv("SDL_VIDEODRIVER", "x11", 1);
}
else if (!bSupportsPresentWait)
{
fprintf(stderr,
"Notice: GPU device does not support present_wait extension\n"
"continuing with present_wait disabled.\n\n");
setenv("vk_khr_present_wait", "false", 1);
if (bCompositor_supports_present_wait)
setenv("SDL_VIDEODRIVER", "wayland", 0); //doing this for consistency, shouldn't cause any issues
}
}
else if (!bSupportsPresentWait)
{
fprintf(stderr,
"Notice: GPU device does not support present_wait extension\n"
"continuing with present_wait disabled.\n\n");
setenv("vk_khr_present_wait", "false", 1);
}

if ( !wlsession_init() )
{
Expand Down Expand Up @@ -786,7 +805,7 @@ int main(int argc, char **argv)
}
#endif

if ( !initOutput( g_nPreferredOutputWidth, g_nPreferredOutputHeight, g_nNestedRefresh ) )
if ( !initOutput( g_nPreferredOutputWidth, g_nPreferredOutputHeight, g_nNestedRefresh, bSupportsPresentWait ) )
{
fprintf( stderr, "Failed to initialize output\n" );
return 1;
Expand Down Expand Up @@ -910,7 +929,7 @@ static void steamCompMgrThreadRun(int argc, char **argv)
pthread_kill( g_mainThread, SIGINT );
}

static bool initOutput( int preferredWidth, int preferredHeight, int preferredRefresh )
static bool initOutput( int preferredWidth, int preferredHeight, int preferredRefresh, bool bSupportsPresentWait )
{
VkInstance instance = vulkan_create_instance();

Expand Down Expand Up @@ -960,7 +979,7 @@ static bool initOutput( int preferredWidth, int preferredHeight, int preferredRe
}
}

if ( !vulkan_init( instance, surface ) )
if ( !vulkan_init( instance, surface, bSupportsPresentWait ) )
{
fprintf( stderr, "Failed to initialize Vulkan\n" );
return false;
Expand All @@ -970,7 +989,7 @@ static bool initOutput( int preferredWidth, int preferredHeight, int preferredRe
}
else
{
if ( !vulkan_init( instance, VK_NULL_HANDLE ) )
if ( !vulkan_init( instance, VK_NULL_HANDLE, bSupportsPresentWait ) )
{
fprintf( stderr, "Failed to initialize Vulkan\n" );
return false;
Expand Down
141 changes: 112 additions & 29 deletions src/rendervulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <bitset>
#include <thread>
#include <vulkan/vulkan_core.h>
#include <dlfcn.h>

#if defined(__linux__)
#include <sys/sysmacros.h>
Expand Down Expand Up @@ -248,7 +249,7 @@ uint32_t DRMFormatGetBPP( uint32_t nDRMFormat )
return false;
}

bool CVulkanDevice::BInit(VkInstance instance, VkSurfaceKHR surface)
bool CVulkanDevice::BInit(VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait)
{
assert(instance);
assert(!m_bInitialized);
Expand All @@ -262,7 +263,7 @@ bool CVulkanDevice::BInit(VkInstance instance, VkSurfaceKHR surface)

if (!selectPhysDev(surface))
return false;
if (!createDevice())
if (!createDevice(bSupportsPresentWait))
return false;
if (!createLayouts())
return false;
Expand Down Expand Up @@ -375,8 +376,66 @@ bool CVulkanDevice::selectPhysDev(VkSurfaceKHR surface)
return true;
}

bool CVulkanDevice::createDevice()
void CVulkanDevice::prepare_tmp_dev(VkInstance tmp_instance)
{
//g_output.surface = VK_NULL_HANDLE;
#define VK_FUNC(x) vk.x = (PFN_vk##x) vkGetInstanceProcAddr(tmp_instance, "vk"#x);
VULKAN_INSTANCE_FUNCTIONS
#undef VK_FUNC


m_instance = tmp_instance;

selectPhysDev(VK_NULL_HANDLE);
}

bool CVulkanDevice::_checkForPresentWaitExt()
{

vk.GetPhysicalDeviceMemoryProperties( physDev(), &m_memoryProperties );

uint32_t supportedExtensionCount;
vk.EnumerateDeviceExtensionProperties( physDev(), NULL, &supportedExtensionCount, NULL );

std::vector<VkExtensionProperties> supportedExts(supportedExtensionCount);
vk.EnumerateDeviceExtensionProperties( physDev(), NULL, &supportedExtensionCount, supportedExts.data() );

bool bSupports_present_wait, bSupports_present_id = false;

for ( uint32_t i = 0; i < supportedExtensionCount; ++i )
{
if ( strcmp(supportedExts[i].extensionName, VK_KHR_PRESENT_ID_EXTENSION_NAME) == 0 )
bSupports_present_id = true;
else if ( strcmp(supportedExts[i].extensionName, VK_KHR_PRESENT_WAIT_EXTENSION_NAME) == 0)
bSupports_present_wait = true;
}

return bSupports_present_wait && bSupports_present_id;
}

bool checkForPresentWaitExt()
{
const char * const prevLayerDisable_env = getenv("VK_LOADER_LAYERS_DISABLE");
setenv("VK_LOADER_LAYERS_DISABLE", "~all~", 1);
void * temp_vulkan_lib = dlopen("libvulkan.so.1", RTLD_LAZY | RTLD_LOCAL);
VkInstance tmp_instance = vulkan_create_instance(true);
CVulkanDevice tmp_dev;
tmp_dev.prepare_tmp_dev(tmp_instance);

const bool bSupportsPresentWait = tmp_dev._checkForPresentWaitExt();

dlclose(temp_vulkan_lib);
if ( prevLayerDisable_env != nullptr )
setenv("VK_LOADER_LAYERS_DISABLE", prevLayerDisable_env, 1);
else
unsetenv("VK_LOADER_LAYERS_DISABLE");

return bSupportsPresentWait;
}

bool CVulkanDevice::createDevice(bool bPresentWaitSupported)
{
m_supports_present_wait = bPresentWaitSupported;
vk.GetPhysicalDeviceMemoryProperties( physDev(), &m_memoryProperties );

uint32_t supportedExtensionCount;
Expand Down Expand Up @@ -504,9 +563,10 @@ bool CVulkanDevice::createDevice()
{
enabledExtensions.push_back( VK_KHR_SWAPCHAIN_EXTENSION_NAME );
enabledExtensions.push_back( VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME );

enabledExtensions.push_back( VK_KHR_PRESENT_ID_EXTENSION_NAME );
enabledExtensions.push_back( VK_KHR_PRESENT_WAIT_EXTENSION_NAME );
if (m_supports_present_wait) {
enabledExtensions.push_back( VK_KHR_PRESENT_ID_EXTENSION_NAME );
enabledExtensions.push_back( VK_KHR_PRESENT_WAIT_EXTENSION_NAME );
}
}

if ( m_bSupportsModifiers )
Expand Down Expand Up @@ -572,11 +632,15 @@ bool CVulkanDevice::createDevice()

VkPhysicalDeviceFeatures2 features2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &presentIdFeatures,
.features = {
.shaderInt16 = m_bSupportsFp16,
},
};

if (m_supports_present_wait)
{
features2.pNext = &presentIdFeatures;
}

VkDeviceCreateInfo deviceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
Expand Down Expand Up @@ -2775,16 +2839,21 @@ void vulkan_present_to_window( void )

VkPresentInfoKHR presentInfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = &presentIdInfo,
.swapchainCount = 1,
.pSwapchains = &g_output.swapChain,
.pImageIndices = &g_output.nOutImage,
};

if ( g_device.m_supports_present_wait)
{
presentInfo.pNext = &presentIdInfo;
}

if ( g_device.vk.QueuePresentKHR( g_device.queue(), &presentInfo ) == VK_SUCCESS )
{
g_currentPresentWaitId = presentId;
g_currentPresentWaitId.notify_all();
if ( g_device.m_supports_present_wait )
g_currentPresentWaitId.notify_all();
}
else
vulkan_remake_swapchain();
Expand Down Expand Up @@ -3032,26 +3101,40 @@ bool vulkan_make_swapchain( VulkanOutput_t *pOutput )
return true;
}


bool vulkan_remake_swapchain( void )
{
std::unique_lock lock(present_wait_lock);
g_currentPresentWaitId = 0;
g_currentPresentWaitId.notify_all();
bool bRet;

auto doit = [&](std::function<void(void)> f) {
VulkanOutput_t *pOutput = &g_output;
g_device.waitIdle();
f();

VulkanOutput_t *pOutput = &g_output;
g_device.waitIdle();
g_device.vk.QueueWaitIdle( g_device.queue() );
pOutput->outputImages.clear();

pOutput->outputImages.clear();
g_device.vk.DestroySwapchainKHR( g_device.device(), pOutput->swapChain, nullptr );

g_device.vk.DestroySwapchainKHR( g_device.device(), pOutput->swapChain, nullptr );
// Delete screenshot image to be remade if needed
for (auto& pScreenshotImage : pOutput->pScreenshotImages)
pScreenshotImage = nullptr;

// Delete screenshot image to be remade if needed
for (auto& pScreenshotImage : pOutput->pScreenshotImages)
pScreenshotImage = nullptr;

bool bRet = vulkan_make_swapchain( pOutput );
assert( bRet ); // Something has gone horribly wrong!
bRet = vulkan_make_swapchain( pOutput );
assert( bRet ); // Something has gone horribly wrong!
};

if (g_device.m_supports_present_wait)
{
std::unique_lock lock(present_wait_lock);
g_currentPresentWaitId = 0;
g_currentPresentWaitId.notify_all();
doit( [&](){g_device.vk.QueueWaitIdle( g_device.queue() );});
}
else
{
doit([&](){return;});
}

return bRet;
}

Expand Down Expand Up @@ -3281,18 +3364,18 @@ static bool init_nis_data()
return true;
}

VkInstance vulkan_create_instance( void )
VkInstance vulkan_create_instance( bool is_temp_instance )
{
VkResult result = VK_ERROR_INITIALIZATION_FAILED;

std::vector< const char * > sdlExtensions;
if ( BIsVRSession() )
if ( !is_temp_instance && BIsVRSession() )
{
#if HAVE_OPENVR
vrsession_append_instance_exts( sdlExtensions );
#endif
}
else if ( BIsSDLSession() )
else if ( !is_temp_instance && BIsSDLSession() )
{
if ( SDL_Vulkan_LoadLibrary( nullptr ) != 0 )
{
Expand Down Expand Up @@ -3346,15 +3429,15 @@ VkInstance vulkan_create_instance( void )
return instance;
}

bool vulkan_init( VkInstance instance, VkSurfaceKHR surface )
bool vulkan_init( VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait )
{
if (!g_device.BInit(instance, surface))
if (!g_device.BInit(instance, surface, bSupportsPresentWait))
return false;

if (!init_nis_data())
return false;

if (BIsNested() && !BIsVRSession())
if (bSupportsPresentWait && BIsNested() && !BIsVRSession())
{
std::thread present_wait_thread( present_wait_thread_func );
present_wait_thread.detach();
Expand Down
21 changes: 16 additions & 5 deletions src/rendervulkan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,8 @@ namespace CompositeDebugFlag
static constexpr uint32_t Tonemap_Reinhard = 1u << 7;
};

VkInstance vulkan_create_instance(void);
bool vulkan_init(VkInstance instance, VkSurfaceKHR surface);
VkInstance vulkan_create_instance(bool is_temp_instance = false);
bool vulkan_init(VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait);
bool vulkan_init_formats(void);
bool vulkan_make_output();

Expand Down Expand Up @@ -700,6 +700,9 @@ static inline uint32_t div_roundup(uint32_t x, uint32_t y)
VK_FUNC(CmdBeginRendering) \
VK_FUNC(CmdEndRendering)


bool checkForPresentWaitExt();

template<typename T, typename U = T>
constexpr T align(T what, U to) {
return (what + to - 1) & ~(to - 1);
Expand All @@ -708,11 +711,15 @@ return (what + to - 1) & ~(to - 1);
class CVulkanDevice
{
public:
bool m_supports_present_wait = true;
bool m_supportsReshade = false;
bool BInit(VkInstance instance, VkSurfaceKHR surface);
bool BInit(VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait);

VkSampler sampler(SamplerState key);
VkPipeline pipeline(ShaderType type, uint32_t layerCount = 1, uint32_t ycbcrMask = 0, uint32_t blur_layers = 0, uint32_t colorspace_mask = 0, uint32_t output_eotf = EOTF_Gamma22, bool itm_enable = false);

bool checkForPresentWaitExt();

int32_t findMemoryType( VkMemoryPropertyFlags properties, uint32_t requiredTypeBits );
std::unique_ptr<CVulkanCmdBuffer> commandBuffer();
uint64_t submit( std::unique_ptr<CVulkanCmdBuffer> cmdBuf);
Expand Down Expand Up @@ -775,9 +782,13 @@ class CVulkanDevice

protected:
friend class CVulkanCmdBuffer;


void prepare_tmp_dev(VkInstance tmp_instance);
bool _checkForPresentWaitExt();
friend bool checkForPresentWaitExt();

bool selectPhysDev(VkSurfaceKHR surface);
bool createDevice();
bool createDevice(bool bSupportsPresentWait);
bool createLayouts();
bool createPools();
bool createShaders();
Expand Down

0 comments on commit e072e39

Please sign in to comment.