mirror of
https://github.com/InstanceBrick/nightcap.git
synced 2026-10-17 22:00:46 +02:00
fix(graphics): retry swapchain creation with the game's present mode
A driver can reject a present mode the device did not enable. Retry once with the mode the game asked for, except after errors another mode cannot fix.
This commit is contained in:
1 parent
545fd8344f
commit
46d1d5ffbe
3 files changed
+113
-1
No files matched your search
@@ -2,6 +2,7 @@
|
||||
#define MOCKTAIL_GRAPHICS_PRESENT_MODE_POLICY_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
@@ -29,6 +30,36 @@ FilterPresentModes(PresentModePolicy policy,
|
||||
const char *PresentModePolicyName(PresentModePolicy policy);
|
||||
const char *PresentModeKhrName(VkPresentModeKHR mode);
|
||||
|
||||
// Creates the swapchain with the mode in `info`. A driver can reject a mode
|
||||
// that the device did not enable, so a failed create is retried once with the
|
||||
// mode the game asked for. `create` takes the info and returns a VkResult.
|
||||
template <typename Create>
|
||||
VkResult CreateSwapchainWithPresentModeFallback(VkSwapchainCreateInfoKHR &info,
|
||||
VkPresentModeKHR requested,
|
||||
Create create) {
|
||||
const VkResult result = create(info);
|
||||
if (result == VK_SUCCESS || info.presentMode == requested) {
|
||||
return result;
|
||||
}
|
||||
switch (result) {
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
||||
case VK_ERROR_DEVICE_LOST:
|
||||
case VK_ERROR_SURFACE_LOST_KHR:
|
||||
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
|
||||
return result; // Another present mode cannot fix these.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
std::fprintf(stderr,
|
||||
" [vulkan] swapchain presentMode %s failed (%d); retrying "
|
||||
"with %s\n",
|
||||
PresentModeKhrName(info.presentMode), static_cast<int>(result),
|
||||
PresentModeKhrName(requested));
|
||||
info.presentMode = requested;
|
||||
return create(info);
|
||||
}
|
||||
|
||||
// Extra images so acquire does not wait on the compositor-held buffer.
|
||||
std::uint32_t PreferSwapchainMinImageCount(PresentModePolicy policy,
|
||||
std::uint32_t requested,
|
||||
|
||||
@@ -2000,7 +2000,11 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR(
|
||||
}
|
||||
}
|
||||
const VkResult result =
|
||||
host_create(device, &host_info, allocator, swapchain);
|
||||
mocktail::graphics::CreateSwapchainWithPresentModeFallback(
|
||||
host_info, create_info->presentMode,
|
||||
[&](const VkSwapchainCreateInfoKHR& attempt) {
|
||||
return host_create(device, &attempt, allocator, swapchain);
|
||||
});
|
||||
if (result != VK_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,83 @@ TEST(PresentModePolicyTest, RequestsExtraSwapchainImagesWhenUnthrottled) {
|
||||
5U);
|
||||
}
|
||||
|
||||
TEST(PresentModePolicyTest, RetriesWithTheGamesModeWhenTheDriverRejectsOurs) {
|
||||
VkSwapchainCreateInfoKHR info{};
|
||||
info.presentMode = VK_PRESENT_MODE_FIFO_LATEST_READY_KHR;
|
||||
std::vector<VkPresentModeKHR> attempts;
|
||||
const VkResult result = CreateSwapchainWithPresentModeFallback(
|
||||
info, VK_PRESENT_MODE_FIFO_KHR,
|
||||
[&](const VkSwapchainCreateInfoKHR &attempt) {
|
||||
attempts.push_back(attempt.presentMode);
|
||||
return attempt.presentMode == VK_PRESENT_MODE_FIFO_LATEST_READY_KHR
|
||||
? VK_ERROR_INITIALIZATION_FAILED
|
||||
: VK_SUCCESS;
|
||||
});
|
||||
EXPECT_EQ(result, VK_SUCCESS);
|
||||
EXPECT_EQ(attempts, (std::vector<VkPresentModeKHR>{
|
||||
VK_PRESENT_MODE_FIFO_LATEST_READY_KHR,
|
||||
VK_PRESENT_MODE_FIFO_KHR}));
|
||||
EXPECT_EQ(info.presentMode, VK_PRESENT_MODE_FIFO_KHR);
|
||||
}
|
||||
|
||||
TEST(PresentModePolicyTest, DoesNotRetryWhenThereIsNothingToFallBackTo) {
|
||||
VkSwapchainCreateInfoKHR info{};
|
||||
info.presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
int calls = 0;
|
||||
EXPECT_EQ(CreateSwapchainWithPresentModeFallback(
|
||||
info, VK_PRESENT_MODE_FIFO_KHR,
|
||||
[&](const VkSwapchainCreateInfoKHR &) {
|
||||
++calls;
|
||||
return VK_SUCCESS;
|
||||
}),
|
||||
VK_SUCCESS);
|
||||
EXPECT_EQ(calls, 1);
|
||||
|
||||
info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
calls = 0;
|
||||
EXPECT_EQ(CreateSwapchainWithPresentModeFallback(
|
||||
info, VK_PRESENT_MODE_FIFO_KHR,
|
||||
[&](const VkSwapchainCreateInfoKHR &) {
|
||||
++calls;
|
||||
return VK_ERROR_SURFACE_LOST_KHR;
|
||||
}),
|
||||
VK_ERROR_SURFACE_LOST_KHR);
|
||||
EXPECT_EQ(calls, 1);
|
||||
}
|
||||
|
||||
TEST(PresentModePolicyTest, DoesNotRetryAfterErrorsAnotherModeCannotFix) {
|
||||
for (const VkResult error :
|
||||
{VK_ERROR_OUT_OF_HOST_MEMORY, VK_ERROR_OUT_OF_DEVICE_MEMORY,
|
||||
VK_ERROR_DEVICE_LOST, VK_ERROR_SURFACE_LOST_KHR,
|
||||
VK_ERROR_NATIVE_WINDOW_IN_USE_KHR}) {
|
||||
VkSwapchainCreateInfoKHR info{};
|
||||
info.presentMode = VK_PRESENT_MODE_FIFO_LATEST_READY_KHR;
|
||||
int calls = 0;
|
||||
EXPECT_EQ(CreateSwapchainWithPresentModeFallback(
|
||||
info, VK_PRESENT_MODE_FIFO_KHR,
|
||||
[&](const VkSwapchainCreateInfoKHR &) {
|
||||
++calls;
|
||||
return error;
|
||||
}),
|
||||
error);
|
||||
EXPECT_EQ(calls, 1) << "error " << static_cast<int>(error);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PresentModePolicyTest, ReturnsTheRetrysResultWhenItAlsoFails) {
|
||||
VkSwapchainCreateInfoKHR info{};
|
||||
info.presentMode = VK_PRESENT_MODE_FIFO_LATEST_READY_KHR;
|
||||
int calls = 0;
|
||||
EXPECT_EQ(CreateSwapchainWithPresentModeFallback(
|
||||
info, VK_PRESENT_MODE_FIFO_KHR,
|
||||
[&](const VkSwapchainCreateInfoKHR &) {
|
||||
return ++calls == 1 ? VK_ERROR_INITIALIZATION_FAILED
|
||||
: VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
}),
|
||||
VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
EXPECT_EQ(calls, 2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace graphics
|
||||
} // namespace mocktail
|
||||
Reference in new issue
Block a user