Skip to content

Commit 3eb2057

Browse files
feat(hdr): add initial HDR DLSS5 bridge adaptation
1 parent 98adaa7 commit 3eb2057

22 files changed

Lines changed: 261 additions & 31 deletions

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ A fresh configuration includes the following modes and selects Lanczos by defaul
3434
| RTX Video VSR Ultra | Video, visual novels, and compressed images | NVIDIA RTX |
3535
| DLSSFG | Experimental x2/x3/x4 frame generation | NVIDIA RTX |
3636
| XeSSFG | Experimental cross-vendor x2 frame generation | Compatible Intel, NVIDIA, or AMD GPU |
37-
| DLSSNR | Same-resolution SDR AI filter | Supported NVIDIA RTX runtime |
37+
| DLSSNR | Same-resolution SDR AI filter; HDR bridge profile available | Supported NVIDIA RTX runtime |
3838

3939
Users upgrading from an older build can import `ScalingModes-v0.5.7-experimental.json` from the Release on the Scaling Modes page. It only appends `DLSSFG`, `XeSSFG`, and `DLSSNR`; it does not remove or replace existing modes.
4040

@@ -59,7 +59,9 @@ x3/x4 require sufficient display refresh capacity. If the generated target excee
5959

6060
### DLSSNR
6161

62-
DLSSNR is a same-resolution SDR post-process and does not upscale. It may affect both game content and composed text or UI. HDR is not currently supported.
62+
DLSSNR is a same-resolution post-process and does not upscale. The regular mode uses an SDR R8 color buffer. For an HDR desktop, import `presets/ScalingModes-HDR-DLSS5.json` and select `HDR DLSS5 Bridge`; it uses `HDRToSDR -> FrameRate_Filter -> DLSSNR\\DLSSNR_AI_Filter -> SDRToHDR` so Windows HDR stays enabled while DLSSNR keeps its supported SDR contract.
63+
64+
See [`docs/experimental/HDR-DLSS5-Bridge.md`](docs/experimental/HDR-DLSS5-Bridge.md) for the format bridge, reference-white tuning, and verification details.
6365

6466
| Parameter | Range | Default | Description |
6567
| --- | --- | --- | --- |

README_ZH.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
| RTX Video VSR Ultra | 视频、视觉小说和压缩画面增强 | NVIDIA RTX |
3535
| DLSSFG | 实验性 x2/x3/x4 帧生成 | NVIDIA RTX |
3636
| XeSSFG | 通用显卡实验性 x2 帧生成 | 兼容 Intel、NVIDIA 或 AMD GPU |
37-
| DLSSNR | 同分辨率 SDR AI 滤镜 | 支持运行时的 NVIDIA RTX |
37+
| DLSSNR | 同分辨率 SDR AI 滤镜;HDR 桥接模式可用 | 支持运行时的 NVIDIA RTX |
3838

3939
旧版本用户可以在“缩放模式”页面导入 Release 附带的 `ScalingModes-v0.5.7-experimental.json`。该文件只追加 `DLSSFG``XeSSFG``DLSSNR`,不会删除或替换已有模式。
4040

@@ -59,7 +59,9 @@ x3/x4 需要足够高的显示器刷新率。生成目标高于显示器刷新
5959

6060
### DLSSNR
6161

62-
DLSSNR 是同分辨率 SDR 后处理,不负责放大。它可能同时改变游戏画面、文字和 UI;HDR 暂不支持。
62+
DLSSNR 是同分辨率后处理,不负责放大。普通模式使用 SDR R8 颜色缓冲;HDR 桌面请导入 `presets/ScalingModes-HDR-DLSS5.json` 中的 `HDR DLSS5 Bridge`。该模式按 `HDRToSDR -> FrameRate_Filter -> DLSSNR\\DLSSNR_AI_Filter -> SDRToHDR` 处理,保持 Windows HDR 开启,同时避开 DLSSNR 直接接收 FP16 scRGB 时的通道异常和过曝。
63+
64+
完整的格式、白点参数和验证方法见 [`docs/experimental/HDR-DLSS5-Bridge.md`](docs/experimental/HDR-DLSS5-Bridge.md)
6365

