diff --git a/features/DeepDVC/Shaders/Features/DeepDVC.ini b/features/DeepDVC/Shaders/Features/DeepDVC.ini new file mode 100644 index 0000000000..000b60a568 --- /dev/null +++ b/features/DeepDVC/Shaders/Features/DeepDVC.ini @@ -0,0 +1,2 @@ +[Info] +Version = 1-0-0 diff --git a/features/Upscaling/Shaders/Upscaling/Streamline/nvngx_deepdvc.dll b/features/Upscaling/Shaders/Upscaling/Streamline/nvngx_deepdvc.dll new file mode 100644 index 0000000000..94a330f168 Binary files /dev/null and b/features/Upscaling/Shaders/Upscaling/Streamline/nvngx_deepdvc.dll differ diff --git a/features/Upscaling/Shaders/Upscaling/Streamline/sl.deepdvc.dll b/features/Upscaling/Shaders/Upscaling/Streamline/sl.deepdvc.dll new file mode 100644 index 0000000000..7c5c709f03 Binary files /dev/null and b/features/Upscaling/Shaders/Upscaling/Streamline/sl.deepdvc.dll differ diff --git a/src/Feature.cpp b/src/Feature.cpp index ab2a8db684..6bae927e83 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -3,6 +3,7 @@ #include "FeatureIssues.h" #include "FeatureVersions.h" #include "Features/CloudShadows.h" +#include "Features/DeepDVC.h" #include "Features/DynamicCubemaps.h" #include "Features/ExponentialHeightFog.h" #include "Features/ExtendedMaterials.h" @@ -236,6 +237,7 @@ const std::vector& Feature::GetFeatureList() &globals::features::ibl, &globals::features::extendedTranslucency, &globals::features::upscaling, + &globals::features::deepDVC, &globals::features::renderDoc, &globals::features::weatherEditor, &globals::features::linearLighting, diff --git a/src/Features/DeepDVC.cpp b/src/Features/DeepDVC.cpp new file mode 100644 index 0000000000..adc586c698 --- /dev/null +++ b/src/Features/DeepDVC.cpp @@ -0,0 +1,221 @@ +#include "DeepDVC.h" + +#include "../Globals.h" +#include "Upscaling.h" + +#include "RE/B/BSShaderRenderTargets.h" +#include "RE/R/Renderer.h" + +// NVIDIA Deep Learning Video Clarity (DeepDVC) +// AI-based post-process filter that significantly enhances color vibrance and +// combats overexposure. Works well alongside traditional post-processing +// (tested with jiayev/pp branch: greatly improves dark-area detail without +// blowing out the overall image). +// Run after tone mapping and before film grain when film grain exists. +// +// Currently VR-only due to lack of an SE/AE test client; flat-screen +// adaptation should be straightforward. + +bool DeepDVC::IsSupported() const +{ + auto& streamline = globals::features::upscaling.streamline; + return globals::game::isVR && streamline.featureDeepDVC; +} + +bool DeepDVC::IsInMenu() const +{ + return IsSupported(); +} + +void DeepDVC::RestoreDefaultSettings() +{ + settings = Settings(); +} + +void DeepDVC::LoadSettings(json& o_json) +{ + settings = o_json; + settings.mode = std::clamp(settings.mode, 0u, 1u); + settings.intensity = std::clamp(settings.intensity, 0.0f, 1.0f); + settings.saturationBoost = std::clamp(settings.saturationBoost, 0.0f, 1.0f); +} + +void DeepDVC::SaveSettings(json& o_json) +{ + o_json = settings; +} + +void DeepDVC::DrawSettings() +{ + if (ImGui::TreeNodeEx("DeepDVC Settings", ImGuiTreeNodeFlags_DefaultOpen)) { + const char* modes[] = { "Off", "On" }; + ImGui::SliderInt("Mode", (int*)&settings.mode, 0, 1, modes[settings.mode]); + + if (settings.mode == 1) { + if (missingInput) { + ImGui::TextColored({ 1.0f, 0.4f, 0.4f, 1.0f }, "Input render target not found."); + } + + ImGui::SliderFloat("Intensity", &settings.intensity, 0.0f, 1.0f, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Controls how strong or subtle the filter effect will be on an image."); + } + + ImGui::SliderFloat("Saturation Boost", &settings.saturationBoost, 0.0f, 1.0f, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("This feature provides RTX Dynamic Vibrance Control, using AI to enhance digital vibrance and adjust color saturation adaptively based on game content, making them more vibrant and eye-catching."); + } + } + + ImGui::TreePop(); + } +} + +void DeepDVC::Evaluate() +{ + if (!IsSupported()) + return; + + auto& upscaling = globals::features::upscaling; + if (!upscaling.streamline.initialized) + return; + + if (settings.mode == 0) { + missingInput = false; + return; + } + + auto context = globals::d3d::context; + auto device = globals::d3d::device; + auto viewport = upscaling.streamline.viewport; + + if (!context || !device) + return; + + if (!upscaling.streamline.EnsureFrameToken()) + return; + + auto* frameToken = upscaling.streamline.frameToken; + if (!frameToken) + return; + + ID3D11Texture2D* targetBuffer = nullptr; + + if (auto renderer = globals::game::renderer) { + auto& rt = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kIMAGESPACE_TEMP_COPY]; + targetBuffer = rt.texture; + } + + if (!targetBuffer) { + missingInput = true; + return; + } + + missingInput = false; + + D3D11_TEXTURE2D_DESC desc; + targetBuffer->GetDesc(&desc); + + sl::Extent extent{}; + extent.width = desc.Width; + extent.height = desc.Height; + + if (proxyBuffer) { + D3D11_TEXTURE2D_DESC proxyDesc; + proxyBuffer->GetDesc(&proxyDesc); + if (proxyDesc.Width != desc.Width || proxyDesc.Height != desc.Height || proxyDesc.Format != desc.Format) { + proxyBuffer->Release(); + proxyBuffer = nullptr; + } + } + + if (!proxyBuffer) { + D3D11_TEXTURE2D_DESC proxyDesc = desc; + proxyDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; + proxyDesc.Usage = D3D11_USAGE_DEFAULT; + proxyDesc.CPUAccessFlags = 0; + proxyDesc.MiscFlags = 0; + + HRESULT hr = device->CreateTexture2D(&proxyDesc, nullptr, &proxyBuffer); + if (FAILED(hr)) { + logger::error("[DeepDVC] Failed to create proxy buffer. HR = {:x}", (uint32_t)hr); + return; + } + logger::info("[DeepDVC] Created UAV-compatible proxy buffer {}x{}", desc.Width, desc.Height); + } + + if (!proxyBuffer || !targetBuffer) { + logger::error("[DeepDVC] Invalid buffer pointers for resource copy"); + return; + } + context->CopyResource(proxyBuffer, targetBuffer); + + sl::Resource colorOut = { sl::ResourceType::eTex2d, proxyBuffer, nullptr, nullptr, 0 }; + sl::ResourceTag colorOutTag = sl::ResourceTag{ &colorOut, sl::kBufferTypeScalingOutputColor, sl::ResourceLifecycle::eOnlyValidNow, &extent }; + + sl::ResourceTag inputs[] = { colorOutTag }; + if (!upscaling.streamline.slSetTag) { + logger::warn("[DeepDVC] slSetTag not available"); + return; + } + upscaling.streamline.slSetTag(viewport, inputs, _countof(inputs), context); + + sl::DeepDVCOptions options{}; + options.mode = (sl::DeepDVCMode)settings.mode; + options.intensity = settings.intensity; + options.saturationBoost = settings.saturationBoost; + + if (upscaling.streamline.slDeepDVCSetOptions) { + sl::Result res = upscaling.streamline.slDeepDVCSetOptions(viewport, options); + if (res != sl::Result::eOk) { + logger::warn("[DeepDVC] slDeepDVCSetOptions failed: {}", magic_enum::enum_name(res)); + } + } + + const sl::BaseStructure* inputsEval[] = { &viewport }; + if (upscaling.streamline.slEvaluateFeature) { + sl::Result res = upscaling.streamline.slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken, inputsEval, _countof(inputsEval), context); + if (res != sl::Result::eOk) { + static int failCount = 0; + if (failCount < 100) { + logger::warn("[DeepDVC] slEvaluateFeature failed: {}", magic_enum::enum_name(res)); + failCount++; + } + } + } + + if (proxyBuffer && targetBuffer) { + context->CopyResource(targetBuffer, proxyBuffer); + } +} + +// ── vtable hook: run DeepDVC after tonemap ────────────────────── + +namespace +{ + template + struct TonemapHook + { + static void thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) + { + func(imageSpaceShader, shape, param); + globals::features::deepDVC.Evaluate(); + } + static inline REL::Relocation func; + }; +} + +void DeepDVC::PostPostLoad() +{ + if (!globals::game::isVR) + return; + + stl::write_vfunc<0x1, + TonemapHook>( + RE::VTABLE_BSImagespaceShaderHDRTonemapBlendCinematic[3]); + stl::write_vfunc<0x1, + TonemapHook>( + RE::VTABLE_BSImagespaceShaderHDRTonemapBlendCinematicFade[3]); + + logger::info("[DeepDVC] Tonemap hooks installed"); +} diff --git a/src/Features/DeepDVC.h b/src/Features/DeepDVC.h new file mode 100644 index 0000000000..21bd4b5e7e --- /dev/null +++ b/src/Features/DeepDVC.h @@ -0,0 +1,56 @@ +#pragma once + +#include "../Feature.h" +#include "Upscaling/Streamline.h" + +struct DeepDVC : public Feature +{ +public: + virtual inline std::string GetName() override { return "DeepDVC"; } + virtual inline std::string GetShortName() override { return "DeepDVC"; } + virtual inline std::string_view GetCategory() const override { return "Display"; } + + virtual inline std::pair> GetFeatureSummary() override + { + return { + "RTX Dynamic Vibrance uses AI to enhance digital vibrance in real-time.", + { "Improves visual clarity", + "Adjusts color saturation adaptively", + "Requires NVIDIA RTX GPU" } + }; + } + + virtual inline bool SupportsVR() override { return true; } + virtual inline bool IsCore() const override { return false; } + virtual bool IsInMenu() const override; + virtual void PostPostLoad() override; + + struct Settings + { + uint32_t mode = 0; + float intensity = 0.3f; + float saturationBoost = 0.2f; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Settings, mode, intensity, saturationBoost); + } settings; + + void Evaluate(); + + virtual void RestoreDefaultSettings() override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + virtual void DrawSettings() override; + + ~DeepDVC() + { + if (proxyBuffer) { + proxyBuffer->Release(); + proxyBuffer = nullptr; + } + } + +private: + bool IsSupported() const; + ID3D11Texture2D* proxyBuffer = nullptr; + bool missingInput = false; +}; diff --git a/src/Features/Upscaling/Streamline.cpp b/src/Features/Upscaling/Streamline.cpp index f5b5ca4599..726e7aadf1 100644 --- a/src/Features/Upscaling/Streamline.cpp +++ b/src/Features/Upscaling/Streamline.cpp @@ -113,7 +113,7 @@ void Streamline::LoadInterposer() sl::Preferences pref; - sl::Feature featuresToLoad[] = { sl::kFeatureDLSS, sl::kFeatureReflex, sl::kFeaturePCL }; + sl::Feature featuresToLoad[] = { sl::kFeatureDLSS, sl::kFeatureReflex, sl::kFeaturePCL, sl::kFeatureDeepDVC }; pref.featuresToLoad = featuresToLoad; pref.numFeaturesToLoad = _countof(featuresToLoad); @@ -244,6 +244,10 @@ void Streamline::CheckFeatures(IDXGIAdapter* a_adapter) } reflexOptionsCache = {}; lastReflexSleepFrame = UINT32_MAX; + + checkFeatureAvailability(sl::kFeatureDeepDVC, "DeepDVC", featureDeepDVC); + + logger::info("[Streamline] DeepDVC {} available", featureDeepDVC ? "is" : "is not"); } void Streamline::PostDevice() @@ -310,6 +314,20 @@ void Streamline::PostDevice() reflexOptionsCache = {}; lastReflexSleepFrame = UINT32_MAX; + + slDeepDVCSetOptions = nullptr; + if (featureDeepDVC && slGetFeatureFunction) { + auto fn = (void*)nullptr; + const sl::Result bindResult = slGetFeatureFunction(sl::kFeatureDeepDVC, "slDeepDVCSetOptions", fn); + if (bindResult == sl::Result::eOk && fn) { + slDeepDVCSetOptions = reinterpret_cast(fn); + logger::info("[Streamline] DeepDVC runtime controls are available"); + } else { + logger::warn("[Streamline] DeepDVC options bind failed with {}; DeepDVC will be disabled", + magic_enum::enum_name(bindResult)); + featureDeepDVC = false; + } + } } /** diff --git a/src/Features/Upscaling/Streamline.h b/src/Features/Upscaling/Streamline.h index f173dd1bde..ec8e24b5a6 100644 --- a/src/Features/Upscaling/Streamline.h +++ b/src/Features/Upscaling/Streamline.h @@ -13,6 +13,7 @@ #pragma warning(disable: 4471) #include #include +#include #include #include #include @@ -36,6 +37,7 @@ class Streamline bool featureReflex = false; bool featurePCL = false; bool reflexSupportedOnCurrentAdapter = false; + bool featureDeepDVC = false; sl::ViewportHandle viewport{ 0 }; sl::ViewportHandle viewportRight{ 1 }; @@ -65,6 +67,8 @@ class Streamline PFun_slDLSSGetOptimalSettings* slDLSSGetOptimalSettings{}; PFun_slDLSSGetState* slDLSSGetState{}; PFun_slDLSSSetOptions* slDLSSSetOptions{}; + // DeepDVC specific functions + PFun_slDeepDVCSetOptions* slDeepDVCSetOptions{}; // Reflex specific functions PFun_slReflexGetState* slReflexGetState{}; diff --git a/src/Globals.cpp b/src/Globals.cpp index 221bb8c3ad..7aeddec4c9 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -2,6 +2,7 @@ #include "Deferred.h" #include "Features/CloudShadows.h" +#include "Features/DeepDVC.h" #include "Features/DynamicCubemaps.h" #include "Features/ExponentialHeightFog.h" #include "Features/ExtendedMaterials.h" @@ -83,6 +84,7 @@ namespace globals ExtendedTranslucency extendedTranslucency{}; Upscaling upscaling{}; HDRDisplay hdrDisplay{}; + DeepDVC deepDVC{}; RenderDoc renderDoc{}; WeatherEditor weatherEditor{}; ExponentialHeightFog exponentialHeightFog{}; diff --git a/src/Globals.h b/src/Globals.h index c73b7c1e9e..3c325750b0 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -32,6 +32,7 @@ struct PerformanceOverlay; struct WetnessEffects; struct ExtendedTranslucency; struct Upscaling; +struct DeepDVC; struct WeatherEditor; struct ExponentialHeightFog; struct HDRDisplay; @@ -90,6 +91,7 @@ namespace globals extern ExtendedTranslucency extendedTranslucency; extern Upscaling upscaling; extern HDRDisplay hdrDisplay; + extern DeepDVC deepDVC; extern RenderDoc renderDoc; extern WeatherEditor weatherEditor; extern ExponentialHeightFog exponentialHeightFog;