17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-09 01:20:32 +01:00

Add DLSS Frame Generation (gfx.framegeneration x2/x3/x4) and Reflex (gfx.reflex) to the Vulkan renderer

This commit is contained in:
2026-10-25 20:52:12 +01:00
parent 1841e6d7b0
commit a6bdeeaa2b
23 changed files with 1474 additions and 85 deletions

View File

@@ -94,8 +94,8 @@ bool swapchain::create(instance const &Instance, device const &Device, GLFWwindo
VkSemaphoreCreateInfo semaphoreinfo{};
semaphoreinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
m_acquiresemaphores.assign(Frames, VK_NULL_HANDLE);
for (std::uint32_t i = 0; i < Frames; ++i)
m_acquiresemaphores.assign(Frames * (1 + m_generatedcapacity), VK_NULL_HANDLE);
for (std::uint32_t i = 0; i < m_acquiresemaphores.size(); ++i)
{
if (!VK14_VERIFY(vkCreateSemaphore(Device.handle(), &semaphoreinfo, nullptr, &m_acquiresemaphores[i])))
{
@@ -210,7 +210,8 @@ bool swapchain::build(std::string &Reason)
}
}
auto imagecount{capabilities.minImageCount + 1};
// with the generated frames one image more for each of them: all the images of a frame are acquired at once
auto imagecount{capabilities.minImageCount + 1 + m_generatedcapacity};
if (capabilities.maxImageCount > 0)
{
imagecount = std::min(imagecount, capabilities.maxImageCount);
@@ -253,6 +254,15 @@ bool swapchain::build(std::string &Reason)
create.presentMode = presentmode;
create.clipped = VK_TRUE;
create.oldSwapchain = oldswapchain;
// reflex (VK_NV_low_latency2) is told about the swapchain when it's made
VkSwapchainLatencyCreateInfoNV latency{};
latency.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_LATENCY_CREATE_INFO_NV;
latency.latencyModeEnable = VK_TRUE;
auto const lowlatency{(m_lowlatency > 0) && m_device->low_latency_enabled()};
if (lowlatency)
{
create.pNext = &latency;
}
VkSwapchainKHR swapchain{VK_NULL_HANDLE};
auto const created{vkCreateSwapchainKHR(device, &create, nullptr, &swapchain)};
@@ -267,6 +277,21 @@ bool swapchain::build(std::string &Reason)
return false;
}
m_swapchain = swapchain;
m_lowlatencyon = false;
if (lowlatency && (vkSetLatencySleepModeNV != nullptr))
{
VkLatencySleepModeInfoNV mode{};
mode.sType = VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV;
mode.lowLatencyMode = VK_TRUE;
mode.lowLatencyBoost = (m_lowlatency > 1 ? VK_TRUE : VK_FALSE);
mode.minimumIntervalUs = 0;
auto const result{vkSetLatencySleepModeNV(device, m_swapchain, &mode)};
m_lowlatencyon = (result == VK_SUCCESS);
if (false == m_lowlatencyon)
{
log_warning(m_name + ": reflex can't be turned on (" + result_string(result) + ")");
}
}
m_format = chosen.format;
m_colorspace = chosen.colorSpace;
m_presentmode = presentmode;
@@ -281,6 +306,8 @@ bool swapchain::build(std::string &Reason)
vkGetSwapchainImagesKHR(device, m_swapchain, &count, nullptr);
m_images.resize(count);
vkGetSwapchainImagesKHR(device, m_swapchain, &count, m_images.data());
// the images the program may hold acquired at once (an acquire which waits without end may be done only within them)
m_acquirable = (count > capabilities.minImageCount ? count - capabilities.minImageCount : 1);
m_states.assign(count, image_state{});
VkSemaphoreCreateInfo semaphoreinfo{};
@@ -309,8 +336,9 @@ bool swapchain::build(std::string &Reason)
return true;
}
swapchain::acquire_result swapchain::acquire(std::uint32_t const Frame)
swapchain::acquire_result swapchain::acquire(std::uint32_t const Frame, std::uint32_t const Generated)
{
m_generated.clear();
if (outdated())
{
std::string reason;
@@ -325,7 +353,7 @@ swapchain::acquire_result swapchain::acquire(std::uint32_t const Frame)
return acquire_result::skipped;
}
auto const result{vkAcquireNextImageKHR(m_device->handle(), m_swapchain, UINT64_MAX, m_acquiresemaphores[Frame], VK_NULL_HANDLE, &m_image)};
auto const result{vkAcquireNextImageKHR(m_device->handle(), m_swapchain, UINT64_MAX, acquire_semaphore(Frame), VK_NULL_HANDLE, &m_image)};
if ((result == VK_SUCCESS) || (result == VK_SUBOPTIMAL_KHR))
{
// what the image held is of no use; its use waits for the acquire semaphore, which the submit waits for at the colour output
@@ -335,6 +363,28 @@ swapchain::acquire_result swapchain::acquire(std::uint32_t const Frame)
}
m_states[m_image] = image_state{VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_2_NONE};
}
// the images of the generated frames, shown before the one drawn; the swapchain has one image more for each of them, so the
// images of a frame can all be acquired. one which can't be had leaves fewer of them
if ((result == VK_SUCCESS) || (result == VK_SUBOPTIMAL_KHR))
{
auto const room{std::min(std::min(Generated, m_generatedcapacity), m_acquirable - 1)};
for (std::uint32_t i = 0; i < room; ++i)
{
std::uint32_t image{0};
auto const extra{vkAcquireNextImageKHR(m_device->handle(), m_swapchain, UINT64_MAX, generated_acquire_semaphore(Frame, i), VK_NULL_HANDLE, &image)};
if ((extra != VK_SUCCESS) && (extra != VK_SUBOPTIMAL_KHR))
{
m_rebuild = true;
break;
}
if (extra == VK_SUBOPTIMAL_KHR)
{
m_rebuild = true;
}
m_states[image] = image_state{VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_2_NONE};
m_generated.push_back(image);
}
}
switch (result)
{
case VK_SUCCESS:
@@ -353,6 +403,39 @@ swapchain::acquire_result swapchain::acquire(std::uint32_t const Frame)
return acquire_result::failed;
}
bool swapchain::latency_sleep(VkSemaphore const Semaphore, std::uint64_t const Value) const
{
if ((false == m_lowlatencyon) || (m_swapchain == VK_NULL_HANDLE) || (vkLatencySleepNV == nullptr))
{
return false;
}
// minimized: nothing is presented, the driver has nothing to measure (the main thread, as glfw wants)
int width{0}, height{0};
glfwGetFramebufferSize(m_window, &width, &height);
if ((width <= 0) || (height <= 0))
{
return false;
}
VkLatencySleepInfoNV info{};
info.sType = VK_STRUCTURE_TYPE_LATENCY_SLEEP_INFO_NV;
info.signalSemaphore = Semaphore;
info.value = Value;
return (vkLatencySleepNV(m_device->handle(), m_swapchain, &info) == VK_SUCCESS);
}
void swapchain::latency_marker(std::uint64_t const PresentId, VkLatencyMarkerNV const Marker) const
{
if ((false == m_lowlatencyon) || (m_swapchain == VK_NULL_HANDLE) || (vkSetLatencyMarkerNV == nullptr))
{
return;
}
VkSetLatencyMarkerInfoNV info{};
info.sType = VK_STRUCTURE_TYPE_SET_LATENCY_MARKER_INFO_NV;
info.presentID = PresentId;
info.marker = Marker;
vkSetLatencyMarkerNV(m_device->handle(), m_swapchain, &info);
}
void swapchain::presented(VkResult const Result)
{
if ((Result == VK_SUBOPTIMAL_KHR) || (Result == VK_ERROR_OUT_OF_DATE_KHR))
@@ -456,6 +539,7 @@ void swapchain::destroy()
}
m_swapchain = VK_NULL_HANDLE;
m_acquiresemaphores.clear();
m_generated.clear();
if ((m_surface != VK_NULL_HANDLE) && (m_instance != nullptr) && (m_instance->handle() != VK_NULL_HANDLE))
{
vkDestroySurfaceKHR(m_instance->handle(), m_surface, nullptr);