Skip to content

Commit 9fa5387

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

20 files changed

Lines changed: 313 additions & 27 deletions
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# HDR + DLSS5 初步适配
2+
3+
这是一套给 Windows HDR 桌面使用的实验配置。它保留系统 HDR 开启,让 DLSSNR,也就是当前测试使用的 DLSS5 神经滤镜,继续处理捕获画面。配置重点在于把 HDR 捕获帧先转换成 DLSSNR 能稳定处理的 SDR R8 颜色缓冲,处理完成后再转换回 FP16 scRGB,交给 HDR 呈现器。
4+
5+
## 使用前必须确认
6+
7+
1. Windows 设置中的显示器 HDR 已经开启。
8+
2. Magpie 配置文件的“捕获方式”必须选择 `Graphics Capture (HDR)`。普通 `Graphics Capture` 走 B8G8R8A8 SDR 捕获路径,这套 HDR 配置的 FP16 输入不会建立。
9+
3. 缩放模式选择 `HDR DLSS5 Bridge`。这个模式来自 `presets/ScalingModes-HDR-DLSS5.json`,导入方式和普通缩放模式相同。
10+
4. `nvngx_dlssnr.dll` 必须放在 Magpie 程序目录,当前测试使用 RTX 4070 和实验包中的 DLSSNR 运行库。
11+
5. 测试时关闭其他帧生成或后处理链,避免 DLSSFG、XeSSFG、RTX Video 等效果混入结果。
12+
13+
## 配置步骤
14+
15+
导入 `presets/ScalingModes-HDR-DLSS5.json` 后,在目标配置文件中设置:
16+
17+
```text
18+
捕获方式: Graphics Capture (HDR)
19+
缩放模式: HDR DLSS5 Bridge
20+
```
21+
22+
配置文件里对应的效果顺序必须保持如下:
23+
24+
```text
25+
HDRToSDR
26+
FrameRate_Filter (targetFrameRate = 60)
27+
DLSSNR\\DLSSNR_AI_Filter
28+
SDRToHDR
29+
```
30+
31+
只导入缩放模式、捕获方式仍选普通 `Graphics Capture` 时,HDR 输入不会进入这条链。只选择 HDR 捕获、缩放模式继续使用普通 `DLSSNR` 时,DLSSNR 会直接接收 FP16 scRGB,当前运行库在本机测试出现过红色通道异常。
32+
33+
## 四段效果分别做什么
34+
35+
| 顺序 | 效果 | 输入 -> 输出 | 作用 |
36+
| --- | --- | --- | --- |
37+
| 1 | `HDRToSDR` | FP16 scRGB -> R8 SDR | 按 Windows HDR 桌面的 SDR 白点缩放,并把线性颜色转换成 sRGB |
38+
| 2 | `FrameRate_Filter` | R8 SDR -> R8 SDR | 将处理帧率限制为 60 FPS,降低静态浏览器画面的重复提交 |
39+
| 3 | `DLSSNR\\DLSSNR_AI_Filter` | R8 SDR -> R8 SDR | 执行 DLSS5/DLSSNR 神经降噪和细节处理 |
40+
| 4 | `SDRToHDR` | R8 SDR -> FP16 scRGB | 将 DLSS5 输出恢复为 HDR 呈现器使用的 FP16 格式 |
41+
42+
DLSSNR 在这套配置中保持原有的 R8 输入输出合约。它的前后格式转换由两个独立效果完成,避免跨格式 `CopyResource` 和 FP16 通道解释问题。
43+
44+
## SDR 白点参数
45+
46+
`HDRToSDR.hlsl``SDRToHDR.hlsl` 当前使用 `sdrWhiteScale = 4.5`。这个值来自测试显示器的 Windows 显示配置:
47+
48+
```text
49+
DISPLAYCONFIG_SDR_WHITE_LEVEL = 4500
50+
4500 / 1000 = 4.5x
51+
4.5x * 80 nits = 360 nits
52+
```
53+
54+
它决定浏览器、文件管理器和其他 SDR 窗口在 HDR 桌面中的亮度。换显示器后,这个值需要重新读取。两个 HLSL 文件必须使用同一个值,否则处理前后的亮度会发生变化。
55+
56+
Windows 原生 HDR 截图工具通常也会读取 `DISPLAYCONFIG_SDR_WHITE_LEVEL`,再将 FP16 scRGB 除以该参考白点。固定写入 4.5 只对应本次测试显示器,其他设备建议按实际参数调整。
57+
58+
## 测试环境和结果
59+
60+
- 操作系统:Windows 11,系统 HDR 保持开启
61+
- 显卡:NVIDIA GeForce RTX 4070
62+
- 测试窗口:Microsoft Edge
63+
- 捕获方式:`Graphics Capture (HDR)`
64+
- 缩放模式:`HDR DLSS5 Bridge`
65+
- DLSSNR:默认 Preset、默认 Style、Intensity 1、Guidance Mode 0
66+
- 帧率限制:60 FPS
67+
- SDR 白点:4.5x,360 nits
68+
69+
浏览器视频画面可以正常显示,DLSSNR 连续执行,测试过程中没有黑屏或红色通道异常。经过 SDR 工作缓冲后,极亮 HDR 高光会受到 R8 范围限制,原始 HDR 动态范围暂时无法完整保留;这也是本次适配使用“初步”描述的原因。
70+
71+
## 日志检查
72+
73+
启动缩放后,在程序目录的 `logs\\magpie.log` 中确认:
74+
75+
```text
76+
当前捕获模式: Graphics Capture (HDR)
77+
读取文本文件: effects\\HDRToSDR.hlsl
78+
读取文本文件: effects\\SDRToHDR.hlsl
79+
DLSSNR STATUS: Feature=18 ... result=0x1 success=...
80+
```
81+
82+
如果看不到 `当前捕获模式: Graphics Capture (HDR)`,请回到配置文件检查捕获方式。如果出现效果文件缺失,说明 Release 没有完整解压到 `effects` 目录。如果 DLSSNR 成功计数持续增加,说明 DLSS5 已经在处理帧。
83+
84+
## 当前限制
85+
86+
- 这是 HDR 的初步适配,HDR 画面会经过 SDR R8 工作缓冲。
87+
- 极亮高光会受到 SDR 工作范围限制,原始 HDR 高光细节暂时无法完整保留。
88+
- SDR 白点当前按测试显示器写入效果文件,换显示器需要重新校准。
89+
- 最大化源窗口会沿用 Magpie 的全屏缩放路径。
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;

