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.fullScreenExclusive = PickFullscreenMode();
|
||||
|
||||
m_presenter = new Presenter(m_device, presenterDesc);
|
||||
m_presenter = new Presenter(m_device, nullptr, presenterDesc);
|
||||
m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate);
|
||||
}
|
||||
|
||||
|
@ -894,7 +894,7 @@ namespace dxvk {
|
||||
presenterDesc.numFormats = PickFormats(EnumerateFormat(m_presentParams.BackBufferFormat), presenterDesc.formats);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -8,18 +8,32 @@
|
||||
namespace dxvk {
|
||||
|
||||
Presenter::Presenter(
|
||||
const Rc<DxvkDevice>& device,
|
||||
const PresenterDesc& desc)
|
||||
: m_device(device),
|
||||
const Rc<DxvkDevice>& device,
|
||||
const Rc<sync::Signal>& signal,
|
||||
const PresenterDesc& desc)
|
||||
: m_device(device), m_signal(signal),
|
||||
m_vki(device->instance()->vki()),
|
||||
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() {
|
||||
destroySwapchain();
|
||||
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);
|
||||
|
||||
VkPresentIdKHR presentId = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR };
|
||||
presentId.swapchainCount = 1;
|
||||
presentId.pPresentIds = &frameId;
|
||||
|
||||
VkSwapchainPresentModeInfoEXT modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT };
|
||||
modeInfo.swapchainCount = 1;
|
||||
modeInfo.pPresentModes = &mode;
|
||||
@ -65,8 +85,11 @@ namespace dxvk {
|
||||
info.pSwapchains = &m_swapchain;
|
||||
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)
|
||||
info.pNext = &modeInfo;
|
||||
modeInfo.pNext = const_cast<void*>(std::exchange(info.pNext, &modeInfo));
|
||||
|
||||
VkResult status = m_vkd->vkQueuePresentKHR(
|
||||
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(
|
||||
const std::function<VkResult (VkSurfaceKHR*)>& fn) {
|
||||
if (m_swapchain)
|
||||
@ -572,6 +618,9 @@ namespace dxvk {
|
||||
|
||||
|
||||
void Presenter::destroySwapchain() {
|
||||
if (m_signal != nullptr)
|
||||
m_signal->wait(m_lastFrameId.load(std::memory_order_acquire));
|
||||
|
||||
for (const auto& img : m_images)
|
||||
m_vkd->vkDestroyImageView(m_vkd->device(), img.view, nullptr);
|
||||
|
||||
@ -596,4 +645,39 @@ namespace dxvk {
|
||||
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_string.h"
|
||||
|
||||
#include "../util/sync/sync_signal.h"
|
||||
|
||||
#include "../vulkan/vulkan_loader.h"
|
||||
|
||||
#include "dxvk_format.h"
|
||||
@ -68,6 +70,14 @@ namespace dxvk {
|
||||
VkSemaphore present;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Queued frame
|
||||
*/
|
||||
struct PresenterFrame {
|
||||
uint64_t frameId;
|
||||
VkResult result;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Vulkan presenter
|
||||
*
|
||||
@ -80,8 +90,9 @@ namespace dxvk {
|
||||
public:
|
||||
|
||||
Presenter(
|
||||
const Rc<DxvkDevice>& device,
|
||||
const PresenterDesc& desc);
|
||||
const Rc<DxvkDevice>& device,
|
||||
const Rc<sync::Signal>& signal,
|
||||
const PresenterDesc& desc);
|
||||
|
||||
~Presenter();
|
||||
|
||||
@ -123,10 +134,27 @@ namespace dxvk {
|
||||
* an error, the swap chain must be recreated,
|
||||
* but do not present before acquiring an image.
|
||||
* \param [in] mode Present mode
|
||||
* \param [in] frameId Frame number.
|
||||
* Must increase monotonically.
|
||||
* \returns Status of the operation
|
||||
*/
|
||||
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
|
||||
@ -196,6 +224,7 @@ namespace dxvk {
|
||||
private:
|
||||
|
||||
Rc<DxvkDevice> m_device;
|
||||
Rc<sync::Signal> m_signal;
|
||||
|
||||
Rc<vk::InstanceFn> m_vki;
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
@ -210,12 +239,19 @@ namespace dxvk {
|
||||
|
||||
std::vector<VkPresentModeKHR> m_dynamicModes;
|
||||
|
||||
uint32_t m_imageIndex = 0;
|
||||
uint32_t m_frameIndex = 0;
|
||||
uint32_t m_imageIndex = 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(
|
||||
const PresenterDesc& desc);
|
||||
@ -251,12 +287,12 @@ namespace dxvk {
|
||||
uint32_t maxImageCount,
|
||||
uint32_t desired);
|
||||
|
||||
VkResult createSurface();
|
||||
|
||||
void destroySwapchain();
|
||||
|
||||
void destroySurface();
|
||||
|
||||
void runFrameThread();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ namespace dxvk {
|
||||
if (entry.submit.cmdList != nullptr)
|
||||
status = entry.submit.cmdList->submit();
|
||||
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)
|
||||
m_callback(false);
|
||||
|
Loading…
Reference in New Issue
Block a user