1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-04 16:24:29 +01:00

[dxvk] Add functionality to wait for a given present operation

This commit is contained in:
Philip Rebohle 2023-06-09 14:45:08 +02:00
parent 215c4f8f6f
commit ca3492570c
5 changed files with 138 additions and 18 deletions

View File

@ -479,7 +479,7 @@ namespace dxvk {
presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats); presenterDesc.numFormats = PickFormats(m_desc.Format, presenterDesc.formats);
presenterDesc.fullScreenExclusive = PickFullscreenMode(); presenterDesc.fullScreenExclusive = PickFullscreenMode();
m_presenter = new Presenter(m_device, presenterDesc); m_presenter = new Presenter(m_device, nullptr, presenterDesc);
m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate); m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
} }

View File

@ -894,7 +894,7 @@ namespace dxvk {
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats); presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
presenterDesc.fullScreenExclusive = PickFullscreenMode(); presenterDesc.fullScreenExclusive = PickFullscreenMode();
m_presenter = new Presenter(m_device, presenterDesc); m_presenter = new Presenter(m_device, nullptr, presenterDesc);
m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate); m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
} }

View File

@ -8,18 +8,32 @@
namespace dxvk { namespace dxvk {
Presenter::Presenter( Presenter::Presenter(
const Rc<DxvkDevice>& device, const Rc<DxvkDevice>& device,
const PresenterDesc& desc) const Rc<sync::Signal>& signal,
: m_device(device), const PresenterDesc& desc)
: m_device(device), m_signal(signal),
m_vki(device->instance()->vki()), m_vki(device->instance()->vki()),
m_vkd(device->vkd()) { m_vkd(device->vkd()) {
// If a frame signal was provided, launch thread that synchronizes
// with present operations and periodically signals the event
if (m_device->features().khrPresentWait.presentWait && m_signal != nullptr)
m_frameThread = dxvk::thread([this] { runFrameThread(); });
} }
Presenter::~Presenter() { Presenter::~Presenter() {
destroySwapchain(); destroySwapchain();
destroySurface(); destroySurface();
if (m_frameThread.joinable()) {
{ std::lock_guard<dxvk::mutex> lock(m_frameMutex);
m_frameQueue.push(PresenterFrame());
m_frameCond.notify_one();
}
m_frameThread.join();
}
} }
@ -51,9 +65,15 @@ namespace dxvk {
} }
VkResult Presenter::presentImage(VkPresentModeKHR mode) { VkResult Presenter::presentImage(
VkPresentModeKHR mode,
uint64_t frameId) {
PresenterSync sync = m_semaphores.at(m_frameIndex); PresenterSync sync = m_semaphores.at(m_frameIndex);
VkPresentIdKHR presentId = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR };
presentId.swapchainCount = 1;
presentId.pPresentIds = &frameId;
VkSwapchainPresentModeInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT }; VkSwapchainPresentModeInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT };
modeInfo.swapchainCount = 1; modeInfo.swapchainCount = 1;
modeInfo.pPresentModes = &mode; modeInfo.pPresentModes = &mode;
@ -65,8 +85,11 @@ namespace dxvk {
info.pSwapchains = &m_swapchain; info.pSwapchains = &m_swapchain;
info.pImageIndices = &m_imageIndex; info.pImageIndices = &m_imageIndex;
if (m_device->features().khrPresentId.presentId && frameId)
presentId.pNext = const_cast<void*>(std::exchange(info.pNext, &presentId));
if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1) if (m_device->features().extSwapchainMaintenance1.swapchainMaintenance1)
info.pNext = &modeInfo; modeInfo.pNext = const_cast<void*>(std::exchange(info.pNext, &modeInfo));
VkResult status = m_vkd->vkQueuePresentKHR( VkResult status = m_vkd->vkQueuePresentKHR(
m_device->queues().graphics.queueHandle, &info); m_device->queues().graphics.queueHandle, &info);
@ -93,6 +116,29 @@ namespace dxvk {
} }
void Presenter::signalFrame(
VkResult result,
uint64_t frameId) {
if (m_signal == nullptr || !frameId)
return;
if (m_device->features().khrPresentWait.presentWait) {
std::lock_guard<dxvk::mutex> lock(m_frameMutex);
PresenterFrame frame = { };
frame.result = result;
frame.frameId = frameId;
m_frameQueue.push(frame);
m_frameCond.notify_one();
} else {
m_signal->signal(frameId);
}
m_lastFrameId.store(frameId, std::memory_order_release);
}
VkResult Presenter::recreateSurface( VkResult Presenter::recreateSurface(
const std::function<VkResult (VkSurfaceKHR*)>& fn) { const std::function<VkResult (VkSurfaceKHR*)>& fn) {
if (m_swapchain) if (m_swapchain)
@ -572,6 +618,9 @@ namespace dxvk {
void Presenter::destroySwapchain() { void Presenter::destroySwapchain() {
if (m_signal != nullptr)
m_signal->wait(m_lastFrameId.load(std::memory_order_acquire));
for (const auto& img : m_images) for (const auto& img : m_images)
m_vkd->vkDestroyImageView(m_vkd->device(), img.view, nullptr); m_vkd->vkDestroyImageView(m_vkd->device(), img.view, nullptr);
@ -596,4 +645,39 @@ namespace dxvk {
m_surface = VK_NULL_HANDLE; m_surface = VK_NULL_HANDLE;
} }
void Presenter::runFrameThread() {
env::setThreadName("dxvk-frame");
while (true) {
std::unique_lock<dxvk::mutex> lock(m_frameMutex);
m_frameCond.wait(lock, [this] {
return !m_frameQueue.empty();
});
PresenterFrame frame = m_frameQueue.front();
m_frameQueue.pop();
lock.unlock();
// Use a frame ID of 0 as an exit condition
if (!frame.frameId)
return;
// If the present operation has succeeded, actually wait for it to complete.
if (frame.result >= 0) {
VkResult vr = m_vkd->vkWaitForPresentKHR(m_vkd->device(),
m_swapchain, frame.frameId, std::numeric_limits<uint64_t>::max());
if (vr < 0 && vr != VK_ERROR_OUT_OF_DATE_KHR && vr != VK_ERROR_SURFACE_LOST_KHR)
Logger::err(str::format("Presenter: vkWaitForPresentKHR failed: ", vr));
}
// Always signal even on error, since failures here
// are transparent to the front-end.
m_signal->signal(frame.frameId);
}
}
} }

View File

@ -10,6 +10,8 @@
#include "../util/util_math.h" #include "../util/util_math.h"
#include "../util/util_string.h" #include "../util/util_string.h"
#include "../util/sync/sync_signal.h"
#include "../vulkan/vulkan_loader.h" #include "../vulkan/vulkan_loader.h"
#include "dxvk_format.h" #include "dxvk_format.h"
@ -68,6 +70,14 @@ namespace dxvk {
VkSemaphore present; VkSemaphore present;
}; };
/**
* \brief Queued frame
*/
struct PresenterFrame {
uint64_t frameId;
VkResult result;
};
/** /**
* \brief Vulkan presenter * \brief Vulkan presenter
* *
@ -80,8 +90,9 @@ namespace dxvk {
public: public:
Presenter( Presenter(
const Rc<DxvkDevice>& device, const Rc<DxvkDevice>& device,
const PresenterDesc& desc); const Rc<sync::Signal>& signal,
const PresenterDesc& desc);
~Presenter(); ~Presenter();
@ -123,10 +134,27 @@ namespace dxvk {
* an error, the swap chain must be recreated, * an error, the swap chain must be recreated,
* but do not present before acquiring an image. * but do not present before acquiring an image.
* \param [in] mode Present mode * \param [in] mode Present mode
* \param [in] frameId Frame number.
* Must increase monotonically.
* \returns Status of the operation * \returns Status of the operation
*/ */
VkResult presentImage( VkResult presentImage(
VkPresentModeKHR mode); VkPresentModeKHR mode,
uint64_t frameId);
/**
* \brief Signals a given frame
*
* Waits for the present operation to complete and then signals
* the presenter signal with the given frame ID. Must not be
* called before GPU work prior to the present submission has
* completed in order to maintain consistency.
* \param [in] result Presentation result
* \param [in] frameId Frame number
*/
void signalFrame(
VkResult result,
uint64_t frameId);
/** /**
* \brief Changes and takes ownership of surface * \brief Changes and takes ownership of surface
@ -196,6 +224,7 @@ namespace dxvk {
private: private:
Rc<DxvkDevice> m_device; Rc<DxvkDevice> m_device;
Rc<sync::Signal> m_signal;
Rc<vk::InstanceFn> m_vki; Rc<vk::InstanceFn> m_vki;
Rc<vk::DeviceFn> m_vkd; Rc<vk::DeviceFn> m_vkd;
@ -210,12 +239,19 @@ namespace dxvk {
std::vector<VkPresentModeKHR> m_dynamicModes; std::vector<VkPresentModeKHR> m_dynamicModes;
uint32_t m_imageIndex = 0; uint32_t m_imageIndex = 0;
uint32_t m_frameIndex = 0; uint32_t m_frameIndex = 0;
VkResult m_acquireStatus = VK_NOT_READY; VkResult m_acquireStatus = VK_NOT_READY;
FpsLimiter m_fpsLimiter; FpsLimiter m_fpsLimiter;
dxvk::mutex m_frameMutex;
dxvk::condition_variable m_frameCond;
dxvk::thread m_frameThread;
std::queue<PresenterFrame> m_frameQueue;
std::atomic<uint64_t> m_lastFrameId = { 0ull };
VkResult recreateSwapChainInternal( VkResult recreateSwapChainInternal(
const PresenterDesc& desc); const PresenterDesc& desc);
@ -251,12 +287,12 @@ namespace dxvk {
uint32_t maxImageCount, uint32_t maxImageCount,
uint32_t desired); uint32_t desired);
VkResult createSurface();
void destroySwapchain(); void destroySwapchain();
void destroySurface(); void destroySurface();
void runFrameThread();
}; };
} }

View File

@ -118,7 +118,7 @@ namespace dxvk {
if (entry.submit.cmdList != nullptr) if (entry.submit.cmdList != nullptr)
status = entry.submit.cmdList->submit(); status = entry.submit.cmdList->submit();
else if (entry.present.presenter != nullptr) else if (entry.present.presenter != nullptr)
status = entry.present.presenter->presentImage(entry.present.presentMode); status = entry.present.presenter->presentImage(entry.present.presentMode, 0);
if (m_callback) if (m_callback)
m_callback(false); m_callback(false);