src/Magpie.Core/DLSSNRFilter.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,10 +1562,12 @@ bool DLSSNRFilter::Initialize(
15621562
return false;
15631563
}
15641564
const bool supportedInput = inputDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM ||
1565-
inputDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM;
1566-
if (!supportedInput || outputDesc.Format != DXGI_FORMAT_R8G8B8A8_UNORM) {
1565+
inputDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM ||
1566+
inputDesc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT;
1567+
if (!supportedInput || (outputDesc.Format != DXGI_FORMAT_R8G8B8A8_UNORM &&
1568+
outputDesc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT)) {
15671569
Logger::Get().Error(fmt::format(
1568-
"DLSSNR SDR path unsupported formats: input={}, output={}",
1570+
"DLSSNR unsupported formats: input={}, output={}",
15691571
(uint32_t)inputDesc.Format, (uint32_t)outputDesc.Format));
15701572
return false;
15711573
}
@@ -1608,7 +1610,7 @@ bool DLSSNRFilter::Initialize(
16081610
}
16091611

16101612
D3D11_TEXTURE2D_DESC sharedDesc = outputDesc;
1611-
sharedDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1613+
sharedDesc.Format = outputDesc.Format;
16121614
sharedDesc.Width = impl->width;
16131615
sharedDesc.Height = impl->height;
16141616
if (!CreateSharedTexture(*impl, sharedDesc, true,

src/Magpie.Core/DeviceResources.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ bool DeviceResources::_TryCreateD3DDevice(const winrt::com_ptr<IDXGIAdapter1>& a
172172
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
173173
}
174174
// WGC 和 D3D11_CREATE_DEVICE_SINGLETHREADED 不兼容
175-
if (isForeground || ScalingWindow::Get().Options().captureMethod != CaptureMethod::GraphicsCapture) {
175+
if (isForeground || (ScalingWindow::Get().Options().captureMethod != CaptureMethod::GraphicsCapture &&
176+
ScalingWindow::Get().Options().captureMethod != CaptureMethod::GraphicsCaptureHDR)) {
176177
createDeviceFlags |= D3D11_CREATE_DEVICE_SINGLETHREADED;
177178
}
178179
#ifdef MP_USE_COMPSWAPCHAIN

0 commit comments

Comments
 (0)