mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 20:34:54 +01:00
Add NVIDIA DLSS to the Vulkan renderer (gfx.antialiasing dlss/dlaa) and gfx.buffer.percent
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -21,6 +21,8 @@ cmake-build-*/
|
||||
# Vcpkg manifest
|
||||
vcpkg.json
|
||||
/ref/crashpad
|
||||
# the NVIDIA DLSS SDK, fetched by tools/fetch_dlss_sdk.cmake (WITH_DLSS)
|
||||
/ref/DLSS/
|
||||
|
||||
# Local Python (setup.bat / CMake) for embedding
|
||||
tools/python314/
|
||||
|
||||
@@ -72,6 +72,7 @@ endif()
|
||||
|
||||
option(WITH_OPENGL_MODERN "Compile with OpenGL modern renderer" ON)
|
||||
option(WITH_VULKAN "Compile with the Vulkan 1.4 renderer (gfxrenderer vulkan); needs the submodules ref/volk and ref/Vulkan-Headers" ON)
|
||||
option(WITH_DLSS "Compile the Vulkan renderer with NVIDIA DLSS (gfx.antialiasing dlss); needs the DLSS SDK in DLSS_SDK_ROOT, cmake -P tools/fetch_dlss_sdk.cmake" ON)
|
||||
option(WITH_PYTHON "Python 3.14 embedded scripting" ON)
|
||||
option(WITH_UART "Compile with libserialport" ON)
|
||||
option(WITH_LUA "Compile with lua scripting support" ON)
|
||||
@@ -463,9 +464,54 @@ if (WITH_VULKAN)
|
||||
"rendering/vulkan14/vulkan14debug.cpp"
|
||||
"rendering/vulkan14/vulkan14commands.cpp"
|
||||
"rendering/vulkan14/vulkan14raytracing.cpp"
|
||||
"rendering/vulkan14/vulkan14dlss.cpp"
|
||||
"ref/volk/volk.c")
|
||||
set(DEFINITIONS ${DEFINITIONS} "WITH_VULKAN")
|
||||
|
||||
# nvidia dlss of the vulkan renderer, from the dlss sdk (github.com/NVIDIA/DLSS) outside the repository: the headers and the
|
||||
# static library of ngx to build with, the dlss library next to the program to run with (rendering/vulkan14/vulkan14dlss.h)
|
||||
if (WITH_DLSS)
|
||||
set(DLSS_SDK_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/ref/DLSS" CACHE PATH "The NVIDIA DLSS SDK, with include/ and lib/ (cmake -P tools/fetch_dlss_sdk.cmake)")
|
||||
if (NOT EXISTS "${DLSS_SDK_ROOT}/include/nvsdk_ngx_helpers_vk.h")
|
||||
message(FATAL_ERROR "WITH_DLSS=ON needs the DLSS SDK in DLSS_SDK_ROOT (${DLSS_SDK_ROOT}): cmake -P tools/fetch_dlss_sdk.cmake")
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$")
|
||||
set(DLSS_ARCH "aarch64")
|
||||
else()
|
||||
set(DLSS_ARCH "x86_64")
|
||||
endif()
|
||||
if (WIN32)
|
||||
# the variants of the dynamic c runtime (/MD, /MDd in debug)
|
||||
# the static libraries of x86_64 are in x64/ (the others of older visual studios beside it), those of arm64 at the top
|
||||
if (DLSS_ARCH STREQUAL "x86_64")
|
||||
set(DLSS_NGX_DIR "${DLSS_SDK_ROOT}/lib/Windows_x86_64/x64")
|
||||
else()
|
||||
set(DLSS_NGX_DIR "${DLSS_SDK_ROOT}/lib/Windows_${DLSS_ARCH}")
|
||||
endif()
|
||||
set(DLSS_NGX_LIBRARY "$<IF:$<CONFIG:Debug>,${DLSS_NGX_DIR}/nvsdk_ngx_d_dbg.lib,${DLSS_NGX_DIR}/nvsdk_ngx_d.lib>")
|
||||
set(DLSS_NGX_CHECK "${DLSS_NGX_DIR}/nvsdk_ngx_d.lib")
|
||||
set(DLSS_RUNTIME_PATTERN "nvngx_dlss.dll")
|
||||
set(DLSS_RUNTIME_DIR "${DLSS_SDK_ROOT}/lib/Windows_${DLSS_ARCH}")
|
||||
elseif (UNIX AND NOT APPLE)
|
||||
set(DLSS_NGX_LIBRARY "${DLSS_SDK_ROOT}/lib/Linux_${DLSS_ARCH}/libnvsdk_ngx.a")
|
||||
set(DLSS_NGX_CHECK "${DLSS_NGX_LIBRARY}")
|
||||
set(DLSS_RUNTIME_PATTERN "libnvidia-ngx-dlss.so.*")
|
||||
set(DLSS_RUNTIME_DIR "${DLSS_SDK_ROOT}/lib/Linux_${DLSS_ARCH}")
|
||||
else()
|
||||
message(FATAL_ERROR "WITH_DLSS=ON: the DLSS SDK has no libraries for this platform")
|
||||
endif()
|
||||
if (NOT EXISTS "${DLSS_NGX_CHECK}")
|
||||
message(FATAL_ERROR "WITH_DLSS=ON: ${DLSS_NGX_CHECK} isn't in the DLSS SDK, fetch the libraries of this platform: cmake -P tools/fetch_dlss_sdk.cmake")
|
||||
endif()
|
||||
file(GLOB DLSS_RUNTIME_RELEASE "${DLSS_RUNTIME_DIR}/rel/${DLSS_RUNTIME_PATTERN}")
|
||||
file(GLOB DLSS_RUNTIME_DEVELOPMENT "${DLSS_RUNTIME_DIR}/dev/${DLSS_RUNTIME_PATTERN}")
|
||||
if (NOT DLSS_RUNTIME_RELEASE)
|
||||
message(WARNING "WITH_DLSS=ON: no ${DLSS_RUNTIME_PATTERN} in ${DLSS_RUNTIME_DIR}/rel, it has to be put next to the program by hand")
|
||||
endif()
|
||||
# the sdk is seen by vulkan14dlss.cpp only; the rest of the renderer asks vk14::dlss::compiled()
|
||||
set_source_files_properties("rendering/vulkan14/vulkan14dlss.cpp" PROPERTIES COMPILE_DEFINITIONS "WITH_DLSS" SKIP_PRECOMPILE_HEADERS ON)
|
||||
endif()
|
||||
|
||||
# glslang compiles the shaders of shaders/vk14 when the program runs (no spirv-tools, no hlsl, no programs of its own)
|
||||
set(GLSLANG_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(GLSLANG_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
@@ -606,6 +652,20 @@ if (WITH_VULKAN)
|
||||
# the vulkan headers are seen by the files of rendering/vulkan14 only, they include them
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${VULKAN_HEADERS_DIR}" "${VOLK_DIR}" "${VMA_DIR}")
|
||||
target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glslang glslang-default-resource-limits)
|
||||
if (WITH_DLSS)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${DLSS_SDK_ROOT}/include")
|
||||
target_link_libraries(${PROJECT_NAME} "${DLSS_NGX_LIBRARY}" ${CMAKE_DL_LIBS})
|
||||
# the dlss library next to the program, the development one (gfx.dlss.developer) in ngx_dev/
|
||||
if (DLSS_RUNTIME_RELEASE)
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DLSS_RUNTIME_RELEASE} "$<TARGET_FILE_DIR:${PROJECT_NAME}>")
|
||||
endif()
|
||||
if (DLSS_RUNTIME_DEVELOPMENT)
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:${PROJECT_NAME}>/ngx_dev"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DLSS_RUNTIME_DEVELOPMENT} "$<TARGET_FILE_DIR:${PROJECT_NAME}>/ngx_dev")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_compile_definitions(eu07_settings INTERFACE ${DEFINITIONS})
|
||||
|
||||
@@ -91,9 +91,27 @@ anti-aliasing with it. The velocities are drawn with it whether the motion blur
|
||||
motion blur smears the image after the TAA, and it also blurs the turning of the camera. Thin lines across the
|
||||
pixels (rails and wires far away) are locked in the history for 8 frames on the world standing still farther
|
||||
than 20..40 m, so the frames whose offset misses them don't make them flicker; on near or fast moving pixels the
|
||||
usual check of the history against the colours around the pixel applies. Further anti-aliasing modes (FSR, DLSS) are meant to be added to
|
||||
usual check of the history against the colours around the pixel applies. Further anti-aliasing modes (FSR) are meant to be added to
|
||||
the same entry.
|
||||
|
||||
| Entry | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `gfx.antialiasing dlss/dlaa` | | NVIDIA DLSS of the Vulkan renderer: the scene of the main window drawn at `gfx.buffer.percent` of the window with one sample, the jitter of `taa` (more offsets the smaller the image: 8 for each pixel of the window) and anti-aliased and upscaled to the window by DLSS. `dlaa` is DLSS at the full size of the window whatever `gfx.buffer.percent` says. Needs a build with `WITH_DLSS`, a GeForce RTX and its driver; otherwise, and with OpenGL, `taa` is taken at the start (the log says why). Should DLSS fail later, the scene is stretched to the window without anti-aliasing (the negative bias of the mipmaps stays until the restart) |
|
||||
| `gfx.buffer.percent` | `1.0` | the size of the image the scene of the main window is drawn at, as a part of the size of the window (`0.5`: half its width and half its height; a value over 4 is taken as percents). With `dlss` it's kept within what the DLSS mode nearest to it takes (about 0.33..1.0) and upscaled by it; with the other modes the image is stretched to the window. Replaces `gfx.framebuffer.width` and `gfx.framebuffer.height`, which aren't read any more. Read at the start |
|
||||
| `gfx.dlss.preset default/j/k/l/m` | `default` | the preset of the DLSS model (the DLSS SDK 310 has these; `default` lets the library pick per mode) |
|
||||
| `gfx.dlss.autoexposure yes/no` | `no` | DLSS works the exposure out itself instead of being given the exposure of the tonemapping |
|
||||
| `gfx.dlss.lodbias` | `0` | added to the bias of the mipmaps of the textures of the scene with DLSS, `log2(drawn / window) - 1` (less negative against moiré, more for sharper textures) |
|
||||
| `gfx.dlss.developer yes/no` | `no` | the development library of DLSS (`ngx_dev/` next to the program): its indicator and the debug keys of the DLSS programming guide (Ctrl+Alt+F12 debug views, Ctrl+Alt+F9 jitter configurations) |
|
||||
|
||||
DLSS needs the NVIDIA DLSS SDK at build time, which isn't in the repository (it keeps the binaries of every platform in
|
||||
git): `cmake -P tools/fetch_dlss_sdk.cmake` takes the headers and the libraries of the platform into `ref/DLSS`
|
||||
(ignored by git), then configure with `-DWITH_DLSS=ON` (`-DDLSS_SDK_ROOT=<dir>` for another place). The build copies
|
||||
`nvngx_dlss.dll` (Windows) or `libnvidia-ngx-dlss.so.<version>` (Linux) next to the program, where NGX looks for it,
|
||||
and the development library into `ngx_dev/`; a program copied elsewhere needs the library next to it. The velocities DLSS
|
||||
is given are those of `taa`, the sky (which has none drawn) gets the motion of the camera; animated parts of models other
|
||||
than wheels (gauges, levers, wipers) have no velocity of their own yet. The debug window of the Vulkan renderer shows the
|
||||
mode and the sizes, and can turn the jitter offsets the other way (to check their direction) and drop the history.
|
||||
|
||||
### Ambient occlusion
|
||||
|
||||
Darkening of creases, corners and the ground under objects, worked out from the depth of the image (screen space)
|
||||
|
||||
@@ -15,8 +15,6 @@ http://mozilla.org/MPL/2.0/.
|
||||
namespace
|
||||
{
|
||||
|
||||
// the offsets go round this many frames: enough to cover a pixel evenly, few enough for the history to hold all of them
|
||||
constexpr std::uint32_t JITTER_PHASES{8};
|
||||
// the camera moved farther than this from one frame to the next (another camera, another vehicle, a jump): the history is dropped
|
||||
constexpr double CAMERA_JUMP{50.0};
|
||||
|
||||
@@ -38,7 +36,8 @@ float halton(std::uint32_t Index, std::uint32_t const Base)
|
||||
|
||||
glm::mat4 gfx::temporal_aa::begin_frame(glm::mat4 const &Projection, glm::dmat3 const &View, glm::dvec3 const &Camera, TDynamicObject const *Cab, glm::ivec2 const &Size)
|
||||
{
|
||||
m_phase = (m_phase + 1) % JITTER_PHASES;
|
||||
// the offsets go round m_phases frames: enough to cover a pixel evenly, few enough for the history to hold all of them
|
||||
m_phase = (m_phase + 1) % m_phases;
|
||||
// the first element of the sequence is 0 in every base; from the second one on they are spread over the pixel
|
||||
m_jitter = glm::vec2{halton(m_phase + 1, 2), halton(m_phase + 1, 3)} - 0.5f;
|
||||
glm::dvec2 const size{static_cast<double>(std::max(Size.x, 1)), static_cast<double>(std::max(Size.y, 1))};
|
||||
|
||||
@@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
@@ -39,6 +40,12 @@ class temporal_aa
|
||||
{
|
||||
m_previous.valid = false;
|
||||
}
|
||||
// the offsets go round Phases frames (at least 1); 8 for the temporal anti-aliasing, more when dlss upscales (8 for each pixel
|
||||
// of the window)
|
||||
void set_phases(std::uint32_t const Phases)
|
||||
{
|
||||
m_phases = std::max<std::uint32_t>(Phases, 1);
|
||||
}
|
||||
// the frame has a history: the last frame was drawn the same way
|
||||
bool history_valid() const
|
||||
{
|
||||
@@ -83,6 +90,7 @@ class temporal_aa
|
||||
|
||||
frame m_previous;
|
||||
std::uint32_t m_phase{0};
|
||||
std::uint32_t m_phases{8};
|
||||
glm::vec2 m_jitter{0.f};
|
||||
glm::mat4 m_worldfuture{1.f};
|
||||
glm::mat4 m_cabfuture{1.f};
|
||||
|
||||
@@ -74,6 +74,13 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
|
||||
if (!Init_caps())
|
||||
return false;
|
||||
|
||||
if (Global.gfx_antialiasing == global_settings::antialiasing_mode::dlss)
|
||||
{
|
||||
// dlss is of the vulkan renderer only: the temporal anti-aliasing of this one instead
|
||||
WriteLog("gfx.antialiasing dlss isn't available with opengl, using taa");
|
||||
Global.gfx_antialiasing = global_settings::antialiasing_mode::taa;
|
||||
}
|
||||
|
||||
WriteLog("preparing renderer..");
|
||||
|
||||
m_window = Window;
|
||||
@@ -6744,12 +6751,15 @@ bool opengl46_renderer::Init_caps()
|
||||
WriteLog("using multisampling x" + std::to_string(1 << Global.iMultisampling));
|
||||
}
|
||||
|
||||
if (Global.gfx_framebuffer_width == -1)
|
||||
Global.gfx_framebuffer_width = Global.fb_size.x;
|
||||
if (Global.gfx_framebuffer_height == -1)
|
||||
Global.gfx_framebuffer_height = Global.fb_size.y;
|
||||
// the image of the main window: a part of the window (gfx.buffer.percent), stretched to it
|
||||
if ((Global.gfx_framebuffer_width == -1) || (Global.gfx_framebuffer_height == -1))
|
||||
{
|
||||
auto const size{Global.gfx_buffer_size()};
|
||||
Global.gfx_framebuffer_width = size.x;
|
||||
Global.gfx_framebuffer_height = size.y;
|
||||
}
|
||||
|
||||
WriteLog("main window size: " + std::to_string(Global.gfx_framebuffer_width) + "x" + std::to_string(Global.gfx_framebuffer_height));
|
||||
WriteLog("main window size: " + std::to_string(Global.fb_size.x) + "x" + std::to_string(Global.fb_size.y) + ", drawn at " + std::to_string(Global.gfx_framebuffer_width) + "x" + std::to_string(Global.gfx_framebuffer_height));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,8 @@ enum call_type : std::uint32_t
|
||||
call_end_query,
|
||||
call_begin_label,
|
||||
call_end_label,
|
||||
call_build_acceleration_structures
|
||||
call_build_acceleration_structures,
|
||||
call_external
|
||||
};
|
||||
|
||||
struct call_header
|
||||
@@ -174,6 +175,11 @@ struct build_call
|
||||
std::size_t geometries;
|
||||
std::size_t ranges;
|
||||
};
|
||||
struct external_call
|
||||
{
|
||||
command_stream::external_function function;
|
||||
void *data;
|
||||
};
|
||||
// the copies and the blit: the info with its regions
|
||||
template <typename Info_> struct copy_call
|
||||
{
|
||||
@@ -728,6 +734,14 @@ void command_stream::build_acceleration_structures(std::uint32_t const Count, Vk
|
||||
work();
|
||||
}
|
||||
|
||||
void command_stream::external(external_function const Function, void *const Data)
|
||||
{
|
||||
assert(m_rendering == no_call);
|
||||
auto const call{add(call_external, sizeof(external_call))};
|
||||
payload<external_call>(call) = external_call{Function, Data};
|
||||
work();
|
||||
}
|
||||
|
||||
// the replay
|
||||
|
||||
void command_stream::execute(std::size_t const Call, VkCommandBuffer const Buffer, VkRenderingFlags const Flags) const
|
||||
@@ -1015,6 +1029,12 @@ void command_stream::execute(std::size_t const Call, VkCommandBuffer const Buffe
|
||||
vkCmdBuildAccelerationStructuresKHR(Buffer, data.count, infos.data(), ranges.data());
|
||||
break;
|
||||
}
|
||||
case call_external:
|
||||
{
|
||||
auto const &data{payload<external_call>(Call)};
|
||||
data.function(Buffer, data.data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,11 @@ class command_stream
|
||||
void end_debug_utils_label();
|
||||
// the builds of acceleration structures (VK_KHR_acceleration_structure, gfx.raytracing), outside of a rendering
|
||||
void build_acceleration_structures(std::uint32_t Count, VkAccelerationStructureBuildGeometryInfoKHR const *Infos, VkAccelerationStructureBuildRangeInfoKHR const *const *Ranges);
|
||||
// a call of a library recording its own commands (the evaluation of dlss), outside of a rendering: Function gets the command
|
||||
// buffer of the replay and Data, which has to live until the replay is done. what it binds is left as it leaves it, so the
|
||||
// calls after it bind what they use again
|
||||
using external_function = void (*)(VkCommandBuffer Buffer, void *Data);
|
||||
void external(external_function Function, void *Data);
|
||||
|
||||
// the stream is made anew
|
||||
void clear();
|
||||
|
||||
@@ -33,6 +33,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "rendering/vulkan14/vulkan14particles.h"
|
||||
#include "rendering/vulkan14/vulkan14commands.h"
|
||||
#include "rendering/vulkan14/vulkan14raytracing.h"
|
||||
#include "rendering/vulkan14/vulkan14dlss.h"
|
||||
#include "utilities/workerpool.h"
|
||||
#include "model/material.h"
|
||||
#include "gl/ubo.h"
|
||||
@@ -119,6 +120,14 @@ struct viewport_targets
|
||||
std::shared_ptr<image> colour;
|
||||
std::shared_ptr<image> motion_image;
|
||||
std::shared_ptr<image> depth;
|
||||
// the size of the images after the upscaling of dlss (the window), the same as extent without it; dlss is on for the viewport
|
||||
VkExtent2D output_extent{0, 0};
|
||||
bool dlss{false};
|
||||
// dlss (gfx.antialiasing dlss): the velocities it takes (those of the sky made from the motion of the camera), the exposure of the
|
||||
// tonemapping in a texel, and its output, the size of the window (hdr, a storage image)
|
||||
std::shared_ptr<image> dlss_motion;
|
||||
std::shared_ptr<image> dlss_exposure;
|
||||
std::shared_ptr<image> dlss_output;
|
||||
// the colour after the motion blur (hdr)
|
||||
std::shared_ptr<image> blurred;
|
||||
// the temporal anti-aliasing (gfx.antialiasing taa, the main viewport): the two images of the history, written in turn (hdr, the
|
||||
@@ -459,6 +468,9 @@ struct vulkan14_renderer::context
|
||||
vk14::resources resources;
|
||||
vk14::imgui_backend imgui;
|
||||
vk14::scene_state scene;
|
||||
// dlss of the main viewport (gfx.antialiasing dlss), with the bias of the mipmaps of the textures of the scene it wants
|
||||
vk14::dlss dlss;
|
||||
float lod_bias{0.f};
|
||||
// the textures of the files; made after the objects above and gone before them
|
||||
std::unique_ptr<texture_bank> textures;
|
||||
// [0]: the main window, then the windows of the extra viewports
|
||||
|
||||
@@ -301,6 +301,12 @@ void vulkan14_renderer::draw_debug_ui()
|
||||
ImGui::Separator();
|
||||
gfx::exposure_settings::draw_debug_ui(scene.exposure.values);
|
||||
ImGui::Separator();
|
||||
// dlss (gfx.antialiasing dlss), set at the start
|
||||
if (Global.gfx_dlss() || (m_dlssrender.x > 0))
|
||||
{
|
||||
m_context->dlss.draw_debug_ui();
|
||||
ImGui::Separator();
|
||||
}
|
||||
// the ambient occlusion (opengl46_ao::draw_debug_ui()): its images are made with the targets of the viewports
|
||||
if (scene.targets.empty() || (scene.targets.front().ssao == nullptr))
|
||||
{
|
||||
|
||||
@@ -17,10 +17,12 @@ namespace vk14
|
||||
|
||||
// samplers
|
||||
|
||||
bool samplers::create(device const &Device, float Anisotropy, std::string &Reason)
|
||||
bool samplers::create(device const &Device, float Anisotropy, std::string &Reason, float const LodBias)
|
||||
{
|
||||
auto const handle{Device.handle()};
|
||||
auto const maxanisotropy{Device.caps().limits.maxSamplerAnisotropy};
|
||||
auto const maxbias{Device.caps().limits.maxSamplerLodBias};
|
||||
auto const lodbias{std::clamp(LodBias, -maxbias, maxbias)};
|
||||
VkFormatProperties depthformat{};
|
||||
vkGetPhysicalDeviceFormatProperties(Device.physical(), VK_FORMAT_D32_SFLOAT, &depthformat);
|
||||
auto const depthlinear{(depthformat.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0};
|
||||
@@ -39,6 +41,12 @@ bool samplers::create(device const &Device, float Anisotropy, std::string &Reaso
|
||||
info.anisotropyEnable = VK_TRUE;
|
||||
info.maxAnisotropy = std::min(Aniso, maxanisotropy);
|
||||
}
|
||||
// the textures of the scene (the mipmapped samplers with the anisotropy of the settings), sampled as at the size of the
|
||||
// window when the scene is drawn smaller for dlss
|
||||
if ((&Sampler == &repeat_aniso) || (&Sampler == &clamp_aniso))
|
||||
{
|
||||
info.mipLodBias = lodbias;
|
||||
}
|
||||
// the border of the clamped samplers: the farthest depth of the reverse z, black
|
||||
info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||
if (Compare)
|
||||
|
||||
@@ -35,7 +35,8 @@ struct samplers
|
||||
// the device filters the depth format, the border the farthest depth (0)
|
||||
VkSampler depth{VK_NULL_HANDLE};
|
||||
|
||||
bool create(device const &Device, float Anisotropy, std::string &Reason);
|
||||
// LodBias: the bias of the mipmaps of the textures of the scene (the anisotropic ones), for dlss
|
||||
bool create(device const &Device, float Anisotropy, std::string &Reason, float LodBias = 0.f);
|
||||
void destroy(VkDevice Device);
|
||||
};
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ bool device::select(VkInstance Instance, VkSurfaceKHR Surface, std::string const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device::create(instance const &Instance, std::string &Reason)
|
||||
bool device::create(instance const &Instance, std::string &Reason, std::vector<std::string> const &Wanted)
|
||||
{
|
||||
if (m_device != VK_NULL_HANDLE)
|
||||
{
|
||||
@@ -271,6 +271,9 @@ bool device::create(instance const &Instance, std::string &Reason)
|
||||
|
||||
// everything else the renderer uses is in the core of vulkan 1.4
|
||||
std::vector<char const *> extensions{VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||
// the names asked for by a library, kept for the call which makes the device
|
||||
std::vector<std::string> wantednames;
|
||||
wantednames.reserve(Wanted.size());
|
||||
{
|
||||
// a device of the portability subset (vulkan on metal) has to be told the program knows it
|
||||
std::uint32_t count{0};
|
||||
@@ -288,6 +291,38 @@ bool device::create(instance const &Instance, std::string &Reason)
|
||||
{
|
||||
extensions.push_back(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
|
||||
}
|
||||
// what a library asks for (dlss, with the features of vulkan 1.2 it takes for granted). the buffer device address of ext
|
||||
// can't go with the one of the core, it's taken as the extension of khr
|
||||
for (auto name : Wanted)
|
||||
{
|
||||
if (name == VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)
|
||||
{
|
||||
name = VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME;
|
||||
}
|
||||
if (std::any_of(extensions.begin(), extensions.end(), [&](char const *Entry) { return name == Entry; }))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (std::none_of(available.begin(), available.end(), [&](VkExtensionProperties const &Entry) { return name == Entry.extensionName; }))
|
||||
{
|
||||
log_warning("the device extension " + name + " asked for isn't available");
|
||||
continue;
|
||||
}
|
||||
if (std::find(wantednames.begin(), wantednames.end(), name) == wantednames.end())
|
||||
{
|
||||
wantednames.push_back(name);
|
||||
}
|
||||
}
|
||||
for (auto const &name : wantednames)
|
||||
{
|
||||
extensions.push_back(name.c_str());
|
||||
}
|
||||
if (false == Wanted.empty())
|
||||
{
|
||||
enabled.v12.timelineSemaphore = m_queried.v12.timelineSemaphore;
|
||||
enabled.v12.descriptorIndexing = m_queried.v12.descriptorIndexing;
|
||||
enabled.v12.bufferDeviceAddress = m_queried.v12.bufferDeviceAddress;
|
||||
}
|
||||
}
|
||||
|
||||
// the ray traced reflections, asked for in eu07.ini (gfx.raytracing), where the device has what they need
|
||||
|
||||
@@ -48,7 +48,8 @@ class device
|
||||
// glfw). Verbose: every gpu and why it can't be used goes to the log. false: no usable gpu, Reason tells why
|
||||
bool select(VkInstance Instance, VkSurfaceKHR Surface, std::string const &Wanted, bool const Verbose, std::string &Reason);
|
||||
// makes the VkDevice of the gpu picked by select()
|
||||
bool create(instance const &Instance, std::string &Reason);
|
||||
// Wanted: the extensions a library asks for (dlss), taken where the device has them, with the features of vulkan 1.2 it uses
|
||||
bool create(instance const &Instance, std::string &Reason, std::vector<std::string> const &Wanted = {});
|
||||
void destroy();
|
||||
|
||||
VkPhysicalDevice physical() const
|
||||
|
||||
776
rendering/vulkan14/vulkan14dlss.cpp
Normal file
776
rendering/vulkan14/vulkan14dlss.cpp
Normal file
@@ -0,0 +1,776 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "rendering/vulkan14/vulkan14dlss.h"
|
||||
|
||||
#include "rendering/vulkan14/vulkan14commands.h"
|
||||
#include "rendering/vulkan14/vulkan14memory.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#if defined(WITH_DLSS)
|
||||
#include <nvsdk_ngx_helpers.h>
|
||||
#include <nvsdk_ngx_helpers_vk.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <system_error>
|
||||
|
||||
#if defined(WITH_DLSS) && !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
#if defined(WITH_DLSS)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// the identifier of the program for ngx (a project of an engine of its own): anything shaped like a guid
|
||||
char const *const project_id{"5b3e2f0a-8c4d-4e6b-9a71-2d0f6c8e1a37"};
|
||||
char const *const engine_version{"1.0"};
|
||||
|
||||
// the modes upscaling from less than the full size, from the largest part of the window to the smallest
|
||||
NVSDK_NGX_PerfQuality_Value const upscaling_modes[]{NVSDK_NGX_PerfQuality_Value_MaxQuality, NVSDK_NGX_PerfQuality_Value_Balanced, NVSDK_NGX_PerfQuality_Value_MaxPerf,
|
||||
NVSDK_NGX_PerfQuality_Value_UltraPerformance};
|
||||
|
||||
char const *mode_name(int const Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case NVSDK_NGX_PerfQuality_Value_MaxPerf:
|
||||
return "performance";
|
||||
case NVSDK_NGX_PerfQuality_Value_Balanced:
|
||||
return "balanced";
|
||||
case NVSDK_NGX_PerfQuality_Value_MaxQuality:
|
||||
return "quality";
|
||||
case NVSDK_NGX_PerfQuality_Value_UltraPerformance:
|
||||
return "ultra performance";
|
||||
case NVSDK_NGX_PerfQuality_Value_UltraQuality:
|
||||
return "ultra quality";
|
||||
case NVSDK_NGX_PerfQuality_Value_DLAA:
|
||||
return "dlaa";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
char const *preset_name(int const Preset)
|
||||
{
|
||||
switch (Preset)
|
||||
{
|
||||
case NVSDK_NGX_DLSS_Hint_Render_Preset_J:
|
||||
return "j";
|
||||
case NVSDK_NGX_DLSS_Hint_Render_Preset_K:
|
||||
return "k";
|
||||
case NVSDK_NGX_DLSS_Hint_Render_Preset_L:
|
||||
return "l";
|
||||
case NVSDK_NGX_DLSS_Hint_Render_Preset_M:
|
||||
return "m";
|
||||
default:
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
|
||||
// the code of a result of ngx with its text (the text is ascii)
|
||||
std::string result_text(NVSDK_NGX_Result const Result)
|
||||
{
|
||||
char code[16];
|
||||
std::snprintf(code, sizeof(code), "0x%08x", static_cast<unsigned int>(Result));
|
||||
std::string text{code};
|
||||
if (auto const *wide{GetNGXResultAsString(Result)})
|
||||
{
|
||||
text += " (";
|
||||
for (auto const *c{wide}; *c != 0; ++c)
|
||||
{
|
||||
text += (*c < 128 ? static_cast<char>(*c) : '?');
|
||||
}
|
||||
text += ")";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// the directory of the program, where the libraries of the features are looked for first
|
||||
std::filesystem::path program_directory()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
wchar_t path[MAX_PATH];
|
||||
auto const length{::GetModuleFileNameW(nullptr, path, MAX_PATH)};
|
||||
if ((length == 0) || (length >= MAX_PATH))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return std::filesystem::path(path).parent_path();
|
||||
#else
|
||||
char path[4096];
|
||||
auto const length{::readlink("/proc/self/exe", path, sizeof(path) - 1)};
|
||||
if (length <= 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
path[length] = '\0';
|
||||
return std::filesystem::path(path).parent_path();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct dlss::state
|
||||
{
|
||||
// ngx: where it writes its logs, where it looks for the libraries besides the directory of the program
|
||||
std::wstring data_path;
|
||||
std::vector<std::wstring> paths;
|
||||
std::vector<wchar_t *> path_list;
|
||||
NVSDK_NGX_FeatureCommonInfo common{};
|
||||
|
||||
VkInstance instance{VK_NULL_HANDLE};
|
||||
VkPhysicalDevice physical{VK_NULL_HANDLE};
|
||||
VkDevice device{VK_NULL_HANDLE};
|
||||
bool initialized{false};
|
||||
bool available{false};
|
||||
// the parameters given to the feature, with the capabilities of the driver in them
|
||||
NVSDK_NGX_Parameter *parameters{nullptr};
|
||||
|
||||
// the feature: the one the frames ask for and the one made
|
||||
struct feature
|
||||
{
|
||||
glm::ivec2 render{0};
|
||||
glm::ivec2 create{0}; // the size the feature is made for, at least the size it's given
|
||||
glm::ivec2 output{0};
|
||||
int mode{-1};
|
||||
int flags{0};
|
||||
int preset{0};
|
||||
bool operator==(feature const &Other) const
|
||||
{
|
||||
return (render == Other.render) && (create == Other.create) && (output == Other.output) && (mode == Other.mode) && (flags == Other.flags) && (preset == Other.preset);
|
||||
}
|
||||
};
|
||||
feature wanted;
|
||||
feature made;
|
||||
NVSDK_NGX_Handle *handle{nullptr};
|
||||
|
||||
// the sizes planned at the start
|
||||
glm::ivec2 plan_render{0};
|
||||
glm::ivec2 plan_create{0};
|
||||
glm::ivec2 plan_output{0};
|
||||
int plan_mode{-1};
|
||||
|
||||
// what the replay of the frame evaluates. written by the main thread while it records the frame, read by the replay: the
|
||||
// main thread waits for the replay of the frame before it records the next one
|
||||
struct resource
|
||||
{
|
||||
VkImage image{VK_NULL_HANDLE};
|
||||
VkImageView view{VK_NULL_HANDLE};
|
||||
VkFormat format{VK_FORMAT_UNDEFINED};
|
||||
VkImageAspectFlags aspect{VK_IMAGE_ASPECT_COLOR_BIT};
|
||||
std::uint32_t width{0};
|
||||
std::uint32_t height{0};
|
||||
bool valid{false};
|
||||
};
|
||||
resource colour, depth, motion, exposure, output;
|
||||
glm::vec2 jitter{0.f};
|
||||
bool reset{false};
|
||||
float frame_time{0.f};
|
||||
|
||||
// set by the replay, read by the main thread once it waited for it (sync())
|
||||
bool failed{false};
|
||||
std::string error;
|
||||
unsigned long long memory{0};
|
||||
unsigned int evaluations{0};
|
||||
// what the main thread shows: taken from the above by sync(), as the statistics are made while the next frame is replayed
|
||||
bool seen_failed{false};
|
||||
bool error_told{false};
|
||||
std::string seen_error;
|
||||
feature seen_feature;
|
||||
unsigned long long seen_memory{0};
|
||||
|
||||
// the debug window: the offsets of the jitter turned the other way, the history dropped
|
||||
bool flip_x{false};
|
||||
bool flip_y{false};
|
||||
bool reset_wanted{false};
|
||||
|
||||
NVSDK_NGX_FeatureDiscoveryInfo discovery() const
|
||||
{
|
||||
NVSDK_NGX_FeatureDiscoveryInfo info{};
|
||||
info.SDKVersion = NVSDK_NGX_Version_API;
|
||||
info.FeatureID = NVSDK_NGX_Feature_SuperSampling;
|
||||
info.Identifier.IdentifierType = NVSDK_NGX_Application_Identifier_Type_Project_Id;
|
||||
info.Identifier.v.ProjectDesc.ProjectId = project_id;
|
||||
info.Identifier.v.ProjectDesc.EngineType = NVSDK_NGX_ENGINE_TYPE_CUSTOM;
|
||||
info.Identifier.v.ProjectDesc.EngineVersion = engine_version;
|
||||
info.ApplicationDataPath = data_path.c_str();
|
||||
info.FeatureInfo = &common;
|
||||
return info;
|
||||
}
|
||||
|
||||
void prepare_paths()
|
||||
{
|
||||
auto logs{user_config_path("ngx")};
|
||||
std::error_code error;
|
||||
if (false == logs.empty())
|
||||
{
|
||||
std::filesystem::create_directories(logs, error);
|
||||
}
|
||||
data_path = ((logs.empty() || error) ? std::filesystem::path(".") : logs).wstring();
|
||||
paths.clear();
|
||||
path_list.clear();
|
||||
if (Global.gfx_dlss_developer)
|
||||
{
|
||||
// the development library, with the indicator and the keys of the debug views
|
||||
auto const directory{program_directory()};
|
||||
paths.push_back(((directory.empty() ? std::filesystem::path(".") : directory) / "ngx_dev").wstring());
|
||||
}
|
||||
for (auto &path : paths)
|
||||
{
|
||||
path_list.push_back(path.data());
|
||||
}
|
||||
common = NVSDK_NGX_FeatureCommonInfo{};
|
||||
common.PathListInfo.Path = path_list.empty() ? nullptr : path_list.data();
|
||||
common.PathListInfo.Length = static_cast<unsigned int>(path_list.size());
|
||||
}
|
||||
|
||||
void fail(std::string const &Error)
|
||||
{
|
||||
if (error.empty())
|
||||
{
|
||||
error = Error;
|
||||
}
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// a failed frame: the output black instead of what was in its memory, which the post-processing (the exposure too) reads
|
||||
void clear_output(VkCommandBuffer const Buffer) const
|
||||
{
|
||||
if (false == output.valid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
VkClearColorValue const black{{0.f, 0.f, 0.f, 0.f}};
|
||||
VkImageSubresourceRange const range{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
vkCmdClearColorImage(Buffer, output.image, VK_IMAGE_LAYOUT_GENERAL, &black, 1, &range);
|
||||
VkMemoryBarrier2 barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_2_CLEAR_BIT;
|
||||
barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_TRANSFER_READ_BIT;
|
||||
VkDependencyInfo dependency{};
|
||||
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
dependency.memoryBarrierCount = 1;
|
||||
dependency.pMemoryBarriers = &barrier;
|
||||
vkCmdPipelineBarrier2(Buffer, &dependency);
|
||||
}
|
||||
|
||||
static NVSDK_NGX_Resource_VK wrap(resource const &Resource, bool const Write)
|
||||
{
|
||||
VkImageSubresourceRange const range{Resource.aspect, 0, 1, 0, 1};
|
||||
return NVSDK_NGX_Create_ImageView_Resource_VK(Resource.view, Resource.image, range, Resource.format, Resource.width, Resource.height, Write);
|
||||
}
|
||||
|
||||
// the replay of the frame: the feature made when it isn't there, then evaluated
|
||||
static void record(VkCommandBuffer const Buffer, void *const Data)
|
||||
{
|
||||
auto &self{*static_cast<state *>(Data)};
|
||||
if (self.failed || (self.parameters == nullptr))
|
||||
{
|
||||
self.clear_output(Buffer);
|
||||
return;
|
||||
}
|
||||
if (self.handle == nullptr)
|
||||
{
|
||||
for (auto const *hint : {NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_DLAA, NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_Quality, NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_Balanced,
|
||||
NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_Performance, NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_UltraPerformance})
|
||||
{
|
||||
NVSDK_NGX_Parameter_SetUI(self.parameters, hint, static_cast<unsigned int>(self.wanted.preset));
|
||||
}
|
||||
NVSDK_NGX_DLSS_Create_Params create{};
|
||||
create.Feature.InWidth = static_cast<unsigned int>(self.wanted.create.x);
|
||||
create.Feature.InHeight = static_cast<unsigned int>(self.wanted.create.y);
|
||||
create.Feature.InTargetWidth = static_cast<unsigned int>(self.wanted.output.x);
|
||||
create.Feature.InTargetHeight = static_cast<unsigned int>(self.wanted.output.y);
|
||||
create.Feature.InPerfQualityValue = static_cast<NVSDK_NGX_PerfQuality_Value>(self.wanted.mode);
|
||||
create.InFeatureCreateFlags = self.wanted.flags;
|
||||
create.InEnableOutputSubrects = false;
|
||||
auto const result{NGX_VULKAN_CREATE_DLSS_EXT1(self.device, Buffer, 1, 1, &self.handle, self.parameters, &create)};
|
||||
if (NVSDK_NGX_FAILED(result) || (self.handle == nullptr))
|
||||
{
|
||||
self.handle = nullptr;
|
||||
self.fail("the feature of dlss can't be made: " + result_text(result));
|
||||
self.clear_output(Buffer);
|
||||
return;
|
||||
}
|
||||
self.made = self.wanted;
|
||||
}
|
||||
auto colour{wrap(self.colour, false)};
|
||||
auto depth{wrap(self.depth, false)};
|
||||
auto motion{wrap(self.motion, false)};
|
||||
auto exposure{wrap(self.exposure, false)};
|
||||
auto output{wrap(self.output, true)};
|
||||
NVSDK_NGX_VK_DLSS_Eval_Params evaluation{};
|
||||
evaluation.Feature.pInColor = &colour;
|
||||
evaluation.Feature.pInOutput = &output;
|
||||
evaluation.pInDepth = &depth;
|
||||
evaluation.pInMotionVectors = &motion;
|
||||
evaluation.pInExposureTexture = (self.exposure.valid ? &exposure : nullptr);
|
||||
evaluation.InJitterOffsetX = self.jitter.x;
|
||||
evaluation.InJitterOffsetY = self.jitter.y;
|
||||
evaluation.InRenderSubrectDimensions = {self.colour.width, self.colour.height};
|
||||
evaluation.InReset = (self.reset ? 1 : 0);
|
||||
// the velocities are parts of the image, dlss takes pixels of the colour
|
||||
evaluation.InMVScaleX = static_cast<float>(self.colour.width);
|
||||
evaluation.InMVScaleY = static_cast<float>(self.colour.height);
|
||||
evaluation.InPreExposure = 1.f;
|
||||
evaluation.InExposureScale = 1.f;
|
||||
// the rows of the images go from the bottom of the picture up (decision K8): the indicator of the library the right way up
|
||||
evaluation.InIndicatorInvertYAxis = 1;
|
||||
evaluation.InFrameTimeDeltaInMsec = self.frame_time;
|
||||
auto const result{NGX_VULKAN_EVALUATE_DLSS_EXT(Buffer, self.handle, self.parameters, &evaluation)};
|
||||
if (NVSDK_NGX_FAILED(result))
|
||||
{
|
||||
self.fail("dlss can't be evaluated: " + result_text(result));
|
||||
self.clear_output(Buffer);
|
||||
return;
|
||||
}
|
||||
// the memory of the features now and then, for the statistics (ngx isn't to be called from two threads at once)
|
||||
if ((self.evaluations++ % 120) == 0)
|
||||
{
|
||||
unsigned long long bytes{0};
|
||||
if (NVSDK_NGX_SUCCEED(NGX_DLSS_GET_STATS(self.parameters, &bytes)))
|
||||
{
|
||||
self.memory = bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void release(deletion_queue *Deletions, std::uint64_t const Value)
|
||||
{
|
||||
if (handle == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto *old{handle};
|
||||
handle = nullptr;
|
||||
made = feature{};
|
||||
if (Deletions != nullptr)
|
||||
{
|
||||
Deletions->push(Value, [old]() { NVSDK_NGX_VULKAN_ReleaseFeature(old); });
|
||||
}
|
||||
else
|
||||
{
|
||||
NVSDK_NGX_VULKAN_ReleaseFeature(old);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dlss::dlss() : m_state{std::make_unique<state>()} {}
|
||||
|
||||
dlss::~dlss()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool dlss::compiled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void dlss::instance_extensions(std::vector<std::string> &Extensions)
|
||||
{
|
||||
state temporary;
|
||||
temporary.prepare_paths();
|
||||
auto const info{temporary.discovery()};
|
||||
std::uint32_t count{0};
|
||||
VkExtensionProperties *properties{nullptr};
|
||||
auto const result{NVSDK_NGX_VULKAN_GetFeatureInstanceExtensionRequirements(&info, &count, &properties)};
|
||||
if (NVSDK_NGX_FAILED(result))
|
||||
{
|
||||
log_warning("dlss: the instance extensions it needs can't be had from ngx: " + result_text(result));
|
||||
return;
|
||||
}
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
Extensions.emplace_back(properties[i].extensionName);
|
||||
}
|
||||
}
|
||||
|
||||
void dlss::device_extensions(VkInstance const Instance, VkPhysicalDevice const Physical, std::vector<std::string> &Extensions)
|
||||
{
|
||||
state temporary;
|
||||
temporary.prepare_paths();
|
||||
auto const info{temporary.discovery()};
|
||||
std::uint32_t count{0};
|
||||
VkExtensionProperties *properties{nullptr};
|
||||
auto const result{NVSDK_NGX_VULKAN_GetFeatureDeviceExtensionRequirements(Instance, Physical, &info, &count, &properties)};
|
||||
if (NVSDK_NGX_FAILED(result))
|
||||
{
|
||||
log_warning("dlss: the device extensions it needs can't be had from ngx: " + result_text(result));
|
||||
return;
|
||||
}
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
Extensions.emplace_back(properties[i].extensionName);
|
||||
}
|
||||
}
|
||||
|
||||
bool dlss::init(VkInstance const Instance, VkPhysicalDevice const Physical, VkDevice const Device, std::string &Reason)
|
||||
{
|
||||
auto &s{*m_state};
|
||||
if (s.initialized)
|
||||
{
|
||||
return s.available;
|
||||
}
|
||||
s.prepare_paths();
|
||||
s.instance = Instance;
|
||||
s.physical = Physical;
|
||||
s.device = Device;
|
||||
auto const result{NVSDK_NGX_VULKAN_Init_with_ProjectID(project_id, NVSDK_NGX_ENGINE_TYPE_CUSTOM, engine_version, s.data_path.c_str(), Instance, Physical, Device, vkGetInstanceProcAddr, vkGetDeviceProcAddr, &s.common,
|
||||
NVSDK_NGX_Version_API)};
|
||||
if (NVSDK_NGX_FAILED(result))
|
||||
{
|
||||
Reason = "ngx can't start: " + result_text(result);
|
||||
return false;
|
||||
}
|
||||
s.initialized = true;
|
||||
// the parameters with the capabilities in them (the drivers before 450 don't give them this way, and are too old anyway)
|
||||
auto const capabilities{NVSDK_NGX_VULKAN_GetCapabilityParameters(&s.parameters)};
|
||||
if (NVSDK_NGX_FAILED(capabilities) || (s.parameters == nullptr))
|
||||
{
|
||||
s.parameters = nullptr;
|
||||
Reason = "ngx gives no parameters, the graphics driver is probably too old: " + result_text(capabilities);
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
int updatedriver{0};
|
||||
if (NVSDK_NGX_SUCCEED(s.parameters->Get(NVSDK_NGX_Parameter_SuperSampling_NeedsUpdatedDriver, &updatedriver)) && (updatedriver != 0))
|
||||
{
|
||||
unsigned int major{0}, minor{0};
|
||||
s.parameters->Get(NVSDK_NGX_Parameter_SuperSampling_MinDriverVersionMajor, &major);
|
||||
s.parameters->Get(NVSDK_NGX_Parameter_SuperSampling_MinDriverVersionMinor, &minor);
|
||||
Reason = "the graphics driver is too old for dlss, it needs " + std::to_string(major) + "." + std::to_string(minor) + " or newer";
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
int supported{0};
|
||||
if (NVSDK_NGX_FAILED(s.parameters->Get(NVSDK_NGX_Parameter_SuperSampling_Available, &supported)) || (supported == 0))
|
||||
{
|
||||
Reason = "dlss isn't available on this gpu (it needs a geforce rtx) or with this driver";
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
supported = 0;
|
||||
if (NVSDK_NGX_FAILED(s.parameters->Get(NVSDK_NGX_Parameter_SuperSampling_FeatureInitResult, &supported)) || (supported == 0))
|
||||
{
|
||||
Reason = "dlss is denied to the program by the driver";
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
s.available = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void dlss::shutdown()
|
||||
{
|
||||
auto &s{*m_state};
|
||||
if (false == s.initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// the gpu is idle: the feature goes at once
|
||||
s.release(nullptr, 0);
|
||||
if (s.parameters != nullptr)
|
||||
{
|
||||
NVSDK_NGX_VULKAN_DestroyParameters(s.parameters);
|
||||
}
|
||||
s.parameters = nullptr;
|
||||
NVSDK_NGX_VULKAN_Shutdown1(s.device);
|
||||
s.initialized = false;
|
||||
s.available = false;
|
||||
}
|
||||
|
||||
bool dlss::available() const
|
||||
{
|
||||
return m_state->available;
|
||||
}
|
||||
|
||||
bool dlss::plan(glm::ivec2 const &Output, float const Scale, glm::ivec2 &Render)
|
||||
{
|
||||
auto &s{*m_state};
|
||||
// the smallest window dlss upscales to
|
||||
if ((false == s.available) || (Output.x < 64) || (Output.y < 32))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (Scale >= 0.999f)
|
||||
{
|
||||
s.plan_render = Output;
|
||||
s.plan_create = Output;
|
||||
s.plan_output = Output;
|
||||
s.plan_mode = NVSDK_NGX_PerfQuality_Value_DLAA;
|
||||
Render = Output;
|
||||
log_info("dlss: dlaa, the scene drawn at the size of the window, " + std::to_string(Output.x) + "x" + std::to_string(Output.y));
|
||||
return true;
|
||||
}
|
||||
glm::ivec2 const wanted{glm::max(glm::ivec2(glm::round(glm::vec2(Output) * Scale)), glm::ivec2(1))};
|
||||
auto bestmode{-1};
|
||||
glm::ivec2 best{0};
|
||||
glm::ivec2 bestoptimal{0};
|
||||
auto bestinside{false};
|
||||
auto bestdistance{1e9f};
|
||||
for (auto const mode : upscaling_modes)
|
||||
{
|
||||
unsigned int optimalwidth{0}, optimalheight{0}, maxwidth{0}, maxheight{0}, minwidth{0}, minheight{0};
|
||||
float sharpness{0.f};
|
||||
auto const result{NGX_DLSS_GET_OPTIMAL_SETTINGS(s.parameters, static_cast<unsigned int>(Output.x), static_cast<unsigned int>(Output.y), mode, &optimalwidth, &optimalheight, &maxwidth, &maxheight, &minwidth,
|
||||
&minheight, &sharpness)};
|
||||
if (NVSDK_NGX_FAILED(result) || (optimalwidth == 0) || (optimalheight == 0))
|
||||
{
|
||||
// a mode not there for this size
|
||||
continue;
|
||||
}
|
||||
glm::ivec2 const low{static_cast<int>(minwidth), static_cast<int>(minheight)};
|
||||
glm::ivec2 const high{static_cast<int>(std::max(maxwidth, minwidth)), static_cast<int>(std::max(maxheight, minheight))};
|
||||
auto const inside{(wanted.x >= low.x) && (wanted.y >= low.y) && (wanted.x <= high.x) && (wanted.y <= high.y)};
|
||||
auto const distance{std::abs(static_cast<float>(optimalwidth) / static_cast<float>(Output.x) - Scale)};
|
||||
// a mode the size fits in goes before one it has to be brought into, then the one whose own size is nearest
|
||||
if ((bestmode < 0) || (inside && (false == bestinside)) || ((inside == bestinside) && (distance < bestdistance)))
|
||||
{
|
||||
bestmode = mode;
|
||||
// brought into the range of the mode by one scale for both sides, which keeps the shape of the window
|
||||
auto const lowscale{std::max(static_cast<float>(low.x) / static_cast<float>(Output.x), static_cast<float>(low.y) / static_cast<float>(Output.y))};
|
||||
auto const highscale{std::min(static_cast<float>(high.x) / static_cast<float>(Output.x), static_cast<float>(high.y) / static_cast<float>(Output.y))};
|
||||
auto const scale{std::clamp(Scale, std::min(lowscale, highscale), highscale)};
|
||||
best = inside ? wanted : glm::clamp(glm::ivec2(glm::round(glm::vec2(Output) * scale)), low, high);
|
||||
bestoptimal = {static_cast<int>(optimalwidth), static_cast<int>(optimalheight)};
|
||||
bestinside = inside;
|
||||
bestdistance = distance;
|
||||
}
|
||||
}
|
||||
if (bestmode < 0)
|
||||
{
|
||||
log_warning("dlss: no mode of it upscales to " + std::to_string(Output.x) + "x" + std::to_string(Output.y));
|
||||
return false;
|
||||
}
|
||||
s.plan_render = best;
|
||||
// the feature made for the size the guide gives for the mode (3.2.2), or for the size drawn when that's larger: the presets l and
|
||||
// m make their buffers for the size given at the creation, and make them anew for a larger image (3.2.2.1)
|
||||
s.plan_create = glm::max(bestoptimal, best);
|
||||
s.plan_output = Output;
|
||||
s.plan_mode = bestmode;
|
||||
Render = best;
|
||||
log_info("dlss: the scene drawn at " + std::to_string(best.x) + "x" + std::to_string(best.y) + " for the window of " + std::to_string(Output.x) + "x" + std::to_string(Output.y) + ", mode " + mode_name(bestmode) +
|
||||
(bestinside ? std::string{} : ", gfx.buffer.percent brought into the range of the mode"));
|
||||
return true;
|
||||
}
|
||||
|
||||
float dlss::lod_bias() const
|
||||
{
|
||||
auto const &s{*m_state};
|
||||
if ((s.plan_output.x <= 0) || (s.plan_render.x <= 0))
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
// the textures sampled as at the size of the window (the dlss programming guide, 3.5)
|
||||
return std::log2(static_cast<float>(s.plan_render.x) / static_cast<float>(s.plan_output.x)) - 1.f + Global.gfx_dlss_lodbias;
|
||||
}
|
||||
|
||||
std::uint32_t dlss::jitter_phases() const
|
||||
{
|
||||
auto const &s{*m_state};
|
||||
if ((s.plan_output.x <= 0) || (s.plan_render.x <= 0))
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
// 8 offsets for each pixel of the window (the dlss programming guide, 3.7.1.1)
|
||||
auto const ratio{static_cast<float>(s.plan_output.x) / static_cast<float>(s.plan_render.x)};
|
||||
return static_cast<std::uint32_t>(std::clamp(std::ceil(8.f * ratio * ratio), 8.f, 128.f));
|
||||
}
|
||||
|
||||
void dlss::evaluate(command_stream &Commands, frame_inputs const &Inputs, deletion_queue &Deletions, std::uint64_t const Value)
|
||||
{
|
||||
auto &s{*m_state};
|
||||
if ((false == s.available) || s.failed || (Inputs.colour == nullptr) || (Inputs.depth == nullptr) || (Inputs.motion == nullptr) || (Inputs.output == nullptr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
state::feature feature;
|
||||
feature.render = {static_cast<int>(Inputs.colour->desc().extent.width), static_cast<int>(Inputs.colour->desc().extent.height)};
|
||||
feature.output = {static_cast<int>(Inputs.output->desc().extent.width), static_cast<int>(Inputs.output->desc().extent.height)};
|
||||
auto const planned{(feature.render == s.plan_render) && (feature.output == s.plan_output)};
|
||||
feature.mode = (feature.render == feature.output) ? static_cast<int>(NVSDK_NGX_PerfQuality_Value_DLAA) : planned ? s.plan_mode : static_cast<int>(NVSDK_NGX_PerfQuality_Value_MaxQuality);
|
||||
feature.create = planned ? s.plan_create : feature.render;
|
||||
// the colour is linear (hdr), the velocities have the size of the colour, the depth is reverse z
|
||||
feature.flags = NVSDK_NGX_DLSS_Feature_Flags_IsHDR | NVSDK_NGX_DLSS_Feature_Flags_MVLowRes | NVSDK_NGX_DLSS_Feature_Flags_DepthInverted | (Inputs.exposure == nullptr ? NVSDK_NGX_DLSS_Feature_Flags_AutoExposure : 0);
|
||||
feature.preset = Global.gfx_dlss_preset;
|
||||
if ((s.handle != nullptr) && (false == (feature == s.made)))
|
||||
{
|
||||
s.release(&Deletions, Value);
|
||||
}
|
||||
s.wanted = feature;
|
||||
auto const describe = [](image const *Image, state::resource &Resource) {
|
||||
Resource.valid = (Image != nullptr);
|
||||
if (Image == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Resource.image = Image->handle();
|
||||
Resource.view = Image->view();
|
||||
Resource.format = Image->desc().format;
|
||||
Resource.aspect = Image->aspect();
|
||||
Resource.width = Image->desc().extent.width;
|
||||
Resource.height = Image->desc().extent.height;
|
||||
};
|
||||
describe(Inputs.colour, s.colour);
|
||||
describe(Inputs.depth, s.depth);
|
||||
describe(Inputs.motion, s.motion);
|
||||
describe(Inputs.exposure, s.exposure);
|
||||
describe(Inputs.output, s.output);
|
||||
s.jitter = Inputs.jitter * glm::vec2{s.flip_x ? -1.f : 1.f, s.flip_y ? -1.f : 1.f};
|
||||
s.reset = Inputs.reset || s.reset_wanted;
|
||||
s.reset_wanted = false;
|
||||
s.frame_time = Inputs.frame_time;
|
||||
Commands.external(&state::record, &s);
|
||||
}
|
||||
|
||||
std::string dlss::sync()
|
||||
{
|
||||
auto &s{*m_state};
|
||||
s.seen_failed = s.failed;
|
||||
s.seen_error = s.error;
|
||||
s.seen_feature = ((s.handle != nullptr) ? s.made : s.wanted);
|
||||
s.seen_memory = s.memory;
|
||||
if ((false == s.failed) || s.error_told)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
s.error_told = true;
|
||||
return s.error;
|
||||
}
|
||||
|
||||
bool dlss::failed() const
|
||||
{
|
||||
return m_state->seen_failed;
|
||||
}
|
||||
|
||||
std::string dlss::info() const
|
||||
{
|
||||
auto const &s{*m_state};
|
||||
if (false == s.available)
|
||||
{
|
||||
return "dlss: not available";
|
||||
}
|
||||
if (s.seen_failed)
|
||||
{
|
||||
return "dlss: off after an error (" + s.seen_error + ")";
|
||||
}
|
||||
auto const &feature{s.seen_feature};
|
||||
if (feature.mode < 0)
|
||||
{
|
||||
return "dlss: ready, not used yet";
|
||||
}
|
||||
// the presets l and m take no exposure, they work it out themselves (the dlss programming guide, 3.9)
|
||||
return std::string("dlss: ") + mode_name(feature.mode) + ", preset " + preset_name(feature.preset) + ", " + std::to_string(feature.render.x) + "x" + std::to_string(feature.render.y) + " to " + std::to_string(feature.output.x) + "x" +
|
||||
std::to_string(feature.output.y) + ((feature.flags & NVSDK_NGX_DLSS_Feature_Flags_AutoExposure) != 0 ? ", its own exposure" : ", given the exposure of the tonemapping") + ", " +
|
||||
std::to_string(s.seen_memory / (1024 * 1024)) + " MiB";
|
||||
}
|
||||
|
||||
void dlss::draw_debug_ui()
|
||||
{
|
||||
auto &s{*m_state};
|
||||
ImGui::TextUnformatted(info().c_str());
|
||||
if (false == s.available)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ImGui::Checkbox("dlss: jitter offset turned the other way along the rows", &s.flip_x);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("along the columns", &s.flip_y);
|
||||
if (ImGui::Button("dlss: drop its history"))
|
||||
{
|
||||
s.reset_wanted = true;
|
||||
}
|
||||
}
|
||||
|
||||
#else // without the sdk: dlss is never available
|
||||
|
||||
struct dlss::state
|
||||
{
|
||||
};
|
||||
|
||||
dlss::dlss() : m_state{std::make_unique<state>()} {}
|
||||
|
||||
dlss::~dlss() = default;
|
||||
|
||||
bool dlss::compiled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void dlss::instance_extensions(std::vector<std::string> &) {}
|
||||
|
||||
void dlss::device_extensions(VkInstance, VkPhysicalDevice, std::vector<std::string> &) {}
|
||||
|
||||
bool dlss::init(VkInstance, VkPhysicalDevice, VkDevice, std::string &Reason)
|
||||
{
|
||||
Reason = "the program was built without the dlss sdk (WITH_DLSS)";
|
||||
return false;
|
||||
}
|
||||
|
||||
void dlss::shutdown() {}
|
||||
|
||||
bool dlss::available() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dlss::plan(glm::ivec2 const &, float, glm::ivec2 &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float dlss::lod_bias() const
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
std::uint32_t dlss::jitter_phases() const
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
void dlss::evaluate(command_stream &, frame_inputs const &, deletion_queue &, std::uint64_t) {}
|
||||
|
||||
std::string dlss::sync()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool dlss::failed() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string dlss::info() const
|
||||
{
|
||||
return "dlss: not in this build (WITH_DLSS)";
|
||||
}
|
||||
|
||||
void dlss::draw_debug_ui()
|
||||
{
|
||||
ImGui::TextUnformatted(info().c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace vk14
|
||||
93
rendering/vulkan14/vulkan14dlss.h
Normal file
93
rendering/vulkan14/vulkan14dlss.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// nvidia dlss (the super resolution of the ngx sdk, built with WITH_DLSS) of the main viewport, gfx.antialiasing dlss: the colour of
|
||||
// the scene with its depth and velocities, drawn at the size gfx.buffer.percent gives, anti-aliased and upscaled to the size of the
|
||||
// window. dlaa is the same at the full size. the sdk isn't part of the repository (tools/fetch_dlss_sdk.cmake, DLSS_SDK_ROOT); without
|
||||
// it dlss is never available and the renderer takes taa
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rendering/vulkan14/vulkan14common.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
class command_stream;
|
||||
class image;
|
||||
class deletion_queue;
|
||||
|
||||
class dlss
|
||||
{
|
||||
public:
|
||||
// what a frame gives it, the images in the states the evaluation takes (the inputs read by the shaders, the output a storage
|
||||
// image in the general layout)
|
||||
struct frame_inputs
|
||||
{
|
||||
image *colour{nullptr}; // hdr, linear, before the exposure
|
||||
image *depth{nullptr}; // reverse z
|
||||
image *motion{nullptr}; // the velocities to the last frame, in parts of the image (uv), without the offset of the frame
|
||||
image *exposure{nullptr}; // 1x1, the exposure the tonemapping applies; null: dlss works it out itself
|
||||
image *output{nullptr}; // the size of the window
|
||||
// the offset of the projection of the frame in pixels of the colour, along its columns and rows
|
||||
glm::vec2 jitter{0.f};
|
||||
bool reset{false}; // the last frames have nothing to do with this one
|
||||
float frame_time{0.f}; // milliseconds
|
||||
};
|
||||
|
||||
dlss();
|
||||
~dlss();
|
||||
dlss(dlss const &) = delete;
|
||||
dlss &operator=(dlss const &) = delete;
|
||||
|
||||
// the program was built with the sdk
|
||||
static bool compiled();
|
||||
// the extensions of the instance and of the device ngx needs; asked for before the instance and the device are made, they are
|
||||
// taken where the driver has them
|
||||
static void instance_extensions(std::vector<std::string> &Extensions);
|
||||
static void device_extensions(VkInstance Instance, VkPhysicalDevice Physical, std::vector<std::string> &Extensions);
|
||||
|
||||
// ngx on the device, and whether the driver and the gpu give dlss. false: Reason tells why
|
||||
bool init(VkInstance Instance, VkPhysicalDevice Physical, VkDevice Device, std::string &Reason);
|
||||
// the gpu is done with everything (the device is idle)
|
||||
void shutdown();
|
||||
bool available() const;
|
||||
// the size the scene is drawn at for the window of the size Output and the part Scale of it (1: dlaa), kept within what dlss takes
|
||||
// for the mode it picks for them. false: dlss can't upscale to Output
|
||||
bool plan(glm::ivec2 const &Output, float Scale, glm::ivec2 &Render);
|
||||
// for the sizes planned: the bias of the mipmaps of the textures of the scene, the number of the offsets the jitter goes through
|
||||
float lod_bias() const;
|
||||
std::uint32_t jitter_phases() const;
|
||||
// the evaluation recorded to Commands (the replay does it on the command buffer of the frame). the feature is made at the first
|
||||
// one and again when the sizes or the settings change; the old one goes once the gpu is past Value of the timeline
|
||||
void evaluate(command_stream &Commands, frame_inputs const &Inputs, deletion_queue &Deletions, std::uint64_t Value);
|
||||
// what the replay of the last frame left (the feature made, an error) taken over for the main thread, once the replay is done
|
||||
// (after the wait for the render thread); what went wrong, the first time, or empty
|
||||
std::string sync();
|
||||
// the feature couldn't be made or evaluated: dlss stays off from then on (as of the last sync())
|
||||
bool failed() const;
|
||||
// the mode and the sizes, for the statistics (as of the last sync())
|
||||
std::string info() const;
|
||||
// the part of the debug window
|
||||
void draw_debug_ui();
|
||||
|
||||
struct state;
|
||||
|
||||
private:
|
||||
std::unique_ptr<state> m_state;
|
||||
};
|
||||
|
||||
} // namespace vk14
|
||||
@@ -265,20 +265,28 @@ void vulkan14_renderer::init_frame_data()
|
||||
{
|
||||
WriteLog("using multisampling x" + std::to_string(1 << Global.iMultisampling));
|
||||
}
|
||||
if (Global.gfx_framebuffer_width == -1)
|
||||
// the image of the main window: a part of the window (gfx.buffer.percent) stretched to it, or with dlss the size it planned,
|
||||
// upscaled to the window
|
||||
if (m_dlssrender.x > 0)
|
||||
{
|
||||
Global.gfx_framebuffer_width = Global.fb_size.x;
|
||||
Global.gfx_framebuffer_width = m_dlssrender.x;
|
||||
Global.gfx_framebuffer_height = m_dlssrender.y;
|
||||
}
|
||||
if (Global.gfx_framebuffer_height == -1)
|
||||
else if ((Global.gfx_framebuffer_width == -1) || (Global.gfx_framebuffer_height == -1))
|
||||
{
|
||||
Global.gfx_framebuffer_height = Global.fb_size.y;
|
||||
auto const size{Global.gfx_buffer_size()};
|
||||
Global.gfx_framebuffer_width = size.x;
|
||||
Global.gfx_framebuffer_height = size.y;
|
||||
}
|
||||
WriteLog("main window size: " + std::to_string(Global.gfx_framebuffer_width) + "x" + std::to_string(Global.gfx_framebuffer_height));
|
||||
WriteLog("main window size: " + std::to_string(Global.fb_size.x) + "x" + std::to_string(Global.fb_size.y) + ", drawn at " + std::to_string(Global.gfx_framebuffer_width) + "x" + std::to_string(Global.gfx_framebuffer_height) +
|
||||
(m_dlssrender.x > 0 ? " for dlss" : ""));
|
||||
|
||||
m_viewports.clear();
|
||||
auto &main{m_viewports.emplace_back()};
|
||||
main.width = Global.gfx_framebuffer_width;
|
||||
main.height = Global.gfx_framebuffer_height;
|
||||
main.output_width = (m_dlssrender.x > 0 ? m_dlssoutput.x : 0);
|
||||
main.output_height = (m_dlssrender.x > 0 ? m_dlssoutput.y : 0);
|
||||
main.main = true;
|
||||
main.shadow = true;
|
||||
main.window = 0;
|
||||
@@ -310,10 +318,10 @@ namespace
|
||||
{
|
||||
|
||||
bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std::uint64_t const Value, std::uint32_t &Formatscount, VkSampleCountFlags const Samplecounts, VkFormat const Bloomformat, VkFormat const Historyformat,
|
||||
vk14::viewport_targets &Targets, int const Width, int const Height, bool const Srgb, bool const Taa, bool const Reflections)
|
||||
vk14::viewport_targets &Targets, int const Width, int const Height, int const OutputWidth, int const OutputHeight, bool const Srgb, bool const Taa, bool const Reflections, bool const Dlss)
|
||||
{
|
||||
// as many samples as asked for, as far as the device has them for the colour and the depth
|
||||
auto samples{static_cast<VkSampleCountFlagBits>(std::clamp(1 << Global.iMultisampling, 1, 8))};
|
||||
// as many samples as asked for, as far as the device has them for the colour and the depth; one for dlss
|
||||
auto samples{static_cast<VkSampleCountFlagBits>(Dlss ? 1 : std::clamp(1 << Global.iMultisampling, 1, 8))};
|
||||
while ((samples > VK_SAMPLE_COUNT_1_BIT) && (0 == (Samplecounts & samples)))
|
||||
{
|
||||
samples = static_cast<VkSampleCountFlagBits>(samples >> 1);
|
||||
@@ -321,8 +329,10 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
|
||||
auto const motion{Global.gfx_motion_vectors()};
|
||||
auto const aberration{Global.gfx_postfx_chromaticaberration_enabled};
|
||||
VkExtent2D const extent{static_cast<std::uint32_t>(std::max(Width, 1)), static_cast<std::uint32_t>(std::max(Height, 1))};
|
||||
if ((Targets.extent.width == extent.width) && (Targets.extent.height == extent.height) && (Targets.samples == samples) && (Targets.motion == motion) && (Targets.taa == Taa) && (Targets.aberration == aberration) &&
|
||||
(Targets.srgb == Srgb) && (Targets.reflections == Reflections) && Targets.output)
|
||||
// the images after the upscaling of dlss have the size of the window, the others that of the scene
|
||||
VkExtent2D const outputextent{Dlss ? VkExtent2D{static_cast<std::uint32_t>(std::max(OutputWidth, 1)), static_cast<std::uint32_t>(std::max(OutputHeight, 1))} : extent};
|
||||
if ((Targets.extent.width == extent.width) && (Targets.extent.height == extent.height) && (Targets.output_extent.width == outputextent.width) && (Targets.output_extent.height == outputextent.height) &&
|
||||
(Targets.samples == samples) && (Targets.motion == motion) && (Targets.taa == Taa) && (Targets.dlss == Dlss) && (Targets.aberration == aberration) && (Targets.srgb == Srgb) && (Targets.reflections == Reflections) && Targets.output)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -333,6 +343,8 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
|
||||
auto const olddepth{Targets.depth_formats};
|
||||
Targets = vk14::viewport_targets{};
|
||||
Targets.extent = extent;
|
||||
Targets.output_extent = outputextent;
|
||||
Targets.dlss = Dlss;
|
||||
Targets.samples = samples;
|
||||
Targets.motion = motion;
|
||||
Targets.taa = Taa;
|
||||
@@ -340,10 +352,10 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
|
||||
Targets.srgb = Srgb;
|
||||
Targets.reflections = Reflections;
|
||||
|
||||
auto make = [&](std::shared_ptr<vk14::image> &Image, VkFormat Format, VkImageUsageFlags Usage, VkSampleCountFlagBits Samples, char const *Name) {
|
||||
auto make = [&](std::shared_ptr<vk14::image> &Image, VkFormat Format, VkImageUsageFlags Usage, VkSampleCountFlagBits Samples, char const *Name, VkExtent2D const Size = VkExtent2D{0, 0}) {
|
||||
vk14::image_desc desc;
|
||||
desc.format = Format;
|
||||
desc.extent = {extent.width, extent.height, 1};
|
||||
desc.extent = {Size.width > 0 ? Size.width : extent.width, Size.height > 0 ? Size.height : extent.height, 1};
|
||||
desc.samples = Samples;
|
||||
desc.usage = Usage;
|
||||
Image = std::make_shared<vk14::image>();
|
||||
@@ -377,7 +389,7 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
|
||||
}
|
||||
if (motion && Global.gfx_postfx_motionblur_enabled)
|
||||
{
|
||||
ok = ok && make(Targets.blurred, hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "motion blurred");
|
||||
ok = ok && make(Targets.blurred, hdr, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "motion blurred", outputextent);
|
||||
}
|
||||
if (Taa)
|
||||
{
|
||||
@@ -402,9 +414,16 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
|
||||
auto const output{Srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM};
|
||||
if (aberration)
|
||||
{
|
||||
ok = ok && make(Targets.tonemapped, output, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "tonemapped");
|
||||
ok = ok && make(Targets.tonemapped, output, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "tonemapped", outputextent);
|
||||
}
|
||||
ok = ok && make(Targets.output, output, sampledcolour | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_SAMPLE_COUNT_1_BIT, "output", outputextent);
|
||||
if (Dlss)
|
||||
{
|
||||
// what dlss takes besides the scene, and its output, which the post-processing reads (and the history of the reflections is
|
||||
// copied from)
|
||||
ok = ok && make(Targets.dlss_motion, velocity, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "dlss velocity") && make(Targets.dlss_exposure, VK_FORMAT_R32_SFLOAT, sampledcolour, VK_SAMPLE_COUNT_1_BIT, "dlss exposure", VkExtent2D{1, 1}) &&
|
||||
make(Targets.dlss_output, hdr, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | historysource, VK_SAMPLE_COUNT_1_BIT, "dlss output", outputextent);
|
||||
}
|
||||
ok = ok && make(Targets.output, output, sampledcolour | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_SAMPLE_COUNT_1_BIT, "output");
|
||||
// the ambient occlusion, half the size, when it's on
|
||||
if (ok && (Global.gfx_postfx_ao != 0))
|
||||
{
|
||||
@@ -431,7 +450,7 @@ bool make_targets(vk14::allocator &Memory, vk14::deletion_queue &Deletions, std:
|
||||
// the bloom chain, made whether the bloom is on or not: it can be switched on in the debug window
|
||||
if (ok)
|
||||
{
|
||||
auto const sizes{gfx::exposure_settings::bloom_sizes(static_cast<int>(extent.width), static_cast<int>(extent.height))};
|
||||
auto const sizes{gfx::exposure_settings::bloom_sizes(static_cast<int>(outputextent.width), static_cast<int>(outputextent.height))};
|
||||
auto makelevel = [&](std::vector<std::shared_ptr<vk14::image>> &Levels, glm::ivec2 const &Size, char const *Name) {
|
||||
vk14::image_desc desc;
|
||||
desc.format = Bloomformat;
|
||||
@@ -516,6 +535,11 @@ bool vulkan14_renderer::Render()
|
||||
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
|
||||
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");
|
||||
}
|
||||
|
||||
// the gpu has to be done with the frame which used the slot before
|
||||
if (frame.done > 0)
|
||||
@@ -816,7 +840,7 @@ bool vulkan14_renderer::Render()
|
||||
{
|
||||
scene.screenshot_wanted = false;
|
||||
auto &output{*scene.targets.front().output};
|
||||
auto const extent{scene.targets.front().extent};
|
||||
VkExtent2D const extent{output.desc().extent.width, output.desc().extent.height};
|
||||
scene.screenshot = std::make_shared<vk14::buffer>();
|
||||
if (scene.screenshot->create(ctx.memory, static_cast<VkDeviceSize>(extent.width) * extent.height * 4, VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::readback, "screenshot"))
|
||||
{
|
||||
@@ -914,17 +938,20 @@ void vulkan14_renderer::render_scene()
|
||||
auto const &viewport{m_viewports[i]};
|
||||
auto const srgb{false == Global.gfx_shadergamma};
|
||||
auto const *history{scene.targets[i].taa_history[0].get()};
|
||||
auto const *upscaled{scene.targets[i].dlss_output.get()};
|
||||
auto const *reflections{scene.targets[i].ssr_history.get()};
|
||||
auto const dlss{viewport.main && dlss_active() && (viewport.output_width > 0) && (viewport.output_height > 0)};
|
||||
if (false == make_targets(ctx.memory, ctx.deletions, ctx.uploads.current_value(), scene.formats_count, ctx.device.caps().limits.framebufferColorSampleCounts & ctx.device.caps().limits.framebufferDepthSampleCounts, scene.bloom_format,
|
||||
scene.history_format, scene.targets[i], viewport.width, viewport.height, srgb, viewport.main && Global.gfx_taa(),
|
||||
viewport.main && (false == Global.gfx_skippipeline) && ((Global.gfx_ssr > 0) || (m_raytracingon && scene.raytracing))))
|
||||
scene.history_format, scene.targets[i], viewport.width, viewport.height, viewport.output_width, viewport.output_height, srgb, viewport.main && Global.gfx_taa(),
|
||||
viewport.main && (false == Global.gfx_skippipeline) && ((Global.gfx_ssr > 0) || (m_raytracingon && scene.raytracing)), dlss))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (viewport.main && (scene.targets[i].taa_history[0].get() != history))
|
||||
if (viewport.main && ((scene.targets[i].taa_history[0].get() != history) || (scene.targets[i].dlss_output.get() != upscaled)))
|
||||
{
|
||||
// new images of the history, nothing in them yet
|
||||
m_taa.reset();
|
||||
m_dlssreset = true;
|
||||
}
|
||||
auto &targets{scene.targets[i]};
|
||||
if (targets.ssr_history.get() != reflections)
|
||||
@@ -1284,12 +1311,15 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
|
||||
m_current_viewport = &vp;
|
||||
|
||||
setup_pass(vp, m_renderpass, rendermode::color);
|
||||
// the temporal anti-aliasing: the projection of the frame moved by a part of a pixel, the velocities pointed to the last frame
|
||||
auto const taa{vp.main && targets.taa && Global.gfx_taa()};
|
||||
// the temporal anti-aliasing or dlss: the projection of the frame moved by a part of a pixel, the velocities pointed to the last
|
||||
// frame. dlss goes through more offsets the smaller the scene is drawn
|
||||
auto const dlss{vp.main && targets.dlss && targets.dlss_output && dlss_active()};
|
||||
auto const taa{(vp.main && targets.taa && Global.gfx_taa()) || dlss};
|
||||
m_motionhistory = taa;
|
||||
m_cabfuture = glm::mat4{1.f};
|
||||
if (taa)
|
||||
{
|
||||
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(
|
||||
m_renderpass.pass_camera.projection(), glm::dmat3{m_renderpass.pass_camera.modelview()}, m_renderpass.pass_camera.position(), cab, glm::ivec2{vp.width, vp.height});
|
||||
@@ -3005,6 +3035,57 @@ void vulkan14_renderer::post_pass(char const *Fragment, vk14::image &Target, std
|
||||
commands->end_rendering();
|
||||
}
|
||||
|
||||
// dlss of the main viewport (gfx.antialiasing dlss): its inputs made (the velocities of the sky, which has none drawn, from the
|
||||
// motion of the camera; the exposure of the tonemapping in a texel), then its evaluation into the output of the size of the window
|
||||
void vulkan14_renderer::render_dlss(std::size_t const Index)
|
||||
{
|
||||
auto &ctx{*m_context};
|
||||
auto &targets{ctx.scene.targets[Index]};
|
||||
vk14::draw_constants motion;
|
||||
auto const &reprojection{m_taa.reprojection()};
|
||||
for (int column = 0; column < 4; ++column)
|
||||
{
|
||||
motion.data[2 + column] = reprojection[column];
|
||||
}
|
||||
post_pass("postfx_dlss_motion.frag", *targets.dlss_motion, {targets.motion_image.get(), targets.depth.get()}, motion, "dlss inputs");
|
||||
vk14::image *exposure{nullptr};
|
||||
if (false == Global.gfx_dlss_autoexposure)
|
||||
{
|
||||
vk14::draw_constants constants;
|
||||
constants.data[0] = gfx::exposure_settings::tonemap_params(0);
|
||||
post_pass("postfx_dlss_exposure.frag", *targets.dlss_exposure, {targets.colour.get()}, constants);
|
||||
exposure = targets.dlss_exposure.get();
|
||||
}
|
||||
// the inputs read and the output written by the passes of the library, whichever stages they are
|
||||
auto const stage{VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT};
|
||||
std::vector<vk14::image_use> uses{vk14::use_of(*targets.colour, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, stage, VK_ACCESS_2_SHADER_READ_BIT),
|
||||
vk14::use_of(*targets.depth, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, stage, VK_ACCESS_2_SHADER_READ_BIT),
|
||||
vk14::use_of(*targets.dlss_motion, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, stage, VK_ACCESS_2_SHADER_READ_BIT),
|
||||
vk14::use_of(*targets.dlss_output, VK_IMAGE_LAYOUT_GENERAL, stage, VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT)};
|
||||
if (exposure != nullptr)
|
||||
{
|
||||
uses.push_back(vk14::use_of(*exposure, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, stage, VK_ACCESS_2_SHADER_READ_BIT));
|
||||
}
|
||||
ctx.passes.begin_pass("dlss", uses);
|
||||
vk14::dlss::frame_inputs inputs;
|
||||
inputs.colour = targets.colour.get();
|
||||
inputs.depth = targets.depth.get();
|
||||
inputs.motion = targets.dlss_motion.get();
|
||||
inputs.exposure = exposure;
|
||||
inputs.output = targets.dlss_output.get();
|
||||
inputs.jitter = m_taa.jitter();
|
||||
inputs.reset = m_dlssreset || (false == m_taa.history_valid());
|
||||
inputs.frame_time = static_cast<float>(Timer::GetDeltaRenderTime() * 1000.0);
|
||||
ctx.dlss.evaluate(*ctx.commands, inputs, ctx.deletions, ctx.uploads.current_value());
|
||||
m_dlssreset = false;
|
||||
ctx.passes.end_pass();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -3023,6 +3104,12 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
|
||||
render_ao(Index, aomode);
|
||||
}
|
||||
auto *hdr{targets.colour.get()};
|
||||
// dlss: the scene anti-aliased and upscaled to the window, into the images the rest of the post-processing reads
|
||||
if (m_motionhistory && targets.dlss && targets.dlss_output && targets.dlss_motion && targets.motion_image && dlss_active())
|
||||
{
|
||||
render_dlss(Index);
|
||||
hdr = targets.dlss_output.get();
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
@@ -3076,8 +3163,9 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
|
||||
{
|
||||
post_pass("postfx_tonemapping.frag", *targets.output, tonemapinput, tonemap, "tonemapping, output");
|
||||
}
|
||||
// the lights of the clusters of the pixels over the image, for the debugging of the lights
|
||||
if (Global.gfx_lights_clusterheatmap)
|
||||
// the lights of the clusters of the pixels over the image, for the debugging of the lights (the clusters are those of the size the
|
||||
// scene is drawn at, which dlss upscales)
|
||||
if (Global.gfx_lights_clusterheatmap && (false == targets.dlss))
|
||||
{
|
||||
render_cluster_heatmap(Index);
|
||||
}
|
||||
|
||||
@@ -299,6 +299,22 @@ bool instance::collect_extensions(std::vector<char const *> &Extensions, instanc
|
||||
{
|
||||
Extensions.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
|
||||
}
|
||||
// what a library asks for; without one of them the library says it can't start
|
||||
for (auto const &name : Config.wanted_extensions)
|
||||
{
|
||||
if (contains(Extensions, name.c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (has_extension(available, name.c_str()))
|
||||
{
|
||||
Extensions.push_back(name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
log_warning("the instance extension " + name + " asked for isn't available");
|
||||
}
|
||||
}
|
||||
|
||||
std::string list;
|
||||
for (auto const *name : Extensions)
|
||||
|
||||
@@ -26,6 +26,8 @@ struct instance_config
|
||||
int validation{0};
|
||||
// VK_EXT_debug_utils: the messages of the validation layer in the log, names of objects
|
||||
bool debug_utils{false};
|
||||
// the extensions a library asks for (dlss), taken where the loader has them
|
||||
std::vector<std::string> wanted_extensions;
|
||||
};
|
||||
|
||||
class instance
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace
|
||||
// background of the opengl renderer (srgb). before that the windows are black, as with the opengl renderer
|
||||
glm::vec3 const clear_colour{51.f / 255.f, 102.f / 255.f, 85.f / 255.f};
|
||||
|
||||
// VkPhysicalDeviceProperties::vendorID of nvidia, the gpus dlss runs on
|
||||
std::uint32_t constexpr nvidia_vendor_id{0x10de};
|
||||
|
||||
// the ring each frame in flight streams its data through: the user interface, the blocks of the scene and of the light clusters,
|
||||
// the light buffers of the passes, the vertices of the particles and the sky; the descriptors read the light buffers with the range
|
||||
// of the whole gl::light_data, which the tail past the last frame leaves room for
|
||||
@@ -332,7 +335,7 @@ bool vulkan14_renderer::context::create_core(std::uint32_t const Frames, std::st
|
||||
return false;
|
||||
}
|
||||
uploads.init(memory, deletions, device.physical());
|
||||
if (false == sampling.create(device, static_cast<float>(Global.AnisotropicFiltering), Reason))
|
||||
if (false == sampling.create(device, static_cast<float>(Global.AnisotropicFiltering), Reason, lod_bias))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -404,6 +407,8 @@ void vulkan14_renderer::context::destroy()
|
||||
uploads = vk14::uploader{};
|
||||
images.clear();
|
||||
deletions.flush();
|
||||
// the features of dlss released by the deletions above, ngx goes before the device
|
||||
dlss.shutdown();
|
||||
pipelines.shutdown();
|
||||
shaders.shutdown();
|
||||
passes.destroy();
|
||||
@@ -494,6 +499,34 @@ gfx_backend_desc vulkan14_renderer::backend_desc()
|
||||
|
||||
bool vulkan14_renderer::renderer_register = gfx_renderer_factory::get_instance()->register_backend("vulkan", vulkan14_renderer::backend_desc());
|
||||
|
||||
// dlss on the device made for it, and the size the scene of the main window is drawn at; where it can't be had, taa instead
|
||||
void vulkan14_renderer::start_dlss()
|
||||
{
|
||||
auto &ctx{*m_context};
|
||||
std::string reason;
|
||||
glm::ivec2 render{0};
|
||||
auto const output{glm::max(Global.fb_size, glm::ivec2(1))};
|
||||
if (ctx.device.caps().vendor_id != nvidia_vendor_id)
|
||||
{
|
||||
reason = ctx.device.caps().device_name + " isn't an nvidia gpu";
|
||||
}
|
||||
else if (ctx.dlss.init(ctx.instance.handle(), ctx.device.physical(), ctx.device.handle(), reason))
|
||||
{
|
||||
if (ctx.dlss.plan(output, Global.gfx_dlss_native ? 1.f : Global.gfx_buffer_scale, render))
|
||||
{
|
||||
m_dlssrender = render;
|
||||
m_dlssoutput = output;
|
||||
ctx.lod_bias = ctx.dlss.lod_bias();
|
||||
vk14::log_info("dlss: the bias of the mipmaps of the scene " + std::to_string(ctx.lod_bias) + ", " + std::to_string(ctx.dlss.jitter_phases()) + " offsets of the jitter");
|
||||
return;
|
||||
}
|
||||
reason = "it doesn't upscale to the window of " + std::to_string(output.x) + "x" + std::to_string(output.y);
|
||||
}
|
||||
ctx.dlss.shutdown();
|
||||
vk14::log_info("gfx.antialiasing dlss: " + reason + ", using taa");
|
||||
Global.gfx_antialiasing = global_settings::antialiasing_mode::taa;
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Init(GLFWwindow *Window)
|
||||
{
|
||||
m_context = std::make_unique<context>();
|
||||
@@ -504,9 +537,30 @@ bool vulkan14_renderer::Init(GLFWwindow *Window)
|
||||
config.validation = Global.gfx_vulkan_validation;
|
||||
config.debug_utils = (Global.gfx_vulkan_validation > 0);
|
||||
|
||||
// dlss (gfx.antialiasing dlss): the extensions ngx needs go into the instance and the device; where it can't be had, taa
|
||||
if (Global.gfx_dlss() && (false == vk14::dlss::compiled()))
|
||||
{
|
||||
vk14::log_info("gfx.antialiasing dlss: this build is without the dlss sdk (WITH_DLSS), using taa");
|
||||
Global.gfx_antialiasing = global_settings::antialiasing_mode::taa;
|
||||
}
|
||||
if (Global.gfx_dlss())
|
||||
{
|
||||
vk14::dlss::instance_extensions(config.wanted_extensions);
|
||||
}
|
||||
|
||||
auto const framesinflight{static_cast<std::uint32_t>(std::clamp(Global.gfx_vulkan_framesinflight, 2, 3))};
|
||||
auto ok{ctx.instance.create(config, reason) && ctx.device.select(ctx.instance.handle(), VK_NULL_HANDLE, Global.gfx_vulkan_device, false, reason) && ctx.device.create(ctx.instance, reason) && ctx.create_frames(framesinflight, reason) &&
|
||||
ctx.create_core(framesinflight, reason)};
|
||||
auto ok{ctx.instance.create(config, reason) && ctx.device.select(ctx.instance.handle(), VK_NULL_HANDLE, Global.gfx_vulkan_device, false, reason)};
|
||||
std::vector<std::string> deviceextensions;
|
||||
if (ok && Global.gfx_dlss() && (ctx.device.caps().vendor_id == nvidia_vendor_id))
|
||||
{
|
||||
vk14::dlss::device_extensions(ctx.instance.handle(), ctx.device.physical(), deviceextensions);
|
||||
}
|
||||
ok = ok && ctx.device.create(ctx.instance, reason, deviceextensions);
|
||||
if (ok && Global.gfx_dlss())
|
||||
{
|
||||
start_dlss();
|
||||
}
|
||||
ok = ok && ctx.create_frames(framesinflight, reason) && ctx.create_core(framesinflight, reason);
|
||||
if (ok)
|
||||
{
|
||||
auto mainwindow{std::make_unique<vk14::swapchain>()};
|
||||
@@ -581,6 +635,13 @@ bool vulkan14_renderer::AddViewport(global_settings::extraviewport_config const
|
||||
viewport = &m_viewports.back();
|
||||
viewport->window = ctx.windows.size() - 1;
|
||||
}
|
||||
if (viewport->main && (viewport->output_width > 0))
|
||||
{
|
||||
// the size dlss was planned for isn't the one of the viewport: the main window without it
|
||||
vk14::log_info("dlss: off for the main window, drawn by the viewport " + conf.monitor + " at a size of its own");
|
||||
viewport->output_width = 0;
|
||||
viewport->output_height = 0;
|
||||
}
|
||||
viewport->width = conf.width;
|
||||
viewport->height = conf.height;
|
||||
viewport->projection = conf.projection;
|
||||
@@ -764,6 +825,11 @@ void vulkan14_renderer::update_info()
|
||||
auto const extent{window->extent()};
|
||||
m_infostats += "window: " + std::to_string(extent.width) + "x" + std::to_string(extent.height) + (window->srgb() ? ", srgb" : ", unorm") + (window->present_mode() == VK_PRESENT_MODE_FIFO_KHR ? ", vsync" : ", no vsync") + "\n";
|
||||
}
|
||||
if (false == m_viewports.empty())
|
||||
{
|
||||
auto const &main{m_viewports.front()};
|
||||
m_infostats += "scene drawn at " + std::to_string(main.width) + "x" + std::to_string(main.height) + (dlss_active() ? ", " + ctx.dlss.info() : std::string{}) + "\n";
|
||||
}
|
||||
// the statistics of the scene and of the cullings, as the opengl renderer gives them
|
||||
collect_cluster_stats();
|
||||
m_infostats += "\n" + scene_info() + cull_info();
|
||||
|
||||
@@ -133,8 +133,12 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
|
||||
// another projection)
|
||||
struct viewport_config
|
||||
{
|
||||
// the size the scene is drawn at
|
||||
int width{0};
|
||||
int height{0};
|
||||
// the size of the image after the upscaling of dlss (the window); 0: the size above
|
||||
int output_width{0};
|
||||
int output_height{0};
|
||||
float draw_range{1.f};
|
||||
bool main{false};
|
||||
bool shadow{false}; // its view is taken into the sun shadows
|
||||
@@ -297,6 +301,7 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
|
||||
void render_editor_overlay();
|
||||
// the post-processing of a viewport: the motion blur, the tonemapping, the chromatic aberration
|
||||
void render_post(std::size_t const Viewport, bool const Motionblur);
|
||||
void render_dlss(std::size_t const Viewport);
|
||||
// the output of a viewport to its window, with the user interface over it for the main window
|
||||
void present_viewport(std::size_t const Viewport);
|
||||
// the light buffer of the pass: the sun, the ambient light, the fog, the headlights, the lamps (the clustered lights)
|
||||
@@ -427,8 +432,16 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
|
||||
double m_environmentupdatetime{0.0};
|
||||
glm::dvec3 m_environmentupdatelocation{0.0};
|
||||
bool m_vehiclesmoved{true};
|
||||
// the temporal anti-aliasing of the main viewport (gfx.antialiasing taa)
|
||||
// the temporal anti-aliasing of the main viewport (gfx.antialiasing taa), whose jitter and history dlss takes too
|
||||
gfx::temporal_aa m_taa;
|
||||
// dlss of the main viewport (gfx.antialiasing dlss): the size the scene is drawn at and the window it's upscaled to, planned at
|
||||
// the start (0: dlss is off); the next frame starts its history anew
|
||||
glm::ivec2 m_dlssrender{0};
|
||||
glm::ivec2 m_dlssoutput{0};
|
||||
bool m_dlssreset{true};
|
||||
void start_dlss();
|
||||
// dlss is used for the main viewport this frame
|
||||
bool dlss_active() const;
|
||||
texture_handle m_suntexture{-1};
|
||||
texture_handle m_moontexture{-1};
|
||||
texture_handle m_smoketexture{-1};
|
||||
|
||||
@@ -630,6 +630,13 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
|
||||
{
|
||||
scene.post_fragments.emplace("postfx_taa.frag", ctx.shaders.get("postfx_taa.frag"));
|
||||
}
|
||||
if (Global.gfx_dlss())
|
||||
{
|
||||
for (auto const *name : {"postfx_dlss_motion.frag", "postfx_dlss_exposure.frag"})
|
||||
{
|
||||
scene.post_fragments.emplace(name, ctx.shaders.get(name));
|
||||
}
|
||||
}
|
||||
auto const build{ctx.shaders.get("cluster_build.comp")};
|
||||
auto const cull{ctx.shaders.get("cluster_cull.comp")};
|
||||
scene.cluster_build = ctx.pipelines.compute(build, scene.cluster_layout, {}, "cluster build");
|
||||
|
||||
16
shaders/vk14/postfx_dlss_exposure.frag
Normal file
16
shaders/vk14/postfx_dlss_exposure.frag
Normal file
@@ -0,0 +1,16 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
// the exposure the tonemapping applies to the image (postfx_tonemapping.frag), in the texel dlss takes it from
|
||||
// (rendering/vulkan14/vulkan14dlss.h). param is pc.data of the push constants: param[0].y 1 if the automatic exposure is applied
|
||||
// (exposure_ssbo of common.glsl), param[0].z the fixed exposure multiplier
|
||||
layout(location = 0) out vec4 out_exposure;
|
||||
|
||||
void main()
|
||||
{
|
||||
float scale = param[0].z;
|
||||
if (param[0].y > 0.5 && exposure > 0.0)
|
||||
scale *= exposure;
|
||||
out_exposure = vec4(scale, 0.0, 0.0, 1.0);
|
||||
}
|
||||
29
shaders/vk14/postfx_dlss_motion.frag
Normal file
29
shaders/vk14/postfx_dlss_motion.frag
Normal file
@@ -0,0 +1,29 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
// the velocities dlss takes (rendering/vulkan14/vulkan14dlss.h): those the colour pass drew, and for the sky, which has none drawn
|
||||
// (the depth of the reverse z at its infinity), the motion of the camera, as the temporal anti-aliasing takes it (taa_resolve.glsl).
|
||||
// param is pc.data of the push constants: param[2..5] the columns of the matrix of the clip space of this frame to the one of the
|
||||
// last frame, for the world standing still (gfx::temporal_aa::reprojection())
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
layout(location = 0) out vec4 out_motion;
|
||||
|
||||
#pragma texture(velocity_tex, 0, RG)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D velocity_tex;
|
||||
|
||||
#pragma texture(depth_tex, 1, R)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 1) uniform sampler2D depth_tex;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
||||
vec2 velocity = texelFetch(velocity_tex, pixel, 0).xy;
|
||||
float depth = texelFetch(depth_tex, pixel, 0).r;
|
||||
if (depth <= 0.0)
|
||||
{
|
||||
vec4 world = mat4(param[2], param[3], param[4], param[5]) * vec4(f_coords * 2.0 - 1.0, depth, 1.0);
|
||||
velocity = (world.w > 0.0) ? (world.xy / world.w * 0.5 + 0.5) - f_coords : vec2(0.0);
|
||||
}
|
||||
out_motion = vec4(velocity, 0.0, 0.0);
|
||||
}
|
||||
82
tools/fetch_dlss_sdk.cmake
Normal file
82
tools/fetch_dlss_sdk.cmake
Normal file
@@ -0,0 +1,82 @@
|
||||
# fetches the parts of the NVIDIA DLSS SDK (github.com/NVIDIA/DLSS) the Vulkan renderer builds with (WITH_DLSS): the headers, the
|
||||
# static library of NGX and the DLSS library for the platform. the SDK keeps the binaries of every platform in git (hundreds of MB a
|
||||
# release), so it isn't a submodule: this takes only the files needed, with a sparse checkout of one tag.
|
||||
#
|
||||
# cmake -P tools/fetch_dlss_sdk.cmake [-DDLSS_SDK_ROOT=<dir>] [-DDLSS_SDK_TAG=<tag>] [-DDLSS_SDK_PLATFORMS=windows;linux] [-DDLSS_SDK_ARCH=x86_64|aarch64]
|
||||
#
|
||||
# DLSS_SDK_ROOT: where it goes, ref/DLSS by default (ignored by git), which CMakeLists.txt looks at too. DLSS_SDK_PLATFORMS: the
|
||||
# platforms whose libraries are taken, the one running this by default. run again to change the tag or the platforms.
|
||||
# the SDK has its own license (LICENSE.txt in it): the DLSS libraries may go out with the program, the SDK itself may not.
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if(NOT DLSS_SDK_ROOT)
|
||||
set(DLSS_SDK_ROOT "${CMAKE_CURRENT_LIST_DIR}/../ref/DLSS")
|
||||
endif()
|
||||
get_filename_component(DLSS_SDK_ROOT "${DLSS_SDK_ROOT}" ABSOLUTE)
|
||||
if(NOT DLSS_SDK_TAG)
|
||||
set(DLSS_SDK_TAG "v310.9.16")
|
||||
endif()
|
||||
if(NOT DLSS_SDK_PLATFORMS)
|
||||
if(CMAKE_HOST_WIN32)
|
||||
set(DLSS_SDK_PLATFORMS "windows")
|
||||
else()
|
||||
set(DLSS_SDK_PLATFORMS "linux")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT DLSS_SDK_ARCH)
|
||||
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$")
|
||||
set(DLSS_SDK_ARCH "aarch64")
|
||||
else()
|
||||
set(DLSS_SDK_ARCH "x86_64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Git REQUIRED)
|
||||
|
||||
# the headers, the license, and for each platform the static library of ngx (the variants of the dynamic c runtime the program
|
||||
# builds with, /MD and /MDd) with the release and the development library of dlss (the latter shows the indicator and takes the
|
||||
# debug keys, gfx.dlss.developer)
|
||||
set(patterns "/include/" "/LICENSE.txt")
|
||||
foreach(platform IN LISTS DLSS_SDK_PLATFORMS)
|
||||
if(platform STREQUAL "windows")
|
||||
# the static libraries of x86_64 are in x64/, those of arm64 at the top of their directory
|
||||
set(static "/lib/Windows_${DLSS_SDK_ARCH}")
|
||||
if(DLSS_SDK_ARCH STREQUAL "x86_64")
|
||||
set(static "${static}/x64")
|
||||
endif()
|
||||
list(APPEND patterns
|
||||
"${static}/nvsdk_ngx_d.lib"
|
||||
"${static}/nvsdk_ngx_d_dbg.lib"
|
||||
"/lib/Windows_${DLSS_SDK_ARCH}/rel/nvngx_dlss.dll"
|
||||
"/lib/Windows_${DLSS_SDK_ARCH}/dev/nvngx_dlss.dll")
|
||||
elseif(platform STREQUAL "linux")
|
||||
list(APPEND patterns
|
||||
"/lib/Linux_${DLSS_SDK_ARCH}/libnvsdk_ngx.a"
|
||||
"/lib/Linux_${DLSS_SDK_ARCH}/rel/libnvidia-ngx-dlss.so.*"
|
||||
"/lib/Linux_${DLSS_SDK_ARCH}/dev/libnvidia-ngx-dlss.so.*")
|
||||
else()
|
||||
message(FATAL_ERROR "unknown platform \"${platform}\" in DLSS_SDK_PLATFORMS, expected windows or linux")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
function(run_git)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" ${ARGN} RESULT_VARIABLE result)
|
||||
if(NOT result EQUAL 0)
|
||||
message(FATAL_ERROR "git ${ARGN} failed (${result})")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(EXISTS "${DLSS_SDK_ROOT}/.git")
|
||||
message(STATUS "updating the DLSS SDK in ${DLSS_SDK_ROOT} to ${DLSS_SDK_TAG}")
|
||||
run_git(-C "${DLSS_SDK_ROOT}" fetch --depth 1 --filter=blob:none origin "refs/tags/${DLSS_SDK_TAG}:refs/tags/${DLSS_SDK_TAG}")
|
||||
run_git(-C "${DLSS_SDK_ROOT}" sparse-checkout set --no-cone ${patterns})
|
||||
run_git(-C "${DLSS_SDK_ROOT}" checkout --detach "${DLSS_SDK_TAG}")
|
||||
else()
|
||||
message(STATUS "fetching the DLSS SDK ${DLSS_SDK_TAG} to ${DLSS_SDK_ROOT}")
|
||||
run_git(clone --filter=blob:none --no-checkout --depth 1 --branch "${DLSS_SDK_TAG}" https://github.com/NVIDIA/DLSS.git "${DLSS_SDK_ROOT}")
|
||||
run_git(-C "${DLSS_SDK_ROOT}" sparse-checkout set --no-cone ${patterns})
|
||||
run_git(-C "${DLSS_SDK_ROOT}" checkout)
|
||||
endif()
|
||||
message(STATUS "the DLSS SDK is in ${DLSS_SDK_ROOT}; configure with -DWITH_DLSS=ON")
|
||||
@@ -77,6 +77,12 @@ void global_settings::FinalizeConfig()
|
||||
#endif
|
||||
}
|
||||
|
||||
glm::ivec2 global_settings::gfx_buffer_size(float const Scale) const
|
||||
{
|
||||
auto const scale{Scale > 0.f ? Scale : gfx_buffer_scale};
|
||||
return glm::max(glm::ivec2(glm::round(glm::vec2(fb_size) * scale)), glm::ivec2(1));
|
||||
}
|
||||
|
||||
bool global_settings::ConfigParseGeneral(cParser& Parser, const std::string& token)
|
||||
{
|
||||
if (token == "sceneryfile")
|
||||
@@ -1463,15 +1469,24 @@ global_settings::ConfigParse_gfx( cParser &Parser, std::string_view const Token
|
||||
Parser.getTokens(1, false);
|
||||
Parser >> reflectiontune.range_vehicles;
|
||||
}
|
||||
else if (Token == "gfx.framebuffer.width")
|
||||
else if ((Token == "gfx.framebuffer.width") || (Token == "gfx.framebuffer.height"))
|
||||
{
|
||||
// the size of the image is a part of the size of the window now (gfx.buffer.percent)
|
||||
Parser.getTokens(1, false);
|
||||
Parser >> gfx_framebuffer_width;
|
||||
int ignored{0};
|
||||
Parser >> ignored;
|
||||
WriteLog("eu07.ini: \"" + std::string(Token) + "\" is no longer supported and was ignored, use gfx.buffer.percent");
|
||||
}
|
||||
else if (Token == "gfx.framebuffer.height")
|
||||
else if (Token == "gfx.buffer.percent")
|
||||
{
|
||||
// a part of the window: 0.5 is half its width and half its height. a value over 4 is taken as percents (50: 0.5)
|
||||
Parser.getTokens(1, false);
|
||||
Parser >> gfx_framebuffer_height;
|
||||
Parser >> gfx_buffer_scale;
|
||||
if (gfx_buffer_scale > 4.f)
|
||||
{
|
||||
gfx_buffer_scale *= 0.01f;
|
||||
}
|
||||
gfx_buffer_scale = std::clamp(gfx_buffer_scale, 0.25f, 2.f);
|
||||
}
|
||||
else if (Token == "gfx.shadowmap.enabled")
|
||||
{
|
||||
@@ -1914,14 +1929,19 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To
|
||||
Parser >> level;
|
||||
gfx_fog_quality = ( level == "low" || level == "1" ) ? 1 : ( level == "high" || level == "3" ) ? 3 : ( level == "ultra" || level == "4" ) ? 4 : 2; }
|
||||
else if( Token == "gfx.antialiasing" ) {
|
||||
// off, msaax2, msaax4, msaax8 or taa; it sets the multisampling too, the key which used to say it
|
||||
// off, msaax2, msaax4, msaax8, taa, dlss or dlaa; it sets the multisampling too, the key which used to say it
|
||||
Parser.getTokens( 1 );
|
||||
std::string mode;
|
||||
Parser >> mode;
|
||||
std::transform( mode.begin(), mode.end(), mode.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
|
||||
gfx_dlss_native = false;
|
||||
if( mode == "taa" ) {
|
||||
gfx_antialiasing = antialiasing_mode::taa;
|
||||
iMultisampling = 0; }
|
||||
else if( mode == "dlss" || mode == "dlaa" ) {
|
||||
gfx_antialiasing = antialiasing_mode::dlss;
|
||||
gfx_dlss_native = ( mode == "dlaa" );
|
||||
iMultisampling = 0; }
|
||||
else if( mode == "msaax2" || mode == "msaax4" || mode == "msaax8" || mode == "msaax16" ) {
|
||||
gfx_antialiasing = antialiasing_mode::msaa;
|
||||
iMultisampling = ( mode == "msaax2" ) ? 1 : ( mode == "msaax4" ) ? 2 : ( mode == "msaax8" ) ? 3 : 4; }
|
||||
@@ -1929,7 +1949,19 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To
|
||||
gfx_antialiasing = antialiasing_mode::off;
|
||||
iMultisampling = 0; }
|
||||
else {
|
||||
ErrorLog( "Bad config: unknown gfx.antialiasing \"" + mode + "\", expected off, msaax2, msaax4, msaax8 or taa" ); } }
|
||||
ErrorLog( "Bad config: unknown gfx.antialiasing \"" + mode + "\", expected off, msaax2, msaax4, msaax8, taa, dlss or dlaa" ); } }
|
||||
else if( Token == "gfx.dlss.preset" ) {
|
||||
// the presets of the dlss sdk 310 (the others are gone, deprecated or reserved)
|
||||
Parser.getTokens( 1 );
|
||||
std::string preset;
|
||||
Parser >> preset;
|
||||
std::transform( preset.begin(), preset.end(), preset.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
|
||||
gfx_dlss_preset = ( preset == "j" ) ? 10 : ( preset == "k" ) ? 11 : ( preset == "l" ) ? 12 : ( preset == "m" ) ? 13 : 0;
|
||||
if( ( gfx_dlss_preset == 0 ) && ( preset != "default" ) ) {
|
||||
ErrorLog( "Bad config: unknown gfx.dlss.preset \"" + preset + "\", expected default, j, k, l or m" ); } }
|
||||
else if( Token == "gfx.dlss.autoexposure" ) { readswitch( gfx_dlss_autoexposure ); }
|
||||
else if( Token == "gfx.dlss.lodbias" ) { readvalue( gfx_dlss_lodbias, -2.f, 2.f ); }
|
||||
else if( Token == "gfx.dlss.developer" ) { readswitch( gfx_dlss_developer ); }
|
||||
else if( Token == "gfx.fog.range" ) { readvalue( gfx_fog_range, 100.f, 10000.f ); }
|
||||
else if( Token == "gfx.fog.lights" ) { readvalue( gfx_fog_lights, 0.f, 10.f ); }
|
||||
else if( Token == "gfx.fog.shadows" ) { readswitch( gfx_fog_shadows ); }
|
||||
@@ -2065,7 +2097,12 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
export_as_text( Output, "gfx.reflections.fidelity", reflectiontune.fidelity );
|
||||
export_as_text( Output, "timespeed", fTimeSpeed );
|
||||
export_as_text( Output, "multisampling", iMultisampling );
|
||||
export_as_text( Output, "gfx.antialiasing", std::string{ gfx_antialiasing == antialiasing_mode::taa ? "taa" : ( gfx_antialiasing == antialiasing_mode::off || iMultisampling == 0 ) ? "off" : "msaax" + std::to_string( 1 << iMultisampling ) } );
|
||||
export_as_text( Output, "gfx.antialiasing", std::string{ gfx_antialiasing == antialiasing_mode::taa ? "taa" : gfx_antialiasing == antialiasing_mode::dlss ? ( gfx_dlss_native ? "dlaa" : "dlss" ) : ( gfx_antialiasing == antialiasing_mode::off || iMultisampling == 0 ) ? "off" : "msaax" + std::to_string( 1 << iMultisampling ) } );
|
||||
export_as_text( Output, "gfx.buffer.percent", gfx_buffer_scale );
|
||||
export_as_text( Output, "gfx.dlss.preset", std::string{ gfx_dlss_preset == 10 ? "j" : gfx_dlss_preset == 11 ? "k" : gfx_dlss_preset == 12 ? "l" : gfx_dlss_preset == 13 ? "m" : "default" } );
|
||||
export_as_text( Output, "gfx.dlss.autoexposure", gfx_dlss_autoexposure );
|
||||
export_as_text( Output, "gfx.dlss.lodbias", gfx_dlss_lodbias );
|
||||
export_as_text( Output, "gfx.dlss.developer", gfx_dlss_developer );
|
||||
export_as_text( Output, "latitude", fLatitudeDeg );
|
||||
export_as_text( Output, "convertmodels", iConvertModels );
|
||||
export_as_text( Output, "file.binary.terrain", file_binary_terrain );
|
||||
@@ -2188,8 +2225,6 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
<< extcam_res.y << "\n";
|
||||
|
||||
export_as_text( Output, "compresstex", compress_tex );
|
||||
export_as_text( Output, "gfx.framebuffer.width", gfx_framebuffer_width );
|
||||
export_as_text( Output, "gfx.framebuffer.height", gfx_framebuffer_height );
|
||||
export_as_text( Output, "gfx.shadowmap.enabled", gfx_shadowmap_enabled );
|
||||
// TODO: export fpslimit
|
||||
export_as_text( Output, "randomseed", Global.random_seed );
|
||||
|
||||
@@ -182,12 +182,30 @@ struct global_settings {
|
||||
GLint iMaxTextureSize{ 4096 }; // maksymalny rozmiar tekstury
|
||||
GLint iMaxCabTextureSize{ 4096 }; // largest allowed texture in vehicle cab
|
||||
int iMultisampling{ 2 }; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px
|
||||
// gfx.antialiasing: off, msaax2/x4/x8 (the samples go to iMultisampling) or taa (one sample, with the history of the last frames;
|
||||
// iMultisampling 0). the old key multisampling sets msaa or off. read at the start
|
||||
enum class antialiasing_mode { off, msaa, taa };
|
||||
// gfx.antialiasing: off, msaax2/x4/x8 (the samples go to iMultisampling), taa (one sample, with the history of the last frames;
|
||||
// iMultisampling 0) or dlss (nvidia dlss of the vulkan renderer, one sample: the image of gfx.buffer.percent upscaled to the
|
||||
// window; dlaa is dlss at the full size of the window). the old key multisampling sets msaa or off. read at the start; where dlss
|
||||
// can't be had at the start (the opengl renderers, no nvidia rtx, a build without WITH_DLSS) the renderer takes taa instead, a
|
||||
// failure of dlss later leaves the scene stretched to the window without anti-aliasing
|
||||
enum class antialiasing_mode { off, msaa, taa, dlss };
|
||||
antialiasing_mode gfx_antialiasing{ antialiasing_mode::msaa };
|
||||
// the temporal anti-aliasing is on: asked for, and the post-processing which does it is there
|
||||
bool gfx_taa() const { return ( gfx_antialiasing == antialiasing_mode::taa ) && ( false == gfx_skippipeline ); }
|
||||
// dlss is on: asked for, with the post-processing (the renderer sets gfx_antialiasing to taa where it can't have it)
|
||||
bool gfx_dlss() const { return ( gfx_antialiasing == antialiasing_mode::dlss ) && ( false == gfx_skippipeline ); }
|
||||
// gfx.buffer.percent: the size of the image the scene of the main window is drawn to, as a part of the size of the window
|
||||
// (0.5: half its width and half its height). dlss upscales it to the window, the other modes stretch it. read at the start
|
||||
float gfx_buffer_scale{ 1.f };
|
||||
// gfx.antialiasing dlaa: dlss at the full size of the window whatever gfx.buffer.percent says
|
||||
bool gfx_dlss_native{ false };
|
||||
// gfx.dlss.preset: default, j, k, l or m, the value of NVSDK_NGX_DLSS_Hint_Render_Preset (0, 10, 11, 12, 13)
|
||||
int gfx_dlss_preset{ 0 };
|
||||
// gfx.dlss.autoexposure: dlss works the exposure out itself instead of taking the one of the tonemapping
|
||||
bool gfx_dlss_autoexposure{ false };
|
||||
// gfx.dlss.lodbias: added to the bias of the mipmaps of the textures of the scene dlss is given (log2(render / window) - 1)
|
||||
float gfx_dlss_lodbias{ 0.f };
|
||||
// gfx.dlss.developer: the development library of dlss (ngx_dev/ next to the program) with its indicator and debug keys
|
||||
bool gfx_dlss_developer{ false };
|
||||
float SplineFidelity{ 1.f }; // determines segment size during conversion of splines to geometry
|
||||
float SleeperDistance{ 250.f }; // max distance (in meters) at which per-track sleeper models are still drawn; 0 disables sleeper rendering entirely
|
||||
std::string Sleepers3D; // model of the sleepers and switch timbers laid where the simulator makes the ballast; empty: none
|
||||
@@ -298,8 +316,13 @@ struct global_settings {
|
||||
std::chrono::duration<float> python_minframetime {0.01f};
|
||||
|
||||
bool gfx_skiprendering = false;
|
||||
// the size of the image the scene of the main window is drawn to: -1 until the renderer works it out from the window and
|
||||
// gfx.buffer.percent (the old keys gfx.framebuffer.width and .height aren't taken any more)
|
||||
int gfx_framebuffer_width = -1;
|
||||
int gfx_framebuffer_height = -1;
|
||||
// the size of the image of the main window drawn from the window and gfx.buffer.percent (Scale: the part of the window, 0: that
|
||||
// of the settings), at least a pixel
|
||||
glm::ivec2 gfx_buffer_size( float Scale = 0.f ) const;
|
||||
bool gfx_shadowmap_enabled = true;
|
||||
bool gfx_envmap_enabled = true;
|
||||
bool gfx_lights_clustered = true; // headlights from the light lists of the clusters (no limit of lit headlights) or the first dynamiclights of them
|
||||
@@ -367,8 +390,8 @@ struct global_settings {
|
||||
// one for each of the last frames, instead of with glBufferSubData. read at the start
|
||||
bool gfx_bufferstreaming = true;
|
||||
bool gfx_postfx_motionblur_enabled = true;
|
||||
// the colour pass writes the velocities of the pixels: for the motion blur, or for the temporal anti-aliasing
|
||||
bool gfx_motion_vectors() const { return gfx_postfx_motionblur_enabled || gfx_taa(); }
|
||||
// the colour pass writes the velocities of the pixels: for the motion blur, the temporal anti-aliasing or dlss
|
||||
bool gfx_motion_vectors() const { return gfx_postfx_motionblur_enabled || gfx_taa() || gfx_dlss(); }
|
||||
float gfx_postfx_motionblur_shutter = 0.01f;
|
||||
GLenum gfx_postfx_motionblur_format = GL_RG16F;
|
||||
GLenum gfx_format_color = GL_RGB16F;
|
||||
|
||||
Reference in New Issue
Block a user