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:
parent
215c4f8f6f
commit
ca3492570c
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user