17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 15:07:29 +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

@@ -529,16 +529,28 @@ bool vulkan14_renderer::Render()
Timer::subsystem.gfx_total.start();
// the render thread is done with the frame before: the queue, the swapchains and the frames are the main thread's again
if (auto const *failure{ctx.drain()})
// the render thread is done with the frame before: the queue, the swapchains and the frames are the main thread's again. with
// the frame generation it may still be presenting it, the queue and the swapchains are waited for before the windows are acquired
auto const latepresent{m_generated > 0};
if (latepresent)
{
ctx.wait_submitted();
}
else if (auto const *failure{ctx.drain()})
{
report_device_lost(failure);
return false;
}
// what the replay left to dlss; failed: off from now on, the scene of the main window stretched to it without anti-aliasing
// reflex: the simulation of the frame is over, the frame is made
if (false == ctx.windows.empty())
{
ctx.windows.front()->latency_marker(ctx.framecount + 1, VK_LATENCY_MARKER_SIMULATION_END_NV);
}
// what the replay left to dlss; failed: off from now on, the scene of the main window stretched to it without anti-aliasing (the
// frame generation alone: the frames it was to make are the frame drawn, then it's off)
if (auto const error{ctx.dlss.sync()}; false == error.empty())
{
vk14::log_error(error + "; dlss is off, the scene is stretched to the window without anti-aliasing");
vk14::log_error(error + (ctx.dlss.failed() ? "; dlss is off, the scene is stretched to the window without anti-aliasing" : "; the frame generation is off"));
}
// the gpu has to be done with the frame which used the slot before
@@ -618,26 +630,46 @@ bool vulkan14_renderer::Render()
}
}
ctx.acquired.clear();
for (auto &window : ctx.windows)
{
switch (window->acquire(slot))
// the frame generation of dlss: the images of the main window for the frames it makes, where the scene is drawn with dlss. the
// frames it can make are known once the windows are acquired; till then the frame is made for all of them
auto const generating{(m_generated > 0) && simulation::is_ready && (false == Global.gfx_skiprendering) && dlss_active() && (false == ctx.dlss.generation_failed()) && (false == m_viewports.empty()) &&
(m_viewports.front().window == 0) && (m_viewports.front().output_width > 0) && (ctx.scene.generation.unusable == VK_FORMAT_UNDEFINED)};
m_generatedframe = (generating ? std::min(m_generated, ctx.windows.front()->generated_room()) : 0);
m_generationinputs = false;
auto const acquire_windows = [&]() {
ctx.acquired.clear();
for (auto &window : ctx.windows)
{
case vk14::swapchain::acquire_result::ready:
ctx.acquired.push_back(window.get());
break;
case vk14::swapchain::acquire_result::skipped:
break;
default:
if (window != ctx.windows.front())
switch (window->acquire(slot, ((window == ctx.windows.front()) && generating) ? m_generated : 0))
{
// a window of an extra viewport which can't be shown doesn't stop the others
case vk14::swapchain::acquire_result::ready:
ctx.acquired.push_back(window.get());
break;
case vk14::swapchain::acquire_result::skipped:
break;
default:
if (window != ctx.windows.front())
{
// a window of an extra viewport which can't be shown doesn't stop the others
break;
}
ctx.acquired.clear();
report_device_lost("acquiring an image");
return false;
}
ctx.acquired.clear();
report_device_lost("acquiring an image");
return false;
}
m_generatedframe = 0;
if (generating && (false == ctx.acquired.empty()) && (ctx.acquired.front() == ctx.windows.front().get()) && (ctx.windows.front()->generated_count() > 0) && prepare_generation(0))
{
m_generatedframe = ctx.windows.front()->generated_count();
}
return true;
};
// with the frame generation the render thread may still be presenting the frame before, spaced over the time of a frame: the
// windows are acquired once the scene is recorded, after it's done
if ((false == latepresent) && (false == acquire_windows()))
{
return false;
}
frame.number = ctx.framecount;
@@ -792,6 +824,19 @@ bool vulkan14_renderer::Render()
Application.render_ui();
Timer::subsystem.gfx_gui.stop();
if (latepresent)
{
if (auto const *failure{ctx.drain()})
{
report_device_lost(failure);
return false;
}
if (false == acquire_windows())
{
return false;
}
}
// the windows: the image of their viewport, the user interface over the main one
bool uidrawn{false};
for (auto *window : ctx.acquired)
@@ -806,6 +851,13 @@ bool vulkan14_renderer::Render()
}
}
auto const ismain{window == ctx.windows.front().get()};
if (ismain && (window->generated_count() > 0) && (viewport < m_viewports.size()))
{
// with the frames generated by dlss (or the frame drawn again in their images, where they can't be made)
render_generation(viewport);
uidrawn = true;
continue;
}
ctx.passes.begin_pass(ismain ? "ui" : "other viewports", {window->use(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colour_stage, colour_access)});
VkClearValue clear{};
clear.color = {{0.f, 0.f, 0.f, 1.f}};
@@ -892,7 +944,16 @@ bool vulkan14_renderer::Render()
signal.semaphore = window->present_semaphore();
signal.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
job.signals.push_back(signal);
// the images of the frames generated, shown before it
for (std::uint32_t i = 0; i < window->generated_count(); ++i)
{
wait.semaphore = window->generated_acquire_semaphore(slot, i);
job.waits.push_back(wait);
signal.semaphore = window->generated_present_semaphore(i);
job.signals.push_back(signal);
}
}
job.present_id = ctx.framecount + 1;
VkSemaphoreSubmitInfo timelinesignal{};
timelinesignal.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
timelinesignal.semaphore = ctx.timeline;
@@ -1319,6 +1380,10 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
m_cabfuture = glm::mat4{1.f};
if (taa)
{
// the frame generation takes the camera without the offset of the frame
m_generationcamera.projection = m_renderpass.pass_camera.projection();
m_generationcamera.view = glm::mat3{glm::dmat3{m_renderpass.pass_camera.modelview()}};
m_generationcamera.position = glm::vec3{m_renderpass.pass_camera.position()};
m_taa.set_phases(dlss ? m_context->dlss.jitter_phases() : 8);
auto const *cab{((false == FreeFlyModeFlag) && Global.render_cab && (simulation::Train != nullptr)) ? simulation::Train->Dynamic() : nullptr};
m_renderpass.pass_camera.projection() = m_taa.begin_frame(
@@ -3086,6 +3151,311 @@ bool vulkan14_renderer::dlss_active() const
return (m_dlssrender.x > 0) && Global.gfx_dlss() && m_context && m_context->dlss.available() && (false == m_context->dlss.failed());
}
namespace
{
// the unorm format with the bits of a format of a window (a storage image can't be srgb)
VkFormat unorm_format(VkFormat const Format)
{
switch (Format)
{
case VK_FORMAT_B8G8R8A8_SRGB:
return VK_FORMAT_B8G8R8A8_UNORM;
case VK_FORMAT_R8G8B8A8_SRGB:
return VK_FORMAT_R8G8B8A8_UNORM;
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
default:
return Format;
}
}
} // namespace
// the images of the frame generation for the size and the format of the main window; false: they can't be had
bool vulkan14_renderer::prepare_generation(std::size_t const Index)
{
auto &ctx{*m_context};
auto &generation{ctx.scene.generation};
auto const &window{*ctx.windows[m_viewports[Index].window]};
auto const extent{window.extent()};
auto const windowformat{window.format()};
if (generation.backbuffer && (generation.extent.width == extent.width) && (generation.extent.height == extent.height) && (generation.window_format == windowformat))
{
return true;
}
if (generation.unusable == windowformat)
{
return false;
}
auto const features = [&](VkFormat const Format) {
VkFormatProperties properties{};
vkGetPhysicalDeviceFormatProperties(ctx.device.physical(), Format, &properties);
return properties.optimalTilingFeatures;
};
VkFormatFeatureFlags const needed{VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT};
// the bits of the format of the window, copied to it; or else rgba, blitted to it
auto format{unorm_format(windowformat)};
auto blit{false};
if ((features(format) & needed) != needed)
{
format = VK_FORMAT_R8G8B8A8_UNORM;
blit = true;
// a blit to an srgb window would encode the colour encoded already
if (((features(format) & (needed | VK_FORMAT_FEATURE_BLIT_SRC_BIT)) != (needed | VK_FORMAT_FEATURE_BLIT_SRC_BIT)) || ((features(windowformat) & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0) || window.srgb())
{
generation.unusable = windowformat;
vk14::log_info("gfx.framegeneration: the format " + std::to_string(windowformat) + " of the main window can't be given the frames, they aren't generated");
return false;
}
}
vk14::generation_targets fresh;
fresh.extent = extent;
fresh.window_format = windowformat;
fresh.format = format;
fresh.blit = blit;
auto make = [&](std::shared_ptr<vk14::image> &Image, VkImageUsageFlags const Usage, char const *Name) {
vk14::image_desc desc;
desc.format = format;
desc.extent = {extent.width, extent.height, 1};
desc.usage = Usage | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
Image = std::make_shared<vk14::image>();
if (false == Image->create(ctx.memory, desc, Name))
{
Image.reset();
return false;
}
return true;
};
auto ok{make(fresh.backbuffer, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, "frame generation backbuffer") && make(fresh.hudless, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, "frame generation hudless")};
for (std::uint32_t i = 0; ok && (i < m_generated); ++i)
{
ok = make(fresh.outputs[i], VK_IMAGE_USAGE_TRANSFER_DST_BIT, "frame generated");
}
// what was made for the window before goes once the frames which use it are done
ctx.deletions.push(ctx.uploads.current_value(), [old = std::move(generation)]() {});
if (false == ok)
{
generation = vk14::generation_targets{};
generation.unusable = windowformat;
vk14::log_error("gfx.framegeneration: the images for the window of " + std::to_string(extent.width) + "x" + std::to_string(extent.height) + " can't be made, the frames aren't generated");
return false;
}
generation = std::move(fresh);
return true;
}
// the depth and the velocities of the main view turned the way up of the window, for the frame generation (after dlss, which made
// the velocities of the sky)
void vulkan14_renderer::flip_generation_inputs(std::size_t const Index)
{
auto &ctx{*m_context};
auto &generation{ctx.scene.generation};
auto &targets{ctx.scene.targets[Index]};
if ((false == static_cast<bool>(targets.dlss_motion)) || (false == static_cast<bool>(targets.depth)))
{
return;
}
auto const render{targets.extent};
if ((false == static_cast<bool>(generation.depth)) || (generation.render.width != render.width) || (generation.render.height != render.height))
{
auto make = [&](std::shared_ptr<vk14::image> &Image, VkFormat const Format, char const *Name) {
vk14::image_desc desc;
desc.format = Format;
desc.extent = {render.width, render.height, 1};
desc.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
Image = std::make_shared<vk14::image>();
if (false == Image->create(ctx.memory, desc, Name))
{
Image.reset();
return false;
}
return true;
};
ctx.deletions.push(ctx.uploads.current_value(), [depth = std::move(generation.depth), motion = std::move(generation.motion)]() {});
generation.depth.reset();
generation.motion.reset();
generation.render = {0, 0};
if ((false == make(generation.depth, VK_FORMAT_R32_SFLOAT, "frame generation depth")) || (false == make(generation.motion, VK_FORMAT_R16G16_SFLOAT, "frame generation velocity")))
{
generation.depth.reset();
generation.motion.reset();
return;
}
generation.render = render;
}
post_pass("postfx_fg_flip.frag", *generation.depth, {targets.depth.get()}, vk14::draw_constants{}, "frame generation inputs");
post_pass("postfx_fg_flip.frag", *generation.motion, {targets.dlss_motion.get()}, vk14::draw_constants{});
m_generationinputs = true;
}
// the main window with the frames generated by dlss: the frame without the user interface and with it, the frames generated from it
// and the last one, all of them copied to the images of the window acquired for them. without what the frame generation needs (its
// images, the inputs of the frame) every image of the window gets the frame drawn
void vulkan14_renderer::render_generation(std::size_t const Index)
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &generation{scene.generation};
auto &window{*ctx.windows[m_viewports[Index].window]};
auto const count{std::min<std::uint32_t>(window.generated_count(), m_generated)};
auto const extent{window.extent()};
auto const drawn{(Index < scene.targets.size()) && scene.targets[Index].output};
VkClearValue clear{};
clear.color = {{0.f, 0.f, 0.f, 1.f}};
auto const ready{drawn && m_generationinputs && generation.backbuffer && generation.depth && (generation.extent.width == extent.width) && (generation.extent.height == extent.height)};
if (false == ready)
{
for (std::uint32_t i = 0; i <= window.generated_count(); ++i)
{
auto const last{i == window.generated_count()};
ctx.passes.begin_pass(last ? "ui" : "frame generation: the frame again",
{last ? window.use(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colour_stage, colour_access) : window.generated_use(i, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colour_stage, colour_access)});
auto attachment{attachment_info(last ? window.view() : window.generated_view(i), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, clear)};
VkRenderingInfo rendering{};
rendering.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
rendering.renderArea = {{0, 0}, extent};
rendering.layerCount = 1;
rendering.colorAttachmentCount = 1;
rendering.pColorAttachments = &attachment;
ctx.commands->begin_rendering(&rendering);
if (drawn)
{
present_viewport(Index);
}
// the user interface in each of them (kept for the next one but the last)
ctx.imgui.record(ctx.commands, extent, window.format(), window.srgb(), false == last);
ctx.commands->end_rendering();
ctx.passes.end_pass();
}
std::vector<vk14::image_use> presents{window.use(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE)};
for (std::uint32_t i = 0; i < window.generated_count(); ++i)
{
presents.push_back(window.generated_use(i, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE));
}
vk14::barriers(ctx.commands, presents);
return;
}
// the frame without the user interface, and with it
auto drawto = [&](vk14::image &Target, bool const Ui, char const *Pass) {
ctx.passes.begin_pass(Pass, {vk14::use_of(Target, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colour_stage, colour_access)});
auto attachment{attachment_info(Target.view(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, clear)};
VkRenderingInfo rendering{};
rendering.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
rendering.renderArea = {{0, 0}, extent};
rendering.layerCount = 1;
rendering.colorAttachmentCount = 1;
rendering.pColorAttachments = &attachment;
ctx.commands->begin_rendering(&rendering);
present_viewport(Index, &Target);
if (Ui)
{
ctx.imgui.record(ctx.commands, extent, Target.desc().format, false);
}
ctx.commands->end_rendering();
ctx.passes.end_pass();
};
drawto(*generation.hudless, false, "frame generation hudless");
drawto(*generation.backbuffer, true, "ui");
// the frames between the last one and this one, by the passes of the library, whichever stages they are
auto const stage{VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT};
auto const read{VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_TRANSFER_READ_BIT};
std::vector<vk14::image_use> uses{vk14::use_of(*generation.backbuffer, VK_IMAGE_LAYOUT_GENERAL, stage, read), vk14::use_of(*generation.hudless, VK_IMAGE_LAYOUT_GENERAL, stage, read),
vk14::use_of(*generation.depth, VK_IMAGE_LAYOUT_GENERAL, stage, read), vk14::use_of(*generation.motion, VK_IMAGE_LAYOUT_GENERAL, stage, read)};
vk14::dlss::generation_inputs inputs;
inputs.backbuffer = generation.backbuffer.get();
inputs.hudless = generation.hudless.get();
inputs.depth = generation.depth.get();
inputs.motion = generation.motion.get();
for (std::uint32_t i = 0; i < count; ++i)
{
uses.push_back(vk14::use_of(*generation.outputs[i], VK_IMAGE_LAYOUT_GENERAL, stage, read | VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT));
inputs.outputs.push_back(generation.outputs[i].get());
}
ctx.passes.begin_pass("frame generation", uses);
auto const &camera{m_generationcamera};
inputs.view_to_clip = camera.projection;
inputs.clip_to_previous_clip = m_taa.reprojection_without_offset();
inputs.position = camera.position;
// the axes of the view in the world: the rows of the rotation of the world to the view (the view looks along its -z)
inputs.right = glm::vec3{camera.view[0][0], camera.view[1][0], camera.view[2][0]};
inputs.up = glm::vec3{camera.view[0][1], camera.view[1][1], camera.view[2][1]};
inputs.forward = -glm::vec3{camera.view[0][2], camera.view[1][2], camera.view[2][2]};
inputs.znear = m_colorpass.znear;
inputs.zfar = m_colorpass.zfar;
auto const yscale{camera.projection[1][1]};
inputs.fov = (yscale > 0.f ? 2.f * std::atan(1.f / yscale) : glm::radians(45.f));
inputs.aspect = ((camera.projection[0][0] > 0.f) ? yscale / camera.projection[0][0] : 1.f);
inputs.jitter = m_taa.jitter();
// its history is of no use after a frame without it (minimized, not drawn, without the images)
inputs.reset = (false == m_taa.history_valid()) || (m_generationframe + 1 != ctx.framecount);
m_generationframe = ctx.framecount;
ctx.dlss.generate(*ctx.commands, inputs, ctx.deletions, ctx.uploads.current_value());
ctx.passes.end_pass();
// the frames to the images of the window: the ones generated first, in the order they are shown, then the one drawn
auto const transfer{VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT};
std::vector<vk14::image_use> copies{vk14::use_of(*generation.backbuffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, transfer, VK_ACCESS_2_TRANSFER_READ_BIT),
window.use(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, transfer, VK_ACCESS_2_TRANSFER_WRITE_BIT)};
for (std::uint32_t i = 0; i < window.generated_count(); ++i)
{
auto &source{*generation.outputs[std::min(i, count - 1)]};
copies.push_back(vk14::use_of(source, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, transfer, VK_ACCESS_2_TRANSFER_READ_BIT));
copies.push_back(window.generated_use(i, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, transfer, VK_ACCESS_2_TRANSFER_WRITE_BIT));
}
ctx.passes.begin_pass("frame generation to the window", copies);
auto const send = [&](vk14::image &Source, VkImage const Destination) {
if (generation.blit)
{
VkImageBlit2 region{};
region.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2;
region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.srcOffsets[1] = {static_cast<std::int32_t>(extent.width), static_cast<std::int32_t>(extent.height), 1};
region.dstOffsets[1] = region.srcOffsets[1];
VkBlitImageInfo2 blit{};
blit.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2;
blit.srcImage = Source.handle();
blit.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
blit.dstImage = Destination;
blit.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
blit.regionCount = 1;
blit.pRegions = &region;
blit.filter = VK_FILTER_NEAREST;
ctx.commands->blit_image2(&blit);
return;
}
VkImageCopy2 region{};
region.sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2;
region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.extent = {extent.width, extent.height, 1};
VkCopyImageInfo2 copy{};
copy.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2;
copy.srcImage = Source.handle();
copy.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
copy.dstImage = Destination;
copy.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
copy.regionCount = 1;
copy.pRegions = &region;
ctx.commands->copy_image2(&copy);
};
for (std::uint32_t i = 0; i < window.generated_count(); ++i)
{
send(*generation.outputs[std::min(i, count - 1)], window.generated_image(i));
}
send(*generation.backbuffer, window.image());
ctx.passes.end_pass();
std::vector<vk14::image_use> presents{window.use(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE)};
for (std::uint32_t i = 0; i < window.generated_count(); ++i)
{
presents.push_back(window.generated_use(i, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE));
}
vk14::barriers(ctx.commands, presents);
}
// the post-processing of a viewport: from the colour of the scene to its output, which the window shows (the part of
// opengl46_renderer::Render_pass() after the colour pass)
void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionblur)
@@ -3109,6 +3479,10 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
{
render_dlss(Index);
hdr = targets.dlss_output.get();
if ((m_generatedframe > 0) && m_viewports[Index].main)
{
flip_generation_inputs(Index);
}
}
// the temporal anti-aliasing: the scene blended into its history (the motion blur takes the result)
if (m_motionhistory && targets.taa && targets.taa_history[0] && targets.taa_history[1] && targets.motion_image)
@@ -3135,6 +3509,12 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
{
vk14::draw_constants constants;
constants.data[0].x = m_framerate / (1.0 / Global.gfx_postfx_motionblur_shutter);
// with the frames generated by dlss each frame drawn is shown for a part of its time: the blur as much shorter (the guide of
// the frame generation)
if (m_generationinputs && m_viewports[Index].main)
{
constants.data[0].x /= static_cast<float>(1 + m_generatedframe);
}
post_pass("postfx_motionblur.frag", *targets.blurred, {hdr, targets.motion_image.get(), targets.depth.get()}, constants, "resolve, ambient occlusion, motion blur");
hdr = targets.blurred.get();
}
@@ -3172,21 +3552,25 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
vk14::barriers(commands, {vk14::use_of(*targets.output, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, sampled_access)});
}
// the output of a viewport drawn to its window turned upside down (decision K8), inside the rendering of the window
void vulkan14_renderer::present_viewport(std::size_t const Index)
// the output of a viewport drawn to its window turned upside down (decision K8), inside the rendering of the window; or to Target,
// an image of the size of the window in the place of it (the frame generation, unorm)
void vulkan14_renderer::present_viewport(std::size_t const Index, vk14::image const *Target)
{
auto &ctx{*m_context};
auto &scene{ctx.scene};
auto &targets{scene.targets[Index]};
auto const &window{*ctx.windows[m_viewports[Index].window]};
auto const commands{ctx.commands};
auto const format{Target != nullptr ? Target->desc().format : window.format()};
auto const srgb{Target != nullptr ? false : window.srgb()};
VkExtent2D const extent{Target != nullptr ? VkExtent2D{Target->desc().extent.width, Target->desc().extent.height} : window.extent()};
// the output encoded already goes as it is; an srgb output to a window which isn't (no srgb format of the surface) is encoded
auto const pipeline{post_pipeline("present.frag", static_cast<int>(window.format()), (targets.srgb && (false == window.srgb())) ? 1 : 0)};
auto const pipeline{post_pipeline("present.frag", static_cast<int>(format), (targets.srgb && (false == srgb)) ? 1 : 0)};
if (pipeline == nullptr)
{
return;
}
vk14::set_draw_state(commands, window.extent());
vk14::set_draw_state(commands, extent);
commands->bind_pipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, reinterpret_cast<VkPipeline>(pipeline));
commands->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
std::array<vk14::descriptor, vk14::bindings::POST_TEXTURES + 1> descriptors;