6466
| 参数 | 范围 | 默认值 | 说明 |
6567
| --- | --- | --- | --- |
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# HDR + DLSS5 Bridge
2+
3+
This experimental profile keeps Windows HDR enabled while running the DLSSNR/DLSS5 neural pass on its supported SDR color-buffer contract.
4+
5+
## Effect order
6+
7+
`HDRToSDR -> FrameRate_Filter -> DLSSNR\\DLSSNR_AI_Filter -> SDRToHDR`
8+
9+
- `HDRToSDR` reads the `R16G16B16A16_FLOAT` scRGB capture and converts it to an `R8G8B8A8_UNORM` SDR buffer.
10+
- The SDR bridge uses a fixed `4.5x` reference-white scale for the tested display. This corresponds to `DISPLAYCONFIG_SDR_WHITE_LEVEL=4500` (360 nits).
11+
- `DLSSNR\\DLSSNR_AI_Filter` stays on the R8 input/output path. Direct FP16 DLSSNR produced invalid channel output on the test system.
12+
- `SDRToHDR` converts the processed SDR buffer back to FP16 scRGB for the HDR presenter.
13+
14+
## Import
15+
16+
Import `presets/ScalingModes-HDR-DLSS5.json` from Magpie's Scaling Modes page, then select `HDR DLSS5 Bridge` in the profile that uses `Graphics Capture (HDR)`.
17+
18+
The profile is intended for HDR desktops where the captured SDR portions are visibly over-bright without normalization. The reference-white value is display-dependent; query `DISPLAYCONFIG_SDR_WHITE_LEVEL` when tuning another monitor.
19+
20+
## Verification
21+
22+
Search the log for:
23+
24+
```text
25+
当前捕获模式: Graphics Capture (HDR)
26+
DLSSNR STATUS: Feature=18 ... result=0x1 success=...
27+
```
28+
29+
The bridge was tested with a browser window on an RTX 4070 and Windows HDR enabled. Native HDR highlights are compressed into the SDR DLSS5 working range, then returned to the HDR desktop through the FP16 presenter.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"scalingModes": [
3+
{
4+
"name": "HDR DLSS5 Bridge",
5+
"effects": [
6+
{
7+
"name": "HDRToSDR"
8+
},
9+
{
10+
"name": "FrameRate_Filter",
11+
"parameters": {
12+
"targetFrameRate": 60
13+
}
14+
},
15+
{
16+
"name": "DLSSNR\\DLSSNR_AI_Filter",
17+
"parameters": {
18+
"nrPreset": 0,
19+
"style": 0,
20+
"intensity": 1,
21+
"localToneStrength": 1,
22+
"localStructureStrength": 1,
23+
"skinStructureStrength": -1,
24+
"useAutoMask": 0,
25+
"uiCorrection": 0,
26+
"guidanceMode": 0,
27+
"depthInferenceInterval": 4
28+
}
29+
},
30+
{
31+
"name": "SDRToHDR"
32+
}
33+
]
34+
}
35+
]
36+
}

scripts/Build-Release.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ if (!$SkipBuild) {
200200
"/p:PatchVersion=$($versionMatch.Groups[3].Value)",
201201
"/p:VersionString=$Version", "/p:CommitId=$shortCommit",
202202
"/p:PreferredToolArchitecture=x64",
203+
"/p:WindowsSdkDir=D:\Windows Kits\10\",
204+
"/p:SDKReferenceDirectoryRoot=D:\Windows Kits\10\Extension SDKs\",
205+
"/p:SDKExtensionDirectoryRoot=D:\Windows Kits\10\Extension SDKs\",
203206
"/p:UseMultiToolTask=false", "/p:CL_MPCount=1",
204207
"/p:DisablePDB=true", "/p:ReproducibleBuild=true"
205208
)

src/Effects/Effects.vcxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@
5151
<FileType>Document</FileType>
5252
<DeploymentContent>true</DeploymentContent>
5353
</CopyFileToFolders>
54+
<CopyFileToFolders Include="HDRToSDR.hlsl">
55+
<FileType>Document</FileType>
56+
<DeploymentContent>true</DeploymentContent>
57+
</CopyFileToFolders>
58+
<CopyFileToFolders Include="SDRToHDR.hlsl">
59+
<FileType>Document</FileType>
60+
<DeploymentContent>true</DeploymentContent>
61+
</CopyFileToFolders>
5462
<CopyFileToFolders Include="FrameRate_Filter.hlsl">
5563
<FileType>Document</FileType>
5664
<DeploymentContent>true</DeploymentContent>

src/Effects/HDRToSDR.hlsl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//!MAGPIE EFFECT
2+
//!VERSION 4
3+
//!SORT_NAME HDR To SDR (HDR Capture Bridge)
4+
5+
//!TEXTURE
6+
//!FORMAT R16G16B16A16_FLOAT
7+
Texture2D INPUT;
8+
9+
//!TEXTURE
10+
//!FORMAT R8G8B8A8_UNORM
11+
Texture2D OUTPUT;
12+
13+
//!SAMPLER
14+
//!FILTER POINT
15+
SamplerState sam;
16+
17+
//!PASS 1
18+
//!STYLE PS
19+
//!IN INPUT
20+
//!OUT OUTPUT
21+
22+
float Srgb(float x) {
23+
return x <= 0.0031308 ? x * 12.92 : 1.055 * pow(x, 1.0 / 2.4) - 0.055;
24+
}
25+
26+
float4 Pass1(float2 pos) {
27+
const float sdrWhiteScale = 4.5;
28+
float4 c = INPUT.SampleLevel(sam, pos, 0) / sdrWhiteScale;
29+
return float4(Srgb(max(c.r, 0.0)), Srgb(max(c.g, 0.0)), Srgb(max(c.b, 0.0)), 1.0);
30+
}

src/Effects/SDRToHDR.hlsl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//!MAGPIE EFFECT
2+
//!VERSION 4
3+
//!SORT_NAME SDR To HDR (HDR Capture Bridge)
4+
5+
//!TEXTURE
6+
//!FORMAT R8G8B8A8_UNORM
7+
Texture2D INPUT;
8+
9+
//!TEXTURE
10+
//!FORMAT R16G16B16A16_FLOAT
11+
Texture2D OUTPUT;
12+
13+
//!SAMPLER
14+
//!FILTER POINT
15+
SamplerState sam;
16+
17+
//!PASS 1
18+
//!STYLE PS
19+
//!IN INPUT
20+
//!OUT OUTPUT
21+
22+
float Linear(float x) {
23+
return x <= 0.04045 ? x / 12.92 : pow((x + 0.055) / 1.055, 2.4);
24+
}
25+
26+
float4 Pass1(float2 pos) {
27+
const float sdrWhiteScale = 4.5;
28+
float4 c = INPUT.SampleLevel(sam, pos, 0);
29+
return float4(Linear(c.r), Linear(c.g), Linear(c.b), 1.0) * sdrWhiteScale;
30+
}

src/Magpie.Core/AdaptivePresenter.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77

88
namespace Magpie {
99

10+
static bool IsHdrPresentation() noexcept {
11+
return ScalingWindow::Get().Options().captureMethod == CaptureMethod::GraphicsCaptureHDR;
12+
}
13+
1014
static bool ShouldLogPresentationDiagnostic(uint32_t count) noexcept {
1115
return count <= 3 || count % 120 == 0;
1216
}
1317

1418
bool AdaptivePresenter::_Initialize(HWND hwndAttach) noexcept {
15-
if (ScalingWindow::Get().Options().IsDirectFlipDisabled()) {
19+
if (ScalingWindow::Get().Options().IsDirectFlipDisabled() && !IsHdrPresentation()) {
1620
// 禁用 DirectFlip 时始终使用 DirectComposition 呈现
1721
if (!_ResizeDCompVisual(hwndAttach)) {
1822
Logger::Get().Error("_ResizeDCompVisual 失败");
@@ -29,7 +33,7 @@ bool AdaptivePresenter::_Initialize(HWND hwndAttach) noexcept {
2933
DXGI_SWAP_CHAIN_DESC1 sd{
3034
.Width = (UINT)rendererSize.cx,
3135
.Height = (UINT)rendererSize.cy,
32-
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
36+
.Format = IsHdrPresentation() ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM,
3337
.SampleDesc = {
3438
.Count = 1
3539
},
@@ -70,6 +74,10 @@ bool AdaptivePresenter::_Initialize(HWND hwndAttach) noexcept {
7074
Logger::Get().Error("获取 IDXGISwapChain2 失败");
7175
return false;
7276
}
77+
if (IsHdrPresentation()) {
78+
hr = _dxgiSwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709);
79+
if (FAILED(hr)) Logger::Get().ComWarn("SetColorSpace1(scRGB) 失败", hr);
80+
}
7381

7482
uint32_t maximumFrameLatency = bufferCount - 1;
7583
for (const EffectOption& effect : ScalingWindow::Get().Options().effects) {
@@ -103,6 +111,10 @@ bool AdaptivePresenter::_Initialize(HWND hwndAttach) noexcept {
103111
Logger::Get().ComError("获取后缓冲区失败", hr);
104112
return false;
105113
}
114+
if (IsHdrPresentation()) {
115+
hr = _dxgiSwapChain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709);
116+
if (FAILED(hr)) Logger::Get().ComWarn("Resize 后设置 scRGB 色彩空间失败", hr);
117+
}
106118

107119
hr = d3dDevice->CreateRenderTargetView(_backBuffer.get(), nullptr, _backBufferRtv.put());
108120
if (FAILED(hr)) {
@@ -383,7 +395,7 @@ bool AdaptivePresenter::_ResizeDCompVisual(HWND hwndAttach) noexcept {
383395
hr = _dcompDevice->CreateVirtualSurface(
384396
(UINT)rendererSize.cx,
385397
(UINT)rendererSize.cy,
386-
DXGI_FORMAT_R8G8B8A8_UNORM,
398+
IsHdrPresentation() ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM,
387399
DXGI_ALPHA_MODE_IGNORE,
388400
_dcompSurface.put()
389401
);

src/Magpie.Core/CompSwapchainPresenter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ bool CompSwapchainPresenter::BeginFrame(
147147
const SIZE rendererSize = Win32Helper::GetSizeOfRect(ScalingWindow::Get().RendererRect());
148148

149149
D3D11_TEXTURE2D_DESC desc{};
150-
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
150+
desc.Format = ScalingWindow::Get().Options().captureMethod == CaptureMethod::GraphicsCaptureHDR
151+
? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM;
151152
desc.SampleDesc.Count = 1;
152153
desc.MipLevels = 1;
153154
desc.ArraySize = 1;

0 commit comments

Comments
 (0)