Initial commit: 在 Minecraft Java 版接入 NVIDIA DLSS 超分与帧生成

This commit is contained in:
WpyQwq
2026-09-19 11:51:46 +08:00
commit 8acb8bbac6
89 changed files with 51201 additions and 0 deletions
+259
View File
@@ -0,0 +1,259 @@
# Wpyw DLSS — Minecraft Java 1.20.1 (Fabric)
在 Minecraft Java 版里接入 **NVIDIA DLSS 超分(Super Resolution)** 与 **帧生成(Frame Generation / 插帧)**。
> 工程名 `wpywdlss` / `MinecraftDLSS` 是**临时名**,随你改。
---
## 一、先讲清楚三个硬事实(省掉走弯路的成本)
### 1. DLSS 5 不是超分,也不是插帧
| | 超分 | 插帧 | 本质 |
|---|---|---|---|
| **DLSS 4** | ✅ Super Resolution | ✅ Multi Frame Generation(最高 6X) | 你要的就是这两个 |
| **DLSS 5** | ❌ | ❌ | **3D 引导神经渲染**:重建材质 / 光照 / 皮肤 / 阴影 / 反射 |
DLSS 5 于 2026-09-03 上线,**官方仅 RTX 50 系**,目前只有 NBA 2K27 一款游戏,集成走 **UE5 插件**路径。
在 **Streamline SDK 2.14.1 的 `include/` 与 `bin/x64/` 里不存在任何 dlssnr / 神经渲染的头文件或插件**——
即通用 Streamline 层根本没有 DLSS 5 的集成接口。
### 2. Minecraft Java 是 OpenGL,而 NGX 不支持 OpenGL
NGX / Streamline 只支持 **D3D11 / D3D12 / Vulkan**。所以 DLSS 进 MC Java 的唯一办法是:
游戏继续用 OpenGL 渲染,模组**内部再起一个 Vulkan 设备**,用外部内存/信号量把纹理零拷贝共享过去,
在 Vulkan 侧跑 NGX。
### 3. 真正的工程量在「运动矢量」
DLSS 超分必须吃 **运动矢量 + 深度 + 抖动投影矩阵**,而 Minecraft 的 OpenGL 管线**一个都不生产**。
这是本项目 80% 的工作量。在 **Iris + 光影包**环境下更难:Iris 接管整条管线、自己控制投影矩阵与 G-buffer,
且还有 Distant Horizons / Voxy 的 LOD 几何要算速度。
---
## 二、架构
```
┌─ Minecraft 1.20.1 (Java, OpenGL / LWJGL) ──────────────────────────┐
│ Fabric 模组 (本仓库 src/main/java) │
│ • Mixin 投影矩阵亚像素抖动 (jitter) │
│ • Mixin 渲染分辨率 → 低分渲染 world framebuffer │
│ • 导出 depth / motion vector 纹理 │
│ • 在 present 点调用原生桥接 │
└────────────────────────┬──────────────────────────────────────────┘
│ JNI
┌────────────────────────▼──────────────────────────────────────────┐
│ 自研原生桥接 DLL (C++ / MSVC) ← native/ 本项目核心 │
│ • 建 Vulkan 1.2+ 设备(同一块 GPU) │
│ • GL↔VK 外部内存互操作 │
│ GL_EXT_memory_object_win32 ↔ VK_KHR_external_memory_win32 │
│ GL_EXT_semaphore_win32 ↔ VK_KHR_external_semaphore_win32 │
│ • slInit / slSetVulkanDevice / slSetConstants │
│ • slDLSSSetOptions + slEvaluateFeature (超分) │
│ • slDLSSGSetOptions (帧生成) │
└────────────────────────┬──────────────────────────────────────────┘
│
┌───────────▼────────────┐
│ sl.interposer.dll │ ← 用户自备,不随模组分发
│ ├─ sl.dlss.dll │ → nvngx_dlss.dll 超分
│ ├─ sl.dlss_g.dll │ → nvngx_dlssg.dll 插帧
│ └─ NvLowLatencyVk.dll│ → Reflex(Vulkan)
└────────────────────────┘
```
---
## 三、NVIDIA 运行时:**绝不打包进模组**
`nvngx_dlss.dll` / `nvngx_dlssg.dll` / `sl.*.dll` 均为 NVIDIA 专有二进制,**不允许随模组再分发**。
本模组只提供**加载逻辑**,二进制由用户自行放置。
运行时查找顺序:
1. JVM 参数 `-Dwpywdlss.runtimeDir=<路径>`
2. `<gameDir>/ngx/`
3. `<gameDir>/config/ngx/`
需要的文件(全部来自 **Streamline SDK 2.14.1 的 `bin/x64/`**):
| 文件 | 大小 | 用途 |
|---|---|---|
| `sl.interposer.dll` | 652,928 | Streamline 核心 |
| `sl.common.dll` | 843,392 | 公共层 |
| `sl.dlss.dll` | 422,016 | 超分插件 |
| `sl.dlss_g.dll` | 636,032 | 帧生成插件 |
| `nvngx_dlss.dll` | 58,956,912 | DLSS 超分模型 (310.9.1.0) |
| `nvngx_dlssg.dll` | 7,460,976 | DLSS 帧生成模型 (310.9.1.0) |
| `NvLowLatencyVk.dll` | 57,840 | Vulkan Reflex(可选) |
> 若 NGX 因缺少 `applicationId` 拒绝加载,可改用 `bin/x64/development/` 下的开发版 DLL 做实验。
---
## 四、构建
### Java 侧(Fabric 模组)
```powershell
$env:JAVA_HOME = 'C:\Program Files\Java\jdk-21.0.10'
.\gradlew.bat build
```
产物:`build/libs/wpywdlss-0.1.0.jar`
版本组合(对齐 Fabric 官方 1.20.1 分支):
`minecraft 1.20.1` · `loader 0.19.5` · `loom 1.17-SNAPSHOT` · `fabric-api 0.92.12+1.20.1` · Mojang 官方映射
### 原生侧(C++ 桥接 DLL)
见 `native/`。使用 MSVC(VS2022 BuildTools 14.44.35207)+ CMake。
**不需要 Vulkan SDK**:Khronos 头文件取自 DLSS demo 自带的
`DLSS_Sample_App/donut/thirdparty/glfw/deps/vulkan/`,导入库由
`dumpbin /exports C:\Windows\System32\vulkan-1.dll` + `lib /def:` 自造。
---
## 五、路线图
| 阶段 | 目标 | 验证方式 |
|---|---|---|
| **P0 地基** | Fabric 工程可编译;原生桥接 DLL 可编译;JNI 打通;Vulkan 设备创建成功;GL↔VK 互操作往返 | 游戏内一张纹理经 GL→VK→GL 往返后画面一致 |
| **P1 数据源** | 深度纹理 + 抖动投影 + 运动矢量。**先在纯净版 / Sodium 下做** | 运动矢量可视化输出正确 |
| **P2 DLSS 超分** | 接 `sl.dlss`,出画面。applicationId 风险在此暴露 | 1080p→4K 提升且画质可接受 |
| **P3 HUD 原生分辨率** | 世界低分渲染、HUD/UI 保持原生清晰 | HUD 文字不糊 |
| **P4 帧生成** | 接 `sl.dlss_g`。最难一步(需接管 present 路径) | 输出帧率提升,伪影可接受 |
| **P5 Iris 兼容** | 在光影包环境下算运动矢量 | 主流光影包下不崩不糊 |
---
## 六、P0 实测结果(已跑通,非推演)
**环境**:RTX 5070 / 驱动 616.92 / Vulkan loader 1.4.341 / Win11 26100
**方式**:`tools/ProbeRunner.java` 独立加载桥接 DLL 调用 `nativeProbe()`,不启动游戏
**结论**:`nativeIsReady() = true`,exit code 0
| 检查项 | 实测结果 |
|---|---|
| Vulkan 实例 | ✅ `VK_SUCCESS`,loader 1.4.341 |
| 物理设备 | 2 个:**RTX 5070**(0x10de, DISCRETE, api 1.4.351)+ AMD 核显(0x1002, INTEGRATED, 1.4.315);自动选中 NVIDIA |
| 设备扩展(291 可用) | ✅ `VK_KHR_external_memory_win32` / `VK_KHR_external_semaphore_win32` / `VK_KHR_timeline_semaphore` / `dedicated_allocation` / `image_format_list` / `synchronization2` 全部可用 |
| 设备创建 | ✅ `VK_SUCCESS`(queue family 0,已启用 timeline semaphore) |
| 互操作函数指针 | ✅ `vkGetMemoryWin32HandleKHR` / `vkGetSemaphoreWin32HandleKHR` / `vkImportSemaphoreWin32HandleKHR` 经 `vkGetDeviceProcAddr` 全部取到 |
| **镜像导出(go/no-go)** | ✅ **`OPAQUE_WIN32`: exportable=YES, importable=YES, dedicatedOnly=no** |
| 信号量 | ✅ `OPAQUE_WIN32`: exportable=YES, importable=YES |
| 显存 | 11,943 MiB device-local |
### 两个必须记住的实测细节
1. **`D3D12_HEAP` / `D3D11_TEXTURE` 只能导入、不能导出**(`exportable=no`)。
这直接影响你那个 `rtx_vsr_host.dll`(只有 `NVSDK_NGX_D3D12_*` 接口):
它无法接收"OpenGL/Vulkan 分配、D3D12 导入"的纹理,**只能反过来由 D3D12 侧分配**再共享出来。
所以 VSR 超分和 DLSS 超分**不能复用同一条互操作管线**,这是两套东西。
2. **`dedicatedOnly=no`**:不强制专用分配,意味着我们可以在同一块 VkDeviceMemory 里规划输入/输出镜像,省一次分配和一次拷贝。
### 常用命令
```powershell
# 编原生桥接(自动处理 vulkan-1.lib)
.\native\tools\build_native.cmd
# 只重造 vulkan-1.lib
.\native\tools\make_vulkan_lib.cmd
# 跑 P0 探测(不需要启动游戏)
$env:JAVA_HOME='C:\Program Files\Java\jdk-21.0.10'
java -cp "build\classes\java\main" `
-Dwpywdlss.bridgePath=native\build\wpywdlss_bridge.dll `
tools\ProbeRunner.java
```
---
## 七、OpenGL 互操作实测结果(2026-09-16,已跑通)
**为什么这一个探测决定所有路线的生死**:OpenGL **只能导入、不能导出**内存和信号量。
所以分配方必须是 Vulkan/D3D12,GL 只负责导入对方给过来的 Win32 HANDLE。
`GL_EXT_external_objects*` 是**导入 Vulkan 对象的另一条路**,走 `OPAQUE_WIN32` 时**不需要**它 ——
把它当必需项会得出假阴性(我第一版就写错了,已修正)。
`nativeProbeOpenGL()` 创建一个**隐藏 WGL 上下文**实测:
| 检查 | 结果 |
|---|---|
| GL_RENDERER | `NVIDIA GeForce RTX 5070/PCIe/SSE2` |
| GL_VERSION | `4.6.0 NVIDIA 616.92` |
| GL 扩展总数 | 405 |
| **`GL_EXT_memory_object`** | ✅ |
| **`GL_EXT_memory_object_win32`** | ✅ |
| **`GL_EXT_semaphore`** | ✅ |
| **`GL_EXT_semaphore_win32`** | ✅ |
| `glCreateMemoryObjectsEXT` | ✅ |
| **`glImportMemoryWin32HandleEXT`** | ✅ |
| **`glTextureStorageMem2DEXT`** | ✅ |
| **`glImportSemaphoreWin32HandleEXT`** | ✅ |
| `glDeleteMemoryObjectsEXT` / `glDeleteSemaphoresEXT` | ✅ |
| `GL_EXT_external_objects*` | ❌(**不需要**,走 OPAQUE_WIN32 时用不到)|
| `glCreateSemaphoresEXT` | ❌(**不需要**,GL 只导入不导出)|
**判定:`VERDICT: the OpenGL transport is AVAILABLE on this machine.`**
两侧合起来的完整链路(能力层面已验证):
```
Vulkan/D3D12 分配镜像
│ vkGetMemoryWin32HandleKHR / D3D12 CreateSharedHandle
▼ (Win32 HANDLE)
OpenGL glCreateMemoryObjectsEXT → glImportMemoryWin32HandleEXT
→ glTextureStorageMem2DEXT (得到可渲染/可采样的 GL 纹理)
▲ (Win32 信号量 HANDLE)
glImportSemaphoreWin32HandleEXT
```
---
## 八、路线 C(ReShade 路线)落地清单
**先明确 C 在 OpenGL 下的天花板**(引自 DLSS5-Feeder 官方 Limitations):
> **DLAA contract, optional reduced work extent on D3D11** — render resolution still equals DLAA output
> resolution... **D3D12, Vulkan and OpenGL paths remain at 100%.** This is not jittered DLSS Super
> Resolution, and **a Quality/Balanced/Performance mode cannot be added**.
即:**OpenGL 下是纯 DLAA + DLSS 5 神经渲染观感,内部渲染分辨率不降 → 零帧率收益,拿不到超分,也没有插帧。**
另外官方承认 **"the UI is processed with the scene"** —— Minecraft 的物品栏/聊天/F3 会被一起处理而拖影。
### 需要的组件
| 组件 | 来源 | 状态 |
|---|---|---|
| ReShade 6.8+(**需 add-on 支持**),装成 `opengl32.dll`,API 选 **OpenGL** | reshade.me | 需你下载 |
| **Generic Depth add-on** 启用并**认到 Minecraft 的场景深度** | ReShade 自带 | ⚠️ 最大风险点,见下 |
| 神经消费者:**Deep Fried Chicken**(推荐)或 Krish 的 `renodx-dlss5.addon64` | 各自 Discord | 需你获取(Discord 门禁,我拿不到)|
| `nvngx_dlssnr.dll` | 已有 | ✅ GUI-DLSS5 包里 |
| `nvngx_dlss.dll`(放在游戏目录旁) | 已有 | ✅ Streamline SDK 里 |
| 运动矢量提供者:**LumeniteFX Kernel(=3,推荐)** | github.com/umar-afzaal/LumeniteFX | 需下载 |
| `dlss5-feed.addon64` + `DLSS5_Feed.fx` | DLSS5-Feeder release | 需下载 |
### Minecraft 特有的四个坑(我预判,待实测确认)
1. **深度缓冲**:ReShade 的 Generic Depth 必须正确识别 Minecraft 的场景深度。
Iris/光影会自建 FBO,Sodium 改了区块渲染 —— 识别很可能失败。
2. **GPU 选择**:这条路要求**渲染真的在 NVIDIA GPU 上**。你有 RTX 5070 + AMD 核显,
必须在 Windows「设置 ▸ 显示 ▸ 图形」里把 Java 强制到 NVIDIA,否则 DLSS 无从谈起。
3. **HUD 拖影**:官方明说 UI 与场景一起处理。要么忍受,要么自己加 UI 遮罩。
4. **驱动版本**:他们实测的坏组合是驱动 **616.56 / 616.64**(`nvngx_dlssnr.dll` 内部 fault)。
**你是 616.92,比他们测过的都新,未知。** 先跑他们的自检
`host64\dlss5-feed-host64.exe --test`(`300/300 evaluates succeeded` 才算 OK)。
---
## 九、许可与合规
- 本模组代码:MIT
- **NVIDIA 专有二进制一律不再分发**,仅由用户自备(同 Salt's Anti-Aliasing 等既有项目的做法)
- 参考但不复制任何现有模组代码;`Super Resolution`(GPL-3.0)与 `Salt's Anti-Aliasing`(MIT)
仅作为「这条路走得通」的存在性证据
- NGX `applicationId` 需向 NVIDIA 申请;在获批前 NVIDIA 目前为宽容放行(会打警告日志)
+141
View File
@@ -0,0 +1,141 @@
plugins {
id 'net.fabricmc.fabric-loom-remap' version "${loom_version}"
}
version = project.version
group = project.maven_group
base {
archivesName = project.archives_base_name
}
repositories {
// Loom adds the essential maven repositories automatically.
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
}
// ---------------------------------------------------------------------------
// Bundled payload
//
// `payload/jvm/` mirrors the layout of the JVM's own bin/ directory, because
// that is where everything has to end up: ReShade works as a *proxy DLL*, so
// `opengl32.dll` must sit next to javaw.exe, and the ReShade add-ons load from
// the directory of the ReShade DLL.
//
// NVIDIA's nvngx_dlss.dll (~59 MB) and nvngx_dlssnr.dll (~166 MB) account for
// 95% of the total size. They are NOT committed to the repository and are NOT
// bundled by default: the installer locates an existing copy on disk instead
// (which is also what Deep Fried Chicken's own installer does). Ship them
// inside the jar only when you deliberately want a single-file distribution:
//
// gradlew build -PbundleNvidia=true -PngxSourceDir=H:/javahome/bin
// ---------------------------------------------------------------------------
def bundleNvidia = (project.findProperty('bundleNvidia') ?: 'false').toString().toBoolean()
def ngxSourceDir = (project.findProperty('ngxSourceDir') ?: 'H:/javahome/bin').toString()
def payloadSrc = file('payload')
def payloadInJar = 'assets/wpywdlss/payload'
// SHA-256 of every bundled file, generated at build time and verified after
// extraction by the installer so a truncated download is caught immediately.
def manifestFile = layout.buildDirectory.file('generated/payload-manifest.txt')
tasks.register('payloadManifest') {
group = 'wpywdlss'
description = 'Generates SHA-256 manifest for the bundled payload'
def outFile = manifestFile.get().asFile
def ngxDir = new File(ngxSourceDir)
outputs.file(manifestFile)
outputs.upToDateWhen { false }
doLast {
def lines = []
def addAll = { File root, String prefix ->
if (!root.isDirectory()) return
root.eachFileRecurse { f ->
if (f.isFile()) {
def rel = prefix + f.absolutePath.substring(root.absolutePath.length() + 1).replace('\\', '/')
def md = java.security.MessageDigest.getInstance('SHA-256')
def hash = md.digest(f.bytes).encodeHex().toString().toUpperCase()
lines << (hash + ' ' + rel + ' ' + f.length())
}
}
}
addAll(payloadSrc, '')
if (bundleNvidia) {
['nvngx_dlss.dll', 'nvngx_dlssnr.dll'].each { n ->
def f = new File(ngxDir, n)
if (f.isFile()) {
def md = java.security.MessageDigest.getInstance('SHA-256')
def hash = md.digest(f.bytes).encodeHex().toString().toUpperCase()
lines << (hash + ' jvm/' + n + ' ' + f.length())
} else {
logger.warn("bundleNvidia=true but ${f} does not exist")
}
}
}
outFile.parentFile.mkdirs()
outFile.text = lines.sort().join('\n') + '\n'
logger.lifecycle("payload manifest: ${lines.size()} file(s) -> ${outFile}")
}
}
processResources {
// payload/jvm/** becomes assets/wpywdlss/payload/jvm/**
from(payloadSrc) {
into payloadInJar
}
from(manifestFile) {
into payloadInJar
}
if (bundleNvidia) {
from(ngxSourceDir) {
include 'nvngx_dlss.dll', 'nvngx_dlssnr.dll'
into payloadInJar + '/jvm'
}
}
dependsOn 'payloadManifest'
def version = project.version
inputs.property "version", version
inputs.property "bundleNvidia", bundleNvidia
filesMatching("fabric.mod.json") {
expand "version": version
}
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 17
}
java {
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
jar {
def projectName = project.name
inputs.property "projectName", projectName
from("LICENSE") {
rename { "${it}_$projectName" }
}
}
tasks.named('build') {
doLast {
def jarFile = file("${layout.buildDirectory.get()}/libs/${project.archives_base_name}-${project.version}.jar")
if (jarFile.exists()) {
logger.lifecycle("")
logger.lifecycle(" mod jar : ${jarFile}")
logger.lifecycle(" size : ${String.format('%.2f', jarFile.length() / 1048576.0)} MB")
logger.lifecycle(" nvidia : ${bundleNvidia ? 'BUNDLED (large)' : 'not bundled - installer locates on disk'}")
logger.lifecycle("")
}
}
}
+156
View File
@@ -0,0 +1,156 @@
# 终末地(Arknights: Endfield)· DLSS 5 神经渲染 完整配方
> 2026-09-18 实测跑通。本文记录**可复现的最小配置**与**每一条失败路径的实证原因**,
> 避免以后再走一遍弯路。
---
## 一、最终可用配置
**形态:单 DLL 外部注入。** 游戏目录里**零外来文件**。
```
注入(只进 Endfield.exe,不进其它进程):
H:\endfield-dlss5\winmm.dll OptiScaler-DLSSNR v0.2.0(改名为 winmm.dll)
├── H:\endfield-dlss5\OptiScaler\ 后端 DLL(FidelityFX / XeSS 等)
├── H:\endfield-dlss5\nvngx.dll_dlssnr.dll 神经转发器(包内自带)
└── H:\endfield-dlss5\nvngx_dlssnr.dll 神经模型本体 310.8.0.0(需自备,165 MB)
游戏目录: 只保留游戏自带文件(含它自己的 nvngx_dlss.dll 310.5.2.0,未改动)
启动参数: -force-d3d11 -launcher_lang=zh-cn -launcher_sub_channel=1
```
**关键 ini 设置**(`H:\endfield-dlss5\OptiScaler.ini`)
| 段 | 键 | 值 | 为什么 |
|---|---|---|---|
| `[Upscalers]` | `Dx11Upscaler` | **`dlss_12`** | ini 注释原文:`dlss_12 (dx11on12, **the only option that can run DLSS Neural Rendering as well**)` —— D3D11 上唯一能跑神经渲染的选项 |
| `[DlssNr]` | `Enabled` | `true` | 默认关 |
| `[Menu]` | `ShortcutKey` | `0x24`(Home) | 默认 `0x2D`(Insert),很多键盘没有 Insert |
| `[DlssNr]` | `WhitePointScale` | `1.0` | 默认 auto 会**逐帧测量**,导致亮度泵动(见「已知问题」) |
| `[ProcessFilter]` | `TargetProcessName` | `endfield.exe` | 双保险;ini 自带的示例就是终末地 |
| `[ProcessFilter]` | `ProcessExclusionList` | `PlatformProcess.exe\|QtWebEngineProcess.exe\|ACE-Setup64.exe\|ACE-Service64.exe\|CefViewWing.exe\|UnityCrashHandler64.exe` | 同上 |
**启动方式**:桌面 `终末地-DLSS5启动.bat` → `H:\endfield-dlss5\launch.ps1` → `injector.exe`
---
## 二、为什么必须是「外部注入 + 单 DLL」
### 2.1 目录代理 DLL 会被同目录所有进程吞掉(ACE 拦的根因)
终末地在同一目录派生:`PlatformProcess.exe`、`QtWebEngineProcess.exe`、`CefViewWing.exe`、
`ACE-Setup64.exe`、`ACE-Service64.exe`、`UnityCrashHandler64.exe`。
**放在游戏目录的代理 DLL 会被它们 `LoadLibrary` 动态加载**(应用目录优先),
于是 ReShade/feeder 跑进了鹰角的辅助进程 —— 实测铁证:
```
dlss5-feed.log 21:18:08.600 host: H:\Arknights Endfield\PlatformProcess.exe ← 不是 Endfield.exe
```
后果:ACE 报「检测到黑客工具」。
(另见 optiscaler/OptiScaler issue #848,报的就是终末地这个游戏。)
**OptiScaler 的 `TargetProcessName`/`ProcessExclusionList` 挡不住这件事** ——
它们只阻止 OptiScaler **初始化**,阻止不了 DLL 被加载进那些进程。
### 2.2 社区的做法是「只注入目标进程」
XXMI(终末地社区实际在用的 mod 加载器)用 `SetWindowsHookEx`,
在**目标进程创建第一个窗口时**把 DLL 送进去,钩子在 `finally` 里必定移除;
其文档明确说这样「更少直接内存操作、**潜在更低的反作弊检测风险**」。
本项目用等价的 `CREATE_SUSPENDED` + `CreateRemoteThread(LoadLibraryW)` + `ResumeThread` 实现。
### 2.3 ReShade 与 feeder 都必须拿掉
* feeder 自己的日志判定:
> 这游戏走 **Streamline**、**自带 DLSS**。OptiScaler 会捕获进程里**每一个** NGX 调用,
> 所以它的神经通路会在游戏的 DLSS 上**和**这个 feed 上各跑一次。**本项目是给没有 DLSS 的
> 游戏用的 —— 请用游戏自己的 DLSS 配 OptiScaler,并移除 `dlss5-feed.addon64`。**
* OptiScaler 自己就挂在游戏自己的 DLSS 上(不需要 feeder):
```
DlssNr::EvaluateAfterUpscale DLSS-NR reached through the game's DLSS input
DlssNr_Dx12::Dispatch DLSS-NR running at 2560x1440, guides 2560x1440
```
* ReShade 插进 D3D11 调用链会造成三方钩子冲突并崩溃:
```
### CRASH RECORDED ### exception 0xC0000005 at ...d3d11.dll
crash stack: d3d11.dll <- reshade.dll <- winmm.dll <- unityplayer.dll
```
(转储 198 MB 已存档)
---
## 三、走过的死路(都不必再试)
| 路径 | 失败原因(实证) |
|---|---|
| `dxgi.dll` 目录代理 ReShade | 被 ACE 辅助进程吞掉 → 「检测到黑客工具」 |
| `d3d12.dll` 目录代理 ReShade | 注入成功,但 ReShade 的 Vulkan/后续链路与游戏冲突 |
| ReShade 的 **Vulkan 层** | 引擎确定性崩溃:`unityplayer.dll +0x57d123`,`0xc0000005`,四次同偏移 |
| Deep Fried Chicken(DFC)作神经消费者 | ① `hook_mode=AUTO` **主动跳过 Streamline**,而游戏的 DLSS 正走 Streamline v2 → 拿不到资源图(`fail-open: complete native resource map unavailable`, `device=0`)② DFC 的 private/standalone 桥在**干净进程里**也失败(`host64 --test`:feeder 300/300 通过,但 DFC 的 `Feature18Create` 返回 `0xBAD00002 PlatformError` + `nvngx_dlssnr.dll` 内 AV)→ 与 ACE、驱动、游戏均无关 |
| `-force-d3d12` | 该构建的 Unity 播放器**没有 D3D12 后端**(`UnityPlayer.dll` 有 `force-d3d11` 却无 `D3D12CreateDevice`),回落到 Vulkan |
| feeder 合成 DLAA 契约 | 本游戏自带 DLSS,多余(见 2.3) |
---
## 四、已知问题与调参
* **画面亮度快速闪动** —— `[DlssNr] WhitePointScale=auto` 会**逐帧从画面测量**白点,
实测在 1.35–1.41 来回扫,每一帧都缩放模型输出 → 亮度泵动。
**改成固定值 `1.0`**(游戏自报曝光就是 1.0)。
该键**实时生效**,也可以直接在 OptiScaler 菜单里改。
* **菜单键**:默认 `Insert`(`0x2D`)。很多键盘没有 → 已改 `0x24`(Home)。
独占全屏下若按键被吞,切无边框窗口。
* **`dlssnr-capture` 未生成** —— `AutoCapture` 默认开,但实测未触发;
改用菜单里的 **DebugView = 3(Difference)** 判断模型是否在改画面:
**整屏灰 = 什么都没改;有颜色 = 确实在改。**
* **NVIDIA Smooth Motion**:进程里有 `NvPresent64.dll` 说明它开着。
Vulkan 路径下**必须关**(文档原文:永远无法共存);D3D11 路径文档说可以留,
但若出现闪动/串帧,用 NVIDIA Profile Inspector 的
`Smooth Motion - Enabled APIs`(`0xB0CC0875`)**清掉 DX11 那一位**(`7` → `5`)。
---
## 五、操作速查
```powershell
# 启动(桌面 .bat 等价)
H:\endfield-dlss5\launch.ps1
# 注入器手动用法
H:\endfield-dlss5\injector.exe --exe "H:\Arknights Endfield\Endfield.exe" `
--args "-force-d3d11 -launcher_lang=zh-cn -launcher_sub_channel=1" `
--basepath "H:\endfield-dlss5" --log "H:\endfield-dlss5\injector-last.txt" `
--dll "H:\endfield-dlss5\winmm.dll"
# 看神经通路是否在跑
Get-Content H:\endfield-dlss5\OptiScaler.log | Select-String 'DlssNr_Dx12::Dispatch|DlssNr::EvaluateAfterUpscale' | Select-Object -Last 5
# 完全回滚:删掉外部目录即可,游戏目录本来就干净
Remove-Item H:\endfield-dlss5 -Recurse -Force
Remove-Item D:\Desktop\终末地-DLSS5启动.bat -Force
```
**验收判据(缺一不可)**
1. `injector-last.txt` 里 `[OK] ...winmm.dll`,`注入完成(1/1)`
2. `OptiScaler.log` 有 `CheckWorkingMode OptiScaler working as winmm.dll, system dll loaded`
3. `OptiScaler.log` 有 `StreamlineHooks::hookInterposer Streamline version: 2.10.3`
4. `OptiScaler.log` 有 `DLSSFeatureDx12::InitDLSS _CreateFeature result: NVSDK_NGX_Result_Success`
5. `OptiScaler.log` 有 `DlssNr::EvaluateAfterUpscale DLSS-NR reached through the game's DLSS input`
6. 游戏目录外来文件数 = **0**
---
## 六、产物清单
| 文件 | 作用 |
|---|---|
| `E:\deepseek\_tmp\feedkit\injector.cs` / `injector.exe` | 注入器(C#,`csc` 编译,x64) |
| `H:\endfield-dlss5\launch.ps1` | 提权启动 + 注入(含两个已修 bug 的说明) |
| `D:\Desktop\终末地-DLSS5启动.bat` | 用户入口(CRLF + 纯 ASCII) |
| `E:\deepseek\_tmp\feedkit\endfield-install-optiscaler.ps1` | 安装脚本 |
| `E:\deepseek\_tmp\feedkit\endfield-migrate-external.ps1` | 目录代理 → 外部注入 迁移 |
| `E:\deepseek\_tmp\feedkit\revert-endfield.ps1` | 回滚(按清单精确删除) |
| `E:\deepseek\game-backups\Endfield\` | 全部证据:日志、模块快照、崩溃转储(198 MB)、安装清单、安装前快照 |
+158
View File
@@ -0,0 +1,158 @@
# 地平线 5(Forza Horizon 5)· DLSS 5 神经渲染 完整配方
> 2026-09-18 实测跑通。姊妹文档:`endfield-dlss5-recipe.md`(终末地)。
> 两者共用同一套结论:**游戏自带 DLSS 时,只需要 OptiScaler-DLSSNR 一个组件。**
---
## 一、最终可用配置
**形态:目录代理单 DLL。** 无反向代理注入、无 ReShade、无 feeder。
```
D:\SteamLibrary\steamapps\common\ForzaHorizon5\
dxgi.dll OptiScaler-DLSSNR v0.2.0(OptiScaler.dll 改名而来)
OptiScaler.ini 配置(见下表)
OptiScaler\ 后端 DLL(FidelityFX / XeSS / D3D12_OptiScaler\D3D12Core.dll)
nvngx.dll_dlssnr.dll 神经转发器(包内自带)
nvngx_dlssnr.dll 神经模型 310.8.0.0(需自备)
nvngx_dlss.dll 游戏自带 3.1.11.0 —— **不要动**
nvngx_dlssg.dll 游戏自带 —— 不要动
sl.*.dll 游戏自带 Streamline 2.10.3 —— 不要动
```
**启动方式:Steam 正常启动,不需要换启动器。**
### 关键 ini 设置(`OptiScaler.ini`)
| 段 | 键 | 值 | 为什么 |
|---|---|---|---|
| `[Upscalers]` | `Dx12Upscaler` | `dlss` | 地平线是 D3D12;神经通路必须骑在游戏自己的 DLSS 上 |
| `[DlssNr]` | `Enabled` | `true` | 默认关 |
| `[DlssNr]` | `WhitePointScale` | `1.0` | 默认 auto 会**逐帧测光**导致亮度泵动(同终末地) |
| `[DlssNr]` | `TransferStrength` | `0.7` | 满档 1.0 = 完全是模型的画面 → 噪点明显。0.7 是降噪起点 |
| `[DlssNr]` | `ColourStrength` | `0.5` | 1.0 = 连模型的颜色一起用 → HDR 场景下色彩噪点偏重 |
| `[Menu]` | `ShortcutKey` | `0x24`(Home) | |
| `[Menu]` | `OverlayMenu` | **`false`** | 见「三、已解决的故障」——覆盖层与输入钩子会卡死游戏 |
| `[Hotfix]` | `ManualInputPolling` | **`true`** | 同上:不 hook WndProc,改成自己轮询 |
| `[Hotfix]` | `CheckForUpdate` | `false` | 关掉联网版本检查,少一个变量 |
---
## 二、验收判据(缺一不可)
读 `D:\SteamLibrary\steamapps\common\ForzaHorizon5\OptiScaler.log`:
1. `CheckWorkingMode OptiScaler working as dxgi.dll, system dll loaded`
2. `HookNgxApi NVSDK_NGX_XXXXXX_GetFeatureRequirements found, hooking!`
3. `DLSSFeatureDx12::InitDLSS _CreateFeature result: NVSDK_NGX_Result_Success`
4. **`DlssNr::EvaluateAfterUpscale DLSS-NR reached through the game's DLSS input`** ← 核心
5. `DlssNr_Dx12::Dispatch DLSS-NR running at 2560x1440`
6. 进程模块表里出现 **两个 `dxgi.dll`**(游戏目录的 OptiScaler + 系统 dxgi)
7. 无 `Application Hang` / `Application Error`
---
## 三、已解决的故障(都有实证,不必再走)
### 3.1 启动后「未响应」→ `Application Hang`
```
OptiInput::InstallWindowSubclass subclass installed previousWndProc:… → optiWndProc:…
OptiInput::InstallHooks Win32 message / key state / GetMessagePos / clip cursor /
HID / **raw input** / windows input / position input hooks installed
OptiInput::UpdateXInputIntegrationLocked XInput hooks installed
… 之后 Windows 记录 Application Hang,游戏被关闭
```
OptiScaler 给游戏主窗口挂了**子类化窗口过程**并装了 **raw input + XInput 钩子**,
而地平线是重度输入的游戏 → 消息泵被卡住。
**修法(两个一起,实测有效)**:
```ini
[Hotfix] ManualInputPolling=true ; 不去 hook WndProc,改成自己轮询输入
[Menu] OverlayMenu=false ; 关掉覆盖层
```
### 3.2 在菜单里切到 FSR → 游戏崩溃
```
21:59:08 FeatureProvider_Dx12::ChangeFeature changing backend to FSR 2.2.1
21:59:11 Application Error: ForzaHorizon5.exe 0xc0000005 偏移 0x92dfbc
```
**不要在地平线里实时切后端。** 要换就改 ini 再重启(而且 `Dx12Upscaler` 必须是 `dlss`,
否则神经通路没有游戏自己的 DLSS 可骑)。
### 3.3 遗留:菜单显示得出,但收不到输入
```
[W] OptiInput::LogInputHealthSnapshotLocked menu is visible but no window/queue/raw input
was received this frame. input:0x…, foreground:0x…, focused:yes, subclassed:yes.
```
菜单能唤出(`menu visibility changed 0 -> 1`),但鼠标键盘进不去。
**不影响神经渲染**,所有参数都从 ini 调、改完重启游戏即可。
### 3.4 游戏自身的帧生成在报错(与 OptiScaler 无关)
```
[E] present.h:288[updateStatus] eDLSSGStatusFailReflexNotDetectedAtRuntime
- sl.reflex must be enabled and active
```
游戏自己的 DLSS-G 处于失败状态。**「远处物体闪动」很可能来自这里** ——
建议在游戏画面设置里关掉帧生成,或把 Reflex 打开。
### 3.5 左上角两个重叠的 FPS 数字
不是 OptiScaler 的(`ShowFps=auto`=关)。是 **NVIDIA App 的性能叠加(绿色)** 与
**Steam 的游戏内 FPS 显示** 都默认在左上角。关掉其中一个即可。
---
## 四、调参对照表(全部来自 ini 里的原文说明)
| 键 | 含义 | 方向 |
|---|---|---|
| `TransferStrength` | **Detail strength**:0 = 超分器原样(真旁路)→ 1 = 模型的画面 → >1 超出模型本意 | ↓ 降噪/压远处闪动 |
| `ColourStrength` | **Colour strength**:0 = 完全保留游戏颜色、只让亮度带模型判断;1 = 连模型颜色一起用 | ↓ 去色彩噪点 |
| `MaxRatio` | **Highlight guard**(默认 2.0x):限制这一遍最多提亮多少倍 | 2x 说明书说已足够 |
| `WorkingScale` | 模型工作分辨率,**画面本身不降**,开销按平方走;**>1.0 超采样在 D3D12 可用**(终末地 D3D11 上不可用) | ↓ 变软/省开销 |
| `ScalingDownscaler` | 只在 `WorkingScale>1.0` 时用 | 上采样时再动 |
| `DebugView` | 0 关 / 1 模型看到的 / 2 模型原始答案 / **3 改动量放大 20 倍** | 灰屏 = 模型没在改 |
| `AutoCapture` | 写 `dlssnr-capture` 前后对比帧 | 实测未触发 |
| `Intensity` / `Preset` / `Style` / `Local*` | NVIDIA 自己的参数;`Preset` 改动需重启 | |
---
## 五、回滚
```powershell
& 'E:\deepseek\_tmp\feedkit\revert-forza.ps1'
```
按安装清单精确删除(12 个文件),并与安装前快照交叉核对。
上一轮的 ReShade / DFC 残留保留在 `E:\deepseek\game-backups\ForzaHorizon5\leftovers-*\`。
---
## 六、与终末地的差异对照
| | 终末地 | 地平线 5 |
|---|---|---|
| 反作弊 | **ACE** → 目录代理会被 `ACE-Setup64.exe` / `PlatformProcess.exe` 等吞掉 | **无** → 目录代理最简形态即可 |
| 注入方式 | 外部注入(`injector.exe`,只进 Endfield.exe) | 目录代理 `dxgi.dll` |
| 渲染 API | 只有 D3D11 / Vulkan(**没有 D3D12 后端**) | D3D12 |
| 超分器键 | `Dx11Upscaler=dlss_12`(dx11on12,**D3D11 上唯一能跑神经渲染的选项**) | `Dx12Upscaler=dlss` |
| 超采样 `WorkingScale>1` | 不可用 | **可用** |
| Smooth Motion | 必须为 Vulkan 关掉 | 无此约束 |
| 启动 | 桌面 `终末地-DLSS5启动.bat`(含提权) | Steam 正常启动 |
| 坑 | 目录代理污染 ACE 进程 | 窗口子类化 + raw input 钩子卡死消息泵 |
---
## 七、产物清单
| 文件 | 作用 |
|---|---|
| `E:\deepseek\_tmp\feedkit\forza-install-optiscaler.ps1` | 安装脚本 |
| `E:\deepseek\_tmp\feedkit\revert-forza.ps1` | 回滚脚本 |
| `E:\deepseek\_tmp\feedkit\OptiScaler-DLSSNR-v0.2.0.zip` | 原料(124 MB,SHA256 `8EECE7A4D7DE6DE5917F0C99AC60540B2D77022E7699BBA717B0A6D9E1829BCE`) |
| `E:\deepseek\game-backups\ForzaHorizon5\` | 证据:`hang-run-*\`(含 Application Hang 事件)、`OptiScaler.ini` 各阶段备份、安装清单、安装前快照、`leftovers-*\` |
+914
View File
@@ -0,0 +1,914 @@
# 路线 C(ReShade 路线)落地现状 — 实测记录
> 本文所有内容均为**实测**(文件系统检查、PE 导出表解析、DLL 字符串扫描、注册表读取),非推演。
> 记录时间:2026-09-16
---
## ★ 环境更正(2026-09-16 晚,推翻了本文早先的假设)
早先的记录基于「用户用 Modrinth App 玩 MC」这一**错误前提**,据此准备的
`H:\Java\zulu...` 专用 Java 与 Modrinth `app.db` 的 `java_path` 改动**打在了错误目标上**。
(当时搜 javaw.exe 的盘符列表**漏了 H 盘**,因此也没发现 `H:\javahome`。)
**实测出的真实环境:**
| 项 | 真实值 |
|---|---|
| 启动器 | **PCL2 便携版** `W:\PCL 正式版 2.12.8.2\Plain Craft Launcher 2.exe` |
| PCL 全局设置 | `W:\PCL 正式版 2.12.8.2\PCL\Setup.ini`(仅 RAM/窗口/目录,**不含 Java 选择**) |
| **MC 实际使用的 Java** | **`H:\javahome\bin\java.exe`** = **Oracle JDK 21.0.12.1**(318 MB,421 文件) |
| Java 清单 | `%APPDATA%\PCL\config.json` 的 `JavaList`(9 项,**不含 H:\javahome**,说明是自动探测) |
| 游戏目录 | `W:\PCL 正式版 2.12.8.2\.minecraft\versions\NGT\.minecraft\` |
| 真实实例 | 该目录下 `versions\1.20.1-Fabric 0.19.3\`(另有 `PVP`) |
| 版本级设置 | 实例内 `PCL\Setup.ini`:`VersionFabric:0.19.3`、`VersionVanilla:20.0.1`、`VersionArgumentJavaV2:3` |
| 该实例的 mods | 含 **Sodium** 0.5.13、DistantHorizons 3.2.0-b、lithium、c2me、modernfix、Jade、JEI 等;另有 `gpu-optimizer-0.1.0.jar.disabled` 与 `mods\disabled\` |
| 实例内的 `reshade` 目录 | 存在但**为空**(早先尝试的残留) |
**结论:ReShade 应注入 `H:\javahome\bin\`(用户已自行完成),Modrinth 相关的改动与
`H:\Java\zulu...` 对路线 C 无意义。**
### 注入结果(用户自行完成,已核验)
| 检查 | 结果 |
|---|---|
| `H:\javahome\bin\opengl32.dll` | 5,255,448 B,产品名 `ReShade`,ver `6.8.0.2158` |
| SHA256 | `B2945C29E7095491A901746B400E58DB9B1592AB092BACF2A888CE37F02D08DA` —— 与已验证那份**一致** |
| `ReShade.ini` | 506 B,安装器默认:`EffectSearchPaths=.\reshade-shaders\Shaders\**`、`KeyOverlay=36`(**Home**)、**无 `[ADDON]` 段** → addon 默认从 DLL 所在目录加载,正合 DLSS5-Feeder 预期 |
| 标准 effect 包 | 11 个 shader + 2 纹理,含 **`DisplayDepth.fx`**(深度自检工具)与 **`UIMask.fx`** |
| **`ReShade.log`** | 仍是 **982 B 安装器占位说明** → **ReShade 尚未加载** |
| 原因(已定位) | MC 进程 21:46 启动,ReShade 21:51 注入 —— **进程早于注入**,需重启游戏 |
### NGX 前置条件(已核验,正常)
| 项 | 值 |
|---|---|
| 注册表 | `HKLM\SOFTWARE\NVIDIA Corporation\Global\NGXCore` → `FullPath=C:\WINDOWS\System32\DriverStore\FileRepository\nv_dispi.inf_amd64_b20cc8aeaed64fc2`,`Installed=1` |
| `nvngx.dll` | **不在 System32 是正常的** —— NGX 靠上述注册表键定位 |
| 驱动 | RTX 5070 / **616.92**(DLSS5-Feeder 实测的坏组合是 616.56 / 616.64,本机更**新**,未知) |
---
## ★★ 里程碑:深度检测已实测通过(2026-09-16 22:0x)
**这是 C 路线最大的风险点,现已排除。绿色通过。**
### 证据链
用户按 `DisplayDepth.fx` 的默认设置截图(`H:\javahome\bin\java 2026-09-16 22-02-43_1.png`,2560×1369,3.7 MB)。
当前模型不支持读图,改用**程序化量化分析**(PIL + numpy),结论反而更硬。
`DisplayDepth.fx` 源码里的选项定义:
```glsl
ui_items = "Depth map\0Normal map\0Show both (Vertical 50/50)\0";
```
`iUIPresentType=2` → **左右分割:左半 = 法线图,右半 = 深度图**
(日文说明原文:「左に法線マップ、右に深度マップ」)。
**逐格灰度占比分析精确命中该分界:**
```
0|CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD|
左半 32 格全部 C(彩色) 右半 32 格全部 D(单色 >80%)
```
**判定「深度认到了」的三条依据:**
1. 右半深度图为单色且层次丰富(亮度跨 0-9 全档,第 11-12 行有清晰**地平线跃变**)。
若深度缓冲缺失,此处应为一片均匀死值。
2. 左半法线图为蓝绿系、**地面偏绿** —— 法线**只能由深度缓冲重建**。
源码说明原文:「正しい見え方では、全体的に青緑風で、**地平線を見たときに地面が緑掛かった色合い**になります」,**实测完全吻合**。
3. 色相分类图中左侧下半部大量 `g`(绿),即地面法线朝上的特征。
此前日志里 3 次 `WARN | A depth-stencil resource was destroyed while still in use.`
**经证实是无害噪声**,不是失败征兆。(我一度把它当成失败征兆,判断错了。)
### 深度方向:经用户实时观测确认为**正确**(我先前判断错了)
我基于**单张静态截图**的亮度分布(上暗下亮)推断「近亮远暗、方向反相」,
并据此改了 `RESHADE_DEPTH_INPUT_IS_REVERSED=1`。
**用户用可转动视角的实时画面直接观测,确认「远处亮」—— 即 `=0` 本来就是正确的。**
该设置**已回退为 `0`**。
**我错在哪**:从一张定格图推断「画面顶部 = 天空 = 远处」是无效推理 ——
镜头俯仰、地形遮挡都会让「屏幕上/下」与「远/近」不再对应,而静态图无法揭示拍摄时的朝向。
**凡是需要判断空间朝向的结论,必须靠可以主动移动视角的实时观测,不能靠单张静态帧。**
`DisplayDepth.fx` 的正确外观(源码定义):近处暗、远处亮。
---
## 一、已确认可用的环境事实
### 1.1 ReShade 注入位置(已成功)
| 项 | 值 |
|---|---|
| 启动器 | **Modrinth App** |
| 实例 | `Aged`(1.20.1 Fabric)、`Fabulously Optimized`(1.21.11 Fabric) |
| 注入目标 | `C:\Users\Administrator\AppData\Roaming\ModrinthApp\meta\java_versions\zulu21.48.17-ca-jre21.0.10-win_x64\bin\` |
| 代理 DLL | `opengl32.dll`,**5,255,448 B** |
| 版本 | `6.8.0.2158`,产品名 `ReShade`,公司 `crosire`,描述 "post-processing injector for 64-bit" |
| 系统原生对比 | `C:\Windows\System32\opengl32.dll` = 983,040 B(证明注入的是代理层) |
| 覆盖层按键 | `ReShade.ini` → `[INPUT] KeyOverlay=36,0,0,0` → key code 36 = **`Home`** |
| 已生成 | `ReShade.ini`(408 B)、`ReShade.log`(982 B)、`ReShadePreset.ini`(0 B) |
**⚠️ 这个 JRE 是 Modrinth App 的公共运行时**,被它所有实例共用 → 注入同时影响 `Aged` 和 `Fabulously Optimized`。
如果改用 PCL / HMCL 启动,它们用别的 Java,**ReShade 不会加载**。
### 1.2 Add-on 支持:**完整版,已用导出表定案**
`ReShade.log` 里那 982 字节是安装器**预置的占位说明**,不是真日志 ——
原文 "If you are reading this after launching the game at least once, it likely means ReShade was
not loaded by the game.",即 **ReShade 尚未真正加载过**。
DLL 里同时存在这两条字符串,光看字符串**无法定案**(两种构建可能都编进去):
```
Skipped loading add-on from '%s' because this build of ReShade has only limited add-on functionality.
Failed to register an event because only limited add-on functionality is available!
```
**定案证据是导出表**(共 494 个导出):
| 导出 | 用途 |
|---|---|
| `ReShadeRegisterAddon` / `ReShadeUnregisterAddon` | addon 注册入口 |
| `ReShadeRegisterEvent` / `ReShadeUnregisterEvent` | 事件 |
| **`ReShadeRegisterEventForAddon`** / `ReShadeUnregisterEventForAddon` | 每 addon 事件 ← DLSS5-Feeder 必需 |
| **`ReShadeRegisterOverlayForAddon`** / `ReShadeUnregisterOverlayForAddon` | 每 addon 覆盖层 |
| **`ReShadeCreateEffectRuntime`** / `ReShadeDestroyEffectRuntime` / **`ReShadeUpdateAndPresentEffectRuntime`** | effect runtime —— `reshade_render_technique` 靠这组工作 |
| `ReShadeGetImGuiFunctionTable` | ImGui 函数表 |
| `ReShadeGetConfigValue` / `ReShadeSetConfigValue` / `ReShadeSetConfigArray` | 配置读写 |
| `ReShadeGetBasePath` / `ReShadeLogMessage` / `ReShadeVersion` | 杂项 |
→ **受限版不会有 `*ForAddon` 系列与 effect-runtime 导出。这是完整 addon 版。**
### 1.3 GPU / 驱动(与官方实测表的对照)
| 项 | 值 |
|---|---|
| GPU | NVIDIA GeForce RTX 5070(+ AMD Radeon 核显) |
| GL | `4.6.0 NVIDIA 616.92`,405 个扩展 |
| **驱动版本风险** | DLSS5-Feeder 实测坏组合是 **616.56 / 616.64**;**本题机器是 616.92,比他们测过的都新,未知** |
| 自检手段 | `host64\dlss5-feed-host64.exe --test`,`300/300 evaluates succeeded` 才算 OK |
---
## 二、实例现状(决定了能不能测)
### 2.1 `Aged` —— 目标实例(1.20.1 Fabric,131 个 mod)
| 存在 | 缺失(与用户原述不符) |
|---|---|
| `iris-1.7.5+mc1.20.1.jar` | **Sodium 缺失** |
| `DistantHorizons-2.2.1-a-1.20.1-forge-fabric.jar` | Voxy 无 |
| `indium-1.0.34+mc1.20.1.jar` | Embeddium 无(且 Embeddium 是 Forge/NeoForge 系,Fabric 上本就不适用) |
| `Shut Up GL Error-fabric-1.20.1-1.0.0.jar` | **shaderpacks = 0 个** |
| `GeckoLibIrisCompat-Fabric-1.0.0.jar` | `config` 目录 **0 项**(131 个 mod 的实例,配置为空,反常) |
**两个必须先处理的问题:**
1. **Indium 硬依赖 Sodium,而 Sodium 不在 `mods/` 里**(按文件名核验,未读 jar 清单)。
Fabric 会在启动时报依赖错误弹红屏 —— **这与 ReShade 无关,但极易被误判成 ReShade 搞坏的**。
2. **`Shut Up GL Error` 会吞掉 GL 错误。** 调试 ReShade 期间应**移出 mods**,否则真实报错会被静默吃掉。
**好消息**:**shaderpacks 为 0** → Iris 不走自定义渲染管线 → 深度识别更接近原版,
比"带光影包"的情形**通过率更高**。这修正了此前"在 Iris 下很难"的判断。
### 2.2 `Fabulously Optimized` —— 1.21.11(非目标版本)
45 个 mod,含 `sodium-fabric-0.8.11+mc1.21.11.jar`、`iris-fabric-1.10.7+mc1.21.11.jar`、
`lithium`、`entityculling` 等;`shaderpacks` 同样为 0。
可作为"验证 ReShade 能否注入 Minecraft"的**干净对照实例**(用一个变量隔离问题)。
---
## 三、ReShade 的目录约定(关键,容易踩)
DLSS5-Feeder 文档原话:
> Add-on discovery is also a non-question: **ReShade *is* the local `opengl32.dll`,
> and add-ons load from its own directory.**
即 addon 默认要从 **`opengl32.dll` 所在的目录**加载 —— 对我们是那个 **JRE 的 `bin\`**,
这很不干净。但 DLL 里有 `AddonPath` 字符串,说明可用配置项改掉:
```ini
; 注入目录\ReShade.ini
[ADDON]
AddonPath=<游戏目录>\reshade-addons
DisabledAddons=
[GENERAL]
EffectSearchPaths=<游戏目录>\reshade-shaders\Shaders
TextureSearchPaths=<游戏目录>\reshade-shaders\Textures
```
- `*.addon` / `*.addon64` 放 `AddonPath`
- `DLSS5_Feed.fx` 放进 `EffectSearchPaths` 才能作为 effect 加载
- 当前安装**没有装任何 effect 包**(`reshade-shaders` 目录不存在),需要手工建目录或补装
---
## 四、所需组件清单
| 组件 | 来源 | 状态 |
|---|---|---|
| ReShade 6.8+ 含 add-on 支持 | reshade.me | ✅ **已装(6.8.0.2158,导出表已证 addon 完整)** |
| Generic Depth add-on | ReShade 自带,需在 Add-ons 页启用 | ⏳ 待首次运行确认 |
| 神经消费者:**Deep Fried Chicken**(推荐)或 Krish `renodx-dlss5.addon64` | 各自 **Discord** | ❌ 需用户获取(AI 无法访问 Discord) |
| `nvngx_dlssnr.dll` | GUI-DLSS5 包 | ✅ 已有 |
| `nvngx_dlss.dll`(放游戏目录旁) | Streamline SDK `bin\x64\` | ✅ 已有 |
| 运动矢量提供者:**LumeniteFX Kernel(=3,推荐)** | github.com/umar-afzaal/LumeniteFX | ❌ 需下载 |
| `dlss5-feed.addon64` + `DLSS5_Feed.fx` | DLSS5-Feeder release | ❌ 需下载 |
---
## 五、C 路线在 OpenGL 下的天花板(引自官方 Limitations)
> **DLAA contract, optional reduced work extent on D3D11** — render resolution still equals DLAA
> output resolution, but the private work extent can be 50–100% of the native backbuffer...
> **D3D12, Vulkan and OpenGL paths remain at 100%.** This is not jittered DLSS Super Resolution,
> and **a Quality/Balanced/Performance mode cannot be added**.
> Estimated motion vectors → temporal artifacts in fast motion; **the UI is processed with the scene**
> (a UI mask / pre-UI colour capture is future work).
**结论:OpenGL 下是纯 DLAA + DLSS 5 神经渲染观感,内部渲染分辨率不降 → 零帧率收益,无超分、无插帧。
且 UI(MC 的物品栏 / 聊天 / F3)会与场景一起被处理而拖影。**
底层机制(DLSS5-Feeder 官方描述 + 本项目实测验证):
```
game frame → ReShade effects → [motion vectors] → [DLSS5_Feed] → 私有 D3D12 设备
深度 + MV 跑真正的 DLSS evaluate
↓
神经结果写回画面 → 后续 effects → present
```
---
## 六、独立 Java 运行时(H 盘)— 已完成并验证
**动机**:Modrinth 的 JRE 是所有实例共用的,且 Modrinth App 可能校验/重下它把注入清掉。
独立一份 Java 可同时解决「污染其他实例」「被覆盖」「JRE 的 `bin\` 不适合作 addon 目录」三个问题。
| 项 | 值 |
|---|---|
| 运行时 | `H:\Java\zulu21.48.17-ca-jre21.0.10-win_x64\` |
| 来源 | Azul 官方 `https://cdn.azul.com/zulu/bin/zulu21.48.17-ca-jre21.0.10-win_x64.zip`(49,573,633 B) |
| 版本校验 | `Zulu21.48+17-CA (build 21.0.10+7-LTS)`;`IMPLEMENTOR=Azul Systems, Inc.`;`OS_ARCH=x86_64` —— **与 Modrinth 那份逐字一致** |
| 洁净性 | 解压后 `bin\` 内**无** `opengl32.dll`(原版) |
| ReShade 代理 | `bin\opengl32.dll` = 5,255,448 B,**SHA256 与已工作那份一致**:`B2945C29E7095491A901746B400E58DB9B1592AB092BACF2A888CE37F02D08DA` |
| 配置 | `bin\ReShade.ini`(本项目手写,1,600 B) |
| 内容根 | `H:\ReShadeMC\{addons, shaders, textures, presets}` |
| 备份 | `H:\Java\_reshade-backup\from_modrinth_jre\`(从 Modrinth JRE 移出的 17 个 ReShade 文件) |
**隔离已验证**:Modrinth 共享 JRE 已还原为 **327 个文件的原版**,
H 盘那份为 **329 个**,差异**恰好**是 `bin\opengl32.dll` 与 `bin\ReShade.ini` 两个文件。
**ini 键名已从 DLL 二进制核准**(非凭记忆):
`AddonPath` ✅ `DisabledAddons` ✅ `EffectSearchPaths` ✅ `TextureSearchPaths` ✅
`PerformanceMode` ✅ `NoEffectCache` ✅ `IntermediateCachePath` ✅
`ScreenshotPath` ❌ 不存在(故未写入)
> ⚠️ 本 ini 是**部分文件**,ReShade 首次运行会与自身默认值合并并**重写**它。
> 首次启动后**必须重读该 ini**,确认这些路径被接受而非被重置。
### 切换实例 Java 的开关(已定位)
`%APPDATA%\ModrinthApp\app.db`(SQLite)→ 表 **`instance_launch_overrides`**,
列 `overrides`(JSONB),字段 **`java_path`**:
```json
{"java_path":null,"extra_launch_args":null,"custom_env_vars":null,"memory":null,
"force_fullscreen":null,"game_resolution":null,
"hooks":{"pre_launch":"","wrapper":"","post_exit":""}}
```
UI 路径:Modrinth App → 实例 → Options → Java 版本 → 覆盖全局设置 → 选
`H:\Java\zulu21.48.17-ca-jre21.0.10-win_x64\bin\javaw.exe`
另:`java_versions` 表登记了 Modrinth 自管的那份:
`21 | 21 | amd64 | ...\ModrinthApp\meta\java_versions\zulu21.48.17-ca-jre21.0.10-win_x64\bin\javaw.exe`
---
## 七、实测发现的阻塞项
### ⚠️ `Aged` 实例根本没安装完
```
instances 表:
legacy:Fabulously Optimized install_stage = installed ✅
legacy:Aged install_stage = not_installed ⚠️ 未完成
```
这一条**解释了此前三个反常现象**:
1. `config` 目录 **0 项**(131 个 mod 的实例不该如此)
2. **Sodium 缺失**(`mods/` 里无 `sodium-*`)
3. **Indium 存在但依赖缺失** → Fabric 会报依赖错误弹红屏
**在修好之前测 ReShade,等于在坏地基上做实验。**
且该红屏**与 ReShade 无关**,极易被误判为 ReShade 搞坏了。
### 建议的测试顺序(单变量隔离)
1. 先拿 **`Fabulously Optimized`(installed,Sodium+Iris 正常)** 验证「ReShade 能否注入 Minecraft」
2. 再修好 `Aged`,验证「1.20.1 下深度识别能否成功」
### 其他
- **`Shut Up GL Error-fabric-1.20.1-1.0.0.jar`** 会吞掉 GL 错误 → 调试期间**移出 `mods/`**
- 已装上 ReShade 标准 effect 包,其中 **`UIMask.fx`** 可能是缓解「HUD 被一起处理」的抓手
---
## 八、待办与判据
| 步骤 | 判据 |
|---|---|
| 1. Modrinth App 里把实例 Java 覆盖为 H 盘那份 | Options → Java 版本 → 覆盖 |
| 2. 先用 `Fabulously Optimized` 启动 | ReShade 覆盖层能用 `Home` 呼出 |
| 3. 读**真** `ReShade.log` | 出现真实初始化日志,且含 `Searching for add-ons (*.addon, *.addon64) in '...'` |
| 4. 确认 `H:\Java\...\bin\ReShade.ini` 被重写后仍保留我们的路径 | `AddonPath` / `EffectSearchPaths` 未被重置 |
| 5. 让 `Aged` 装完(`install_stage` 变 `installed`) | Sodium 到位,无依赖红屏 |
| 6. 移出 `Shut Up GL Error` | — |
| 7. `Aged` 启动后进 **Add-ons** 页 | **`Generic Depth` 认到 MC 的场景深度 ← 整条 C 路线的生死线** |
| 8. 若第 7 步失败 | 记录 `ReShade.log` 全文;结论:C 在 MC 上不可行 → 回退路线 A/B |
**已备好的退路**:本项目自己的桥接已实测证明 GL↔VK 互操作完全可用
(见 README 第七节),因此即使 C 失败,超分/插帧仍有 A/B 两条路可走。
---
## ★★ 组件部署完成(2026-09-16 晚,全部经静态核验)
### 文件归位
| 文件 | 大小 | 放置位置 | 来源 | 核验 |
|---|---|---|---|---|
| `opengl32.dll` | 5,255,448 | `H:\javahome\bin\` | ReShade 6.8.0.2158 | SHA256 `B2945C29…08DA` |
| `nvngx_dlss.dll` | 58,956,912 | `H:\javahome\bin\` | Streamline SDK 2.14.1 | ver `310.9.1.0` |
| **`nvngx_dlssnr.dll`** | **165,840,496** | `H:\javahome\bin\` | GUI-DLSS5 v0.1.46 | **SHA256 `E16BCF15…1FC8E`,与原始 zip 逐位一致** |
| `dlss5-feed.addon64` | 308,224 | `H:\javahome\bin\` | DLSS5-Feeder 1.16.0-beta.2 | 见下 |
| `DLSS5_Feed.fx` | 51,193 | `…\bin\reshade-shaders\Shaders\` | 同上 | — |
| `lumenite_*.fx` ×8 | — | `…\Shaders\` | LumeniteFX `mainline` | — |
| `lumenite_*.fxh` ×4 | — | `…\Shaders\include\` | 同上(**必需**,缺则编译失败) | — |
| `lumenite_bluenoise256.png` | 259,314 | `…\reshade-shaders\Textures\` | 同上 | — |
### `nvngx_dlssnr.dll` 导出符号(证实是 NGX 插件)
55 个导出,含完整 NGX API:
`NVSDK_NGX_VULKAN_Init` / `_CreateFeature` / `_EvaluateFeature` / `_GetScratchBufferSize`,
`NVSDK_NGX_D3D12_Init` / `_CreateFeature` / `_EvaluateFeature`,
`NVSDK_NGX_D3D11_*`、`NVSDK_NGX_CUDA_*`。
> 注:`dlssnr_init/process/...` 那组**不在**这个文件里 —— 它们在 `dlssnr_host.dll`(另一个 shim)。
> 我一开始按 `dlssnr*` 过滤导出,什么也没匹配到,就是这个原因。
### `dlss5-feed.addon64` 的加载机制(曾误判,已澄清)
`dumpbin /exports` 确认它**只导出 `DESCRIPTION` 与 `NAME`**,我一度以为它无法被 ReShade 加载。
真相:它的**导入表里只有系统 DLL**(VERSION/KERNEL32/USER32/ADVAPI32/MSVCP140/VCRUNTIME140/ucrt),
**没有任何 ReShade 模块依赖**。二进制里那些 `ReShadeRegisterAddon` / `ReShadeGetConfigValue` /
`ReShadeRegisterEvent` 等是**字符串**(运行时用 `GetProcAddress` 解析)。
**结论:我们的主机命名为 `opengl32.dll`(而非 `ReShade64.dll`)不会造成任何问题。**
### ★★ 重大纠正:ReShade 分两个构建,导出表**证明不了**是哪一种
我早前从导出表看到 `ReShadeRegisterAddon` / `*ForAddon` / `ReShadeCreateEffectRuntime` 等,
据此写了「已用导出表定案:这是完整 addon 版」。**这个结论是错的。**
实测对照(同一目录、同一 addon,**只换 DLL**):
| ReShade DLL | 大小 | `ReShade.log` 结果 |
|---|---|---|
| 标准/受限版 | 5,255,448 | `WARN \| Skipped loading add-on from '...' because this build of ReShade has only limited add-on functionality.` ❌ |
| **Addon 版** | **5,592,064** | `INFO \| Registered add-on "DLSS 5 Feed 1.16.0-beta.2" v1.16.0.2 using ReShade API version 20.` ✅ |
**教训**:受限版**照样导出完整 addon API**(好让 addon 能编译链接),真正的开关是**编译期**
`RESHADE_ADDON_LITE`,**只在运行时拒绝加载时才暴露**。
→ 判断这类能力时,**导出表只能证明「API 存在」,不能证明「功能启用」;必须做运行时验证**。
### ReShade 两个构建的获取方式
| 构建 | 安装包 | 大小 |
|---|---|---|
| 标准版(受限 addon) | `https://reshade.me/downloads/ReShade_Setup_6.8.0.exe` | 4,075,064 B |
| **Addon 版(完整 addon)** | `https://reshade.me/downloads/ReShade_Setup_6.8.0_Addon.exe` | **4,318,424 B** |
Addon 版安装包是自定义格式(载荷压缩存在 `.rsrc`),但**可用 7-Zip 直接解开**,
无需运行安装器:
```
7z x ReShade_Setup_6.8.0_Addon.exe -o<目录>
→ ReShade64.dll 5,592,064 B ver 6.8.0.2155
SHA256 0CEE63F9C9F13F3AC909C5B4903F4DBB4B719A7AB3B4F13B0DEAF83C814B94F7
(另有 ReShade32.dll + 4 个 Vulkan/XR layer json)
```
把 `ReShade64.dll` 改名为 `opengl32.dll` 放进 Java 的 `bin\` 即等效于安装。
**受限版已备份**:`H:\DLSS\reshade_addon_extract\opengl32.dll.limited-build.bak`。
依赖预检:15 个依赖全部可解析(MSVC 运行时在 System32 与 JDK 自带副本中均有)。
### `DLSS5_Feed.fx` 源码揭示的必知规则
1. **addon 在 `DLSS5_Feed` technique 渲染完之后立刻运行 DLSS** → **必须勾选 `DLSS5_Feed.fx`**
2. 运动矢量 provider:`0`=texMotionVectors(默认)/ `1`=Launchpad / **`2`=VORT(源码标注 recommended)** /
**`3`=LumeniteFX Kernel** / `4`=LumeniteFX QuantMotion。已备好 3 与 4
3. provider 的 technique **必须排在本 effect 之上**才会生效
4. 它带有一条针对我们最担心失败模式的自我诊断字符串:
`"<-- depth is FLAT while the scene moves: ReShade's Generic Depth is on the wrong buffer"`
5. **无神经消费者时仍可运行「纯 DLAA」** —— 驱动兼容表里 `(none — DLAA only, no neural pass)` 一行是 `✅ 300/300`。
因此**不必等 Discord 组件就能验证整条管线**
### 仍缺(只能从 Discord 取,AI 无法访问)
`deep-fried-chicken.addon64` + `deep-fried-chicken-nvngx.dll` + `deep-fried-chicken.cfg`
**或** Krish 的 `renodx-dlss5.addon64`(RenoDX Discord `#DLSS5`)。**两者只能放一个。**
两者均不自带 `nvngx_dlssnr.dll`。
### 下一步操作(用户)
1. ReShade 全局预处理定义加 `DLSS5_MV_PROVIDER=3`
2. 效果列表中:**先启用 `lumenite_Kernel.fx` 的 technique,再在其下方启用 `DLSS5_Feed.fx`**;关掉 `DisplayDepth.fx`
3. 重启 MC(addon 与 effect 变更需重新加载)
4. 读日志:`H:\javahome\bin\dlss5-feed.log`(新增)与 `ReShade.log`
—— DLSS5-Feeder 要求 `dlss5-feed.log` **第 1 行**为
`dlss5-feed 1.16.0-beta.2 commit a6c23bd`
---
## ★★★ 驱动与 NGX 栈预检:完全通过(2026-09-16 22:24)
DLSS5-Feeder 自带 `host64\dlss5-feed-host64.exe --test`,可在**不启动游戏**的前提下
单独验证「驱动 + NGX + NGX 插件」这一栈。它需要三样东西与它同目录:
| 需要 | 说明 |
|---|---|
| `nvngx_dlss.dll`、`nvngx_dlssnr.dll` | 它在**自己所在目录**查找 NGX 运行时(不是在游戏目录) |
| `dxgi.dll` | ReShade 的 DLL **改名为 `dxgi.dll`**(同一份二进制可直接改名复用)——它需要自己起一个私有 D3D12 swapchain |
| 工作目录 = 它自己所在目录 | 结果写进 `dlss5-feed-host.log`,**stdout 无输出**,退出码反映结果 |
### 实测结果
```
[host] device adapter: NVIDIA GeForce RTX 5070 PCI 10DE:2F04 driver 616.92
[host] NGX runtime nvngx_dlssnr.dll: 310.8.0.0, NVIDIA DLSSNR - DVS PRODUCTION, CL 38718415
[host] NGX runtime nvngx_dlss.dll: 310.9.1.0, NVIDIA Deep Learning SuperSampling, CL 38868064
[host] NGX feature requirements: SuperSampling (DLSS) -> supported (min arch 0x160)
[host] NGX feature requirements: feature 18 (neural rendering) -> supported (min arch 0x1B0)
[host] NVSDK_NGX_D3D12_Init -> 0x00000001 (Success)
[host] NGX capabilities: SuperSampling.Available=1 NeedsUpdatedDriver=0 MinDriver=470.0
[host] DLSS Quality at 1920x1080: optimal 1280x720, render range 960x540 .. 1920x1080
[host] feature ready: 640x360 DLAA flags=74
[host] --test finished: 300/300 evaluates succeeded
[host] --test: DLSS GPU 0.43 ms/frame over 299 timed frames at 640x360
[host] exit 0
```
**`300/300 evaluates succeeded` 即官方规定的通过标准。**
### 这一条解决了哪些悬置的未知
| 未知 | 结论 |
|---|---|
| **驱动 616.92 可用性**(官方实测的坏组合是 616.56 / 616.64,本机更新) | ✅ **可用**,`NeedsUpdatedDriver=0`,`MinDriver=470.0` |
| `nvngx_dlssnr.dll` 身份 | ✅ `NVIDIA DLSSNR - DVS PRODUCTION` `310.8.0.0` |
| feature 18(神经渲染)在 RTX 5070 上是否支持 | ✅ 支持,最低架构 `0x1B0` = Blackwell |
| DLSS evaluate 实际吞吐 | ✅ 实测 **0.43 ms/帧 @640×360** |
| 无神经消费者时能否运行 | ✅ 能 —— 本次即「无消费者」配置,对应官方表 `(none — DLAA only, no neural pass)` 一行 |
**推论:不必等 Discord 组件即可验证游戏侧整条管线(纯 DLAA 路径)。**
### 已可清理
`H:\DLSS\DLSS5-Neural-Render-v0.1.46-windows-x64.zip`(467,988,619 B)——
`nvngx_dlssnr.dll` 已解出并通过哈希核验,此 zip 可删。
---
## ★★★ 全部组件安装完成(2026-09-17 18:45)
### 最终文件清单(`H:\javahome\bin\`)
| 文件 | 大小 | 来源 | 校验 |
|---|---|---|---|
| `opengl32.dll` | 5,592,064 | ReShade 6.8.0.2155 **Addon 版** | SHA256 `0CEE63F9…B94F7` |
| `nvngx_dlss.dll` | 58,956,912 | Streamline SDK 2.14.1 | Authenticode **Valid**(NVIDIA) |
| `nvngx_dlssnr.dll` | 165,840,496 | GUI-DLSS5 v0.1.46 | SHA256 `E16BCF15…1FC8E` |
| `dlss5-feed.addon64` | 308,224 | DLSS5-Feeder 1.16.0-beta.2 | — |
| `deep-fried-chicken.addon64` | 4,422,656 | DFC 2.0.0-CP184 | SHA256 `C99C3528…4F303` ✅ |
| `deep-fried-chicken-nvngx.dll` | 3,584 | 同上 | SHA256 `9350E044…394FF` ✅ |
| `deep-fried-chicken.cfg` | 19,790 | 同上 | SHA256 `A52A51EA…6A9AD` ✅ |
| `ReShade.ini` | 2,982 | — | 见下 |
| `.dfc-installer\manifest.json` | 1,558 | Chicken Assist | `status: installed` |
effect 侧:`DLSS5_Feed.fx` + `lumenite_*.fx`(8) + `include\*.fxh`(4) + `lumenite_bluenoise256.png`
### DFC 安装(用官方 Chicken Assist,非手动复制)
```
CHICKEN-ASSIST.cmd -Action Install -Target H:\javahome\bin \
-GameExecutable H:\javahome\bin\java.exe -Api opengl -Yes -NonInteractive
```
安装卡实测输出:
```
Route : DFC through external DLSS5-Feeder
Layout : x64-external-core
Plan : An external feeder is already present. DFC will keep it and
install only the DFC consumer.
[PASS] ×10,仅 [WARN] Runtime evidence(启动游戏后可消除)
```
**路线矩阵里明确列着我们的情况**(Chicken Assist `installer/README.md`):
> `| x64 OpenGL | DFC through external DLSS5-Feeder | Chicken Assist invokes the official
> cross-API route; **optical-flow motion may be required** |`
→ 正对应我们已配好的 LumeniteFX Kernel 光流(`DLSS5_MV_PROVIDER=3`)。
### 三个 payload 变体是同一份二进制
`payload-manifest.json` 里 `x64-core` / `x64-external-core` / `x64-native-d3d11-core`
的 SHA256 **完全相同**(`C99C3528…` / `9350E044…` / `A52A51EA…`),只是安装器的记账分类,
**不存在放错变体的风险**。
### Chicken Assist 对我们配置的改动:仅一行
安装器**主动删除**了我手写的 `[ADDON] LoadFromDllMain=deep-fried-chicken.addon64`,
理由是该键在 2.0 中已废弃(对应 1.4.3 更新说明:Chicken 会自己加入 early-load 列表)。
逐行 diff 确认**其余配置一字未动**:`DLSS5_MV_PROVIDER=3`、effect 路径、预设路径全部保留。
精确备份:`.dfc-installer\backup\reshade\ReShade.ini.D4A4C0F8….pre-cp47.bak`
### ⚠️ 环境坑:调用 Chicken Assist 必须先修正 `PSModulePath`
DSH 会话的 `PSModulePath` 是 PowerShell 7 的(且含空条目与 `W:\Fuck`),
子进程 `powershell.exe` 5.1 继承后会**无法自动加载自己的 `Microsoft.PowerShell.*` 模块**,
表现为 `Get-AuthenticodeSignature` / `Get-FileHash` "not recognized",进而误报
`[FAIL] Ownership record`、`DLSS-NR DLL: missing`。**规律已实测确认**:
| 调用 | 修正 PSModulePath | 结果 |
|---|---|---|
| Inspect #1 | ❌ | FAIL(`Get-AuthenticodeSignature` 找不到) |
| Inspect #2 | ✅ | PASS |
| Install | ✅ | PASS |
| Diagnose #1 | ❌ | FAIL(`Get-FileHash` 找不到,误报) |
| Diagnose #2 | ✅ | **全 PASS** |
修法:调用前设
```
$env:PSModulePath='C:\Users\Administrator\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\Windows\System32\WindowsPowerShell\v1.0\Modules'
```
**这是一条通用教训**:从本会话派生的任何 5.1 子进程都可能撞上,涉及签名的判断尤其要先确认模块可用。
### 关键配置现状
| 项 | 值 |
|---|---|
| `Techniques` | `Lumenite_Kernel@lumenite_Kernel.fx,DLSS5_Feed@DLSS5_Feed.fx` |
| `TechniqueSorting` 前 3 位 | `Lumenite_Kernel` → `Daltonize` → `Deband`(**生产者在前** ✅) |
| `DLSS5_MV_PROVIDER` | `3`(LumeniteFX Kernel) |
| `RESHADE_DEPTH_INPUT_IS_REVERSED` | `0`(据用户实时观测确认) |
| `dlss5-feed.cfg` | `enabled=1 mode=2 work_resolution=100 stall_log_ms=50` |
### 唯一剩余步骤
**启动游戏**(此前的 `ReShade.log`/`dlss5-feed.log` 都是安装前的)。
DFC 1.4.3 说明:会自己加入 early-load 列表,**首次启动是注册,可能需再完整重启一次**。
之后应产出新文件 `deep-fried-chicken.log`,再跑 `-Action Diagnose` 即可拿到运行期裁决。
---
# 2026-09-17 19:07 — 模组侧收尾:`/dlss` 命令 + 视频设置里的总开关与面板入口
路线 C 已经跑通(见上文运行期证据)。这一轮做的是**模组自己的壳**:让玩家不需要记命令就能开 DLSS。
## 交付物
| 东西 | 位置 |
|---|---|
| 构建产物 | `build/libs/wpywdlss-0.1.0.jar`(3.83 MB,SHA `765E71434912A74F96199FD4C2CE50942F1BAC49FACBB270685F910EBDA125BB`) |
| 已部署 | `…\1.20.1-Fabric 0.19.3\mods\wpywdlss-0.1.0.jar`(与构建产物哈希一致) |
| 新增源码 | `cfg/MasterSwitch.java`、`gui/VideoSettingsHook.java` |
| 改动源码 | `cfg/FlatCfg.java`(行尾保留,见下)、`DlssClientMod.java`、`gui/DlssSetupScreen.java`、`fabric.mod.json` |
## 1. 命令改名 `wpywdlss` → `dlss`
子命令:`dlss`(开面板)/ `status` / `install` / `uninstall` / `on` / `off`。
`openPanel()` 现在用 `mc.screen` 当 parent,所以从哪个界面进去、关掉就回到哪个界面,而不是一律掉回游戏。
## 2. 视频设置里注入两个按钮(`gui/VideoSettingsHook.java`)
**左侧 = 总开关(`DLSS: 开` / `DLSS: 关`),右侧 = 面板入口(`DLSS · 已安装` 等,标签直接带实时状态)。**
### 为什么用事件而不是 mixin
`Screens.getButtons(screen)` 返回的是 Fabric 的 `ButtonList`,构造时**同时持有屏幕自己的 `drawables` / `selectables` / `children` 三个列表**,
`add()` 往三个里都插 —— 也就是 `addRenderableWidget` 做的事。**渲染、点击派发、键盘焦点全部走原版同一条路径**,
不需要 accessor / `@Invoker`。这一点是抽 `ButtonList.java` 源码确认的,不是推测。
### 布局常量是 dump 出来的,不是猜的
`javap -c net.minecraft.client.gui.screens.VideoSettingsScreen` 实测:
```
new OptionsList(minecraft, width, height, 32, height - 32, 25)
Done → Button.builder(GUI_DONE, …).bounds(width/2 - 100, height - 27, 200, 20)
```
所以**选项行止于 `height - 32`,Done 所在那条横带左右两侧都是空的**,两个按钮就放那里:
```
side = width/2 - 100 - 6 - 4 // 4 = 离窗口边的余量
w = min(170, side) // side < 64 时干脆不注入,/dlss 仍可用
左侧 x = width/2 - 100 - 6 - w 右侧 x = width/2 + 100 + 6
y = height - 27
```
窗口宽度实测:853 → w=170 两侧都放得下;640 → w=170 仍在界内;427 → w=103 仍在界内。
### 重复注入的疑虑(查清了,是安全的)
`Screen.init(mc,w,h)` 有个 `initialized` 标志位,且 `ScreenMixin` 在 `init` TAIL **和** `resize` TAIL 都调 `afterInit`,
一度怀疑会重复加按钮。dump `Screen` 字节码后确认三条路径都只重建一次:
| 路径 | `init(mc,w,h)` 里发生了什么 | 结果 |
|---|---|---|
| 首次打开 | `initialized=false` → `init()` | 一次 |
| 从面板返回(同一个 Screen 实例) | `initialized=true` → `repositionElements()` | 一次 |
| 窗口缩放 | `resize` → `repositionElements()` | 一次 |
关键:`Screen.repositionElements()` 的实现就是 `rebuildWidgets()` = `clearWidgets(); clearFocus(); init();`,
而 `resize` 只调 `repositionElements()`、**不再回调 `init(mc,w,h)`**,所以不会叠加触发。
而且 `clearWidgets()` 清的正是 `ButtonList` 持有的那三个列表,与注入点完全对齐。
### 探测缓存
`analyze()` 要哈希 ~10 MB 载荷,所以状态探测带 1.5 s TTL;`DlssSetupScreen.refresh()` 与 `install/uninstall/on/off` 都会
`VideoSettingsHook.invalidate()` 强制下次重读。
## 3. `cfg/MasterSwitch.java`:一个开关同时管两个 add-on
DLSS 不是单一标志位 —— **DLSS5-Feeder 和 Deep Fried Chicken 各有一个 `enabled`,必须同时开**,
半开状态(Feeder 在请求神经帧但没人渲染)是真实且难查的故障模式。所以 `read/write` 一律成对处理,且:
- 任一 cfg 缺失 → 不算「开」(不把「没装」显示成「已开」);
- 未安装时 `write` 直接拒绝,**不创建空 cfg 文件**;
- 值没变就不写盘(`FlatCfg.set` 返回 false),避免无谓重写 666 行、与 add-on 自己的写回打架;
- 返回的是**写完之后磁盘上的真实状态**,不是调用方要求的状态。
## 4. ★ 实测抓到的真 bug:`FlatCfg.save()` 会把别的程序的文件整份改成 LF
真实环境的行尾实测:
| 文件 | CRLF | 裸 LF | 字节 |
|---|---|---|---|
| `dlss5-feed.cfg` | 23 | 0 | 353 |
| `deep-fried-chicken.cfg` | **666** | **0** | 19790 |
| `ReShade.ini` | 95 | 0 | 2982 |
| `ReShadePreset.ini` | 109 | 0 | 2452 |
而 `FlatCfg.save()` 固定 `String.join("\n", …)` —— **一按开关就会把 DFC 那份 666 行的 CRLF 文件连行尾一起重写**。
`FlatCfg` 原本的注释还把「用 LF 保持 diff 友好」当成优点,恰恰搞反了:这两个文件属于别的程序。
修法:`load()` 读原始字节并探测主导行尾,`save()` 用读进来的那个行尾;新建文件才回退 `System.lineSeparator()`。
顺带补了 `decodeLines()`,语义对齐 `Files.readAllLines`(末尾终止符不产生空行、中间空行保留)。
`IniFile.save()` 本来就固定写 CRLF,与真实文件一致,**未改动**(避免无谓churn)。
## 5. 验证
`MasterSwitch` 只碰文件系统、不依赖 MC 与 GL,所以**能真跑**:`E:\deepseek\_tmp\tlstest\MasterSwitchTest.java`,
**35 项全过**:
- 未安装:不创建文件、`write` 被拒且给出原因;
- 半安装(只有 feeder cfg):`switchable=false`、`on=false`;
- **CRLF 场景(照抄真实安装)**:605 行 CRLF 文件翻转后 **CRLF=605 / 裸 LF=0**,且
「恰好一行变化,其余逐字保留」—— 首键、末键、注释行、空行全部原样;
- **LF 场景**:LF 进 LF 出,确认修法没有把 CRLF 写死;
- 冗余写是真空操作(mtime 与字节都不变);
- 真值判定:`0`/`1`/`true`/`TRUE`/`enabled = 1`(等号两侧空格)。
对真实安装目录只读探测:`on=true switchable=true feedPresent=true chickPresent=true`。
## 6. 静态验证清单(每条都对着实际编译好的 jar 查过)
| 依赖 | 依据 |
|---|---|
| `Screens.getButtons(Screen) → List<AbstractWidget>` | `javap` fabric-screen-api-v1-2.0.9 |
| `ButtonList.add` 同时进三个列表 | 抽 `ButtonList.java` 源码 |
| `ScreenEvents.AFTER_INIT` 回调签名 `(client, screen, w, h)` | 抽 `ScreenMixin.java` 源码 |
| `Button.Builder.tooltip(Tooltip)` / `Tooltip.create(Component)` | `javap` minecraft-merged 1.20.1 |
| `OptionsList` 只有 `addBig/addSmall(OptionInstance…)` | `javap` —— 所以塞自定义控件行要 2–3 个 mixin accessor,**不划算,放弃** |
| 已装 `fabric-api-0.92.11` 内含 `fabric-screen-api-v1` | 列出 jar 内嵌 `META-INF/jars/fabric-screen-api-v1-0.92.11.jar` |
## 7. 待用户确认
**必须重启游戏**(ReShade 是 `opengl32.dll` 代理,本次启动时早已解析完毕,模组无法在进程内激活它)。
重启后进 **设置 → 选项 → 视频设置**,应看到:
- 右侧 `DLSS · 已安装`(若显示未安装/需修复,说明 `H:\javahome\bin` 的载荷状态变了);
- 左侧 `DLSS: 开`(只有两个 cfg 都在时才出现)。
点左侧应能即时切换(两个 cfg 都带 CRLF 保留、只有 `enabled=` 一行变化),点右侧进面板。
## 8. 顺手发现(与本模组无关)
`mods` 目录里有**两个 MouseTweaks**(2.25 与 2.26)同时存在,是重复 mod;另外
`gpu-optimizer-0.1.0.jar.disabled` 已被改名为 `.disabled` 所以不会加载。
---
# 2026-09-17 19:40 — 【重大】1.21.1 + VulkanMod 的 Vulkan 通路打通
**结论先行:能。** DLSS5-Feeder 有**一等公民的 Vulkan 传输层**,不需要 OpenGL,也不是"勉强识别"。
这一轮把整条链路在**不启动 Minecraft 的前提下**验证到了 API 层。
## 8.1 起因:为什么之前认为不可能,以及为什么那是错的
1.21.1 实例的 `mods` 里有 **VulkanMod 0.6.7**(`== VulkanMod ==` / `Backend library: LWJGL 3.3.3`,
日志里没有 `OpenGL Version` 行),而当时 ReShade 是**按 OpenGL 装的**(`H:\javahome\bin\opengl32.dll` 代理)。
19:15 那次启动的实测证据(说明 GL 装法在 Vulkan 游戏上确实拿不到帧):
| 事实 | 出处 |
|---|---|
| ReShade 初始化了,但只挂到 `wglSetPixelFormat`(GLFW 的临时 GL 上下文) | `ReShade.log` 19:15:21 |
| 两个 add-on 注册后 **6 毫秒就被卸载**(`.358` 注册 → `.364/.365` Unloading) | 同上 |
| feeder/chicken 日志**只有启动行**,零帧 | `dlss5-feed.log` |
**但那只是"装法不对",不是"做不到"。** feeder 的二进制里带完整的 Vulkan 实现:
> *"Feeds DLSS 5 neural rendering ... in **D3D11, D3D12, Vulkan and OpenGL** games without DLSS"*
## 8.2 feeder 的 Vulkan 机制(源码级)
`jlrouzies-fr/DLSS5-Feeder` 是**开源**的,`docs/` 与 `layer/` 都在。README §The Vulkan path:
- DLSS 5 add-on **只 hook D3D12 NGX 入口**,所以即使 NGX 有 Vulkan API 也没用;
- 因此 evaluate 跑在**私有 D3D12 设备**上,帧通过**共享内存**跨 API 边界(不是拷到内存再回来);
- D3D12 侧建 `D3D12_HEAP_FLAG_SHARED` 纹理 + 两个 `D3D12_FENCE_FLAG_SHARED` 栅栏并导出 NT handle;
- add-on 用**裸 Vulkan** 把它们导入游戏自己的 `VkDevice`
(`VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT` 等)—— **D3D12 栅栏和 Vulkan timeline semaphore 是同一个对象**;
- 互操作扩展**在 `vkCreateDevice` 时就固定了**,而游戏很少主动要 → add-on 在 `vulkan-1.dll` 的
`vkCreateDevice` 上打 MinHook 把扩展追加进去(`src/feed_vk_hook.h`)。
## 8.3 ★ 两个必须知道的坑(都是实测定位的,不是猜的)
### 坑 1:`ReShade64.dll` 加载失败 `error 1114` —— 根因是 `<exe目录>\ReShade.ini` 必须存在
现象:loader 报 `Failed to open dynamic library "C:\ProgramData\ReShade\.\ReShade64.dll" with error 1114`。
定位过程(每一步都排除了一个假设):
| 实验 | 结果 | 排除了什么 |
|---|---|---|
| `DONT_RESOLVE_DLL_REFERENCES` 加载 | 成功,`vkNegotiateLoaderLayerInterfaceVersion` **是导出符号** | 导出表缺失 |
| 同字节改名 `ReShade64.dll` 放同一目录 | 1114 | 目录/DLL 损坏 |
| 把它加进 `ReShadeApps.ini` 白名单 | 仍 1114 | 白名单(至少不是这么用的) |
| 补 `ReShade32.dll` / `ReShade32.json` 兄弟文件 | 仍 1114 | 32 位兄弟 |
| 同字节命名为 `opengl32.dll` | **成功** | → 决定因素是**模块名** |
然后读 `crosire/[email protected]` 的 `source/dll_main.cpp`:
```cpp
const bool is_opengl = _wcsicmp(module_name.c_str(), L"opengl32") == 0;
...
const bool default_base_to_target_executable_path =
!is_d3d && !is_dxgi && !is_opengl && !is_dinput && !is_asi && !is_uwp_app();
...
// "This e.g. prevents loading the implicit Vulkan layer when not explicitly enabled for an application"
if (default_base_to_target_executable_path && !GetEnvironmentVariableW(L"RESHADE_DISABLE_LOADING_CHECK", ...))
if (!std::filesystem::exists(config.path(), ec))
return FALSE; // ← 1114 的来源
```
而 `ini_file.cpp`:
```cpp
reshade::ini_file &reshade::global_config() {
return ini_file::load_cache(g_reshade_base_path / L"ReShade.ini");
}
```
**所以那道门就是 `<基路径>\ReShade.ini` 是否存在**;`opengl32.dll` 这种代理名直接跳过该检查,
`ReShade64.dll` 这种 layer 名则必须过。`vulkaninfo.exe` 在 System32、那里没有 `ReShade.ini` → 必然 1114。
(注意:`ReShadeApps.ini` 在 **ReShade DLL 源码里根本不存在**,它是 setup 工具的概念。)
### 坑 2:GL 代理与 Vulkan layer **不能共存于同一进程**
`dll_main.cpp` L200-211:
```cpp
for (DWORD i = 1; i < ...; ++i)
if (modules[i] != hModule && GetProcAddress(modules[i], "ReShadeVersion") != nullptr) {
// "Another ReShade instance was already loaded from '%s'! Aborting initialization ..."
return FALSE;
}
```
`ReShadeVersion` 是 `extern "C" __declspec(dllexport) const char *ReShadeVersion`,我们的 `opengl32.dll`
**确实导出它**。隔离实验(用 `RESHADE_DISABLE_LOADING_CHECK=1` 绕过坑 1 以触及坑 2):
| 场景 | 结果 |
|---|---|
| 单独加载 layer 的 `ReShade64.dll` | **成功** |
| 先加载 GL 代理 `opengl32.dll`,再加载 layer | **1114 —— 被挡** |
而 **GLFW 初始化时必然加载 exe 目录里的 `opengl32.dll`**(`wglGetProcAddress`),且它常驻不卸载。
所以**只要 `opengl32.dll` 还在 JVM 目录里,Vulkan layer 永远起不来** —— 这解释了为什么
"共用 `H:\javahome`"这条路走不通。
## 8.4 解法:给 1.21.1 一个干净的 JVM 目录,配置用绝对 BasePath 共享
`H:\javahome` 原样不动(1.20.1 的 OpenGL 那套继续用),新建:
```
H:\javahome-vulkan\ ← 从 C:\Program Files\Microsoft\jdk-21.0.9.10-hotspot 复制的干净 JDK
(327 MB / 486 文件;bin 里没有 opengl32.dll)
bin\ReShade.ini ← 唯一需要的文件,两行:
[INSTALL]
BasePath=H:\javahome\bin
conf\ include\ jmods\ legal\ lib\ release ← 原样复制
```
`get_base_path()` 会先读 `<exe目录>\ReShade.ini` 的 `[INSTALL] BasePath`(绝对路径直接用),
于是这个实例的**全部配置、预设、shader、add-on、NGX 运行时都取自 `H:\javahome\bin`,一份都没复制**。
### 机器级 Vulkan layer 目录
```
C:\ProgramData\ReShade\
ReShade64.dll 5,592,064 B ← 与 H:\javahome\bin\opengl32.dll **字节完全相同**
(都是 ReShade 6.8.0.2155 add-on 构建,双方 SHA 一致)
ReShade64.json 526 B ← crosire 官方 layer 清单(name=VK_LAYER_reshade)
ReShade32.dll / ReShade32.json
ReShadeApps.ini ← Apps=<java 路径列表>,UTF-8+BOM
```
注册:**`HKCU\SOFTWARE\Khronos\Vulkan\ImplicitLayers`**(名称=json 路径,DWORD 0)。
**故意不用 HKLM** —— 本会话是 Medium 完整性、写 HKLM 被拒;而本机 OBS 的
`obs-vulkan64.json` 就在 HKCU,证明这条在 loader 1.4.341 上确实会加载。
## 8.5 验证结果(不依赖 Minecraft)
把 `vulkaninfo.exe` 临时复制进 `H:\javahome-vulkan\bin\` 再跑(跑完删除):
```
[Vulkan Loader] DEBUG | LAYER: Loading layer library C:\ProgramData\ReShade\.\ReShade64.dll
[Vulkan Loader] INFO | LAYER: Insert instance layer "VK_LAYER_reshade"
[Vulkan Loader] INFO | LAYER: Inserted device layer "VK_LAYER_reshade"
```
```
ReShade.log:
Initializing ReShade 6.8.0.2155 loaded from 'C:\ProgramData\ReShade\ReShade64.dll'
into 'H:\javahome-vulkan\bin\vulkaninfo.exe' ...
Redirecting vkCreateInstance(...)
Searching for add-ons (*.addon, *.addon64) in 'H:\javahome\bin' ...
Registered add-on "Deep Fried Chicken 2.0.0"
Registered add-on "DLSS 5 Feed 1.16.0-beta.4"
```
```
dlss5-feed.log:
[feed] vkCreateDevice hook installed on vulkan-1!vkCreateDevice
[feed] vkQueuePresentKHR hook installed
[feed] vkCreateDevice #1/#2: app asked for 0 extension(s), added 7, timelineSemaphore enabled
[feed] VK_KHR_external_memory / _win32 / external_semaphore / _win32 /
[feed] dedicated_allocation / get_memory_requirements2 / timeline_semaphore ← 全部 ADDED
[feed] Vulkan present dependency hook installed at device dispatch
[feed] vkCreateDevice -> 0
```
唯一没验证的只剩"**真实 swapchain 每帧投递**"(vulkaninfo 不呈现画面),那需要真正启动游戏。
## 8.6 顺带升级与产物
- feeder **beta.2 → beta.4**(`dlss5-feed.addon64` SHA 从 `0994FC75…` 变 `875B6BAA…`;
`DLSS5_Feed.fx` 两份一致)。beta.4 zip 的 SHA-256 与官方 release 页公布值
`D16F8B527F76FF1F531682A576D699CB5634838C0E2899585E3671814EDA2745` **完全一致**。
- 兜底 layer 放到 `H:\javahome\bin\layer-x64\`(`VkLayer_feed_vk.dll/json` + `run-with-feed-layer.bat`),
仅当 `vkCreateDevice` hook 装不上时才需要,当前**未启用**。
- `H:\javahome\bin\ReShade.ini` 的 `[ADDON]` 补了 `AddonPath=H:\javahome\bin`(原来是空段)。
- 官方离线产物存到 `tools\feeder\`(4.8 MB):beta.4 zip、`Install-DLSS5Feeder.ps1`、
`Verify-DLSS5Feeder.ps1`、`ReShade_Setup_6.8.0_Addon.exe`、两份 layer json、README。
## 8.7 待用户执行
PCL2 里把 **1.21.1 实例**的 Java 单独指到 `H:\javahome-vulkan`(版本设置 → 设置 → Java 选择),
**不要改全局**(全局若变,1.20.1 也会离开带 `opengl32.dll` 的目录,那条已跑通的路会断)。
启动后看三处:`ReShade.log` 是否出现 `loaded from 'C:\ProgramData\ReShade\ReShade64.dll'`、
`dlss5-feed.log` 是否有 `frame N delivered (…, Vulkan transport, 1 submit)`、
`deep-fried-chicken.log` 是否有 `feeder_marker=` / `interop_state=`。官方校验脚本:
`powershell -File tools\feeder\Verify-DLSS5Feeder.ps1 -GamePath H:\javahome-vulkan\bin`。
+25
View File
@@ -0,0 +1,25 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true
# Pinned JDK for the build. C:\Program Files\Java\jdk-21.0.10 (the JDK originally
# used here) was removed during a disk cleanup, which silently breaks the build
# with "JAVA_HOME is set to an invalid directory". Pinning an explicit path keeps
# `gradlew build` reproducible; change it if this JDK moves.
org.gradle.java.home=C:\\Program Files\\Microsoft\\jdk-21.0.9.10-hotspot
# IntelliJ IDEA is not yet fully compatible with configuration cache
org.gradle.configuration-cache=false
# Fabric Properties (check these on https://fabricmc.net/develop)
minecraft_version=1.20.1
loader_version=0.19.5
loom_version=1.17-SNAPSHOT
# Mod Properties
version=0.1.0
maven_group=dev.wpyw.dlss
archives_base_name=wpywdlss
# Dependencies
fabric_api_version=0.92.12+1.20.1
Binary file not shown.
+13
View File
@@ -0,0 +1,13 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# NOTE: services.gradle.org is unreachable from this network (TCP connect times out
# even though port 443 looks open), so the wrapper points at a mirror.
# Revert to the official URL if you are on a network where it works:
# https://services.gradle.org/distributions/gradle-9.5.1-bin.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-9.5.1-bin.zip
networkTimeout=60000
retries=3
retryBackOffMs=1000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+248
View File
@@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+82
View File
@@ -0,0 +1,82 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
@rem Execute Gradle
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+90
View File
@@ -0,0 +1,90 @@
cmake_minimum_required(VERSION 3.20)
# ---------------------------------------------------------------------------
# wpywdlss native bridge
#
# A small C++ shim that lets the Java/Fabric side talk to NVIDIA Streamline/NGX
# over Vulkan, sharing textures with Minecraft's OpenGL context via Win32
# external memory + semaphores.
#
# Phase 0 scope: bring up a Vulkan device and PROVE the interop capabilities we
# need actually exist on this machine. No NVIDIA headers required yet -- the
# Streamline/NGX integration is added in phase 2 (see STREAMLINE_SDK_DIR below).
# ---------------------------------------------------------------------------
project(wpywdlss_bridge LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# --- JDK for jni.h ---------------------------------------------------------
# Prefer an explicit -DJAVA_HOME=... otherwise fall back to the environment.
if(NOT DEFINED JAVA_HOME)
if(DEFINED ENV{JAVA_HOME})
set(JAVA_HOME "$ENV{JAVA_HOME}")
else()
message(FATAL_ERROR "JAVA_HOME is not set and -DJAVA_HOME= was not given")
endif()
endif()
message(STATUS "JAVA_HOME = ${JAVA_HOME}")
# --- Vulkan headers + import library --------------------------------------
# Both are vendored/generated by tools/make_vulkan_lib.cmd; no LunarG SDK needed.
set(VULKAN_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/vulkan/include")
set(VULKAN_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/vulkan/lib")
if(NOT EXISTS "${VULKAN_INCLUDE_DIR}/vulkan/vulkan.h")
message(FATAL_ERROR "Vulkan headers missing at ${VULKAN_INCLUDE_DIR}/vulkan")
endif()
if(NOT EXISTS "${VULKAN_LIB_DIR}/vulkan-1.lib")
message(FATAL_ERROR "vulkan-1.lib missing. Run native/tools/make_vulkan_lib.cmd first.")
endif()
# --- optional Streamline / NGX (phase 2) ----------------------------------
# Point -DSTREAMLINE_SDK_DIR=D:/Download/streamline-sdk-v2.14.1 to enable the
# DLSS code paths. Left optional so phase 0 builds without it.
set(STREAMLINE_SDK_DIR "" CACHE PATH "Path to the extracted NVIDIA Streamline SDK")
add_library(wpywdlss_bridge SHARED
src/dlss_bridge.cpp
src/gl_probe.cpp
)
target_include_directories(wpywdlss_bridge PRIVATE
"${VULKAN_INCLUDE_DIR}"
"${JAVA_HOME}/include"
"${JAVA_HOME}/include/win32"
)
target_link_directories(wpywdlss_bridge PRIVATE "${VULKAN_LIB_DIR}")
# vulkan-1 for the compute/interop device, opengl32 for the GL side of the bridge.
target_link_libraries(wpywdlss_bridge PRIVATE vulkan-1 opengl32)
if(STREAMLINE_SDK_DIR)
message(STATUS "Streamline SDK enabled: ${STREAMLINE_SDK_DIR}")
target_include_directories(wpywdlss_bridge PRIVATE "${STREAMLINE_SDK_DIR}/include")
target_compile_definitions(wpywdlss_bridge PRIVATE WPYWDLSS_HAVE_STREAMLINE=1)
else()
message(STATUS "Streamline SDK not configured -- phase 0 (Vulkan probe) only")
endif()
target_compile_definitions(wpywdlss_bridge PRIVATE
VK_USE_PLATFORM_WIN32_KHR
WIN32_LEAN_AND_MEAN
NOMINMAX
)
# The DLL is loaded from a jar at runtime, so keep it self-contained.
set_target_properties(wpywdlss_bridge PROPERTIES
PREFIX ""
OUTPUT_NAME "wpywdlss_bridge"
)
if(MSVC)
target_compile_options(wpywdlss_bridge PRIVATE /W4 /permissive- /utf-8)
target_link_options(wpywdlss_bridge PRIVATE /INCREMENTAL:NO)
endif()
+448
View File
@@ -0,0 +1,448 @@
// ---------------------------------------------------------------------------
// wpywdlss native bridge -- phase 0
//
// Purpose
// -------
// Minecraft Java renders with OpenGL, but NVIDIA's DLSS lives behind NGX /
// Streamline, which only speak D3D11 / D3D12 / Vulkan. There is no OpenGL path
// into DLSS. The way through is therefore:
//
// OpenGL renders -> share the texture with Vulkan (Win32 external memory)
// -> run DLSS on Vulkan
// -> share the result back to OpenGL
//
// Before writing any DLSS code we must prove that sharing actually works on the
// target machine. This file does exactly that, and reports everything it finds
// as one human-readable string so the Java side can log it.
//
// It deliberately does NOT need the NVIDIA SDKs to build: phase 0 only needs
// the Vulkan loader plus a few Win32 external-memory entry points we fetch at
// runtime via vkGetDeviceProcAddr.
// ---------------------------------------------------------------------------
#include <jni.h>
#include <windows.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_win32.h>
#include <algorithm>
#include <cstring>
#include <sstream>
#include <string>
#include <vector>
namespace {
constexpr const char* kBridgeVersion = "0.1.0-p0";
// --------------------------------------------------------------------------
// small helpers
// --------------------------------------------------------------------------
std::string vkResultName(VkResult r) {
switch (r) {
case VK_SUCCESS: return "VK_SUCCESS";
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
default: {
std::ostringstream os;
os << "VkResult(" << static_cast<int>(r) << ")";
return os.str();
}
}
}
std::string apiVersionString(uint32_t v) {
std::ostringstream os;
os << VK_API_VERSION_MAJOR(v) << '.' << VK_API_VERSION_MINOR(v)
<< '.' << VK_API_VERSION_PATCH(v);
return os.str();
}
std::string driverVersionString(uint32_t v) {
// NVIDIA encodes the driver version in a slightly odd way.
std::ostringstream os;
os << ((v >> 22) & 0x3FF) << '.' << ((v >> 14) & 0x0FF) << '.'
<< ((v >> 6) & 0x0FF) << '.' << (v & 0x03F);
return os.str();
}
const char* yesNo(bool b) { return b ? "YES" : "no "; }
bool hasExtension(const std::vector<VkExtensionProperties>& list, const char* name) {
return std::any_of(list.begin(), list.end(), [name](const VkExtensionProperties& e) {
return std::strcmp(e.extensionName, name) == 0;
});
}
// --------------------------------------------------------------------------
// GL<->VK interop symbol table
//
// These are device-level extension entry points; the Vulkan loader does NOT
// export them, so they must be resolved through vkGetDeviceProcAddr.
// --------------------------------------------------------------------------
struct InteropFns {
PFN_vkGetMemoryWin32HandleKHR getMemoryWin32Handle = nullptr;
PFN_vkGetSemaphoreWin32HandleKHR getSemaphoreWin32Handle = nullptr;
PFN_vkImportSemaphoreWin32HandleKHR importSemaphoreWin32Handle = nullptr;
};
InteropFns resolveInteropFns(VkDevice device) {
InteropFns f{};
auto load = [device](const char* n) {
return vkGetDeviceProcAddr(device, n);
};
f.getMemoryWin32Handle =
reinterpret_cast<PFN_vkGetMemoryWin32HandleKHR>(load("vkGetMemoryWin32HandleKHR"));
f.getSemaphoreWin32Handle =
reinterpret_cast<PFN_vkGetSemaphoreWin32HandleKHR>(load("vkGetSemaphoreWin32HandleKHR"));
f.importSemaphoreWin32Handle =
reinterpret_cast<PFN_vkImportSemaphoreWin32HandleKHR>(load("vkImportSemaphoreWin32HandleKHR"));
return f;
}
// --------------------------------------------------------------------------
// probe state (kept alive between probe() and shutdown())
// --------------------------------------------------------------------------
struct ProbeState {
VkInstance instance = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkPhysicalDevice phys = VK_NULL_HANDLE;
bool valid = false;
};
ProbeState g_state;
// --------------------------------------------------------------------------
void appendExternalImageSupport(std::ostringstream& os, VkPhysicalDevice phys,
VkExternalMemoryHandleTypeFlagBits handleType,
const char* label) {
VkPhysicalDeviceExternalImageFormatInfo extInfo{};
extInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
extInfo.handleType = handleType;
VkPhysicalDeviceImageFormatInfo2 fmtInfo{};
fmtInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
fmtInfo.pNext = &extInfo;
fmtInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
fmtInfo.type = VK_IMAGE_TYPE_2D;
fmtInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
fmtInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
fmtInfo.flags = 0;
VkExternalImageFormatProperties extProps{};
extProps.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES;
VkImageFormatProperties2 props{};
props.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
props.pNext = &extProps;
VkResult r = vkGetPhysicalDeviceImageFormatProperties2(phys, &fmtInfo, &props);
os << " " << label << " (R8G8B8A8_UNORM 2D OPTIMAL, color|sampled|transfer): ";
if (r != VK_SUCCESS) {
os << "query failed -> " << vkResultName(r) << "\n";
return;
}
const auto& m = extProps.externalMemoryProperties;
os << "exportable=" << yesNo(m.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)
<< " importable=" << yesNo(m.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)
<< " dedicatedOnly=" << yesNo(m.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT)
<< "\n";
}
std::string runProbe() {
std::ostringstream os;
os << "wpywdlss native bridge " << kBridgeVersion << "\n";
os << "========================================================\n";
// ---- 1. loader ------------------------------------------------------
// vulkan-1.dll 1.4.x always exports this; we link it directly.
uint32_t loaderVersion = 0;
VkResult verResult = vkEnumerateInstanceVersion(&loaderVersion);
os << "[1] Vulkan loader\n";
os << " vkEnumerateInstanceVersion : " << vkResultName(verResult) << "\n";
os << " instance version : " << apiVersionString(loaderVersion) << "\n";
// ---- 2. instance extensions we need ---------------------------------
uint32_t extCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr);
std::vector<VkExtensionProperties> instExts(extCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extCount, instExts.data());
const char* wantInstanceExts[] = {
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
};
os << " instance extensions (" << instExts.size() << " available)\n";
std::vector<const char*> enabledInstExts;
for (const char* w : wantInstanceExts) {
bool ok = hasExtension(instExts, w);
os << " " << yesNo(ok) << " " << w << "\n";
if (ok) enabledInstExts.push_back(w);
}
// ---- 3. create instance ---------------------------------------------
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Minecraft (wpywdlss)";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 20, 1);
appInfo.pEngineName = "wpywdlss-bridge";
appInfo.engineVersion = VK_MAKE_VERSION(0, 1, 0);
// Ask for 1.2: FSR/DLSS-grade temporal work needs timeline semaphores.
appInfo.apiVersion = VK_API_VERSION_1_2;
VkInstanceCreateInfo ci{};
ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
ci.pApplicationInfo = &appInfo;
ci.enabledExtensionCount = static_cast<uint32_t>(enabledInstExts.size());
ci.ppEnabledExtensionNames = enabledInstExts.data();
os << "[2] vkCreateInstance\n";
VkResult r = vkCreateInstance(&ci, nullptr, &g_state.instance);
os << " result : " << vkResultName(r) << "\n";
if (r != VK_SUCCESS) {
return os.str();
}
// ---- 4. pick a physical device --------------------------------------
uint32_t devCount = 0;
vkEnumeratePhysicalDevices(g_state.instance, &devCount, nullptr);
if (devCount == 0) {
os << "[3] no Vulkan physical devices found\n";
return os.str();
}
std::vector<VkPhysicalDevice> devices(devCount);
vkEnumeratePhysicalDevices(g_state.instance, &devCount, devices.data());
os << "[3] physical devices (" << devCount << ")\n";
VkPhysicalDevice best = VK_NULL_HANDLE;
VkPhysicalDeviceProperties bestProps{};
for (VkPhysicalDevice d : devices) {
VkPhysicalDeviceProperties p{};
vkGetPhysicalDeviceProperties(d, &p);
const bool isNvidia = (p.vendorID == 0x10DE);
os << " - " << p.deviceName
<< " vendor=0x" << std::hex << p.vendorID << std::dec
<< " type=" << p.deviceType
<< " api=" << apiVersionString(p.apiVersion)
<< " driver=" << driverVersionString(p.driverVersion)
<< (isNvidia ? " <-- NVIDIA" : "") << "\n";
// Preference order: NVIDIA (DLSS requires it) > discrete > anything else.
bool better = false;
if (best == VK_NULL_HANDLE) {
better = true;
} else if (isNvidia) {
better = true;
} else {
better = bestProps.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
p.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
}
if (better) {
best = d;
bestProps = p;
}
}
g_state.phys = best;
const VkPhysicalDeviceProperties& props = bestProps;
// ---- 5. device extensions for sharing -------------------------------
uint32_t dxCount = 0;
vkEnumerateDeviceExtensionProperties(best, nullptr, &dxCount, nullptr);
std::vector<VkExtensionProperties> devExts(dxCount);
vkEnumerateDeviceExtensionProperties(best, nullptr, &dxCount, devExts.data());
const char* wantDeviceExts[] = {
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,
VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
};
os << "[4] device extensions (" << devExts.size() << " available)\n";
std::vector<const char*> enabledDevExts;
for (const char* w : wantDeviceExts) {
bool ok = hasExtension(devExts, w);
os << " " << yesNo(ok) << " " << w << "\n";
if (ok) enabledDevExts.push_back(w);
}
// ---- 6. queue family -------------------------------------------------
uint32_t qCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(best, &qCount, nullptr);
std::vector<VkQueueFamilyProperties> queues(qCount);
vkGetPhysicalDeviceQueueFamilyProperties(best, &qCount, queues.data());
uint32_t queueIndex = UINT32_MAX;
for (uint32_t i = 0; i < qCount; ++i) {
if ((queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
(queues[i].queueFlags & VK_QUEUE_COMPUTE_BIT)) {
queueIndex = i;
break;
}
}
if (queueIndex == UINT32_MAX) {
os << "[5] no graphics+compute queue family found\n";
return os.str();
}
// ---- 7. create the device -------------------------------------------
float priority = 1.0f;
VkDeviceQueueCreateInfo qci{};
qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
qci.queueFamilyIndex = queueIndex;
qci.queueCount = 1;
qci.pQueuePriorities = &priority;
VkPhysicalDeviceTimelineSemaphoreFeatures timeline{};
timeline.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
timeline.timelineSemaphore = VK_TRUE;
VkDeviceCreateInfo dci{};
dci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
dci.pNext = &timeline;
dci.queueCreateInfoCount = 1;
dci.pQueueCreateInfos = &qci;
dci.enabledExtensionCount = static_cast<uint32_t>(enabledDevExts.size());
dci.ppEnabledExtensionNames = enabledDevExts.data();
os << "[5] vkCreateDevice (queue family " << queueIndex << ")\n";
r = vkCreateDevice(best, &dci, nullptr, &g_state.device);
os << " result : " << vkResultName(r) << "\n";
if (r != VK_SUCCESS) {
return os.str();
}
g_state.valid = true;
// ---- 8. resolve the interop entry points ----------------------------
InteropFns fns = resolveInteropFns(g_state.device);
os << "[6] GL<->VK interop entry points (via vkGetDeviceProcAddr)\n";
os << " " << yesNo(fns.getMemoryWin32Handle != nullptr)
<< " vkGetMemoryWin32HandleKHR (export VK memory as a Win32 HANDLE)\n";
os << " " << yesNo(fns.getSemaphoreWin32Handle != nullptr)
<< " vkGetSemaphoreWin32HandleKHR (export semaphore)\n";
os << " " << yesNo(fns.importSemaphoreWin32Handle != nullptr)
<< " vkImportSemaphoreWin32HandleKHR (import GL-exported semaphore)\n";
if (!fns.getMemoryWin32Handle || !fns.importSemaphoreWin32Handle) {
os << " >> sharing is NOT available; this blocks the whole approach\n";
return os.str();
}
// ---- 9. THE decisive question: can this device export images? -------
os << "[7] external image export support <<< this is the go/no-go check\n";
appendExternalImageSupport(os, best, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, "OPAQUE_WIN32 ");
appendExternalImageSupport(os, best, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT, "D3D11_TEXTURE ");
appendExternalImageSupport(os, best, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT, "D3D12_HEAP ");
// ---- 10. semaphore sharing ------------------------------------------
os << "[8] external semaphore support\n";
{
VkPhysicalDeviceExternalSemaphoreInfo si{};
si.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO;
si.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT;
VkExternalSemaphoreProperties sp{};
sp.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES;
vkGetPhysicalDeviceExternalSemaphoreProperties(best, &si, &sp);
os << " OPAQUE_WIN32 : exportable="
<< yesNo(sp.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT)
<< " importable="
<< yesNo(sp.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT)
<< "\n";
}
// ---- 11. memory budget ----------------------------------------------
os << "[9] device properties\n";
os << " device name : " << props.deviceName << "\n";
os << " device type : " << props.deviceType << "\n";
{
VkPhysicalDeviceMemoryProperties mp{};
vkGetPhysicalDeviceMemoryProperties(best, &mp);
for (uint32_t i = 0; i < mp.memoryHeapCount; ++i) {
if (mp.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
os << " device-local : heap " << i << " = "
<< (mp.memoryHeaps[i].size / (1024ull * 1024ull)) << " MiB\n";
}
}
}
os << "========================================================\n";
os << "phase 0 verdict: Vulkan device up, interop entry points resolved.\n";
return os.str();
}
void teardown() {
if (g_state.device != VK_NULL_HANDLE) {
vkDeviceWaitIdle(g_state.device);
vkDestroyDevice(g_state.device, nullptr);
g_state.device = VK_NULL_HANDLE;
}
if (g_state.instance != VK_NULL_HANDLE) {
vkDestroyInstance(g_state.instance, nullptr);
g_state.instance = VK_NULL_HANDLE;
}
g_state.phys = VK_NULL_HANDLE;
g_state.valid = false;
}
} // namespace
// ---------------------------------------------------------------------------
// JNI surface: dev.wpyw.dlss.NativeBridge
// ---------------------------------------------------------------------------
extern "C" {
JNIEXPORT jstring JNICALL
Java_dev_wpyw_dlss_NativeBridge_nativeVersion(JNIEnv* env, jclass) {
return env->NewStringUTF(kBridgeVersion);
}
JNIEXPORT jstring JNICALL
Java_dev_wpyw_dlss_NativeBridge_nativeProbe(JNIEnv* env, jclass) {
std::string report;
try {
report = runProbe();
} catch (const std::exception& e) {
report = std::string("probe threw: ") + e.what();
} catch (...) {
report = "probe threw an unknown exception";
}
return env->NewStringUTF(report.c_str());
}
JNIEXPORT jboolean JNICALL
Java_dev_wpyw_dlss_NativeBridge_nativeIsReady(JNIEnv*, jclass) {
return g_state.valid ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT void JNICALL
Java_dev_wpyw_dlss_NativeBridge_nativeShutdown(JNIEnv*, jclass) {
teardown();
}
} // extern "C"
+333
View File
@@ -0,0 +1,333 @@
// ---------------------------------------------------------------------------
// wpywdlss native bridge -- OpenGL capability probe
//
// Why this exists
// ---------------
// Every viable route for AI upscaling / DLSS in Minecraft Java depends on
// sharing textures between OpenGL and a DX12/Vulkan device, and the OpenGL half
// of that is a GPU/driver capability, not something we can compile in.
//
// DLSS5-Feeder's own documentation puts it exactly this way:
//
// "If GL_EXT_memory_object_win32 and GL_EXT_semaphore_win32 are in the
// extension string, the transport works. If they are not, the frame is not
// being rendered on an NVIDIA GPU, so DLSS could not run either way."
//
// So this probe creates a hidden window with a real WGL context and reports
// exactly which side of that line this machine is on. It is the go/no-go check
// for the OpenGL route, and it also validates the GL half of our own bridge.
// ---------------------------------------------------------------------------
#include <jni.h>
#include <windows.h>
#include <GL/gl.h>
#include <cstring>
#include <sstream>
#include <string>
#include <vector>
// --- GL types / constants the Windows SDK's GL 1.1 header does not define ---
#ifndef APIENTRY
#define APIENTRY __stdcall
#endif
typedef char GLchar;
typedef ptrdiff_t GLsizeiptr;
typedef ptrdiff_t GLintptr;
typedef unsigned long long GLuint64;
#define GL_NUM_EXTENSIONS 0x821D
#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587
#define GL_DEVICE_UUID_EXT 0x9597
#define GL_DRIVER_UUID_EXT 0x9598
// GL 2.0 constant: the Windows SDK's GL 1.1 gl.h does not define it.
#ifndef GL_SHADING_LANGUAGE_VERSION
#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
#endif
// --- entry points we need (declared locally: no glext.h dependency) --------
typedef const GLubyte* (APIENTRY* PFNGLGETSTRINGI)(GLenum, GLuint);
typedef void (APIENTRY* PFNGLCREATEMEMORYOBJECTSEXT)(GLsizei, GLuint*);
typedef void (APIENTRY* PFNGLDELETEMEMORYOBJECTSEXT)(GLsizei, const GLuint*);
typedef void (APIENTRY* PFNGLTEXTURESTORAGEMEM2DEXT)(GLuint, GLsizei, GLenum, GLsizei, GLsizei, GLuint, GLuint64);
typedef void (APIENTRY* PFNGLIMPORTMEMORYWIN32HANDLEEXT)(GLuint, GLuint64, GLenum, void*, const GLchar*);
typedef void (APIENTRY* PFNGLCREATESEMAPHORESEXT)(GLsizei, GLuint*);
typedef void (APIENTRY* PFNGLDELETESEMAPHORESEXT)(GLsizei, const GLuint*);
typedef void (APIENTRY* PFNGLIMPORTSEMAPHOREWIN32HANDLEEXT)(GLuint, GLenum, void*, const GLchar*);
typedef void (APIENTRY* PFNGLSEMAPHORESIGNALEXT)(GLuint, GLuint, GLuint, GLenum, const GLuint*);
typedef void (APIENTRY* PFNGLGETUNSIGNEDBYTEVEXT)(GLenum, GLubyte*);
typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARB)(HDC);
typedef BOOL (WINAPI* PFNWGLCHOOSEPIXELFORMATARB)(HDC, const int*, const FLOAT*, UINT, int*, UINT*);
typedef HGLRC (WINAPI* PFNWGLCREATECONTEXTATTRIBSARB)(HDC, HGLRC, const int*);
namespace {
const char* kModule = "gl_probe";
template <typename T>
T glGet(const char* name) {
return reinterpret_cast<T>(wglGetProcAddress(name));
}
struct HiddenGlContext {
HWND hwnd = nullptr;
HDC hdc = nullptr;
HGLRC hglrc = nullptr;
bool ok = false;
~HiddenGlContext() { destroy(); }
bool create() {
static bool classRegistered = false;
const wchar_t* kClass = L"wpywdlss_probe_wnd";
if (!classRegistered) {
WNDCLASSEXW wc{};
wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.lpfnWndProc = DefWindowProcW;
wc.hInstance = GetModuleHandleW(nullptr);
wc.lpszClassName = kClass;
if (!RegisterClassExW(&wc)) {
return false;
}
classRegistered = true;
}
// Deliberately hidden: we only want a context to query, no visible window.
hwnd = CreateWindowExW(0, kClass, L"wpywdlss probe", WS_POPUP,
0, 0, 16, 16, nullptr, nullptr,
GetModuleHandleW(nullptr), nullptr);
if (!hwnd) {
return false;
}
hdc = GetDC(hwnd);
if (!hdc) {
return false;
}
PIXELFORMATDESCRIPTOR pfd{};
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
int pf = ChoosePixelFormat(hdc, &pfd);
if (pf == 0 || !SetPixelFormat(hdc, pf, &pfd)) {
return false;
}
hglrc = wglCreateContext(hdc);
if (!hglrc) {
return false;
}
if (!wglMakeCurrent(hdc, hglrc)) {
return false;
}
ok = true;
return true;
}
void destroy() {
if (hglrc) {
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(hglrc);
hglrc = nullptr;
}
if (hdc && hwnd) {
ReleaseDC(hwnd, hdc);
hdc = nullptr;
}
if (hwnd) {
DestroyWindow(hwnd);
hwnd = nullptr;
}
ok = false;
}
};
std::vector<std::string> enumerateExtensions() {
std::vector<std::string> out;
PFNGLGETSTRINGI glGetStringi = glGet<PFNGLGETSTRINGI>("glGetStringi");
if (glGetStringi) {
GLint count = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &count);
for (GLint i = 0; i < count; ++i) {
const GLubyte* s = glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(i));
if (s) {
out.emplace_back(reinterpret_cast<const char*>(s));
}
}
if (!out.empty()) {
return out;
}
}
// GL 2.1 fallback: one space-separated string.
const GLubyte* all = glGetString(GL_EXTENSIONS);
if (all) {
std::istringstream is(reinterpret_cast<const char*>(all));
std::string tok;
while (is >> tok) {
out.push_back(tok);
}
}
return out;
}
bool has(const std::vector<std::string>& v, const char* name) {
for (const auto& e : v) {
if (e == name) {
return true;
}
}
return false;
}
} // namespace
// ---------------------------------------------------------------------------
// JNI: Java_dev_wpyw_dlss_NativeBridge_nativeProbeOpenGL
// ---------------------------------------------------------------------------
extern "C" JNIEXPORT jstring JNICALL
Java_dev_wpyw_dlss_NativeBridge_nativeProbeOpenGL(JNIEnv* env, jclass) {
std::ostringstream os;
os << "OpenGL capability probe (hidden WGL context)\n";
os << "========================================================\n";
HiddenGlContext ctx;
if (!ctx.create()) {
os << "[FAIL] could not create a hidden WGL context.\n";
os << " This is a probe-environment problem, not necessarily a GPU one.\n";
return env->NewStringUTF(os.str().c_str());
}
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
const char* glsl = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
os << "[1] context\n";
os << " GL_RENDERER : " << (renderer ? renderer : "?") << "\n";
os << " GL_VENDOR : " << (vendor ? vendor : "?") << "\n";
os << " GL_VERSION : " << (version ? version : "?") << "\n";
os << " GLSL : " << (glsl ? glsl : "?") << "\n";
bool nvidiaRenderer = false;
if (renderer) {
nvidiaRenderer = (std::strstr(renderer, "NVIDIA") != nullptr) ||
(std::strstr(renderer, "GeForce") != nullptr) ||
(std::strstr(renderer, "RTX") != nullptr);
}
os << " NVIDIA GL driver : " << (nvidiaRenderer ? "YES" : "NO (DLSS needs the NVIDIA GPU to render)") << "\n";
os << "[2] WGL extension string\n";
PFNWGLGETEXTENSIONSSTRINGARB wglGetExtensionsStringARB =
glGet<PFNWGLGETEXTENSIONSSTRINGARB>("wglGetExtensionsStringARB");
int wglExtCount = 0;
if (wglGetExtensionsStringARB) {
const char* s = wglGetExtensionsStringARB(ctx.hdc);
if (s) {
std::istringstream is(s);
std::string tok;
while (is >> tok) {
++wglExtCount;
}
}
os << " available : " << wglExtCount << " WGL extensions\n";
} else {
os << " wglGetExtensionsStringARB unavailable\n";
}
os << "[3] GL extensions (" ;
std::vector<std::string> exts = enumerateExtensions();
os << exts.size() << " available)\n";
// ---- THE go/no-go set -------------------------------------------------
//
// Direction matters, and getting it wrong produces a false negative:
// OpenGL can only IMPORT external memory and semaphores -- it cannot export
// them. So the allocator is always Vulkan/D3D12, and GL imports the Win32
// HANDLE it is handed. The transport we rely on is the OPAQUE_WIN32 one
// (Vulkan reported exportable=YES for it), NOT the Vulkan-specific
// GL_EXT_external_objects family, which is a separate mechanism.
struct Check { const char* name; const char* why; };
const Check required[] = {
{"GL_EXT_memory_object", "wrap imported memory as a texture"},
{"GL_EXT_memory_object_win32", "accept a Win32 HANDLE for that memory"},
{"GL_EXT_semaphore", "import external sync objects"},
{"GL_EXT_semaphore_win32", "accept a Win32 HANDLE for sync"},
};
os << " --- REQUIRED: GL importing what Vulkan/D3D12 exported (go/no-go) ---\n";
bool allRequiredExts = true;
for (const Check& c : required) {
bool ok = has(exts, c.name);
if (!ok) {
allRequiredExts = false;
}
os << " " << (ok ? "YES" : "no ") << " " << c.name << " (" << c.why << ")\n";
}
const Check informational[] = {
{"GL_EXT_external_objects", "Vulkan-specific import path; the OPAQUE_WIN32 path does not need it"},
{"GL_EXT_external_objects_win32", "same"},
{"GL_EXT_memory_object_fd", "Linux only"},
{"GL_EXT_semaphore_fd", "Linux only"},
};
os << " --- informational (not needed for the OPAQUE_WIN32 path) ---\n";
for (const Check& c : informational) {
os << " " << (has(exts, c.name) ? "YES" : "no ") << " " << c.name << "\n";
}
// ---- entry points ------------------------------------------------------
os << "[4] interop entry points (via wglGetProcAddress)\n";
struct Fn { const char* name; void* ptr; const char* why; };
const Fn requiredFns[] = {
{"glCreateMemoryObjectsEXT", reinterpret_cast<void*>(glGet<PFNGLCREATEMEMORYOBJECTSEXT>("glCreateMemoryObjectsEXT")), "allocate a GL memory object handle"},
{"glImportMemoryWin32HandleEXT", reinterpret_cast<void*>(glGet<PFNGLIMPORTMEMORYWIN32HANDLEEXT>("glImportMemoryWin32HandleEXT")), "import the VK/D3D12-exported HANDLE"},
{"glTextureStorageMem2DEXT", reinterpret_cast<void*>(glGet<PFNGLTEXTURESTORAGEMEM2DEXT>("glTextureStorageMem2DEXT")), "wrap that memory as a 2D texture"},
{"glImportSemaphoreWin32HandleEXT", reinterpret_cast<void*>(glGet<PFNGLIMPORTSEMAPHOREWIN32HANDLEEXT>("glImportSemaphoreWin32HandleEXT")), "import a fence HANDLE"},
{"glDeleteMemoryObjectsEXT", reinterpret_cast<void*>(glGet<PFNGLDELETEMEMORYOBJECTSEXT>("glDeleteMemoryObjectsEXT")), "cleanup"},
{"glDeleteSemaphoresEXT", reinterpret_cast<void*>(glGet<PFNGLDELETESEMAPHORESEXT>("glDeleteSemaphoresEXT")), "cleanup"},
};
os << " --- REQUIRED ---\n";
bool allRequiredFns = true;
for (const Fn& f : requiredFns) {
bool ok = (f.ptr != nullptr);
if (!ok) {
allRequiredFns = false;
}
os << " " << (ok ? "YES" : "no ") << " " << f.name << " (" << f.why << ")\n";
}
const Fn informationalFns[] = {
{"glCreateSemaphoresEXT", reinterpret_cast<void*>(glGet<PFNGLCREATESEMAPHORESEXT>("glCreateSemaphoresEXT")), "GL-allocated semaphore; unused, we only import"},
{"glGetUnsignedBytevEXT", reinterpret_cast<void*>(glGet<PFNGLGETUNSIGNEDBYTEVEXT>("glGetUnsignedBytevEXT")), "query device UUID; diagnostics only"},
};
os << " --- informational ---\n";
for (const Fn& f : informationalFns) {
os << " " << (f.ptr ? "YES" : "no ") << " " << f.name << "\n";
}
os << "========================================================\n";
if (nvidiaRenderer && allRequiredExts && allRequiredFns) {
os << "VERDICT: the OpenGL transport is AVAILABLE on this machine.\n";
os << " GL can import textures allocated and exported by Vulkan/D3D12,\n";
os << " and synchronise through imported Win32 semaphores.\n";
os << " Every AI-upscaling route is unblocked on the GL side.\n";
} else if (!nvidiaRenderer) {
os << "VERDICT: BLOCKED -- OpenGL is not on the NVIDIA GPU in this probe.\n";
os << " (In Minecraft this would mean the game is rendering on the iGPU.)\n";
} else {
os << "VERDICT: BLOCKED -- the NVIDIA GPU is rendering, but the required\n";
os << " interop extensions or entry points are missing.\n";
}
ctx.destroy();
return env->NewStringUTF(os.str().c_str());
}
@@ -0,0 +1,394 @@
#ifndef VULKAN_VIDEO_CODEC_AV1STD_H_
#define VULKAN_VIDEO_CODEC_AV1STD_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_av1std is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_av1std 1
#include "vulkan_video_codecs_common.h"
#define STD_VIDEO_AV1_NUM_REF_FRAMES 8U
#define STD_VIDEO_AV1_REFS_PER_FRAME 7U
#define STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME 8U
#define STD_VIDEO_AV1_MAX_TILE_COLS 64U
#define STD_VIDEO_AV1_MAX_TILE_ROWS 64U
#define STD_VIDEO_AV1_MAX_SEGMENTS 8U
#define STD_VIDEO_AV1_SEG_LVL_MAX 8U
#define STD_VIDEO_AV1_PRIMARY_REF_NONE 7U
#define STD_VIDEO_AV1_SELECT_INTEGER_MV 2U
#define STD_VIDEO_AV1_SELECT_SCREEN_CONTENT_TOOLS 2U
#define STD_VIDEO_AV1_SKIP_MODE_FRAMES 2U
#define STD_VIDEO_AV1_MAX_LOOP_FILTER_STRENGTHS 4U
#define STD_VIDEO_AV1_LOOP_FILTER_ADJUSTMENTS 2U
#define STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS 8U
#define STD_VIDEO_AV1_MAX_NUM_PLANES 3U
#define STD_VIDEO_AV1_GLOBAL_MOTION_PARAMS 6U
#define STD_VIDEO_AV1_MAX_NUM_Y_POINTS 14U
#define STD_VIDEO_AV1_MAX_NUM_CB_POINTS 10U
#define STD_VIDEO_AV1_MAX_NUM_CR_POINTS 10U
#define STD_VIDEO_AV1_MAX_NUM_POS_LUMA 24U
#define STD_VIDEO_AV1_MAX_NUM_POS_CHROMA 25U
typedef enum StdVideoAV1Profile {
STD_VIDEO_AV1_PROFILE_MAIN = 0,
STD_VIDEO_AV1_PROFILE_HIGH = 1,
STD_VIDEO_AV1_PROFILE_PROFESSIONAL = 2,
STD_VIDEO_AV1_PROFILE_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_PROFILE_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1Profile;
typedef enum StdVideoAV1Level {
STD_VIDEO_AV1_LEVEL_2_0 = 0,
STD_VIDEO_AV1_LEVEL_2_1 = 1,
STD_VIDEO_AV1_LEVEL_2_2 = 2,
STD_VIDEO_AV1_LEVEL_2_3 = 3,
STD_VIDEO_AV1_LEVEL_3_0 = 4,
STD_VIDEO_AV1_LEVEL_3_1 = 5,
STD_VIDEO_AV1_LEVEL_3_2 = 6,
STD_VIDEO_AV1_LEVEL_3_3 = 7,
STD_VIDEO_AV1_LEVEL_4_0 = 8,
STD_VIDEO_AV1_LEVEL_4_1 = 9,
STD_VIDEO_AV1_LEVEL_4_2 = 10,
STD_VIDEO_AV1_LEVEL_4_3 = 11,
STD_VIDEO_AV1_LEVEL_5_0 = 12,
STD_VIDEO_AV1_LEVEL_5_1 = 13,
STD_VIDEO_AV1_LEVEL_5_2 = 14,
STD_VIDEO_AV1_LEVEL_5_3 = 15,
STD_VIDEO_AV1_LEVEL_6_0 = 16,
STD_VIDEO_AV1_LEVEL_6_1 = 17,
STD_VIDEO_AV1_LEVEL_6_2 = 18,
STD_VIDEO_AV1_LEVEL_6_3 = 19,
STD_VIDEO_AV1_LEVEL_7_0 = 20,
STD_VIDEO_AV1_LEVEL_7_1 = 21,
STD_VIDEO_AV1_LEVEL_7_2 = 22,
STD_VIDEO_AV1_LEVEL_7_3 = 23,
STD_VIDEO_AV1_LEVEL_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_LEVEL_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1Level;
typedef enum StdVideoAV1FrameType {
STD_VIDEO_AV1_FRAME_TYPE_KEY = 0,
STD_VIDEO_AV1_FRAME_TYPE_INTER = 1,
STD_VIDEO_AV1_FRAME_TYPE_INTRA_ONLY = 2,
STD_VIDEO_AV1_FRAME_TYPE_SWITCH = 3,
STD_VIDEO_AV1_FRAME_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_FRAME_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1FrameType;
typedef enum StdVideoAV1ReferenceName {
STD_VIDEO_AV1_REFERENCE_NAME_INTRA_FRAME = 0,
STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME = 1,
STD_VIDEO_AV1_REFERENCE_NAME_LAST2_FRAME = 2,
STD_VIDEO_AV1_REFERENCE_NAME_LAST3_FRAME = 3,
STD_VIDEO_AV1_REFERENCE_NAME_GOLDEN_FRAME = 4,
STD_VIDEO_AV1_REFERENCE_NAME_BWDREF_FRAME = 5,
STD_VIDEO_AV1_REFERENCE_NAME_ALTREF2_FRAME = 6,
STD_VIDEO_AV1_REFERENCE_NAME_ALTREF_FRAME = 7,
STD_VIDEO_AV1_REFERENCE_NAME_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_REFERENCE_NAME_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1ReferenceName;
typedef enum StdVideoAV1InterpolationFilter {
STD_VIDEO_AV1_INTERPOLATION_FILTER_EIGHTTAP = 0,
STD_VIDEO_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH = 1,
STD_VIDEO_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP = 2,
STD_VIDEO_AV1_INTERPOLATION_FILTER_BILINEAR = 3,
STD_VIDEO_AV1_INTERPOLATION_FILTER_SWITCHABLE = 4,
STD_VIDEO_AV1_INTERPOLATION_FILTER_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_INTERPOLATION_FILTER_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1InterpolationFilter;
typedef enum StdVideoAV1TxMode {
STD_VIDEO_AV1_TX_MODE_ONLY_4X4 = 0,
STD_VIDEO_AV1_TX_MODE_LARGEST = 1,
STD_VIDEO_AV1_TX_MODE_SELECT = 2,
STD_VIDEO_AV1_TX_MODE_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_TX_MODE_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1TxMode;
typedef enum StdVideoAV1FrameRestorationType {
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_NONE = 0,
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_WIENER = 1,
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_SGRPROJ = 2,
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_SWITCHABLE = 3,
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1FrameRestorationType;
typedef enum StdVideoAV1ColorPrimaries {
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_709 = 1,
STD_VIDEO_AV1_COLOR_PRIMARIES_UNSPECIFIED = 2,
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_470_M = 4,
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_470_B_G = 5,
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_601 = 6,
STD_VIDEO_AV1_COLOR_PRIMARIES_SMPTE_240 = 7,
STD_VIDEO_AV1_COLOR_PRIMARIES_GENERIC_FILM = 8,
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_2020 = 9,
STD_VIDEO_AV1_COLOR_PRIMARIES_XYZ = 10,
STD_VIDEO_AV1_COLOR_PRIMARIES_SMPTE_431 = 11,
STD_VIDEO_AV1_COLOR_PRIMARIES_SMPTE_432 = 12,
STD_VIDEO_AV1_COLOR_PRIMARIES_EBU_3213 = 22,
STD_VIDEO_AV1_COLOR_PRIMARIES_INVALID = 0x7FFFFFFF,
// STD_VIDEO_AV1_COLOR_PRIMARIES_BT_UNSPECIFIED is a legacy alias
STD_VIDEO_AV1_COLOR_PRIMARIES_BT_UNSPECIFIED = STD_VIDEO_AV1_COLOR_PRIMARIES_UNSPECIFIED,
STD_VIDEO_AV1_COLOR_PRIMARIES_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1ColorPrimaries;
typedef enum StdVideoAV1TransferCharacteristics {
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_RESERVED_0 = 0,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_709 = 1,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_UNSPECIFIED = 2,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_RESERVED_3 = 3,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_470_M = 4,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_470_B_G = 5,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_601 = 6,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SMPTE_240 = 7,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_LINEAR = 8,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_LOG_100 = 9,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_LOG_100_SQRT10 = 10,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_IEC_61966 = 11,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_1361 = 12,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SRGB = 13,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_2020_10_BIT = 14,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_BT_2020_12_BIT = 15,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SMPTE_2084 = 16,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_SMPTE_428 = 17,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_HLG = 18,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1TransferCharacteristics;
typedef enum StdVideoAV1MatrixCoefficients {
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_IDENTITY = 0,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_709 = 1,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_UNSPECIFIED = 2,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_RESERVED_3 = 3,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_FCC = 4,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_470_B_G = 5,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_601 = 6,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_SMPTE_240 = 7,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_SMPTE_YCGCO = 8,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_2020_NCL = 9,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_BT_2020_CL = 10,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_SMPTE_2085 = 11,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_CHROMAT_NCL = 12,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_CHROMAT_CL = 13,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_ICTCP = 14,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_MATRIX_COEFFICIENTS_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1MatrixCoefficients;
typedef enum StdVideoAV1ChromaSamplePosition {
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_UNKNOWN = 0,
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_VERTICAL = 1,
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_COLOCATED = 2,
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_RESERVED = 3,
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_INVALID = 0x7FFFFFFF,
STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_MAX_ENUM = 0x7FFFFFFF
} StdVideoAV1ChromaSamplePosition;
typedef struct StdVideoAV1ColorConfigFlags {
uint32_t mono_chrome : 1;
uint32_t color_range : 1;
uint32_t separate_uv_delta_q : 1;
uint32_t color_description_present_flag : 1;
uint32_t reserved : 28;
} StdVideoAV1ColorConfigFlags;
typedef struct StdVideoAV1ColorConfig {
StdVideoAV1ColorConfigFlags flags;
uint8_t BitDepth;
uint8_t subsampling_x;
uint8_t subsampling_y;
uint8_t reserved1;
StdVideoAV1ColorPrimaries color_primaries;
StdVideoAV1TransferCharacteristics transfer_characteristics;
StdVideoAV1MatrixCoefficients matrix_coefficients;
StdVideoAV1ChromaSamplePosition chroma_sample_position;
} StdVideoAV1ColorConfig;
typedef struct StdVideoAV1TimingInfoFlags {
uint32_t equal_picture_interval : 1;
uint32_t reserved : 31;
} StdVideoAV1TimingInfoFlags;
typedef struct StdVideoAV1TimingInfo {
StdVideoAV1TimingInfoFlags flags;
uint32_t num_units_in_display_tick;
uint32_t time_scale;
uint32_t num_ticks_per_picture_minus_1;
} StdVideoAV1TimingInfo;
typedef struct StdVideoAV1LoopFilterFlags {
uint32_t loop_filter_delta_enabled : 1;
uint32_t loop_filter_delta_update : 1;
uint32_t reserved : 30;
} StdVideoAV1LoopFilterFlags;
typedef struct StdVideoAV1LoopFilter {
StdVideoAV1LoopFilterFlags flags;
uint8_t loop_filter_level[STD_VIDEO_AV1_MAX_LOOP_FILTER_STRENGTHS];
uint8_t loop_filter_sharpness;
uint8_t update_ref_delta;
int8_t loop_filter_ref_deltas[STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME];
uint8_t update_mode_delta;
int8_t loop_filter_mode_deltas[STD_VIDEO_AV1_LOOP_FILTER_ADJUSTMENTS];
} StdVideoAV1LoopFilter;
typedef struct StdVideoAV1QuantizationFlags {
uint32_t using_qmatrix : 1;
uint32_t diff_uv_delta : 1;
uint32_t reserved : 30;
} StdVideoAV1QuantizationFlags;
typedef struct StdVideoAV1Quantization {
StdVideoAV1QuantizationFlags flags;
uint8_t base_q_idx;
int8_t DeltaQYDc;
int8_t DeltaQUDc;
int8_t DeltaQUAc;
int8_t DeltaQVDc;
int8_t DeltaQVAc;
uint8_t qm_y;
uint8_t qm_u;
uint8_t qm_v;
} StdVideoAV1Quantization;
typedef struct StdVideoAV1Segmentation {
uint8_t FeatureEnabled[STD_VIDEO_AV1_MAX_SEGMENTS];
int16_t FeatureData[STD_VIDEO_AV1_MAX_SEGMENTS][STD_VIDEO_AV1_SEG_LVL_MAX];
} StdVideoAV1Segmentation;
typedef struct StdVideoAV1TileInfoFlags {
uint32_t uniform_tile_spacing_flag : 1;
uint32_t reserved : 31;
} StdVideoAV1TileInfoFlags;
typedef struct StdVideoAV1TileInfo {
StdVideoAV1TileInfoFlags flags;
uint8_t TileCols;
uint8_t TileRows;
uint16_t context_update_tile_id;
uint8_t tile_size_bytes_minus_1;
uint8_t reserved1[7];
const uint16_t* pMiColStarts;
const uint16_t* pMiRowStarts;
const uint16_t* pWidthInSbsMinus1;
const uint16_t* pHeightInSbsMinus1;
} StdVideoAV1TileInfo;
typedef struct StdVideoAV1CDEF {
uint8_t cdef_damping_minus_3;
uint8_t cdef_bits;
uint8_t cdef_y_pri_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
uint8_t cdef_y_sec_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
uint8_t cdef_uv_pri_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
uint8_t cdef_uv_sec_strength[STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS];
} StdVideoAV1CDEF;
typedef struct StdVideoAV1LoopRestoration {
StdVideoAV1FrameRestorationType FrameRestorationType[STD_VIDEO_AV1_MAX_NUM_PLANES];
uint16_t LoopRestorationSize[STD_VIDEO_AV1_MAX_NUM_PLANES];
} StdVideoAV1LoopRestoration;
typedef struct StdVideoAV1GlobalMotion {
uint8_t GmType[STD_VIDEO_AV1_NUM_REF_FRAMES];
int32_t gm_params[STD_VIDEO_AV1_NUM_REF_FRAMES][STD_VIDEO_AV1_GLOBAL_MOTION_PARAMS];
} StdVideoAV1GlobalMotion;
typedef struct StdVideoAV1FilmGrainFlags {
uint32_t chroma_scaling_from_luma : 1;
uint32_t overlap_flag : 1;
uint32_t clip_to_restricted_range : 1;
uint32_t update_grain : 1;
uint32_t reserved : 28;
} StdVideoAV1FilmGrainFlags;
typedef struct StdVideoAV1FilmGrain {
StdVideoAV1FilmGrainFlags flags;
uint8_t grain_scaling_minus_8;
uint8_t ar_coeff_lag;
uint8_t ar_coeff_shift_minus_6;
uint8_t grain_scale_shift;
uint16_t grain_seed;
uint8_t film_grain_params_ref_idx;
uint8_t num_y_points;
uint8_t point_y_value[STD_VIDEO_AV1_MAX_NUM_Y_POINTS];
uint8_t point_y_scaling[STD_VIDEO_AV1_MAX_NUM_Y_POINTS];
uint8_t num_cb_points;
uint8_t point_cb_value[STD_VIDEO_AV1_MAX_NUM_CB_POINTS];
uint8_t point_cb_scaling[STD_VIDEO_AV1_MAX_NUM_CB_POINTS];
uint8_t num_cr_points;
uint8_t point_cr_value[STD_VIDEO_AV1_MAX_NUM_CR_POINTS];
uint8_t point_cr_scaling[STD_VIDEO_AV1_MAX_NUM_CR_POINTS];
int8_t ar_coeffs_y_plus_128[STD_VIDEO_AV1_MAX_NUM_POS_LUMA];
int8_t ar_coeffs_cb_plus_128[STD_VIDEO_AV1_MAX_NUM_POS_CHROMA];
int8_t ar_coeffs_cr_plus_128[STD_VIDEO_AV1_MAX_NUM_POS_CHROMA];
uint8_t cb_mult;
uint8_t cb_luma_mult;
uint16_t cb_offset;
uint8_t cr_mult;
uint8_t cr_luma_mult;
uint16_t cr_offset;
} StdVideoAV1FilmGrain;
typedef struct StdVideoAV1SequenceHeaderFlags {
uint32_t still_picture : 1;
uint32_t reduced_still_picture_header : 1;
uint32_t use_128x128_superblock : 1;
uint32_t enable_filter_intra : 1;
uint32_t enable_intra_edge_filter : 1;
uint32_t enable_interintra_compound : 1;
uint32_t enable_masked_compound : 1;
uint32_t enable_warped_motion : 1;
uint32_t enable_dual_filter : 1;
uint32_t enable_order_hint : 1;
uint32_t enable_jnt_comp : 1;
uint32_t enable_ref_frame_mvs : 1;
uint32_t frame_id_numbers_present_flag : 1;
uint32_t enable_superres : 1;
uint32_t enable_cdef : 1;
uint32_t enable_restoration : 1;
uint32_t film_grain_params_present : 1;
uint32_t timing_info_present_flag : 1;
uint32_t initial_display_delay_present_flag : 1;
uint32_t reserved : 13;
} StdVideoAV1SequenceHeaderFlags;
typedef struct StdVideoAV1SequenceHeader {
StdVideoAV1SequenceHeaderFlags flags;
StdVideoAV1Profile seq_profile;
uint8_t frame_width_bits_minus_1;
uint8_t frame_height_bits_minus_1;
uint16_t max_frame_width_minus_1;
uint16_t max_frame_height_minus_1;
uint8_t delta_frame_id_length_minus_2;
uint8_t additional_frame_id_length_minus_1;
uint8_t order_hint_bits_minus_1;
uint8_t seq_force_integer_mv;
uint8_t seq_force_screen_content_tools;
uint8_t reserved1[5];
const StdVideoAV1ColorConfig* pColorConfig;
const StdVideoAV1TimingInfo* pTimingInfo;
} StdVideoAV1SequenceHeader;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,109 @@
#ifndef VULKAN_VIDEO_CODEC_AV1STD_DECODE_H_
#define VULKAN_VIDEO_CODEC_AV1STD_DECODE_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_av1std_decode is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_av1std_decode 1
#include "vulkan_video_codec_av1std.h"
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_API_VERSION_1_0_0
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_av1_decode"
typedef struct StdVideoDecodeAV1PictureInfoFlags {
uint32_t error_resilient_mode : 1;
uint32_t disable_cdf_update : 1;
uint32_t use_superres : 1;
uint32_t render_and_frame_size_different : 1;
uint32_t allow_screen_content_tools : 1;
uint32_t is_filter_switchable : 1;
uint32_t force_integer_mv : 1;
uint32_t frame_size_override_flag : 1;
uint32_t buffer_removal_time_present_flag : 1;
uint32_t allow_intrabc : 1;
uint32_t frame_refs_short_signaling : 1;
uint32_t allow_high_precision_mv : 1;
uint32_t is_motion_mode_switchable : 1;
uint32_t use_ref_frame_mvs : 1;
uint32_t disable_frame_end_update_cdf : 1;
uint32_t allow_warped_motion : 1;
uint32_t reduced_tx_set : 1;
uint32_t reference_select : 1;
uint32_t skip_mode_present : 1;
uint32_t delta_q_present : 1;
uint32_t delta_lf_present : 1;
uint32_t delta_lf_multi : 1;
uint32_t segmentation_enabled : 1;
uint32_t segmentation_update_map : 1;
uint32_t segmentation_temporal_update : 1;
uint32_t segmentation_update_data : 1;
uint32_t UsesLr : 1;
uint32_t usesChromaLr : 1;
uint32_t apply_grain : 1;
uint32_t reserved : 3;
} StdVideoDecodeAV1PictureInfoFlags;
typedef struct StdVideoDecodeAV1PictureInfo {
StdVideoDecodeAV1PictureInfoFlags flags;
StdVideoAV1FrameType frame_type;
uint32_t current_frame_id;
uint8_t OrderHint;
uint8_t primary_ref_frame;
uint8_t refresh_frame_flags;
uint8_t reserved1;
StdVideoAV1InterpolationFilter interpolation_filter;
StdVideoAV1TxMode TxMode;
uint8_t delta_q_res;
uint8_t delta_lf_res;
uint8_t SkipModeFrame[STD_VIDEO_AV1_SKIP_MODE_FRAMES];
uint8_t coded_denom;
uint8_t reserved2[3];
uint8_t OrderHints[STD_VIDEO_AV1_NUM_REF_FRAMES];
uint32_t expectedFrameId[STD_VIDEO_AV1_NUM_REF_FRAMES];
const StdVideoAV1TileInfo* pTileInfo;
const StdVideoAV1Quantization* pQuantization;
const StdVideoAV1Segmentation* pSegmentation;
const StdVideoAV1LoopFilter* pLoopFilter;
const StdVideoAV1CDEF* pCDEF;
const StdVideoAV1LoopRestoration* pLoopRestoration;
const StdVideoAV1GlobalMotion* pGlobalMotion;
const StdVideoAV1FilmGrain* pFilmGrain;
} StdVideoDecodeAV1PictureInfo;
typedef struct StdVideoDecodeAV1ReferenceInfoFlags {
uint32_t disable_frame_end_update_cdf : 1;
uint32_t segmentation_enabled : 1;
uint32_t reserved : 30;
} StdVideoDecodeAV1ReferenceInfoFlags;
typedef struct StdVideoDecodeAV1ReferenceInfo {
StdVideoDecodeAV1ReferenceInfoFlags flags;
uint8_t frame_type;
uint8_t RefFrameSignBias;
uint8_t OrderHint;
uint8_t SavedOrderHints[STD_VIDEO_AV1_NUM_REF_FRAMES];
} StdVideoDecodeAV1ReferenceInfo;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,143 @@
#ifndef VULKAN_VIDEO_CODEC_AV1STD_ENCODE_H_
#define VULKAN_VIDEO_CODEC_AV1STD_ENCODE_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_av1std_encode is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_av1std_encode 1
#include "vulkan_video_codec_av1std.h"
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_API_VERSION_1_0_0
#define VK_STD_VULKAN_VIDEO_CODEC_AV1_ENCODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_av1_encode"
typedef struct StdVideoEncodeAV1DecoderModelInfo {
uint8_t buffer_delay_length_minus_1;
uint8_t buffer_removal_time_length_minus_1;
uint8_t frame_presentation_time_length_minus_1;
uint8_t reserved1;
uint32_t num_units_in_decoding_tick;
} StdVideoEncodeAV1DecoderModelInfo;
typedef struct StdVideoEncodeAV1ExtensionHeader {
uint8_t temporal_id;
uint8_t spatial_id;
} StdVideoEncodeAV1ExtensionHeader;
typedef struct StdVideoEncodeAV1OperatingPointInfoFlags {
uint32_t decoder_model_present_for_this_op : 1;
uint32_t low_delay_mode_flag : 1;
uint32_t initial_display_delay_present_for_this_op : 1;
uint32_t reserved : 29;
} StdVideoEncodeAV1OperatingPointInfoFlags;
typedef struct StdVideoEncodeAV1OperatingPointInfo {
StdVideoEncodeAV1OperatingPointInfoFlags flags;
uint16_t operating_point_idc;
uint8_t seq_level_idx;
uint8_t seq_tier;
uint32_t decoder_buffer_delay;
uint32_t encoder_buffer_delay;
uint8_t initial_display_delay_minus_1;
} StdVideoEncodeAV1OperatingPointInfo;
typedef struct StdVideoEncodeAV1PictureInfoFlags {
uint32_t error_resilient_mode : 1;
uint32_t disable_cdf_update : 1;
uint32_t use_superres : 1;
uint32_t render_and_frame_size_different : 1;
uint32_t allow_screen_content_tools : 1;
uint32_t is_filter_switchable : 1;
uint32_t force_integer_mv : 1;
uint32_t frame_size_override_flag : 1;
uint32_t buffer_removal_time_present_flag : 1;
uint32_t allow_intrabc : 1;
uint32_t frame_refs_short_signaling : 1;
uint32_t allow_high_precision_mv : 1;
uint32_t is_motion_mode_switchable : 1;
uint32_t use_ref_frame_mvs : 1;
uint32_t disable_frame_end_update_cdf : 1;
uint32_t allow_warped_motion : 1;
uint32_t reduced_tx_set : 1;
uint32_t skip_mode_present : 1;
uint32_t delta_q_present : 1;
uint32_t delta_lf_present : 1;
uint32_t delta_lf_multi : 1;
uint32_t segmentation_enabled : 1;
uint32_t segmentation_update_map : 1;
uint32_t segmentation_temporal_update : 1;
uint32_t segmentation_update_data : 1;
uint32_t UsesLr : 1;
uint32_t usesChromaLr : 1;
uint32_t show_frame : 1;
uint32_t showable_frame : 1;
uint32_t reserved : 3;
} StdVideoEncodeAV1PictureInfoFlags;
typedef struct StdVideoEncodeAV1PictureInfo {
StdVideoEncodeAV1PictureInfoFlags flags;
StdVideoAV1FrameType frame_type;
uint32_t frame_presentation_time;
uint32_t current_frame_id;
uint8_t order_hint;
uint8_t primary_ref_frame;
uint8_t refresh_frame_flags;
uint8_t coded_denom;
uint16_t render_width_minus_1;
uint16_t render_height_minus_1;
StdVideoAV1InterpolationFilter interpolation_filter;
StdVideoAV1TxMode TxMode;
uint8_t delta_q_res;
uint8_t delta_lf_res;
uint8_t ref_order_hint[STD_VIDEO_AV1_NUM_REF_FRAMES];
int8_t ref_frame_idx[STD_VIDEO_AV1_REFS_PER_FRAME];
uint8_t reserved1[3];
uint32_t delta_frame_id_minus_1[STD_VIDEO_AV1_REFS_PER_FRAME];
const StdVideoAV1TileInfo* pTileInfo;
const StdVideoAV1Quantization* pQuantization;
const StdVideoAV1Segmentation* pSegmentation;
const StdVideoAV1LoopFilter* pLoopFilter;
const StdVideoAV1CDEF* pCDEF;
const StdVideoAV1LoopRestoration* pLoopRestoration;
const StdVideoAV1GlobalMotion* pGlobalMotion;
const StdVideoEncodeAV1ExtensionHeader* pExtensionHeader;
const uint32_t* pBufferRemovalTimes;
} StdVideoEncodeAV1PictureInfo;
typedef struct StdVideoEncodeAV1ReferenceInfoFlags {
uint32_t disable_frame_end_update_cdf : 1;
uint32_t segmentation_enabled : 1;
uint32_t reserved : 30;
} StdVideoEncodeAV1ReferenceInfoFlags;
typedef struct StdVideoEncodeAV1ReferenceInfo {
StdVideoEncodeAV1ReferenceInfoFlags flags;
uint32_t RefFrameId;
StdVideoAV1FrameType frame_type;
uint8_t OrderHint;
uint8_t reserved1[3];
const StdVideoEncodeAV1ExtensionHeader* pExtensionHeader;
} StdVideoEncodeAV1ReferenceInfo;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,314 @@
#ifndef VULKAN_VIDEO_CODEC_H264STD_H_
#define VULKAN_VIDEO_CODEC_H264STD_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_h264std is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_h264std 1
#include "vulkan_video_codecs_common.h"
#define STD_VIDEO_H264_CPB_CNT_LIST_SIZE 32U
#define STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS 6U
#define STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS 16U
#define STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS 6U
#define STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS 64U
#define STD_VIDEO_H264_MAX_NUM_LIST_REF 32U
#define STD_VIDEO_H264_MAX_CHROMA_PLANES 2U
#define STD_VIDEO_H264_NO_REFERENCE_PICTURE 0xFFU
typedef enum StdVideoH264ChromaFormatIdc {
STD_VIDEO_H264_CHROMA_FORMAT_IDC_MONOCHROME = 0,
STD_VIDEO_H264_CHROMA_FORMAT_IDC_420 = 1,
STD_VIDEO_H264_CHROMA_FORMAT_IDC_422 = 2,
STD_VIDEO_H264_CHROMA_FORMAT_IDC_444 = 3,
STD_VIDEO_H264_CHROMA_FORMAT_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_CHROMA_FORMAT_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264ChromaFormatIdc;
typedef enum StdVideoH264ProfileIdc {
STD_VIDEO_H264_PROFILE_IDC_BASELINE = 66,
STD_VIDEO_H264_PROFILE_IDC_MAIN = 77,
STD_VIDEO_H264_PROFILE_IDC_HIGH = 100,
STD_VIDEO_H264_PROFILE_IDC_HIGH_10 = 110,
STD_VIDEO_H264_PROFILE_IDC_HIGH_422 = 122,
STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE = 244,
STD_VIDEO_H264_PROFILE_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_PROFILE_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264ProfileIdc;
typedef enum StdVideoH264LevelIdc {
STD_VIDEO_H264_LEVEL_IDC_1_0 = 0,
STD_VIDEO_H264_LEVEL_IDC_1_1 = 1,
STD_VIDEO_H264_LEVEL_IDC_1_2 = 2,
STD_VIDEO_H264_LEVEL_IDC_1_3 = 3,
STD_VIDEO_H264_LEVEL_IDC_2_0 = 4,
STD_VIDEO_H264_LEVEL_IDC_2_1 = 5,
STD_VIDEO_H264_LEVEL_IDC_2_2 = 6,
STD_VIDEO_H264_LEVEL_IDC_3_0 = 7,
STD_VIDEO_H264_LEVEL_IDC_3_1 = 8,
STD_VIDEO_H264_LEVEL_IDC_3_2 = 9,
STD_VIDEO_H264_LEVEL_IDC_4_0 = 10,
STD_VIDEO_H264_LEVEL_IDC_4_1 = 11,
STD_VIDEO_H264_LEVEL_IDC_4_2 = 12,
STD_VIDEO_H264_LEVEL_IDC_5_0 = 13,
STD_VIDEO_H264_LEVEL_IDC_5_1 = 14,
STD_VIDEO_H264_LEVEL_IDC_5_2 = 15,
STD_VIDEO_H264_LEVEL_IDC_6_0 = 16,
STD_VIDEO_H264_LEVEL_IDC_6_1 = 17,
STD_VIDEO_H264_LEVEL_IDC_6_2 = 18,
STD_VIDEO_H264_LEVEL_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_LEVEL_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264LevelIdc;
typedef enum StdVideoH264PocType {
STD_VIDEO_H264_POC_TYPE_0 = 0,
STD_VIDEO_H264_POC_TYPE_1 = 1,
STD_VIDEO_H264_POC_TYPE_2 = 2,
STD_VIDEO_H264_POC_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_POC_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264PocType;
typedef enum StdVideoH264AspectRatioIdc {
STD_VIDEO_H264_ASPECT_RATIO_IDC_UNSPECIFIED = 0,
STD_VIDEO_H264_ASPECT_RATIO_IDC_SQUARE = 1,
STD_VIDEO_H264_ASPECT_RATIO_IDC_12_11 = 2,
STD_VIDEO_H264_ASPECT_RATIO_IDC_10_11 = 3,
STD_VIDEO_H264_ASPECT_RATIO_IDC_16_11 = 4,
STD_VIDEO_H264_ASPECT_RATIO_IDC_40_33 = 5,
STD_VIDEO_H264_ASPECT_RATIO_IDC_24_11 = 6,
STD_VIDEO_H264_ASPECT_RATIO_IDC_20_11 = 7,
STD_VIDEO_H264_ASPECT_RATIO_IDC_32_11 = 8,
STD_VIDEO_H264_ASPECT_RATIO_IDC_80_33 = 9,
STD_VIDEO_H264_ASPECT_RATIO_IDC_18_11 = 10,
STD_VIDEO_H264_ASPECT_RATIO_IDC_15_11 = 11,
STD_VIDEO_H264_ASPECT_RATIO_IDC_64_33 = 12,
STD_VIDEO_H264_ASPECT_RATIO_IDC_160_99 = 13,
STD_VIDEO_H264_ASPECT_RATIO_IDC_4_3 = 14,
STD_VIDEO_H264_ASPECT_RATIO_IDC_3_2 = 15,
STD_VIDEO_H264_ASPECT_RATIO_IDC_2_1 = 16,
STD_VIDEO_H264_ASPECT_RATIO_IDC_EXTENDED_SAR = 255,
STD_VIDEO_H264_ASPECT_RATIO_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_ASPECT_RATIO_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264AspectRatioIdc;
typedef enum StdVideoH264WeightedBipredIdc {
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_DEFAULT = 0,
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_EXPLICIT = 1,
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_IMPLICIT = 2,
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264WeightedBipredIdc;
typedef enum StdVideoH264ModificationOfPicNumsIdc {
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_SUBTRACT = 0,
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_ADD = 1,
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_LONG_TERM = 2,
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_END = 3,
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264ModificationOfPicNumsIdc;
typedef enum StdVideoH264MemMgmtControlOp {
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_END = 0,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_SHORT_TERM = 1,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_LONG_TERM = 2,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MARK_LONG_TERM = 3,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_SET_MAX_LONG_TERM_INDEX = 4,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_ALL = 5,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MARK_CURRENT_AS_LONG_TERM = 6,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264MemMgmtControlOp;
typedef enum StdVideoH264CabacInitIdc {
STD_VIDEO_H264_CABAC_INIT_IDC_0 = 0,
STD_VIDEO_H264_CABAC_INIT_IDC_1 = 1,
STD_VIDEO_H264_CABAC_INIT_IDC_2 = 2,
STD_VIDEO_H264_CABAC_INIT_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_CABAC_INIT_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264CabacInitIdc;
typedef enum StdVideoH264DisableDeblockingFilterIdc {
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_DISABLED = 0,
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_ENABLED = 1,
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_PARTIAL = 2,
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264DisableDeblockingFilterIdc;
typedef enum StdVideoH264SliceType {
STD_VIDEO_H264_SLICE_TYPE_P = 0,
STD_VIDEO_H264_SLICE_TYPE_B = 1,
STD_VIDEO_H264_SLICE_TYPE_I = 2,
STD_VIDEO_H264_SLICE_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_SLICE_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264SliceType;
typedef enum StdVideoH264PictureType {
STD_VIDEO_H264_PICTURE_TYPE_P = 0,
STD_VIDEO_H264_PICTURE_TYPE_B = 1,
STD_VIDEO_H264_PICTURE_TYPE_I = 2,
STD_VIDEO_H264_PICTURE_TYPE_IDR = 5,
STD_VIDEO_H264_PICTURE_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_PICTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264PictureType;
typedef enum StdVideoH264NonVclNaluType {
STD_VIDEO_H264_NON_VCL_NALU_TYPE_SPS = 0,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_PPS = 1,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_AUD = 2,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_PREFIX = 3,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_END_OF_SEQUENCE = 4,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_END_OF_STREAM = 5,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_PRECODED = 6,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_H264_NON_VCL_NALU_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH264NonVclNaluType;
typedef struct StdVideoH264SpsVuiFlags {
uint32_t aspect_ratio_info_present_flag : 1;
uint32_t overscan_info_present_flag : 1;
uint32_t overscan_appropriate_flag : 1;
uint32_t video_signal_type_present_flag : 1;
uint32_t video_full_range_flag : 1;
uint32_t color_description_present_flag : 1;
uint32_t chroma_loc_info_present_flag : 1;
uint32_t timing_info_present_flag : 1;
uint32_t fixed_frame_rate_flag : 1;
uint32_t bitstream_restriction_flag : 1;
uint32_t nal_hrd_parameters_present_flag : 1;
uint32_t vcl_hrd_parameters_present_flag : 1;
} StdVideoH264SpsVuiFlags;
typedef struct StdVideoH264HrdParameters {
uint8_t cpb_cnt_minus1;
uint8_t bit_rate_scale;
uint8_t cpb_size_scale;
uint8_t reserved1;
uint32_t bit_rate_value_minus1[STD_VIDEO_H264_CPB_CNT_LIST_SIZE];
uint32_t cpb_size_value_minus1[STD_VIDEO_H264_CPB_CNT_LIST_SIZE];
uint8_t cbr_flag[STD_VIDEO_H264_CPB_CNT_LIST_SIZE];
uint32_t initial_cpb_removal_delay_length_minus1;
uint32_t cpb_removal_delay_length_minus1;
uint32_t dpb_output_delay_length_minus1;
uint32_t time_offset_length;
} StdVideoH264HrdParameters;
typedef struct StdVideoH264SequenceParameterSetVui {
StdVideoH264SpsVuiFlags flags;
StdVideoH264AspectRatioIdc aspect_ratio_idc;
uint16_t sar_width;
uint16_t sar_height;
uint8_t video_format;
uint8_t colour_primaries;
uint8_t transfer_characteristics;
uint8_t matrix_coefficients;
uint32_t num_units_in_tick;
uint32_t time_scale;
uint8_t max_num_reorder_frames;
uint8_t max_dec_frame_buffering;
uint8_t chroma_sample_loc_type_top_field;
uint8_t chroma_sample_loc_type_bottom_field;
uint32_t reserved1;
const StdVideoH264HrdParameters* pHrdParameters;
} StdVideoH264SequenceParameterSetVui;
typedef struct StdVideoH264SpsFlags {
uint32_t constraint_set0_flag : 1;
uint32_t constraint_set1_flag : 1;
uint32_t constraint_set2_flag : 1;
uint32_t constraint_set3_flag : 1;
uint32_t constraint_set4_flag : 1;
uint32_t constraint_set5_flag : 1;
uint32_t direct_8x8_inference_flag : 1;
uint32_t mb_adaptive_frame_field_flag : 1;
uint32_t frame_mbs_only_flag : 1;
uint32_t delta_pic_order_always_zero_flag : 1;
uint32_t separate_colour_plane_flag : 1;
uint32_t gaps_in_frame_num_value_allowed_flag : 1;
uint32_t qpprime_y_zero_transform_bypass_flag : 1;
uint32_t frame_cropping_flag : 1;
uint32_t seq_scaling_matrix_present_flag : 1;
uint32_t vui_parameters_present_flag : 1;
} StdVideoH264SpsFlags;
typedef struct StdVideoH264ScalingLists {
uint16_t scaling_list_present_mask;
uint16_t use_default_scaling_matrix_mask;
uint8_t ScalingList4x4[STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS][STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS];
uint8_t ScalingList8x8[STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS][STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS];
} StdVideoH264ScalingLists;
typedef struct StdVideoH264SequenceParameterSet {
StdVideoH264SpsFlags flags;
StdVideoH264ProfileIdc profile_idc;
StdVideoH264LevelIdc level_idc;
StdVideoH264ChromaFormatIdc chroma_format_idc;
uint8_t seq_parameter_set_id;
uint8_t bit_depth_luma_minus8;
uint8_t bit_depth_chroma_minus8;
uint8_t log2_max_frame_num_minus4;
StdVideoH264PocType pic_order_cnt_type;
int32_t offset_for_non_ref_pic;
int32_t offset_for_top_to_bottom_field;
uint8_t log2_max_pic_order_cnt_lsb_minus4;
uint8_t num_ref_frames_in_pic_order_cnt_cycle;
uint8_t max_num_ref_frames;
uint8_t reserved1;
uint32_t pic_width_in_mbs_minus1;
uint32_t pic_height_in_map_units_minus1;
uint32_t frame_crop_left_offset;
uint32_t frame_crop_right_offset;
uint32_t frame_crop_top_offset;
uint32_t frame_crop_bottom_offset;
uint32_t reserved2;
const int32_t* pOffsetForRefFrame;
const StdVideoH264ScalingLists* pScalingLists;
const StdVideoH264SequenceParameterSetVui* pSequenceParameterSetVui;
} StdVideoH264SequenceParameterSet;
typedef struct StdVideoH264PpsFlags {
uint32_t transform_8x8_mode_flag : 1;
uint32_t redundant_pic_cnt_present_flag : 1;
uint32_t constrained_intra_pred_flag : 1;
uint32_t deblocking_filter_control_present_flag : 1;
uint32_t weighted_pred_flag : 1;
uint32_t bottom_field_pic_order_in_frame_present_flag : 1;
uint32_t entropy_coding_mode_flag : 1;
uint32_t pic_scaling_matrix_present_flag : 1;
} StdVideoH264PpsFlags;
typedef struct StdVideoH264PictureParameterSet {
StdVideoH264PpsFlags flags;
uint8_t seq_parameter_set_id;
uint8_t pic_parameter_set_id;
uint8_t num_ref_idx_l0_default_active_minus1;
uint8_t num_ref_idx_l1_default_active_minus1;
StdVideoH264WeightedBipredIdc weighted_bipred_idc;
int8_t pic_init_qp_minus26;
int8_t pic_init_qs_minus26;
int8_t chroma_qp_index_offset;
int8_t second_chroma_qp_index_offset;
const StdVideoH264ScalingLists* pScalingLists;
} StdVideoH264PictureParameterSet;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,77 @@
#ifndef VULKAN_VIDEO_CODEC_H264STD_DECODE_H_
#define VULKAN_VIDEO_CODEC_H264STD_DECODE_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_h264std_decode is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_h264std_decode 1
#include "vulkan_video_codec_h264std.h"
#define VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
#define VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_API_VERSION_1_0_0
#define VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h264_decode"
#define STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_LIST_SIZE 2U
typedef enum StdVideoDecodeH264FieldOrderCount {
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_TOP = 0,
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_BOTTOM = 1,
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_INVALID = 0x7FFFFFFF,
STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_MAX_ENUM = 0x7FFFFFFF
} StdVideoDecodeH264FieldOrderCount;
typedef struct StdVideoDecodeH264PictureInfoFlags {
uint32_t field_pic_flag : 1;
uint32_t is_intra : 1;
uint32_t IdrPicFlag : 1;
uint32_t bottom_field_flag : 1;
uint32_t is_reference : 1;
uint32_t complementary_field_pair : 1;
} StdVideoDecodeH264PictureInfoFlags;
typedef struct StdVideoDecodeH264PictureInfo {
StdVideoDecodeH264PictureInfoFlags flags;
uint8_t seq_parameter_set_id;
uint8_t pic_parameter_set_id;
uint8_t reserved1;
uint8_t reserved2;
uint16_t frame_num;
uint16_t idr_pic_id;
int32_t PicOrderCnt[STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_LIST_SIZE];
} StdVideoDecodeH264PictureInfo;
typedef struct StdVideoDecodeH264ReferenceInfoFlags {
uint32_t top_field_flag : 1;
uint32_t bottom_field_flag : 1;
uint32_t used_for_long_term_reference : 1;
uint32_t is_non_existing : 1;
} StdVideoDecodeH264ReferenceInfoFlags;
typedef struct StdVideoDecodeH264ReferenceInfo {
StdVideoDecodeH264ReferenceInfoFlags flags;
uint16_t FrameNum;
uint16_t reserved;
int32_t PicOrderCnt[STD_VIDEO_DECODE_H264_FIELD_ORDER_COUNT_LIST_SIZE];
} StdVideoDecodeH264ReferenceInfo;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,147 @@
#ifndef VULKAN_VIDEO_CODEC_H264STD_ENCODE_H_
#define VULKAN_VIDEO_CODEC_H264STD_ENCODE_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_h264std_encode is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_h264std_encode 1
#include "vulkan_video_codec_h264std.h"
#define VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
#define VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_API_VERSION_1_0_0
#define VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h264_encode"
typedef struct StdVideoEncodeH264WeightTableFlags {
uint32_t luma_weight_l0_flag;
uint32_t chroma_weight_l0_flag;
uint32_t luma_weight_l1_flag;
uint32_t chroma_weight_l1_flag;
} StdVideoEncodeH264WeightTableFlags;
typedef struct StdVideoEncodeH264WeightTable {
StdVideoEncodeH264WeightTableFlags flags;
uint8_t luma_log2_weight_denom;
uint8_t chroma_log2_weight_denom;
int8_t luma_weight_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF];
int8_t luma_offset_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF];
int8_t chroma_weight_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
int8_t chroma_offset_l0[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
int8_t luma_weight_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF];
int8_t luma_offset_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF];
int8_t chroma_weight_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
int8_t chroma_offset_l1[STD_VIDEO_H264_MAX_NUM_LIST_REF][STD_VIDEO_H264_MAX_CHROMA_PLANES];
} StdVideoEncodeH264WeightTable;
typedef struct StdVideoEncodeH264SliceHeaderFlags {
uint32_t direct_spatial_mv_pred_flag : 1;
uint32_t num_ref_idx_active_override_flag : 1;
uint32_t reserved : 30;
} StdVideoEncodeH264SliceHeaderFlags;
typedef struct StdVideoEncodeH264PictureInfoFlags {
uint32_t IdrPicFlag : 1;
uint32_t is_reference : 1;
uint32_t no_output_of_prior_pics_flag : 1;
uint32_t long_term_reference_flag : 1;
uint32_t adaptive_ref_pic_marking_mode_flag : 1;
uint32_t reserved : 27;
} StdVideoEncodeH264PictureInfoFlags;
typedef struct StdVideoEncodeH264ReferenceInfoFlags {
uint32_t used_for_long_term_reference : 1;
uint32_t reserved : 31;
} StdVideoEncodeH264ReferenceInfoFlags;
typedef struct StdVideoEncodeH264ReferenceListsInfoFlags {
uint32_t ref_pic_list_modification_flag_l0 : 1;
uint32_t ref_pic_list_modification_flag_l1 : 1;
uint32_t reserved : 30;
} StdVideoEncodeH264ReferenceListsInfoFlags;
typedef struct StdVideoEncodeH264RefListModEntry {
StdVideoH264ModificationOfPicNumsIdc modification_of_pic_nums_idc;
uint16_t abs_diff_pic_num_minus1;
uint16_t long_term_pic_num;
} StdVideoEncodeH264RefListModEntry;
typedef struct StdVideoEncodeH264RefPicMarkingEntry {
StdVideoH264MemMgmtControlOp memory_management_control_operation;
uint16_t difference_of_pic_nums_minus1;
uint16_t long_term_pic_num;
uint16_t long_term_frame_idx;
uint16_t max_long_term_frame_idx_plus1;
} StdVideoEncodeH264RefPicMarkingEntry;
typedef struct StdVideoEncodeH264ReferenceListsInfo {
StdVideoEncodeH264ReferenceListsInfoFlags flags;
uint8_t num_ref_idx_l0_active_minus1;
uint8_t num_ref_idx_l1_active_minus1;
uint8_t RefPicList0[STD_VIDEO_H264_MAX_NUM_LIST_REF];
uint8_t RefPicList1[STD_VIDEO_H264_MAX_NUM_LIST_REF];
uint8_t refList0ModOpCount;
uint8_t refList1ModOpCount;
uint8_t refPicMarkingOpCount;
uint8_t reserved1[7];
const StdVideoEncodeH264RefListModEntry* pRefList0ModOperations;
const StdVideoEncodeH264RefListModEntry* pRefList1ModOperations;
const StdVideoEncodeH264RefPicMarkingEntry* pRefPicMarkingOperations;
} StdVideoEncodeH264ReferenceListsInfo;
typedef struct StdVideoEncodeH264PictureInfo {
StdVideoEncodeH264PictureInfoFlags flags;
uint8_t seq_parameter_set_id;
uint8_t pic_parameter_set_id;
uint16_t idr_pic_id;
StdVideoH264PictureType primary_pic_type;
uint32_t frame_num;
int32_t PicOrderCnt;
uint8_t temporal_id;
uint8_t reserved1[3];
const StdVideoEncodeH264ReferenceListsInfo* pRefLists;
} StdVideoEncodeH264PictureInfo;
typedef struct StdVideoEncodeH264ReferenceInfo {
StdVideoEncodeH264ReferenceInfoFlags flags;
StdVideoH264PictureType primary_pic_type;
uint32_t FrameNum;
int32_t PicOrderCnt;
uint16_t long_term_pic_num;
uint16_t long_term_frame_idx;
uint8_t temporal_id;
} StdVideoEncodeH264ReferenceInfo;
typedef struct StdVideoEncodeH264SliceHeader {
StdVideoEncodeH264SliceHeaderFlags flags;
uint32_t first_mb_in_slice;
StdVideoH264SliceType slice_type;
int8_t slice_alpha_c0_offset_div2;
int8_t slice_beta_offset_div2;
int8_t slice_qp_delta;
uint8_t reserved1;
StdVideoH264CabacInitIdc cabac_init_idc;
StdVideoH264DisableDeblockingFilterIdc disable_deblocking_filter_idc;
const StdVideoEncodeH264WeightTable* pWeightTable;
} StdVideoEncodeH264SliceHeader;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,446 @@
#ifndef VULKAN_VIDEO_CODEC_H265STD_H_
#define VULKAN_VIDEO_CODEC_H265STD_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_h265std is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_h265std 1
#include "vulkan_video_codecs_common.h"
#define STD_VIDEO_H265_CPB_CNT_LIST_SIZE 32U
#define STD_VIDEO_H265_SUBLAYERS_LIST_SIZE 7U
#define STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS 6U
#define STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS 16U
#define STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS 6U
#define STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS 64U
#define STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS 6U
#define STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS 64U
#define STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS 2U
#define STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS 64U
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE 6U
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_COLS_LIST_SIZE 19U
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_ROWS_LIST_SIZE 21U
#define STD_VIDEO_H265_PREDICTOR_PALETTE_COMPONENTS_LIST_SIZE 3U
#define STD_VIDEO_H265_PREDICTOR_PALETTE_COMP_ENTRIES_LIST_SIZE 128U
#define STD_VIDEO_H265_MAX_NUM_LIST_REF 15U
#define STD_VIDEO_H265_MAX_CHROMA_PLANES 2U
#define STD_VIDEO_H265_MAX_SHORT_TERM_REF_PIC_SETS 64U
#define STD_VIDEO_H265_MAX_DPB_SIZE 16U
#define STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS 32U
#define STD_VIDEO_H265_MAX_LONG_TERM_PICS 16U
#define STD_VIDEO_H265_MAX_DELTA_POC 48U
#define STD_VIDEO_H265_NO_REFERENCE_PICTURE 0xFFU
typedef enum StdVideoH265ChromaFormatIdc {
STD_VIDEO_H265_CHROMA_FORMAT_IDC_MONOCHROME = 0,
STD_VIDEO_H265_CHROMA_FORMAT_IDC_420 = 1,
STD_VIDEO_H265_CHROMA_FORMAT_IDC_422 = 2,
STD_VIDEO_H265_CHROMA_FORMAT_IDC_444 = 3,
STD_VIDEO_H265_CHROMA_FORMAT_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H265_CHROMA_FORMAT_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265ChromaFormatIdc;
typedef enum StdVideoH265ProfileIdc {
STD_VIDEO_H265_PROFILE_IDC_MAIN = 1,
STD_VIDEO_H265_PROFILE_IDC_MAIN_10 = 2,
STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE = 3,
STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS = 4,
STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS = 9,
STD_VIDEO_H265_PROFILE_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H265_PROFILE_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265ProfileIdc;
typedef enum StdVideoH265LevelIdc {
STD_VIDEO_H265_LEVEL_IDC_1_0 = 0,
STD_VIDEO_H265_LEVEL_IDC_2_0 = 1,
STD_VIDEO_H265_LEVEL_IDC_2_1 = 2,
STD_VIDEO_H265_LEVEL_IDC_3_0 = 3,
STD_VIDEO_H265_LEVEL_IDC_3_1 = 4,
STD_VIDEO_H265_LEVEL_IDC_4_0 = 5,
STD_VIDEO_H265_LEVEL_IDC_4_1 = 6,
STD_VIDEO_H265_LEVEL_IDC_5_0 = 7,
STD_VIDEO_H265_LEVEL_IDC_5_1 = 8,
STD_VIDEO_H265_LEVEL_IDC_5_2 = 9,
STD_VIDEO_H265_LEVEL_IDC_6_0 = 10,
STD_VIDEO_H265_LEVEL_IDC_6_1 = 11,
STD_VIDEO_H265_LEVEL_IDC_6_2 = 12,
STD_VIDEO_H265_LEVEL_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H265_LEVEL_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265LevelIdc;
typedef enum StdVideoH265SliceType {
STD_VIDEO_H265_SLICE_TYPE_B = 0,
STD_VIDEO_H265_SLICE_TYPE_P = 1,
STD_VIDEO_H265_SLICE_TYPE_I = 2,
STD_VIDEO_H265_SLICE_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_H265_SLICE_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265SliceType;
typedef enum StdVideoH265PictureType {
STD_VIDEO_H265_PICTURE_TYPE_P = 0,
STD_VIDEO_H265_PICTURE_TYPE_B = 1,
STD_VIDEO_H265_PICTURE_TYPE_I = 2,
STD_VIDEO_H265_PICTURE_TYPE_IDR = 3,
STD_VIDEO_H265_PICTURE_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_H265_PICTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265PictureType;
typedef enum StdVideoH265AspectRatioIdc {
STD_VIDEO_H265_ASPECT_RATIO_IDC_UNSPECIFIED = 0,
STD_VIDEO_H265_ASPECT_RATIO_IDC_SQUARE = 1,
STD_VIDEO_H265_ASPECT_RATIO_IDC_12_11 = 2,
STD_VIDEO_H265_ASPECT_RATIO_IDC_10_11 = 3,
STD_VIDEO_H265_ASPECT_RATIO_IDC_16_11 = 4,
STD_VIDEO_H265_ASPECT_RATIO_IDC_40_33 = 5,
STD_VIDEO_H265_ASPECT_RATIO_IDC_24_11 = 6,
STD_VIDEO_H265_ASPECT_RATIO_IDC_20_11 = 7,
STD_VIDEO_H265_ASPECT_RATIO_IDC_32_11 = 8,
STD_VIDEO_H265_ASPECT_RATIO_IDC_80_33 = 9,
STD_VIDEO_H265_ASPECT_RATIO_IDC_18_11 = 10,
STD_VIDEO_H265_ASPECT_RATIO_IDC_15_11 = 11,
STD_VIDEO_H265_ASPECT_RATIO_IDC_64_33 = 12,
STD_VIDEO_H265_ASPECT_RATIO_IDC_160_99 = 13,
STD_VIDEO_H265_ASPECT_RATIO_IDC_4_3 = 14,
STD_VIDEO_H265_ASPECT_RATIO_IDC_3_2 = 15,
STD_VIDEO_H265_ASPECT_RATIO_IDC_2_1 = 16,
STD_VIDEO_H265_ASPECT_RATIO_IDC_EXTENDED_SAR = 255,
STD_VIDEO_H265_ASPECT_RATIO_IDC_INVALID = 0x7FFFFFFF,
STD_VIDEO_H265_ASPECT_RATIO_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265AspectRatioIdc;
typedef struct StdVideoH265DecPicBufMgr {
uint32_t max_latency_increase_plus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
uint8_t max_dec_pic_buffering_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
uint8_t max_num_reorder_pics[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
} StdVideoH265DecPicBufMgr;
typedef struct StdVideoH265SubLayerHrdParameters {
uint32_t bit_rate_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
uint32_t cpb_size_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
uint32_t cpb_size_du_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
uint32_t bit_rate_du_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
uint32_t cbr_flag;
} StdVideoH265SubLayerHrdParameters;
typedef struct StdVideoH265HrdFlags {
uint32_t nal_hrd_parameters_present_flag : 1;
uint32_t vcl_hrd_parameters_present_flag : 1;
uint32_t sub_pic_hrd_params_present_flag : 1;
uint32_t sub_pic_cpb_params_in_pic_timing_sei_flag : 1;
uint32_t fixed_pic_rate_general_flag : 8;
uint32_t fixed_pic_rate_within_cvs_flag : 8;
uint32_t low_delay_hrd_flag : 8;
} StdVideoH265HrdFlags;
typedef struct StdVideoH265HrdParameters {
StdVideoH265HrdFlags flags;
uint8_t tick_divisor_minus2;
uint8_t du_cpb_removal_delay_increment_length_minus1;
uint8_t dpb_output_delay_du_length_minus1;
uint8_t bit_rate_scale;
uint8_t cpb_size_scale;
uint8_t cpb_size_du_scale;
uint8_t initial_cpb_removal_delay_length_minus1;
uint8_t au_cpb_removal_delay_length_minus1;
uint8_t dpb_output_delay_length_minus1;
uint8_t cpb_cnt_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
uint16_t elemental_duration_in_tc_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
uint16_t reserved[3];
const StdVideoH265SubLayerHrdParameters* pSubLayerHrdParametersNal;
const StdVideoH265SubLayerHrdParameters* pSubLayerHrdParametersVcl;
} StdVideoH265HrdParameters;
typedef struct StdVideoH265VpsFlags {
uint32_t vps_temporal_id_nesting_flag : 1;
uint32_t vps_sub_layer_ordering_info_present_flag : 1;
uint32_t vps_timing_info_present_flag : 1;
uint32_t vps_poc_proportional_to_timing_flag : 1;
} StdVideoH265VpsFlags;
typedef struct StdVideoH265ProfileTierLevelFlags {
uint32_t general_tier_flag : 1;
uint32_t general_progressive_source_flag : 1;
uint32_t general_interlaced_source_flag : 1;
uint32_t general_non_packed_constraint_flag : 1;
uint32_t general_frame_only_constraint_flag : 1;
} StdVideoH265ProfileTierLevelFlags;
typedef struct StdVideoH265ProfileTierLevel {
StdVideoH265ProfileTierLevelFlags flags;
StdVideoH265ProfileIdc general_profile_idc;
StdVideoH265LevelIdc general_level_idc;
} StdVideoH265ProfileTierLevel;
typedef struct StdVideoH265VideoParameterSet {
StdVideoH265VpsFlags flags;
uint8_t vps_video_parameter_set_id;
uint8_t vps_max_sub_layers_minus1;
uint8_t reserved1;
uint8_t reserved2;
uint32_t vps_num_units_in_tick;
uint32_t vps_time_scale;
uint32_t vps_num_ticks_poc_diff_one_minus1;
uint32_t reserved3;
const StdVideoH265DecPicBufMgr* pDecPicBufMgr;
const StdVideoH265HrdParameters* pHrdParameters;
const StdVideoH265ProfileTierLevel* pProfileTierLevel;
} StdVideoH265VideoParameterSet;
typedef struct StdVideoH265ScalingLists {
uint8_t ScalingList4x4[STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS];
uint8_t ScalingList8x8[STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS];
uint8_t ScalingList16x16[STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS];
uint8_t ScalingList32x32[STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS];
uint8_t ScalingListDCCoef16x16[STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS];
uint8_t ScalingListDCCoef32x32[STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS];
} StdVideoH265ScalingLists;
typedef struct StdVideoH265SpsVuiFlags {
uint32_t aspect_ratio_info_present_flag : 1;
uint32_t overscan_info_present_flag : 1;
uint32_t overscan_appropriate_flag : 1;
uint32_t video_signal_type_present_flag : 1;
uint32_t video_full_range_flag : 1;
uint32_t colour_description_present_flag : 1;
uint32_t chroma_loc_info_present_flag : 1;
uint32_t neutral_chroma_indication_flag : 1;
uint32_t field_seq_flag : 1;
uint32_t frame_field_info_present_flag : 1;
uint32_t default_display_window_flag : 1;
uint32_t vui_timing_info_present_flag : 1;
uint32_t vui_poc_proportional_to_timing_flag : 1;
uint32_t vui_hrd_parameters_present_flag : 1;
uint32_t bitstream_restriction_flag : 1;
uint32_t tiles_fixed_structure_flag : 1;
uint32_t motion_vectors_over_pic_boundaries_flag : 1;
uint32_t restricted_ref_pic_lists_flag : 1;
} StdVideoH265SpsVuiFlags;
typedef struct StdVideoH265SequenceParameterSetVui {
StdVideoH265SpsVuiFlags flags;
StdVideoH265AspectRatioIdc aspect_ratio_idc;
uint16_t sar_width;
uint16_t sar_height;
uint8_t video_format;
uint8_t colour_primaries;
uint8_t transfer_characteristics;
uint8_t matrix_coeffs;
uint8_t chroma_sample_loc_type_top_field;
uint8_t chroma_sample_loc_type_bottom_field;
uint8_t reserved1;
uint8_t reserved2;
uint16_t def_disp_win_left_offset;
uint16_t def_disp_win_right_offset;
uint16_t def_disp_win_top_offset;
uint16_t def_disp_win_bottom_offset;
uint32_t vui_num_units_in_tick;
uint32_t vui_time_scale;
uint32_t vui_num_ticks_poc_diff_one_minus1;
uint16_t min_spatial_segmentation_idc;
uint16_t reserved3;
uint8_t max_bytes_per_pic_denom;
uint8_t max_bits_per_min_cu_denom;
uint8_t log2_max_mv_length_horizontal;
uint8_t log2_max_mv_length_vertical;
const StdVideoH265HrdParameters* pHrdParameters;
} StdVideoH265SequenceParameterSetVui;
typedef struct StdVideoH265PredictorPaletteEntries {
uint16_t PredictorPaletteEntries[STD_VIDEO_H265_PREDICTOR_PALETTE_COMPONENTS_LIST_SIZE][STD_VIDEO_H265_PREDICTOR_PALETTE_COMP_ENTRIES_LIST_SIZE];
} StdVideoH265PredictorPaletteEntries;
typedef struct StdVideoH265SpsFlags {
uint32_t sps_temporal_id_nesting_flag : 1;
uint32_t separate_colour_plane_flag : 1;
uint32_t conformance_window_flag : 1;
uint32_t sps_sub_layer_ordering_info_present_flag : 1;
uint32_t scaling_list_enabled_flag : 1;
uint32_t sps_scaling_list_data_present_flag : 1;
uint32_t amp_enabled_flag : 1;
uint32_t sample_adaptive_offset_enabled_flag : 1;
uint32_t pcm_enabled_flag : 1;
uint32_t pcm_loop_filter_disabled_flag : 1;
uint32_t long_term_ref_pics_present_flag : 1;
uint32_t sps_temporal_mvp_enabled_flag : 1;
uint32_t strong_intra_smoothing_enabled_flag : 1;
uint32_t vui_parameters_present_flag : 1;
uint32_t sps_extension_present_flag : 1;
uint32_t sps_range_extension_flag : 1;
uint32_t transform_skip_rotation_enabled_flag : 1;
uint32_t transform_skip_context_enabled_flag : 1;
uint32_t implicit_rdpcm_enabled_flag : 1;
uint32_t explicit_rdpcm_enabled_flag : 1;
uint32_t extended_precision_processing_flag : 1;
uint32_t intra_smoothing_disabled_flag : 1;
uint32_t high_precision_offsets_enabled_flag : 1;
uint32_t persistent_rice_adaptation_enabled_flag : 1;
uint32_t cabac_bypass_alignment_enabled_flag : 1;
uint32_t sps_scc_extension_flag : 1;
uint32_t sps_curr_pic_ref_enabled_flag : 1;
uint32_t palette_mode_enabled_flag : 1;
uint32_t sps_palette_predictor_initializers_present_flag : 1;
uint32_t intra_boundary_filtering_disabled_flag : 1;
} StdVideoH265SpsFlags;
typedef struct StdVideoH265ShortTermRefPicSetFlags {
uint32_t inter_ref_pic_set_prediction_flag : 1;
uint32_t delta_rps_sign : 1;
} StdVideoH265ShortTermRefPicSetFlags;
typedef struct StdVideoH265ShortTermRefPicSet {
StdVideoH265ShortTermRefPicSetFlags flags;
uint32_t delta_idx_minus1;
uint16_t use_delta_flag;
uint16_t abs_delta_rps_minus1;
uint16_t used_by_curr_pic_flag;
uint16_t used_by_curr_pic_s0_flag;
uint16_t used_by_curr_pic_s1_flag;
uint16_t reserved1;
uint8_t reserved2;
uint8_t reserved3;
uint8_t num_negative_pics;
uint8_t num_positive_pics;
uint16_t delta_poc_s0_minus1[STD_VIDEO_H265_MAX_DPB_SIZE];
uint16_t delta_poc_s1_minus1[STD_VIDEO_H265_MAX_DPB_SIZE];
} StdVideoH265ShortTermRefPicSet;
typedef struct StdVideoH265LongTermRefPicsSps {
uint32_t used_by_curr_pic_lt_sps_flag;
uint32_t lt_ref_pic_poc_lsb_sps[STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS];
} StdVideoH265LongTermRefPicsSps;
typedef struct StdVideoH265SequenceParameterSet {
StdVideoH265SpsFlags flags;
StdVideoH265ChromaFormatIdc chroma_format_idc;
uint32_t pic_width_in_luma_samples;
uint32_t pic_height_in_luma_samples;
uint8_t sps_video_parameter_set_id;
uint8_t sps_max_sub_layers_minus1;
uint8_t sps_seq_parameter_set_id;
uint8_t bit_depth_luma_minus8;
uint8_t bit_depth_chroma_minus8;
uint8_t log2_max_pic_order_cnt_lsb_minus4;
uint8_t log2_min_luma_coding_block_size_minus3;
uint8_t log2_diff_max_min_luma_coding_block_size;
uint8_t log2_min_luma_transform_block_size_minus2;
uint8_t log2_diff_max_min_luma_transform_block_size;
uint8_t max_transform_hierarchy_depth_inter;
uint8_t max_transform_hierarchy_depth_intra;
uint8_t num_short_term_ref_pic_sets;
uint8_t num_long_term_ref_pics_sps;
uint8_t pcm_sample_bit_depth_luma_minus1;
uint8_t pcm_sample_bit_depth_chroma_minus1;
uint8_t log2_min_pcm_luma_coding_block_size_minus3;
uint8_t log2_diff_max_min_pcm_luma_coding_block_size;
uint8_t reserved1;
uint8_t reserved2;
uint8_t palette_max_size;
uint8_t delta_palette_max_predictor_size;
uint8_t motion_vector_resolution_control_idc;
uint8_t sps_num_palette_predictor_initializers_minus1;
uint32_t conf_win_left_offset;
uint32_t conf_win_right_offset;
uint32_t conf_win_top_offset;
uint32_t conf_win_bottom_offset;
const StdVideoH265ProfileTierLevel* pProfileTierLevel;
const StdVideoH265DecPicBufMgr* pDecPicBufMgr;
const StdVideoH265ScalingLists* pScalingLists;
const StdVideoH265ShortTermRefPicSet* pShortTermRefPicSet;
const StdVideoH265LongTermRefPicsSps* pLongTermRefPicsSps;
const StdVideoH265SequenceParameterSetVui* pSequenceParameterSetVui;
const StdVideoH265PredictorPaletteEntries* pPredictorPaletteEntries;
} StdVideoH265SequenceParameterSet;
typedef struct StdVideoH265PpsFlags {
uint32_t dependent_slice_segments_enabled_flag : 1;
uint32_t output_flag_present_flag : 1;
uint32_t sign_data_hiding_enabled_flag : 1;
uint32_t cabac_init_present_flag : 1;
uint32_t constrained_intra_pred_flag : 1;
uint32_t transform_skip_enabled_flag : 1;
uint32_t cu_qp_delta_enabled_flag : 1;
uint32_t pps_slice_chroma_qp_offsets_present_flag : 1;
uint32_t weighted_pred_flag : 1;
uint32_t weighted_bipred_flag : 1;
uint32_t transquant_bypass_enabled_flag : 1;
uint32_t tiles_enabled_flag : 1;
uint32_t entropy_coding_sync_enabled_flag : 1;
uint32_t uniform_spacing_flag : 1;
uint32_t loop_filter_across_tiles_enabled_flag : 1;
uint32_t pps_loop_filter_across_slices_enabled_flag : 1;
uint32_t deblocking_filter_control_present_flag : 1;
uint32_t deblocking_filter_override_enabled_flag : 1;
uint32_t pps_deblocking_filter_disabled_flag : 1;
uint32_t pps_scaling_list_data_present_flag : 1;
uint32_t lists_modification_present_flag : 1;
uint32_t slice_segment_header_extension_present_flag : 1;
uint32_t pps_extension_present_flag : 1;
uint32_t cross_component_prediction_enabled_flag : 1;
uint32_t chroma_qp_offset_list_enabled_flag : 1;
uint32_t pps_curr_pic_ref_enabled_flag : 1;
uint32_t residual_adaptive_colour_transform_enabled_flag : 1;
uint32_t pps_slice_act_qp_offsets_present_flag : 1;
uint32_t pps_palette_predictor_initializers_present_flag : 1;
uint32_t monochrome_palette_flag : 1;
uint32_t pps_range_extension_flag : 1;
} StdVideoH265PpsFlags;
typedef struct StdVideoH265PictureParameterSet {
StdVideoH265PpsFlags flags;
uint8_t pps_pic_parameter_set_id;
uint8_t pps_seq_parameter_set_id;
uint8_t sps_video_parameter_set_id;
uint8_t num_extra_slice_header_bits;
uint8_t num_ref_idx_l0_default_active_minus1;
uint8_t num_ref_idx_l1_default_active_minus1;
int8_t init_qp_minus26;
uint8_t diff_cu_qp_delta_depth;
int8_t pps_cb_qp_offset;
int8_t pps_cr_qp_offset;
int8_t pps_beta_offset_div2;
int8_t pps_tc_offset_div2;
uint8_t log2_parallel_merge_level_minus2;
uint8_t log2_max_transform_skip_block_size_minus2;
uint8_t diff_cu_chroma_qp_offset_depth;
uint8_t chroma_qp_offset_list_len_minus1;
int8_t cb_qp_offset_list[STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE];
int8_t cr_qp_offset_list[STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE];
uint8_t log2_sao_offset_scale_luma;
uint8_t log2_sao_offset_scale_chroma;
int8_t pps_act_y_qp_offset_plus5;
int8_t pps_act_cb_qp_offset_plus5;
int8_t pps_act_cr_qp_offset_plus3;
uint8_t pps_num_palette_predictor_initializers;
uint8_t luma_bit_depth_entry_minus8;
uint8_t chroma_bit_depth_entry_minus8;
uint8_t num_tile_columns_minus1;
uint8_t num_tile_rows_minus1;
uint8_t reserved1;
uint8_t reserved2;
uint16_t column_width_minus1[STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_COLS_LIST_SIZE];
uint16_t row_height_minus1[STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_ROWS_LIST_SIZE];
uint32_t reserved3;
const StdVideoH265ScalingLists* pScalingLists;
const StdVideoH265PredictorPaletteEntries* pPredictorPaletteEntries;
} StdVideoH265PictureParameterSet;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,67 @@
#ifndef VULKAN_VIDEO_CODEC_H265STD_DECODE_H_
#define VULKAN_VIDEO_CODEC_H265STD_DECODE_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_h265std_decode is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_h265std_decode 1
#include "vulkan_video_codec_h265std.h"
#define VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
#define VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_API_VERSION_1_0_0
#define VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h265_decode"
#define STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE 8U
typedef struct StdVideoDecodeH265PictureInfoFlags {
uint32_t IrapPicFlag : 1;
uint32_t IdrPicFlag : 1;
uint32_t IsReference : 1;
uint32_t short_term_ref_pic_set_sps_flag : 1;
} StdVideoDecodeH265PictureInfoFlags;
typedef struct StdVideoDecodeH265PictureInfo {
StdVideoDecodeH265PictureInfoFlags flags;
uint8_t sps_video_parameter_set_id;
uint8_t pps_seq_parameter_set_id;
uint8_t pps_pic_parameter_set_id;
uint8_t NumDeltaPocsOfRefRpsIdx;
int32_t PicOrderCntVal;
uint16_t NumBitsForSTRefPicSetInSlice;
uint16_t reserved;
uint8_t RefPicSetStCurrBefore[STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE];
uint8_t RefPicSetStCurrAfter[STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE];
uint8_t RefPicSetLtCurr[STD_VIDEO_DECODE_H265_REF_PIC_SET_LIST_SIZE];
} StdVideoDecodeH265PictureInfo;
typedef struct StdVideoDecodeH265ReferenceInfoFlags {
uint32_t used_for_long_term_reference : 1;
uint32_t unused_for_reference : 1;
} StdVideoDecodeH265ReferenceInfoFlags;
typedef struct StdVideoDecodeH265ReferenceInfo {
StdVideoDecodeH265ReferenceInfoFlags flags;
int32_t PicOrderCntVal;
} StdVideoDecodeH265ReferenceInfo;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,157 @@
#ifndef VULKAN_VIDEO_CODEC_H265STD_ENCODE_H_
#define VULKAN_VIDEO_CODEC_H265STD_ENCODE_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_h265std_encode is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_h265std_encode 1
#include "vulkan_video_codec_h265std.h"
#define VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
#define VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_API_VERSION_1_0_0
#define VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_h265_encode"
typedef struct StdVideoEncodeH265WeightTableFlags {
uint16_t luma_weight_l0_flag;
uint16_t chroma_weight_l0_flag;
uint16_t luma_weight_l1_flag;
uint16_t chroma_weight_l1_flag;
} StdVideoEncodeH265WeightTableFlags;
typedef struct StdVideoEncodeH265WeightTable {
StdVideoEncodeH265WeightTableFlags flags;
uint8_t luma_log2_weight_denom;
int8_t delta_chroma_log2_weight_denom;
int8_t delta_luma_weight_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
int8_t luma_offset_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
int8_t delta_chroma_weight_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
int8_t delta_chroma_offset_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
int8_t delta_luma_weight_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
int8_t luma_offset_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
int8_t delta_chroma_weight_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
int8_t delta_chroma_offset_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF][STD_VIDEO_H265_MAX_CHROMA_PLANES];
} StdVideoEncodeH265WeightTable;
typedef struct StdVideoEncodeH265SliceSegmentHeaderFlags {
uint32_t first_slice_segment_in_pic_flag : 1;
uint32_t dependent_slice_segment_flag : 1;
uint32_t slice_sao_luma_flag : 1;
uint32_t slice_sao_chroma_flag : 1;
uint32_t num_ref_idx_active_override_flag : 1;
uint32_t mvd_l1_zero_flag : 1;
uint32_t cabac_init_flag : 1;
uint32_t cu_chroma_qp_offset_enabled_flag : 1;
uint32_t deblocking_filter_override_flag : 1;
uint32_t slice_deblocking_filter_disabled_flag : 1;
uint32_t collocated_from_l0_flag : 1;
uint32_t slice_loop_filter_across_slices_enabled_flag : 1;
uint32_t reserved : 20;
} StdVideoEncodeH265SliceSegmentHeaderFlags;
typedef struct StdVideoEncodeH265SliceSegmentHeader {
StdVideoEncodeH265SliceSegmentHeaderFlags flags;
StdVideoH265SliceType slice_type;
uint32_t slice_segment_address;
uint8_t collocated_ref_idx;
uint8_t MaxNumMergeCand;
int8_t slice_cb_qp_offset;
int8_t slice_cr_qp_offset;
int8_t slice_beta_offset_div2;
int8_t slice_tc_offset_div2;
int8_t slice_act_y_qp_offset;
int8_t slice_act_cb_qp_offset;
int8_t slice_act_cr_qp_offset;
int8_t slice_qp_delta;
uint16_t reserved1;
const StdVideoEncodeH265WeightTable* pWeightTable;
} StdVideoEncodeH265SliceSegmentHeader;
typedef struct StdVideoEncodeH265ReferenceListsInfoFlags {
uint32_t ref_pic_list_modification_flag_l0 : 1;
uint32_t ref_pic_list_modification_flag_l1 : 1;
uint32_t reserved : 30;
} StdVideoEncodeH265ReferenceListsInfoFlags;
typedef struct StdVideoEncodeH265ReferenceListsInfo {
StdVideoEncodeH265ReferenceListsInfoFlags flags;
uint8_t num_ref_idx_l0_active_minus1;
uint8_t num_ref_idx_l1_active_minus1;
uint8_t RefPicList0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
uint8_t RefPicList1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
uint8_t list_entry_l0[STD_VIDEO_H265_MAX_NUM_LIST_REF];
uint8_t list_entry_l1[STD_VIDEO_H265_MAX_NUM_LIST_REF];
} StdVideoEncodeH265ReferenceListsInfo;
typedef struct StdVideoEncodeH265PictureInfoFlags {
uint32_t is_reference : 1;
uint32_t IrapPicFlag : 1;
uint32_t used_for_long_term_reference : 1;
uint32_t discardable_flag : 1;
uint32_t cross_layer_bla_flag : 1;
uint32_t pic_output_flag : 1;
uint32_t no_output_of_prior_pics_flag : 1;
uint32_t short_term_ref_pic_set_sps_flag : 1;
uint32_t slice_temporal_mvp_enabled_flag : 1;
uint32_t reserved : 23;
} StdVideoEncodeH265PictureInfoFlags;
typedef struct StdVideoEncodeH265LongTermRefPics {
uint8_t num_long_term_sps;
uint8_t num_long_term_pics;
uint8_t lt_idx_sps[STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS];
uint8_t poc_lsb_lt[STD_VIDEO_H265_MAX_LONG_TERM_PICS];
uint16_t used_by_curr_pic_lt_flag;
uint8_t delta_poc_msb_present_flag[STD_VIDEO_H265_MAX_DELTA_POC];
uint8_t delta_poc_msb_cycle_lt[STD_VIDEO_H265_MAX_DELTA_POC];
} StdVideoEncodeH265LongTermRefPics;
typedef struct StdVideoEncodeH265PictureInfo {
StdVideoEncodeH265PictureInfoFlags flags;
StdVideoH265PictureType pic_type;
uint8_t sps_video_parameter_set_id;
uint8_t pps_seq_parameter_set_id;
uint8_t pps_pic_parameter_set_id;
uint8_t short_term_ref_pic_set_idx;
int32_t PicOrderCntVal;
uint8_t TemporalId;
uint8_t reserved1[7];
const StdVideoEncodeH265ReferenceListsInfo* pRefLists;
const StdVideoH265ShortTermRefPicSet* pShortTermRefPicSet;
const StdVideoEncodeH265LongTermRefPics* pLongTermRefPics;
} StdVideoEncodeH265PictureInfo;
typedef struct StdVideoEncodeH265ReferenceInfoFlags {
uint32_t used_for_long_term_reference : 1;
uint32_t unused_for_reference : 1;
uint32_t reserved : 30;
} StdVideoEncodeH265ReferenceInfoFlags;
typedef struct StdVideoEncodeH265ReferenceInfo {
StdVideoEncodeH265ReferenceInfoFlags flags;
StdVideoH265PictureType pic_type;
int32_t PicOrderCntVal;
uint8_t TemporalId;
} StdVideoEncodeH265ReferenceInfo;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,151 @@
#ifndef VULKAN_VIDEO_CODEC_VP9STD_H_
#define VULKAN_VIDEO_CODEC_VP9STD_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_vp9std is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_vp9std 1
#include "vulkan_video_codecs_common.h"
#define STD_VIDEO_VP9_NUM_REF_FRAMES 8U
#define STD_VIDEO_VP9_REFS_PER_FRAME 3U
#define STD_VIDEO_VP9_MAX_REF_FRAMES 4U
#define STD_VIDEO_VP9_LOOP_FILTER_ADJUSTMENTS 2U
#define STD_VIDEO_VP9_MAX_SEGMENTS 8U
#define STD_VIDEO_VP9_SEG_LVL_MAX 4U
#define STD_VIDEO_VP9_MAX_SEGMENTATION_TREE_PROBS 7U
#define STD_VIDEO_VP9_MAX_SEGMENTATION_PRED_PROB 3U
typedef enum StdVideoVP9Profile {
STD_VIDEO_VP9_PROFILE_0 = 0,
STD_VIDEO_VP9_PROFILE_1 = 1,
STD_VIDEO_VP9_PROFILE_2 = 2,
STD_VIDEO_VP9_PROFILE_3 = 3,
STD_VIDEO_VP9_PROFILE_INVALID = 0x7FFFFFFF,
STD_VIDEO_VP9_PROFILE_MAX_ENUM = 0x7FFFFFFF
} StdVideoVP9Profile;
typedef enum StdVideoVP9Level {
STD_VIDEO_VP9_LEVEL_1_0 = 0,
STD_VIDEO_VP9_LEVEL_1_1 = 1,
STD_VIDEO_VP9_LEVEL_2_0 = 2,
STD_VIDEO_VP9_LEVEL_2_1 = 3,
STD_VIDEO_VP9_LEVEL_3_0 = 4,
STD_VIDEO_VP9_LEVEL_3_1 = 5,
STD_VIDEO_VP9_LEVEL_4_0 = 6,
STD_VIDEO_VP9_LEVEL_4_1 = 7,
STD_VIDEO_VP9_LEVEL_5_0 = 8,
STD_VIDEO_VP9_LEVEL_5_1 = 9,
STD_VIDEO_VP9_LEVEL_5_2 = 10,
STD_VIDEO_VP9_LEVEL_6_0 = 11,
STD_VIDEO_VP9_LEVEL_6_1 = 12,
STD_VIDEO_VP9_LEVEL_6_2 = 13,
STD_VIDEO_VP9_LEVEL_INVALID = 0x7FFFFFFF,
STD_VIDEO_VP9_LEVEL_MAX_ENUM = 0x7FFFFFFF
} StdVideoVP9Level;
typedef enum StdVideoVP9FrameType {
STD_VIDEO_VP9_FRAME_TYPE_KEY = 0,
STD_VIDEO_VP9_FRAME_TYPE_NON_KEY = 1,
STD_VIDEO_VP9_FRAME_TYPE_INVALID = 0x7FFFFFFF,
STD_VIDEO_VP9_FRAME_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoVP9FrameType;
typedef enum StdVideoVP9ReferenceName {
STD_VIDEO_VP9_REFERENCE_NAME_INTRA_FRAME = 0,
STD_VIDEO_VP9_REFERENCE_NAME_LAST_FRAME = 1,
STD_VIDEO_VP9_REFERENCE_NAME_GOLDEN_FRAME = 2,
STD_VIDEO_VP9_REFERENCE_NAME_ALTREF_FRAME = 3,
STD_VIDEO_VP9_REFERENCE_NAME_INVALID = 0x7FFFFFFF,
STD_VIDEO_VP9_REFERENCE_NAME_MAX_ENUM = 0x7FFFFFFF
} StdVideoVP9ReferenceName;
typedef enum StdVideoVP9InterpolationFilter {
STD_VIDEO_VP9_INTERPOLATION_FILTER_EIGHTTAP = 0,
STD_VIDEO_VP9_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH = 1,
STD_VIDEO_VP9_INTERPOLATION_FILTER_EIGHTTAP_SHARP = 2,
STD_VIDEO_VP9_INTERPOLATION_FILTER_BILINEAR = 3,
STD_VIDEO_VP9_INTERPOLATION_FILTER_SWITCHABLE = 4,
STD_VIDEO_VP9_INTERPOLATION_FILTER_INVALID = 0x7FFFFFFF,
STD_VIDEO_VP9_INTERPOLATION_FILTER_MAX_ENUM = 0x7FFFFFFF
} StdVideoVP9InterpolationFilter;
typedef enum StdVideoVP9ColorSpace {
STD_VIDEO_VP9_COLOR_SPACE_UNKNOWN = 0,
STD_VIDEO_VP9_COLOR_SPACE_BT_601 = 1,
STD_VIDEO_VP9_COLOR_SPACE_BT_709 = 2,
STD_VIDEO_VP9_COLOR_SPACE_SMPTE_170 = 3,
STD_VIDEO_VP9_COLOR_SPACE_SMPTE_240 = 4,
STD_VIDEO_VP9_COLOR_SPACE_BT_2020 = 5,
STD_VIDEO_VP9_COLOR_SPACE_RESERVED = 6,
STD_VIDEO_VP9_COLOR_SPACE_RGB = 7,
STD_VIDEO_VP9_COLOR_SPACE_INVALID = 0x7FFFFFFF,
STD_VIDEO_VP9_COLOR_SPACE_MAX_ENUM = 0x7FFFFFFF
} StdVideoVP9ColorSpace;
typedef struct StdVideoVP9ColorConfigFlags {
uint32_t color_range : 1;
uint32_t reserved : 31;
} StdVideoVP9ColorConfigFlags;
typedef struct StdVideoVP9ColorConfig {
StdVideoVP9ColorConfigFlags flags;
uint8_t BitDepth;
uint8_t subsampling_x;
uint8_t subsampling_y;
uint8_t reserved1;
StdVideoVP9ColorSpace color_space;
} StdVideoVP9ColorConfig;
typedef struct StdVideoVP9LoopFilterFlags {
uint32_t loop_filter_delta_enabled : 1;
uint32_t loop_filter_delta_update : 1;
uint32_t reserved : 30;
} StdVideoVP9LoopFilterFlags;
typedef struct StdVideoVP9LoopFilter {
StdVideoVP9LoopFilterFlags flags;
uint8_t loop_filter_level;
uint8_t loop_filter_sharpness;
uint8_t update_ref_delta;
int8_t loop_filter_ref_deltas[STD_VIDEO_VP9_MAX_REF_FRAMES];
uint8_t update_mode_delta;
int8_t loop_filter_mode_deltas[STD_VIDEO_VP9_LOOP_FILTER_ADJUSTMENTS];
} StdVideoVP9LoopFilter;
typedef struct StdVideoVP9SegmentationFlags {
uint32_t segmentation_update_map : 1;
uint32_t segmentation_temporal_update : 1;
uint32_t segmentation_update_data : 1;
uint32_t segmentation_abs_or_delta_update : 1;
uint32_t reserved : 28;
} StdVideoVP9SegmentationFlags;
typedef struct StdVideoVP9Segmentation {
StdVideoVP9SegmentationFlags flags;
uint8_t segmentation_tree_probs[STD_VIDEO_VP9_MAX_SEGMENTATION_TREE_PROBS];
uint8_t segmentation_pred_prob[STD_VIDEO_VP9_MAX_SEGMENTATION_PRED_PROB];
uint8_t FeatureEnabled[STD_VIDEO_VP9_MAX_SEGMENTS];
int16_t FeatureData[STD_VIDEO_VP9_MAX_SEGMENTS][STD_VIDEO_VP9_SEG_LVL_MAX];
} StdVideoVP9Segmentation;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,68 @@
#ifndef VULKAN_VIDEO_CODEC_VP9STD_DECODE_H_
#define VULKAN_VIDEO_CODEC_VP9STD_DECODE_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codec_vp9std_decode is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_vp9std_decode 1
#include "vulkan_video_codec_vp9std.h"
#define VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_API_VERSION_1_0_0 VK_MAKE_VIDEO_STD_VERSION(1, 0, 0)
#define VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_SPEC_VERSION VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_API_VERSION_1_0_0
#define VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_EXTENSION_NAME "VK_STD_vulkan_video_codec_vp9_decode"
typedef struct StdVideoDecodeVP9PictureInfoFlags {
uint32_t error_resilient_mode : 1;
uint32_t intra_only : 1;
uint32_t allow_high_precision_mv : 1;
uint32_t refresh_frame_context : 1;
uint32_t frame_parallel_decoding_mode : 1;
uint32_t segmentation_enabled : 1;
uint32_t show_frame : 1;
uint32_t UsePrevFrameMvs : 1;
uint32_t reserved : 24;
} StdVideoDecodeVP9PictureInfoFlags;
typedef struct StdVideoDecodeVP9PictureInfo {
StdVideoDecodeVP9PictureInfoFlags flags;
StdVideoVP9Profile profile;
StdVideoVP9FrameType frame_type;
uint8_t frame_context_idx;
uint8_t reset_frame_context;
uint8_t refresh_frame_flags;
uint8_t ref_frame_sign_bias_mask;
StdVideoVP9InterpolationFilter interpolation_filter;
uint8_t base_q_idx;
int8_t delta_q_y_dc;
int8_t delta_q_uv_dc;
int8_t delta_q_uv_ac;
uint8_t tile_cols_log2;
uint8_t tile_rows_log2;
uint16_t reserved1[3];
const StdVideoVP9ColorConfig* pColorConfig;
const StdVideoVP9LoopFilter* pLoopFilter;
const StdVideoVP9Segmentation* pSegmentation;
} StdVideoDecodeVP9PictureInfo;
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,36 @@
#ifndef VULKAN_VIDEO_CODECS_COMMON_H_
#define VULKAN_VIDEO_CODECS_COMMON_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// vulkan_video_codecs_common is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codecs_common 1
#if !defined(VK_NO_STDINT_H)
#include <stdint.h>
#endif
#define VK_MAKE_VIDEO_STD_VERSION(major, minor, patch) \
((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch)))
#ifdef __cplusplus
}
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
//
// File: vk_platform.h
//
/*
** Copyright 2014-2026 The Khronos Group Inc.
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#ifndef VK_PLATFORM_H_
#define VK_PLATFORM_H_
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
/*
***************************************************************************************************
* Platform-specific directives and type declarations
***************************************************************************************************
*/
/* Platform-specific calling convention macros.
*
* Platforms should define these so that Vulkan clients call Vulkan commands
* with the same calling conventions that the Vulkan implementation expects.
*
* VKAPI_ATTR - Placed before the return type in function declarations.
* Useful for C++11 and GCC/Clang-style function attribute syntax.
* VKAPI_CALL - Placed after the return type in function declarations.
* Useful for MSVC-style calling convention syntax.
* VKAPI_PTR - Placed between the '(' and '*' in function pointer types.
*
* Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void);
* Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void);
*/
#if defined(_WIN32)
// On Windows, Vulkan commands use the stdcall convention
#define VKAPI_ATTR
#define VKAPI_CALL __stdcall
#define VKAPI_PTR VKAPI_CALL
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
#error "Vulkan is not supported for the 'armeabi' NDK ABI"
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
// On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
// calling convention, i.e. float parameters are passed in registers. This
// is true even if the rest of the application passes floats on the stack,
// as it does by default when compiling for the armeabi-v7a NDK ABI.
#define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
#define VKAPI_CALL
#define VKAPI_PTR VKAPI_ATTR
#else
// On other platforms, use the default calling convention
#define VKAPI_ATTR
#define VKAPI_CALL
#define VKAPI_PTR
#endif
#if !defined(VK_NO_STDDEF_H)
#include <stddef.h>
#endif // !defined(VK_NO_STDDEF_H)
#if !defined(VK_NO_STDINT_H)
#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#endif // !defined(VK_NO_STDINT_H)
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif
+102
View File
@@ -0,0 +1,102 @@
#ifndef VULKAN_H_
#define VULKAN_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#include "vk_platform.h"
#include "vulkan_core.h"
#ifdef VK_USE_PLATFORM_ANDROID_KHR
#include "vulkan_android.h"
#endif
#ifdef VK_USE_PLATFORM_FUCHSIA
#include <zircon/types.h>
#include "vulkan_fuchsia.h"
#endif
#ifdef VK_USE_PLATFORM_IOS_MVK
#include "vulkan_ios.h"
#endif
#ifdef VK_USE_PLATFORM_MACOS_MVK
#include "vulkan_macos.h"
#endif
#ifdef VK_USE_PLATFORM_METAL_EXT
#include "vulkan_metal.h"
#endif
#ifdef VK_USE_PLATFORM_VI_NN
#include "vulkan_vi.h"
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include "vulkan_wayland.h"
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
#include <windows.h>
#include "vulkan_win32.h"
#endif
#ifdef VK_USE_PLATFORM_XCB_KHR
#include <xcb/xcb.h>
#include "vulkan_xcb.h"
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
#include <X11/Xlib.h>
#include "vulkan_xlib.h"
#endif
#ifdef VK_USE_PLATFORM_DIRECTFB_EXT
#include <directfb.h>
#include "vulkan_directfb.h"
#endif
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include "vulkan_xlib_xrandr.h"
#endif
#ifdef VK_USE_PLATFORM_GGP
#include <ggp_c/vulkan_types.h>
#include "vulkan_ggp.h"
#endif
#ifdef VK_USE_PLATFORM_SCREEN_QNX
#include <screen/screen.h>
#include "vulkan_screen.h"
#endif
#ifdef VK_USE_PLATFORM_SCI
#include <nvscisync.h>
#include <nvscibuf.h>
#include "vulkan_sci.h"
#endif
#ifdef VK_ENABLE_BETA_EXTENSIONS
#include "vulkan_beta.h"
#endif
#ifdef VK_USE_PLATFORM_OHOS
#include "vulkan_ohos.h"
#endif
#endif // VULKAN_H_
File diff suppressed because it is too large Load Diff
+372
View File
@@ -0,0 +1,372 @@
#ifndef VULKAN_WIN32_H_
#define VULKAN_WIN32_H_ 1
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/
#ifdef __cplusplus
extern "C" {
#endif
// VK_KHR_win32_surface is a preprocessor guard. Do not pass it to API calls.
#define VK_KHR_win32_surface 1
#define VK_KHR_WIN32_SURFACE_SPEC_VERSION 6
#define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface"
typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
typedef struct VkWin32SurfaceCreateInfoKHR {
VkStructureType sType;
const void* pNext;
VkWin32SurfaceCreateFlagsKHR flags;
HINSTANCE hinstance;
HWND hwnd;
} VkWin32SurfaceCreateInfoKHR;
typedef VkResult (VKAPI_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
#ifndef VK_NO_PROTOTYPES
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR(
VkInstance instance,
const VkWin32SurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSurfaceKHR* pSurface);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex);
#endif
#endif
// VK_KHR_external_memory_win32 is a preprocessor guard. Do not pass it to API calls.
#define VK_KHR_external_memory_win32 1
#define VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
#define VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHR_external_memory_win32"
typedef struct VkImportMemoryWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
VkExternalMemoryHandleTypeFlagBits handleType;
HANDLE handle;
LPCWSTR name;
} VkImportMemoryWin32HandleInfoKHR;
typedef struct VkExportMemoryWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
const SECURITY_ATTRIBUTES* pAttributes;
DWORD dwAccess;
LPCWSTR name;
} VkExportMemoryWin32HandleInfoKHR;
typedef struct VkMemoryWin32HandlePropertiesKHR {
VkStructureType sType;
void* pNext;
uint32_t memoryTypeBits;
} VkMemoryWin32HandlePropertiesKHR;
typedef struct VkMemoryGetWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
VkDeviceMemory memory;
VkExternalMemoryHandleTypeFlagBits handleType;
} VkMemoryGetWin32HandleInfoKHR;
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleKHR)(VkDevice device, const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle);
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties);
#ifndef VK_NO_PROTOTYPES
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleKHR(
VkDevice device,
const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo,
HANDLE* pHandle);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHR(
VkDevice device,
VkExternalMemoryHandleTypeFlagBits handleType,
HANDLE handle,
VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties);
#endif
#endif
// VK_KHR_win32_keyed_mutex is a preprocessor guard. Do not pass it to API calls.
#define VK_KHR_win32_keyed_mutex 1
#define VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION 1
#define VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHR_win32_keyed_mutex"
typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHR {
VkStructureType sType;
const void* pNext;
uint32_t acquireCount;
const VkDeviceMemory* pAcquireSyncs;
const uint64_t* pAcquireKeys;
const uint32_t* pAcquireTimeouts;
uint32_t releaseCount;
const VkDeviceMemory* pReleaseSyncs;
const uint64_t* pReleaseKeys;
} VkWin32KeyedMutexAcquireReleaseInfoKHR;
// VK_KHR_external_semaphore_win32 is a preprocessor guard. Do not pass it to API calls.
#define VK_KHR_external_semaphore_win32 1
#define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1
#define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHR_external_semaphore_win32"
typedef struct VkImportSemaphoreWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
VkSemaphore semaphore;
VkSemaphoreImportFlags flags;
VkExternalSemaphoreHandleTypeFlagBits handleType;
HANDLE handle;
LPCWSTR name;
} VkImportSemaphoreWin32HandleInfoKHR;
typedef struct VkExportSemaphoreWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
const SECURITY_ATTRIBUTES* pAttributes;
DWORD dwAccess;
LPCWSTR name;
} VkExportSemaphoreWin32HandleInfoKHR;
typedef struct VkD3D12FenceSubmitInfoKHR {
VkStructureType sType;
const void* pNext;
uint32_t waitSemaphoreValuesCount;
const uint64_t* pWaitSemaphoreValues;
uint32_t signalSemaphoreValuesCount;
const uint64_t* pSignalSemaphoreValues;
} VkD3D12FenceSubmitInfoKHR;
typedef struct VkSemaphoreGetWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
VkSemaphore semaphore;
VkExternalSemaphoreHandleTypeFlagBits handleType;
} VkSemaphoreGetWin32HandleInfoKHR;
typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreWin32HandleKHR)(VkDevice device, const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo);
typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreWin32HandleKHR)(VkDevice device, const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle);
#ifndef VK_NO_PROTOTYPES
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreWin32HandleKHR(
VkDevice device,
const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreWin32HandleKHR(
VkDevice device,
const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo,
HANDLE* pHandle);
#endif
#endif
// VK_KHR_external_fence_win32 is a preprocessor guard. Do not pass it to API calls.
#define VK_KHR_external_fence_win32 1
#define VK_KHR_EXTERNAL_FENCE_WIN32_SPEC_VERSION 1
#define VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME "VK_KHR_external_fence_win32"
typedef struct VkImportFenceWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
VkFence fence;
VkFenceImportFlags flags;
VkExternalFenceHandleTypeFlagBits handleType;
HANDLE handle;
LPCWSTR name;
} VkImportFenceWin32HandleInfoKHR;
typedef struct VkExportFenceWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
const SECURITY_ATTRIBUTES* pAttributes;
DWORD dwAccess;
LPCWSTR name;
} VkExportFenceWin32HandleInfoKHR;
typedef struct VkFenceGetWin32HandleInfoKHR {
VkStructureType sType;
const void* pNext;
VkFence fence;
VkExternalFenceHandleTypeFlagBits handleType;
} VkFenceGetWin32HandleInfoKHR;
typedef VkResult (VKAPI_PTR *PFN_vkImportFenceWin32HandleKHR)(VkDevice device, const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo);
typedef VkResult (VKAPI_PTR *PFN_vkGetFenceWin32HandleKHR)(VkDevice device, const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle);
#ifndef VK_NO_PROTOTYPES
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkImportFenceWin32HandleKHR(
VkDevice device,
const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceWin32HandleKHR(
VkDevice device,
const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo,
HANDLE* pHandle);
#endif
#endif
// VK_NV_external_memory_win32 is a preprocessor guard. Do not pass it to API calls.
#define VK_NV_external_memory_win32 1
#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
typedef struct VkImportMemoryWin32HandleInfoNV {
VkStructureType sType;
const void* pNext;
VkExternalMemoryHandleTypeFlagsNV handleType;
HANDLE handle;
} VkImportMemoryWin32HandleInfoNV;
typedef struct VkExportMemoryWin32HandleInfoNV {
VkStructureType sType;
const void* pNext;
const SECURITY_ATTRIBUTES* pAttributes;
DWORD dwAccess;
} VkExportMemoryWin32HandleInfoNV;
typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle);
#ifndef VK_NO_PROTOTYPES
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV(
VkDevice device,
VkDeviceMemory memory,
VkExternalMemoryHandleTypeFlagsNV handleType,
HANDLE* pHandle);
#endif
#endif
// VK_NV_win32_keyed_mutex is a preprocessor guard. Do not pass it to API calls.
#define VK_NV_win32_keyed_mutex 1
#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 2
#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV {
VkStructureType sType;
const void* pNext;
uint32_t acquireCount;
const VkDeviceMemory* pAcquireSyncs;
const uint64_t* pAcquireKeys;
const uint32_t* pAcquireTimeoutMilliseconds;
uint32_t releaseCount;
const VkDeviceMemory* pReleaseSyncs;
const uint64_t* pReleaseKeys;
} VkWin32KeyedMutexAcquireReleaseInfoNV;
// VK_EXT_full_screen_exclusive is a preprocessor guard. Do not pass it to API calls.
#define VK_EXT_full_screen_exclusive 1
#define VK_EXT_FULL_SCREEN_EXCLUSIVE_SPEC_VERSION 4
#define VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME "VK_EXT_full_screen_exclusive"
typedef enum VkFullScreenExclusiveEXT {
VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT = 0,
VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT = 1,
VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT = 2,
VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT = 3,
VK_FULL_SCREEN_EXCLUSIVE_MAX_ENUM_EXT = 0x7FFFFFFF
} VkFullScreenExclusiveEXT;
typedef struct VkSurfaceFullScreenExclusiveInfoEXT {
VkStructureType sType;
void* pNext;
VkFullScreenExclusiveEXT fullScreenExclusive;
} VkSurfaceFullScreenExclusiveInfoEXT;
typedef struct VkSurfaceCapabilitiesFullScreenExclusiveEXT {
VkStructureType sType;
void* pNext;
VkBool32 fullScreenExclusiveSupported;
} VkSurfaceCapabilitiesFullScreenExclusiveEXT;
typedef struct VkSurfaceFullScreenExclusiveWin32InfoEXT {
VkStructureType sType;
const void* pNext;
HMONITOR hmonitor;
} VkSurfaceFullScreenExclusiveWin32InfoEXT;
typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes);
typedef VkResult (VKAPI_PTR *PFN_vkAcquireFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain);
typedef VkResult (VKAPI_PTR *PFN_vkReleaseFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain);
typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModes2EXT)(VkDevice device, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkDeviceGroupPresentModeFlagsKHR* pModes);
#ifndef VK_NO_PROTOTYPES
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModes2EXT(
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
uint32_t* pPresentModeCount,
VkPresentModeKHR* pPresentModes);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireFullScreenExclusiveModeEXT(
VkDevice device,
VkSwapchainKHR swapchain);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkReleaseFullScreenExclusiveModeEXT(
VkDevice device,
VkSwapchainKHR swapchain);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModes2EXT(
VkDevice device,
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
VkDeviceGroupPresentModeFlagsKHR* pModes);
#endif
#endif
// VK_NV_acquire_winrt_display is a preprocessor guard. Do not pass it to API calls.
#define VK_NV_acquire_winrt_display 1
#define VK_NV_ACQUIRE_WINRT_DISPLAY_SPEC_VERSION 1
#define VK_NV_ACQUIRE_WINRT_DISPLAY_EXTENSION_NAME "VK_NV_acquire_winrt_display"
typedef VkResult (VKAPI_PTR *PFN_vkAcquireWinrtDisplayNV)(VkPhysicalDevice physicalDevice, VkDisplayKHR display);
typedef VkResult (VKAPI_PTR *PFN_vkGetWinrtDisplayNV)(VkPhysicalDevice physicalDevice, uint32_t deviceRelativeId, VkDisplayKHR* pDisplay);
#ifndef VK_NO_PROTOTYPES
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireWinrtDisplayNV(
VkPhysicalDevice physicalDevice,
VkDisplayKHR display);
#endif
#ifndef VK_ONLY_EXPORTED_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkGetWinrtDisplayNV(
VkPhysicalDevice physicalDevice,
uint32_t deviceRelativeId,
VkDisplayKHR* pDisplay);
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif
+62
View File
@@ -0,0 +1,62 @@
@echo off
REM ---------------------------------------------------------------------------
REM Build the native bridge DLL (wpywdlss_bridge.dll).
REM
REM Sets up the MSVC x64 environment itself, so this can be run from a plain
REM cmd or PowerShell. Requires CMake and Ninja on PATH.
REM
REM Avoids parenthesised if-blocks on purpose: the VS path contains "(x86)",
REM and expanding such a value inside a ( ) block truncates it.
REM ---------------------------------------------------------------------------
setlocal EnableExtensions
set "VCVARS=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
set "HERE=%~dp0"
set "NATIVE=%HERE%.."
set "BUILD=%NATIVE%\build"
if not defined JAVA_HOME set "JAVA_HOME=C:\Program Files\Java\jdk-21.0.10"
if not exist "%VCVARS%" goto :err_vcvars
if not exist "%JAVA_HOME%\include\jni.h" goto :err_javahome
echo [1/4] entering MSVC x64 environment
call "%VCVARS%" >nul 2>&1
if errorlevel 1 goto :err_vcvars
echo [2/4] ensuring vulkan-1.lib exists
if not exist "%NATIVE%\third_party\vulkan\lib\vulkan-1.lib" call "%HERE%make_vulkan_lib.cmd"
if not exist "%NATIVE%\third_party\vulkan\lib\vulkan-1.lib" goto :err_vulkanlib
echo [3/4] configuring (cmake -G Ninja)
cmake -S "%NATIVE%" -B "%BUILD%" -G Ninja -DCMAKE_BUILD_TYPE=Release -DJAVA_HOME="%JAVA_HOME%"
if errorlevel 1 goto :err_cmake
echo [4/4] building
cmake --build "%BUILD%"
if errorlevel 1 goto :err_build
echo.
echo [OK] built:
dir /b "%BUILD%\wpywdlss_bridge.dll" 2>nul
exit /b 0
:err_vcvars
echo [ERROR] vcvars64.bat missing or failed: %VCVARS%
exit /b 1
:err_javahome
echo [ERROR] jni.h not found under JAVA_HOME=%JAVA_HOME%
exit /b 1
:err_vulkanlib
echo [ERROR] could not produce vulkan-1.lib
exit /b 1
:err_cmake
echo [ERROR] cmake configure failed
exit /b 1
:err_build
echo [ERROR] cmake build failed
exit /b 1
+76
View File
@@ -0,0 +1,76 @@
@echo off
REM ---------------------------------------------------------------------------
REM Synthesise vulkan-1.lib from the system vulkan-1.dll.
REM
REM This removes the need to install the LunarG Vulkan SDK (~1 GB) just to get
REM an import library: Windows already ships vulkan-1.dll, and we only need the
REM matching .lib so the MSVC linker can resolve vkCreateInstance & friends.
REM
REM Run from a *plain* cmd/PowerShell: this script sets up the MSVC environment
REM itself via vcvars64.bat.
REM
REM NOTE: deliberately avoids parenthesised if-blocks and avoids
REM `setlocal EnableDelayedExpansion`, because this script's own paths contain
REM "(x86)" -- expanding such a value inside a ( ) block truncates the block
REM and yields "\Microsoft was unexpected at this time."
REM ---------------------------------------------------------------------------
setlocal EnableExtensions
set "VCVARS=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
set "SYS_DLL=%SystemRoot%\System32\vulkan-1.dll"
set "HERE=%~dp0"
set "OUTDIR=%HERE%..\third_party\vulkan\lib"
set "SCRATCH=%HERE%..\build\scratch"
set "TMPDEF=%SCRATCH%\vulkan-1.def"
set "TMPEXP=%SCRATCH%\vulkan_exports.txt"
if not exist "%VCVARS%" goto :err_vcvars
if not exist "%SYS_DLL%" goto :err_dll
if not exist "%SCRATCH%" mkdir "%SCRATCH%"
echo [1/4] entering MSVC x64 environment
call "%VCVARS%" >nul 2>&1
if errorlevel 1 goto :err_vcvars
echo [2/4] dumping exports from the system vulkan-1.dll
dumpbin /nologo /exports "%SYS_DLL%" > "%TMPEXP%"
if errorlevel 1 goto :err_dumpbin
echo [3/4] writing module definition file
> "%TMPDEF%" echo LIBRARY vulkan-1.dll
>> "%TMPDEF%" echo EXPORTS
for /f "usebackq tokens=1,2,3,4" %%a in ("%TMPEXP%") do call :emit "%%d"
if not exist "%OUTDIR%" mkdir "%OUTDIR%"
echo [4/4] generating import library
lib /nologo /def:"%TMPDEF%" /machine:x64 /out:"%OUTDIR%\vulkan-1.lib"
if errorlevel 1 goto :err_lib
echo.
echo [OK] wrote %OUTDIR%\vulkan-1.lib
exit /b 0
REM ---- helper: append a symbol to the .def only if it looks like a Vulkan entry
:emit
echo %~1 | findstr /b /r "vk" >nul
if errorlevel 1 exit /b 0
>> "%TMPDEF%" echo %~1
exit /b 0
:err_vcvars
echo [ERROR] vcvars64.bat missing or failed: %VCVARS%
exit /b 1
:err_dll
echo [ERROR] vulkan-1.dll not found: %SYS_DLL%
exit /b 1
:err_dumpbin
echo [ERROR] dumpbin failed
exit /b 1
:err_lib
echo [ERROR] lib failed
exit /b 1
Binary file not shown.
+666
View File
@@ -0,0 +1,666 @@
config_schema=13
arm=1
hook_mode=3
native_vulkan_enabled=0
nr_interpolation_enabled=0
early_load_policy=0
safe_neutral_start=0
menu_accent_rgb=4364026
ui_correction_mode=0
toggle_hotkey_vk=0
toggle_hotkey_modifiers=0
enabled=1
passes=1.0
layers=1
neural_work_percent=100
neural_work_divisor=1
neural_placement_mode=0
neural_correction_domain=0
multipass_balance_mode=1
auto_balance_profile=1
balance_pass_cleanup=1.000
balance_pass_halo=1.000
balance_scale_sharpen=1.000
balance_scale_halo=1.000
balance_detail_retention=1.000
frame_generation_coexistence=0
preserve_native_tone_color=0
preserve_native_tone_color_strength=0.000
creative_lut=0
creative_lut_strength=1.000
clean_fry_enabled=0
clean_fry_cleanup_strength=0.650
clean_fry_detail_retention=0.850
motion_stability_enabled=0
motion_stability_strength=0.200
motion_stability_detail_retention=1.000
texture_boost=0
texture_boost_strength=1.000
adaptive_scene_white=0
adaptive_cascade_style=0
adaptive_cascade_quality=3
adaptive_cascade_maximum_depth=4
adaptive_cascade_novelty=0.8500
adaptive_cascade_reinforcement=0.5500
adaptive_cascade_detail_budget=0.8000
adaptive_cascade_temporal=0.5500
adaptive_cascade_motion_rejection=0.7500
adaptive_cascade_depth_rejection=0.8000
adaptive_cascade_disocclusion_rejection=0.9000
adaptive_cascade_residual_energy_threshold=0.001500
adaptive_cascade_debug_view=0
adaptive_cascade_debug_stage=3
adaptive_cascade_foundation_structure=1.2000
adaptive_cascade_foundation_tone=0.3500
adaptive_cascade_foundation_gain=1.0000
adaptive_cascade_foundation_excitation=0.0000
adaptive_cascade_foundation_renderer_anchor=0.0000
adaptive_cascade_foundation_intensity=1.1000
adaptive_cascade_foundation_response_weight=0.0000
adaptive_cascade_foundation_excitation_limit=0.0000
adaptive_cascade_foundation_neural_feedback=0.0000
adaptive_cascade_foundation_contribution_headroom=1.0000
adaptive_cascade_meso_structure=1.4000
adaptive_cascade_meso_tone=0.1000
adaptive_cascade_meso_gain=1.0000
adaptive_cascade_meso_excitation=0.7000
adaptive_cascade_meso_renderer_anchor=0.1000
adaptive_cascade_meso_intensity=1.3500
adaptive_cascade_meso_response_weight=0.9000
adaptive_cascade_meso_excitation_limit=0.1000
adaptive_cascade_meso_neural_feedback=0.2000
adaptive_cascade_meso_contribution_headroom=1.8000
adaptive_cascade_fine_structure=1.6500
adaptive_cascade_fine_tone=0.0500
adaptive_cascade_fine_gain=0.8000
adaptive_cascade_fine_excitation=0.6500
adaptive_cascade_fine_renderer_anchor=0.1500
adaptive_cascade_fine_intensity=1.5500
adaptive_cascade_fine_response_weight=1.0000
adaptive_cascade_fine_excitation_limit=0.0800
adaptive_cascade_fine_neural_feedback=0.4000
adaptive_cascade_fine_contribution_headroom=2.0000
adaptive_cascade_micro_structure=1.9000
adaptive_cascade_micro_tone=0.0000
adaptive_cascade_micro_gain=0.6000
adaptive_cascade_micro_excitation=0.5500
adaptive_cascade_micro_renderer_anchor=0.2000
adaptive_cascade_micro_intensity=1.8000
adaptive_cascade_micro_response_weight=1.0000
adaptive_cascade_micro_excitation_limit=0.0600
adaptive_cascade_micro_neural_feedback=0.5500
adaptive_cascade_micro_contribution_headroom=2.0000
scene_paper_white_scale=1.765
hdr_transfer_strength=1.000
color_strength=1.000
detail_color_coupling=0.000
stack_tone_policy=2
layer_1_nr_preset=1
layer_1_nr_style=0
layer_1_intensity=1.000
layer_1_local_tone=1.000
layer_1_local_structure=1.000
layer_1_influence=1.000
layer_1_skin_structure=1.000
layer_1_auto_mask=1
layer_1_ui_correction=1
layer_1_depth_convention=0
layer_1_mvec_scale_x_multiplier=1.000
layer_1_mvec_scale_y_multiplier=1.000
layer_2_nr_preset=1
layer_2_nr_style=0
layer_2_intensity=1.000
layer_2_local_tone=1.000
layer_2_local_structure=1.000
layer_2_influence=1.000
layer_2_skin_structure=1.000
layer_2_auto_mask=1
layer_2_ui_correction=1
layer_2_depth_convention=0
layer_2_mvec_scale_x_multiplier=1.000
layer_2_mvec_scale_y_multiplier=1.000
layer_3_nr_preset=1
layer_3_nr_style=0
layer_3_intensity=1.000
layer_3_local_tone=1.000
layer_3_local_structure=1.000
layer_3_influence=1.000
layer_3_skin_structure=1.000
layer_3_auto_mask=1
layer_3_ui_correction=1
layer_3_depth_convention=0
layer_3_mvec_scale_x_multiplier=1.000
layer_3_mvec_scale_y_multiplier=1.000
layer_4_nr_preset=1
layer_4_nr_style=0
layer_4_intensity=1.000
layer_4_local_tone=1.000
layer_4_local_structure=1.000
layer_4_influence=1.000
layer_4_skin_structure=1.000
layer_4_auto_mask=1
layer_4_ui_correction=1
layer_4_depth_convention=0
layer_4_mvec_scale_x_multiplier=1.000
layer_4_mvec_scale_y_multiplier=1.000
layer_5_nr_preset=1
layer_5_nr_style=0
layer_5_intensity=1.000
layer_5_local_tone=1.000
layer_5_local_structure=1.000
layer_5_influence=1.000
layer_5_skin_structure=1.000
layer_5_auto_mask=1
layer_5_ui_correction=1
layer_5_depth_convention=0
layer_5_mvec_scale_x_multiplier=1.000
layer_5_mvec_scale_y_multiplier=1.000
layer_6_nr_preset=1
layer_6_nr_style=0
layer_6_intensity=1.000
layer_6_local_tone=1.000
layer_6_local_structure=1.000
layer_6_influence=1.000
layer_6_skin_structure=1.000
layer_6_auto_mask=1
layer_6_ui_correction=1
layer_6_depth_convention=0
layer_6_mvec_scale_x_multiplier=1.000
layer_6_mvec_scale_y_multiplier=1.000
layer_7_nr_preset=1
layer_7_nr_style=0
layer_7_intensity=1.000
layer_7_local_tone=1.000
layer_7_local_structure=1.000
layer_7_influence=1.000
layer_7_skin_structure=1.000
layer_7_auto_mask=1
layer_7_ui_correction=1
layer_7_depth_convention=0
layer_7_mvec_scale_x_multiplier=1.000
layer_7_mvec_scale_y_multiplier=1.000
layer_8_nr_preset=1
layer_8_nr_style=0
layer_8_intensity=1.000
layer_8_local_tone=1.000
layer_8_local_structure=1.000
layer_8_influence=1.000
layer_8_skin_structure=1.000
layer_8_auto_mask=1
layer_8_ui_correction=1
layer_8_depth_convention=0
layer_8_mvec_scale_x_multiplier=1.000
layer_8_mvec_scale_y_multiplier=1.000
layer_9_nr_preset=1
layer_9_nr_style=0
layer_9_intensity=1.000
layer_9_local_tone=1.000
layer_9_local_structure=1.000
layer_9_influence=1.000
layer_9_skin_structure=1.000
layer_9_auto_mask=1
layer_9_ui_correction=1
layer_9_depth_convention=0
layer_9_mvec_scale_x_multiplier=1.000
layer_9_mvec_scale_y_multiplier=1.000
layer_10_nr_preset=1
layer_10_nr_style=0
layer_10_intensity=1.000
layer_10_local_tone=1.000
layer_10_local_structure=1.000
layer_10_influence=1.000
layer_10_skin_structure=1.000
layer_10_auto_mask=1
layer_10_ui_correction=1
layer_10_depth_convention=0
layer_10_mvec_scale_x_multiplier=1.000
layer_10_mvec_scale_y_multiplier=1.000
layer_11_nr_preset=1
layer_11_nr_style=0
layer_11_intensity=1.000
layer_11_local_tone=1.000
layer_11_local_structure=1.000
layer_11_influence=1.000
layer_11_skin_structure=1.000
layer_11_auto_mask=1
layer_11_ui_correction=1
layer_11_depth_convention=0
layer_11_mvec_scale_x_multiplier=1.000
layer_11_mvec_scale_y_multiplier=1.000
layer_12_nr_preset=1
layer_12_nr_style=0
layer_12_intensity=1.000
layer_12_local_tone=1.000
layer_12_local_structure=1.000
layer_12_influence=1.000
layer_12_skin_structure=1.000
layer_12_auto_mask=1
layer_12_ui_correction=1
layer_12_depth_convention=0
layer_12_mvec_scale_x_multiplier=1.000
layer_12_mvec_scale_y_multiplier=1.000
layer_13_nr_preset=1
layer_13_nr_style=0
layer_13_intensity=1.000
layer_13_local_tone=1.000
layer_13_local_structure=1.000
layer_13_influence=1.000
layer_13_skin_structure=1.000
layer_13_auto_mask=1
layer_13_ui_correction=1
layer_13_depth_convention=0
layer_13_mvec_scale_x_multiplier=1.000
layer_13_mvec_scale_y_multiplier=1.000
layer_14_nr_preset=1
layer_14_nr_style=0
layer_14_intensity=1.000
layer_14_local_tone=1.000
layer_14_local_structure=1.000
layer_14_influence=1.000
layer_14_skin_structure=1.000
layer_14_auto_mask=1
layer_14_ui_correction=1
layer_14_depth_convention=0
layer_14_mvec_scale_x_multiplier=1.000
layer_14_mvec_scale_y_multiplier=1.000
layer_15_nr_preset=1
layer_15_nr_style=0
layer_15_intensity=1.000
layer_15_local_tone=1.000
layer_15_local_structure=1.000
layer_15_influence=1.000
layer_15_skin_structure=1.000
layer_15_auto_mask=1
layer_15_ui_correction=1
layer_15_depth_convention=0
layer_15_mvec_scale_x_multiplier=1.000
layer_15_mvec_scale_y_multiplier=1.000
layer_16_nr_preset=1
layer_16_nr_style=0
layer_16_intensity=1.000
layer_16_local_tone=1.000
layer_16_local_structure=1.000
layer_16_influence=1.000
layer_16_skin_structure=1.000
layer_16_auto_mask=1
layer_16_ui_correction=1
layer_16_depth_convention=0
layer_16_mvec_scale_x_multiplier=1.000
layer_16_mvec_scale_y_multiplier=1.000
layer_17_nr_preset=1
layer_17_nr_style=0
layer_17_intensity=1.000
layer_17_local_tone=1.000
layer_17_local_structure=1.000
layer_17_influence=1.000
layer_17_skin_structure=1.000
layer_17_auto_mask=1
layer_17_ui_correction=1
layer_17_depth_convention=0
layer_17_mvec_scale_x_multiplier=1.000
layer_17_mvec_scale_y_multiplier=1.000
layer_18_nr_preset=1
layer_18_nr_style=0
layer_18_intensity=1.000
layer_18_local_tone=1.000
layer_18_local_structure=1.000
layer_18_influence=1.000
layer_18_skin_structure=1.000
layer_18_auto_mask=1
layer_18_ui_correction=1
layer_18_depth_convention=0
layer_18_mvec_scale_x_multiplier=1.000
layer_18_mvec_scale_y_multiplier=1.000
layer_19_nr_preset=1
layer_19_nr_style=0
layer_19_intensity=1.000
layer_19_local_tone=1.000
layer_19_local_structure=1.000
layer_19_influence=1.000
layer_19_skin_structure=1.000
layer_19_auto_mask=1
layer_19_ui_correction=1
layer_19_depth_convention=0
layer_19_mvec_scale_x_multiplier=1.000
layer_19_mvec_scale_y_multiplier=1.000
layer_20_nr_preset=1
layer_20_nr_style=0
layer_20_intensity=1.000
layer_20_local_tone=1.000
layer_20_local_structure=1.000
layer_20_influence=1.000
layer_20_skin_structure=1.000
layer_20_auto_mask=1
layer_20_ui_correction=1
layer_20_depth_convention=0
layer_20_mvec_scale_x_multiplier=1.000
layer_20_mvec_scale_y_multiplier=1.000
layer_21_nr_preset=1
layer_21_nr_style=0
layer_21_intensity=1.000
layer_21_local_tone=1.000
layer_21_local_structure=1.000
layer_21_influence=1.000
layer_21_skin_structure=1.000
layer_21_auto_mask=1
layer_21_ui_correction=1
layer_21_depth_convention=0
layer_21_mvec_scale_x_multiplier=1.000
layer_21_mvec_scale_y_multiplier=1.000
layer_22_nr_preset=1
layer_22_nr_style=0
layer_22_intensity=1.000
layer_22_local_tone=1.000
layer_22_local_structure=1.000
layer_22_influence=1.000
layer_22_skin_structure=1.000
layer_22_auto_mask=1
layer_22_ui_correction=1
layer_22_depth_convention=0
layer_22_mvec_scale_x_multiplier=1.000
layer_22_mvec_scale_y_multiplier=1.000
layer_23_nr_preset=1
layer_23_nr_style=0
layer_23_intensity=1.000
layer_23_local_tone=1.000
layer_23_local_structure=1.000
layer_23_influence=1.000
layer_23_skin_structure=1.000
layer_23_auto_mask=1
layer_23_ui_correction=1
layer_23_depth_convention=0
layer_23_mvec_scale_x_multiplier=1.000
layer_23_mvec_scale_y_multiplier=1.000
layer_24_nr_preset=1
layer_24_nr_style=0
layer_24_intensity=1.000
layer_24_local_tone=1.000
layer_24_local_structure=1.000
layer_24_influence=1.000
layer_24_skin_structure=1.000
layer_24_auto_mask=1
layer_24_ui_correction=1
layer_24_depth_convention=0
layer_24_mvec_scale_x_multiplier=1.000
layer_24_mvec_scale_y_multiplier=1.000
layer_25_nr_preset=1
layer_25_nr_style=0
layer_25_intensity=1.000
layer_25_local_tone=1.000
layer_25_local_structure=1.000
layer_25_influence=1.000
layer_25_skin_structure=1.000
layer_25_auto_mask=1
layer_25_ui_correction=1
layer_25_depth_convention=0
layer_25_mvec_scale_x_multiplier=1.000
layer_25_mvec_scale_y_multiplier=1.000
layer_26_nr_preset=1
layer_26_nr_style=0
layer_26_intensity=1.000
layer_26_local_tone=1.000
layer_26_local_structure=1.000
layer_26_influence=1.000
layer_26_skin_structure=1.000
layer_26_auto_mask=1
layer_26_ui_correction=1
layer_26_depth_convention=0
layer_26_mvec_scale_x_multiplier=1.000
layer_26_mvec_scale_y_multiplier=1.000
layer_27_nr_preset=1
layer_27_nr_style=0
layer_27_intensity=1.000
layer_27_local_tone=1.000
layer_27_local_structure=1.000
layer_27_influence=1.000
layer_27_skin_structure=1.000
layer_27_auto_mask=1
layer_27_ui_correction=1
layer_27_depth_convention=0
layer_27_mvec_scale_x_multiplier=1.000
layer_27_mvec_scale_y_multiplier=1.000
layer_28_nr_preset=1
layer_28_nr_style=0
layer_28_intensity=1.000
layer_28_local_tone=1.000
layer_28_local_structure=1.000
layer_28_influence=1.000
layer_28_skin_structure=1.000
layer_28_auto_mask=1
layer_28_ui_correction=1
layer_28_depth_convention=0
layer_28_mvec_scale_x_multiplier=1.000
layer_28_mvec_scale_y_multiplier=1.000
layer_29_nr_preset=1
layer_29_nr_style=0
layer_29_intensity=1.000
layer_29_local_tone=1.000
layer_29_local_structure=1.000
layer_29_influence=1.000
layer_29_skin_structure=1.000
layer_29_auto_mask=1
layer_29_ui_correction=1
layer_29_depth_convention=0
layer_29_mvec_scale_x_multiplier=1.000
layer_29_mvec_scale_y_multiplier=1.000
layer_30_nr_preset=1
layer_30_nr_style=0
layer_30_intensity=1.000
layer_30_local_tone=1.000
layer_30_local_structure=1.000
layer_30_influence=1.000
layer_30_skin_structure=1.000
layer_30_auto_mask=1
layer_30_ui_correction=1
layer_30_depth_convention=0
layer_30_mvec_scale_x_multiplier=1.000
layer_30_mvec_scale_y_multiplier=1.000
depth_bridge_1_enabled=0
depth_bridge_1_mode=0
depth_bridge_1_strength=0.500
depth_bridge_1_start=0.650
depth_bridge_1_far_limit=1.000
depth_bridge_1_curve=2.000
depth_bridge_1_edge_protection=1.000
depth_bridge_2_enabled=0
depth_bridge_2_mode=0
depth_bridge_2_strength=0.500
depth_bridge_2_start=0.650
depth_bridge_2_far_limit=1.000
depth_bridge_2_curve=2.000
depth_bridge_2_edge_protection=1.000
depth_bridge_3_enabled=0
depth_bridge_3_mode=0
depth_bridge_3_strength=0.500
depth_bridge_3_start=0.650
depth_bridge_3_far_limit=1.000
depth_bridge_3_curve=2.000
depth_bridge_3_edge_protection=1.000
depth_bridge_4_enabled=0
depth_bridge_4_mode=0
depth_bridge_4_strength=0.500
depth_bridge_4_start=0.650
depth_bridge_4_far_limit=1.000
depth_bridge_4_curve=2.000
depth_bridge_4_edge_protection=1.000
depth_bridge_5_enabled=0
depth_bridge_5_mode=0
depth_bridge_5_strength=0.500
depth_bridge_5_start=0.650
depth_bridge_5_far_limit=1.000
depth_bridge_5_curve=2.000
depth_bridge_5_edge_protection=1.000
depth_bridge_6_enabled=0
depth_bridge_6_mode=0
depth_bridge_6_strength=0.500
depth_bridge_6_start=0.650
depth_bridge_6_far_limit=1.000
depth_bridge_6_curve=2.000
depth_bridge_6_edge_protection=1.000
depth_bridge_7_enabled=0
depth_bridge_7_mode=0
depth_bridge_7_strength=0.500
depth_bridge_7_start=0.650
depth_bridge_7_far_limit=1.000
depth_bridge_7_curve=2.000
depth_bridge_7_edge_protection=1.000
depth_bridge_8_enabled=0
depth_bridge_8_mode=0
depth_bridge_8_strength=0.500
depth_bridge_8_start=0.650
depth_bridge_8_far_limit=1.000
depth_bridge_8_curve=2.000
depth_bridge_8_edge_protection=1.000
depth_bridge_9_enabled=0
depth_bridge_9_mode=0
depth_bridge_9_strength=0.500
depth_bridge_9_start=0.650
depth_bridge_9_far_limit=1.000
depth_bridge_9_curve=2.000
depth_bridge_9_edge_protection=1.000
depth_bridge_10_enabled=0
depth_bridge_10_mode=0
depth_bridge_10_strength=0.500
depth_bridge_10_start=0.650
depth_bridge_10_far_limit=1.000
depth_bridge_10_curve=2.000
depth_bridge_10_edge_protection=1.000
depth_bridge_11_enabled=0
depth_bridge_11_mode=0
depth_bridge_11_strength=0.500
depth_bridge_11_start=0.650
depth_bridge_11_far_limit=1.000
depth_bridge_11_curve=2.000
depth_bridge_11_edge_protection=1.000
depth_bridge_12_enabled=0
depth_bridge_12_mode=0
depth_bridge_12_strength=0.500
depth_bridge_12_start=0.650
depth_bridge_12_far_limit=1.000
depth_bridge_12_curve=2.000
depth_bridge_12_edge_protection=1.000
depth_bridge_13_enabled=0
depth_bridge_13_mode=0
depth_bridge_13_strength=0.500
depth_bridge_13_start=0.650
depth_bridge_13_far_limit=1.000
depth_bridge_13_curve=2.000
depth_bridge_13_edge_protection=1.000
depth_bridge_14_enabled=0
depth_bridge_14_mode=0
depth_bridge_14_strength=0.500
depth_bridge_14_start=0.650
depth_bridge_14_far_limit=1.000
depth_bridge_14_curve=2.000
depth_bridge_14_edge_protection=1.000
depth_bridge_15_enabled=0
depth_bridge_15_mode=0
depth_bridge_15_strength=0.500
depth_bridge_15_start=0.650
depth_bridge_15_far_limit=1.000
depth_bridge_15_curve=2.000
depth_bridge_15_edge_protection=1.000
depth_bridge_16_enabled=0
depth_bridge_16_mode=0
depth_bridge_16_strength=0.500
depth_bridge_16_start=0.650
depth_bridge_16_far_limit=1.000
depth_bridge_16_curve=2.000
depth_bridge_16_edge_protection=1.000
depth_bridge_17_enabled=0
depth_bridge_17_mode=0
depth_bridge_17_strength=0.500
depth_bridge_17_start=0.650
depth_bridge_17_far_limit=1.000
depth_bridge_17_curve=2.000
depth_bridge_17_edge_protection=1.000
depth_bridge_18_enabled=0
depth_bridge_18_mode=0
depth_bridge_18_strength=0.500
depth_bridge_18_start=0.650
depth_bridge_18_far_limit=1.000
depth_bridge_18_curve=2.000
depth_bridge_18_edge_protection=1.000
depth_bridge_19_enabled=0
depth_bridge_19_mode=0
depth_bridge_19_strength=0.500
depth_bridge_19_start=0.650
depth_bridge_19_far_limit=1.000
depth_bridge_19_curve=2.000
depth_bridge_19_edge_protection=1.000
depth_bridge_20_enabled=0
depth_bridge_20_mode=0
depth_bridge_20_strength=0.500
depth_bridge_20_start=0.650
depth_bridge_20_far_limit=1.000
depth_bridge_20_curve=2.000
depth_bridge_20_edge_protection=1.000
depth_bridge_21_enabled=0
depth_bridge_21_mode=0
depth_bridge_21_strength=0.500
depth_bridge_21_start=0.650
depth_bridge_21_far_limit=1.000
depth_bridge_21_curve=2.000
depth_bridge_21_edge_protection=1.000
depth_bridge_22_enabled=0
depth_bridge_22_mode=0
depth_bridge_22_strength=0.500
depth_bridge_22_start=0.650
depth_bridge_22_far_limit=1.000
depth_bridge_22_curve=2.000
depth_bridge_22_edge_protection=1.000
depth_bridge_23_enabled=0
depth_bridge_23_mode=0
depth_bridge_23_strength=0.500
depth_bridge_23_start=0.650
depth_bridge_23_far_limit=1.000
depth_bridge_23_curve=2.000
depth_bridge_23_edge_protection=1.000
depth_bridge_24_enabled=0
depth_bridge_24_mode=0
depth_bridge_24_strength=0.500
depth_bridge_24_start=0.650
depth_bridge_24_far_limit=1.000
depth_bridge_24_curve=2.000
depth_bridge_24_edge_protection=1.000
depth_bridge_25_enabled=0
depth_bridge_25_mode=0
depth_bridge_25_strength=0.500
depth_bridge_25_start=0.650
depth_bridge_25_far_limit=1.000
depth_bridge_25_curve=2.000
depth_bridge_25_edge_protection=1.000
depth_bridge_26_enabled=0
depth_bridge_26_mode=0
depth_bridge_26_strength=0.500
depth_bridge_26_start=0.650
depth_bridge_26_far_limit=1.000
depth_bridge_26_curve=2.000
depth_bridge_26_edge_protection=1.000
depth_bridge_27_enabled=0
depth_bridge_27_mode=0
depth_bridge_27_strength=0.500
depth_bridge_27_start=0.650
depth_bridge_27_far_limit=1.000
depth_bridge_27_curve=2.000
depth_bridge_27_edge_protection=1.000
depth_bridge_28_enabled=0
depth_bridge_28_mode=0
depth_bridge_28_strength=0.500
depth_bridge_28_start=0.650
depth_bridge_28_far_limit=1.000
depth_bridge_28_curve=2.000
depth_bridge_28_edge_protection=1.000
depth_bridge_29_enabled=0
depth_bridge_29_mode=0
depth_bridge_29_strength=0.500
depth_bridge_29_start=0.650
depth_bridge_29_far_limit=1.000
depth_bridge_29_curve=2.000
depth_bridge_29_edge_protection=1.000
# Live neural residual controls; neutral preserves the normal result.
residual_shadow_multiplier=1.000
residual_light_multiplier=1.000
glow_suppression=0.000
Binary file not shown.
@@ -0,0 +1,589 @@
/*------------------.
| :: Description :: |
'-------------------/
Blending Header (version 0.8)
Blending Algorithm Sources:
https://www.khronos.org/registry/OpenGL/extensions/NV/NV_blend_equation_advanced.txt
http://www.nathanm.com/photoshop-blending-math/
(Alt) https://github.com/cplotts/WPFSLBlendModeFx/blob/master/PhotoshopMathFP.hlsl
Header Authors: originalnicodr, prod80, uchu suzume, Marot Satil
About:
Provides a variety of blending methods for you to use as you wish. Just include this header.
History:
(*) Feature (+) Improvement (x) Bugfix (-) Information (!) Compatibility
Version 0.1 by Marot Satil & uchu suzume
* Added and improved upon multiple blending modes thanks to the work of uchu suzume, prod80, and originalnicodr.
Version 0.2 by uchu suzume & Marot Satil
* Added Addition, Subtract, Divide blending modes and improved code readability.
Version 0.3 by uchu suzume & Marot Satil
* Sorted blending modes in a more logical fashion, grouping by type.
Version 0.4 by uchu suzume
x Corrected Color Dodge blending behavior.
Version 0.5 by Marot Satil & uchu suzume
* Added preprocessor macros for uniform variable combo UI element & lerp.
Version 0.6 by Marot Satil & uchu suzume
* Added Divide (Alternative) and Divide (Photoshop) blending modes.
Version 0.7 by prod80
- Added original sources for blending algorithms.
x Corrected average luminosity values.
Version 0.8 by Marot Satil
* Added a new funciton to output blended data.
+ Moved all code into the BlendingH namespace, which is part of the ComHeaders common namespace meant to be used by other headers.
! Removed old preprocessor macro blending output.
.------------------.
| :: How To Use :: |
'------------------/
Blending two variables using this header in your own shaders is very straightforward.
Very basic example code using the "Darken" blending mode follows:
// First, include the header.
#include "Blending.fxh"
// You can use this preprocessor macro to generate an attractive and functional uniform int UI combo element containing the list of blending techniques:
// BLENDING_COMBO(variable_name, label, tooltip, category, category_closed, spacing, default_value)
BLENDING_COMBO(_BlendMode, "Blending Mode", "Select the blending mode applied to the layer.", "Blending Options", false, 0, 0)
// Inside of your function you can call this function to apply the blending option specified by an int (variable) to your float3 (input) via
// a lerp between your float3 (input), float3 (output), and a float (blending) for the alpha channel.
// ComHeaders::Blending::Blend(int variable, float3 input, float3 output, float blending)
outColor.rgb = ComHeaders::Blending::Blend(_BlendMode, inColor, outColor, outColor.a);
*/
// -------------------------------------
// Preprocessor Macros
// -------------------------------------
#undef BLENDING_COMBO
#define BLENDING_COMBO(variable, name_label, description, group, grp_closed, space, default_value) \
uniform int variable \
< \
ui_category = group; \
ui_category_closed = grp_closed; \
ui_items = \
"Normal\0" \
/* "Darken" */ \
"Darken\0" \
" Multiply\0" \
" Color Burn\0" \
" Linear Burn\0" \
/* "Lighten" */ \
"Lighten\0" \
" Screen\0" \
" Color Dodge\0" \
" Linear Dodge\0" \
" Addition\0" \
" Glow\0" \
/* "Contrast" */ \
"Overlay\0" \
" Soft Light\0" \
" Hard Light\0" \
" Vivid Light\0" \
" Linear Light\0" \
" Pin Light\0" \
" Hard Mix\0" \
/* "Inversion" */ \
"Difference\0" \
" Exclusion\0" \
/* "Cancelation" */ \
"Subtract\0" \
" Divide\0" \
" Divide (Alternative)\0" \
" Divide (Photoshop)\0" \
" Reflect\0" \
" Grain Extract\0" \
" Grain Merge\0" \
/* "Component" */ \
"Hue\0" \
" Saturation\0" \
" Color\0" \
" Luminosity\0"; \
ui_label = name_label; \
ui_tooltip = description; \
ui_type = "combo"; \
ui_spacing = space; \
> = default_value;
namespace ComHeaders
{
namespace Blending
{
// -------------------------------------
// Helper Functions
// -------------------------------------
float3 Aux(float3 a)
{
if (a.r <= 0.25 && a.g <= 0.25 && a.b <= 0.25)
return ((16.0 * a - 12.0) * a + 4) * a;
else
return sqrt(a);
}
float Lum(float3 a)
{
return (0.33333 * a.r + 0.33334 * a.g + 0.33333 * a.b);
}
float3 SetLum (float3 a, float b){
const float c = b - Lum(a);
return float3(a.r + c, a.g + c, a.b + c);
}
float min3 (float a, float b, float c)
{
return min(a, (min(b, c)));
}
float max3 (float a, float b, float c)
{
return max(a, max(b, c));
}
float3 SetSat(float3 a, float b){
float ar = a.r;
float ag = a.g;
float ab = a.b;
if (ar == max3(ar, ag, ab) && ab == min3(ar, ag, ab))
{
//caso r->max g->mid b->min
if (ar > ab)
{
ag = (((ag - ab) * b) / (ar - ab));
ar = b;
}
else
{
ag = 0.0;
ar = 0.0;
}
ab = 0.0;
}
else
{
if (ar == max3(ar, ag, ab) && ag == min3(ar, ag, ab))
{
//caso r->max b->mid g->min
if (ar > ag)
{
ab = (((ab - ag) * b) / (ar - ag));
ar = b;
}
else
{
ab = 0.0;
ar = 0.0;
}
ag = 0.0;
}
else
{
if (ag == max3(ar, ag, ab) && ab == min3(ar, ag, ab))
{
//caso g->max r->mid b->min
if (ag > ab)
{
ar = (((ar - ab) * b) / (ag - ab));
ag = b;
}
else
{
ar = 0.0;
ag = 0.0;
}
ab = 0.0;
}
else
{
if (ag == max3(ar, ag, ab) && ar == min3(ar, ag, ab))
{
//caso g->max b->mid r->min
if (ag > ar)
{
ab = (((ab - ar) * b) / (ag - ar));
ag = b;
}
else
{
ab = 0.0;
ag = 0.0;
}
ar = 0.0;
}
else
{
if (ab == max3(ar, ag, ab) && ag == min3(ar, ag, ab))
{
//caso b->max r->mid g->min
if (ab > ag)
{
ar = (((ar - ag) * b) / (ab - ag));
ab = b;
}
else
{
ar = 0.0;
ab = 0.0;
}
ag = 0.0;
}
else
{
if (ab == max3(ar, ag, ab) && ar == min3(ar, ag, ab))
{
//caso b->max g->mid r->min
if (ab > ar)
{
ag = (((ag - ar) * b) / (ab - ar));
ab = b;
}
else
{
ag = 0.0;
ab = 0.0;
}
ar = 0.0;
}
}
}
}
}
}
return float3(ar, ag, ab);
}
float Sat(float3 a)
{
return max3(a.r, a.g, a.b) - min3(a.r, a.g, a.b);
}
// -------------------------------------
// Blending Modes
// -------------------------------------
// Darken
float3 Darken(float3 a, float3 b)
{
return min(a, b);
}
// Multiply
float3 Multiply(float3 a, float3 b)
{
return a * b;
}
// Color Burn
float3 ColorBurn(float3 a, float3 b)
{
if (b.r > 0 && b.g > 0 && b.b > 0)
return 1.0 - min(1.0, (0.5 - a) / b);
else
return 0.0;
}
// Linear Burn
float3 LinearBurn(float3 a, float3 b)
{
return max(a + b - 1.0f, 0.0f);
}
// Lighten
float3 Lighten(float3 a, float3 b)
{
return max(a, b);
}
// Screen
float3 Screen(float3 a, float3 b)
{
return 1.0 - (1.0 - a) * (1.0 - b);
}
// Color Dodge
float3 ColorDodge(float3 a, float3 b)
{
if (b.r < 1 && b.g < 1 && b.b < 1)
return min(1.0, a / (1.0 - b));
else
return 1.0;
}
// Linear Dodge
float3 LinearDodge(float3 a, float3 b)
{
return min(a + b, 1.0f);
}
// Addition
float3 Addition(float3 a, float3 b)
{
return min((a + b), 1);
}
// Reflect
float3 Reflect(float3 a, float3 b)
{
if (b.r >= 0.999999 || b.g >= 0.999999 || b.b >= 0.999999)
return b;
else
return saturate(a * a / (1.0f - b));
}
// Glow
float3 Glow(float3 a, float3 b)
{
return Reflect(b, a);
}
// Overlay
float3 Overlay(float3 a, float3 b)
{
return lerp(2 * a * b, 1.0 - 2 * (1.0 - a) * (1.0 - b), step(0.5, a));
}
// Soft Light
float3 SoftLight(float3 a, float3 b)
{
if (b.r <= 0.5 && b.g <= 0.5 && b.b <= 0.5)
return clamp(a - (1.0 - 2 * b) * a * (1 - a), 0,1);
else
return clamp(a + (2 * b - 1.0) * (Aux(a) - a), 0, 1);
}
// Hard Light
float3 HardLight(float3 a, float3 b)
{
return lerp(2 * a * b, 1.0 - 2 * (1.0 - b) * (1.0 - a), step(0.5, b));
}
// Vivid Light
float3 VividLight(float3 a, float3 b)
{
return lerp(2 * a * b, b / (2 * (1.01 - a)), step(0.50, a));
}
// Linear Light
float3 LinearLight(float3 a, float3 b)
{
if (b.r < 0.5 || b.g < 0.5 || b.b < 0.5)
return LinearBurn(a, (2.0 * b));
else
return LinearDodge(a, (2.0 * (b - 0.5)));
}
// Pin Light
float3 PinLight(float3 a, float3 b)
{
if (b.r < 0.5 || b.g < 0.5 || b.b < 0.5)
return Darken(a, (2.0 * b));
else
return Lighten(a, (2.0 * (b - 0.5)));
}
// Hard Mix
float3 HardMix(float3 a, float3 b)
{
const float3 vl = VividLight(a, b);
if (vl.r < 0.5 || vl.g < 0.5 || vl.b < 0.5)
return 0.0;
else
return 1.0;
}
// Difference
float3 Difference(float3 a, float3 b)
{
return max(a - b, b - a);
}
// Exclusion
float3 Exclusion(float3 a, float3 b)
{
return a + b - 2 * a * b;
}
// Subtract
float3 Subtract(float3 a, float3 b)
{
return max((a - b), 0);
}
// Divide
float3 Divide(float3 a, float3 b)
{
return (saturate(a / (b + 0.01)));
}
// Divide (Alternative)
float3 DivideAlt(float3 a, float3 b)
{
return (saturate(1.0 / (a / b)));
}
// Divide (Photoshop)
float3 DividePS(float3 a, float3 b)
{
return (saturate(a / b));
}
// Grain Merge
float3 GrainMerge(float3 a, float3 b)
{
return saturate(b + a - 0.5);
}
// Grain Extract
float3 GrainExtract(float3 a, float3 b)
{
return saturate(a - b + 0.5);
}
// Hue
float3 Hue(float3 a, float3 b)
{
return SetLum(SetSat(b, Sat(a)), Lum(a));
}
// Saturation
float3 Saturation(float3 a, float3 b)
{
return SetLum(SetSat(a, Sat(b)), Lum(a));
}
// Color
float3 ColorB(float3 a, float3 b)
{
return SetLum(b, Lum(a));
}
// Luminousity
float3 Luminosity(float3 a, float3 b)
{
return SetLum(a, Lum(b));
}
// -------------------------------------
// Output Functions
// -------------------------------------
float3 Blend(int mode, float3 input, float3 output, float blending)
{
switch (mode)
{
// Normal
default:
return lerp(input.rgb, output.rgb, blending);
// Darken
case 1:
return lerp(input.rgb, Darken(input.rgb, output.rgb), blending);
// Multiply
case 2:
return lerp(input.rgb, Multiply(input.rgb, output.rgb), blending);
// Color Burn
case 3:
return lerp(input.rgb, ColorBurn(input.rgb, output.rgb), blending);
// Linear Burn
case 4:
return lerp(input.rgb, LinearBurn(input.rgb, output.rgb), blending);
// Lighten
case 5:
return lerp(input.rgb, Lighten(input.rgb, output.rgb), blending);
// Screen
case 6:
return lerp(input.rgb, Screen(input.rgb, output.rgb), blending);
// Color Dodge
case 7:
return lerp(input.rgb, ColorDodge(input.rgb, output.rgb), blending);
// Linear Dodge
case 8:
return lerp(input.rgb, LinearDodge(input.rgb, output.rgb), blending);
// Addition
case 9:
return lerp(input.rgb, Addition(input.rgb, output.rgb), blending);
// Glow
case 10:
return lerp(input.rgb, Glow(input.rgb, output.rgb), blending);
// Overlay
case 11:
return lerp(input.rgb, Overlay(input.rgb, output.rgb), blending);
// Soft Light
case 12:
return lerp(input.rgb, SoftLight(input.rgb, output.rgb), blending);
// Hard Light
case 13:
return lerp(input.rgb, HardLight(input.rgb, output.rgb), blending);
// Vivid Light
case 14:
return lerp(input.rgb, VividLight(input.rgb, output.rgb), blending);
// Linear Light
case 15:
return lerp(input.rgb, LinearLight(input.rgb, output.rgb), blending);
// Pin Light
case 16:
return lerp(input.rgb, PinLight(input.rgb, output.rgb), blending);
// Hard Mix
case 17:
return lerp(input.rgb, HardMix(input.rgb, output.rgb), blending);
// Difference
case 18:
return lerp(input.rgb, Difference(input.rgb, output.rgb), blending);
// Exclusion
case 19:
return lerp(input.rgb, Exclusion(input.rgb, output.rgb), blending);
// Subtract
case 20:
return lerp(input.rgb, Subtract(input.rgb, output.rgb), blending);
// Divide
case 21:
return lerp(input.rgb, Divide(input.rgb, output.rgb), blending);
// Divide (Alternative)
case 22:
return lerp(input.rgb, DivideAlt(input.rgb, output.rgb), blending);
// Divide (Photoshop)
case 23:
return lerp(input.rgb, DividePS(input.rgb, output.rgb), blending);
// Reflect
case 24:
return lerp(input.rgb, Reflect(input.rgb, output.rgb), blending);
// Grain Merge
case 25:
return lerp(input.rgb, GrainMerge(input.rgb, output.rgb), blending);
// Grain Extract
case 26:
return lerp(input.rgb, GrainExtract(input.rgb, output.rgb), blending);
// Hue
case 27:
return lerp(input.rgb, Hue(input.rgb, output.rgb), blending);
// Saturation
case 28:
return lerp(input.rgb, Saturation(input.rgb, output.rgb), blending);
// Color
case 29:
return lerp(input.rgb, ColorB(input.rgb, output.rgb), blending);
// Luminosity
case 30:
return lerp(input.rgb, Luminosity(input.rgb, output.rgb), blending);
}
}
}
}
@@ -0,0 +1,893 @@
/*
DLSS5_Feed.fx - companion effect for the "DLSS 5 Feed" ReShade add-on (dlss5-feed.addon64/32).
It turns what ReShade already has into the guide textures DLSS needs, in the exact layout
the add-on expects:
DLSS5_MV RG16F motion vectors in PIXELS, pointing from the current pixel to where it was
in the previous frame (DLSS convention). Vectors that fail validation
(below) are zeroed.
DLSS5_Depth R32F the game's raw hardware depth (not linearised), sampled at backbuffer size,
with ReShade's RESHADE_DEPTH_INPUT_* orientation fixes applied.
DLSS5_Mask R8 "bias current colour" mask for DLSS: 1 where the motion vector could not
be trusted, so DLSS leans on the current frame there instead of warping
history in. Optional -- an add-on that does not know it ignores it.
MOTION VECTOR PROVIDER -- set the DLSS5_MV_PROVIDER preprocessor definition (ReShade overlay:
this effect's "Preprocessor definitions", or the global list) and enable that provider's
technique ABOVE this one in the effect list:
0 texMotionVectors the community-standard shared texture: qUINT_motionvectors,
dh_uber_motion, ReshadeMotionEstimation (DRME -- NOTE: DRME does not
compile on ReShade 6.8, "cannot sample from texture that is also used
as render target"; it then silently writes nothing) [default]
1 Launchpad iMMERSE Launchpad (MartysMods_LAUNCHPAD.fx): Deferred::MotionVectorsTex.
Launchpad only runs its optical flow when asked to, so this mode also
files that per-frame request (Launchpad's IPC buffer, see below).
2 VORT vort_Motion.fx (MIT): MotVectTexVort -- the recommended provider
3 LumeniteFX Kernel lumenite_Kernel.fx ("LUMENITE: Kernel"): Kernel::tFlow -- pyramidal
optical flow with per-level median + a-trous filtering and previous-
frame seeding. 1/8 resolution, upsampled here. Needs no depth buffer.
4 LumeniteFX QuantMotion
lumenite_QuantMotion.fx: QuantMotion::tFlow -- the light cut of 3.
This is the same mechanism dh_uber_rt (USE_MARTY_LAUNCHPAD_MOTION / USE_VORT_MOTION) and
vort (V_MV_MODE) use: the selected provider's OUTPUT texture is declared here exactly as the
provider declares it, so ReShade binds the same resource, and only that one is allocated.
Every provider above hands out delta UV with prev_uv = uv + mv. Nothing of any provider is
included or bundled: this file contains no third-party code and includes no third-party
files beyond ReShade's own headers.
VALIDATION -- why it exists. A game's motion vectors are geometric: a static wall under a
flickering light has vectors of exactly zero. Every provider above is OPTICAL FLOW: it
matches pixels, so a lighting change (flicker, flames, particles) is answered with a vector
that points at whatever happened to match -- confidently wrong, and DLSS then warps its
history in from there. That is the "warping around flames" and the "bad dither when the
light flickers". The fix is the one every production TAA uses: reproject and CHECK.
For each pixel, three tests against the previous frame at uv + mv:
- luma: the previous luma must fall inside the current 3x3 neighbourhood's range
(flicker moves the whole range, so a stale match falls outside);
- depth: the previous linear depth must match the current one (disocclusions);
- consistency: the previous frame's vector at that spot must resemble this one
(real motion is smooth frame to frame; flow on fire is erratic).
A vector failing any test is zeroed (the surface is treated as static -- the right answer
for a lit wall) and the pixel is flagged in DLSS5_Mask so DLSS trusts the current frame there.
The add-on runs DLSS + DLSS 5 neural rendering right after the "DLSS5_Feed" technique has
rendered, so anything placed below it in the list is applied on top of the neural output.
*/
#include "ReShade.fxh"
// D3D9 is not a target. The add-on attaches to D3D10/11/12, OpenGL and Vulkan runtimes only,
// so if ReShade is on its DirectX 9 backend the effect could never be fed anyway -- and the
// geometric-fit solver below cannot compile there (SM3 has no tex2Dfetch and must unroll the
// [loop]s over dynamically indexed arrays, which is the "error X3531: can't unroll loops
// marked with loop attribute" of issue #56). Say which of those two facts the user is looking
// at, because the compiler error alone sends people hunting through their shader list.
#if __RENDERER__ < 0xA000
#error "DLSS5_Feed needs D3D10 or newer, and ReShade has loaded its DirectX 9 backend. For a D3D9 game the dgVoodoo2 wrapper must be in effect first (check DisableAndPassThru=false in dgVoodoo.conf); see the README's 'Install for a DirectX 9 game' section. A 64-bit D3D9 game does not need this add-on at all -- renodx-dlss handles those on its own."
#endif
// Expose ReShade's completed frame to the add-on as an SRV. The 64-bit D3D11 path
// uses this only when its work-resolution control is below 100%; no extra pass or
// copy is introduced by this declaration.
texture DLSS5_ColorInput : COLOR;
sampler sDLSS5_ColorInput { Texture = DLSS5_ColorInput; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Point; MagFilter = Point; };
#ifndef DLSS5_MV_PROVIDER
#define DLSS5_MV_PROVIDER 0
#endif
// ---------------------------------------------------------------------------------------------
// The selected provider's output, declared byte for byte like the provider itself does.
// ---------------------------------------------------------------------------------------------
#if DLSS5_MV_PROVIDER == 1
// iMMERSE Launchpad (MartysMods/mmx_deferred.fxh)
namespace Deferred {
texture MotionVectorsTex { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RG16F; };
// Launchpad's request buffer. Launchpad only computes optical flow when a consumer asked
// for it during the previous frame (it reads this 1x1 RGBA8 at the top of its technique
// and clears it at the bottom; bit 4 = optical flow, written through the render-target
// write mask). Being below Launchpad in the list, our request lands for the next frame.
// Declared like Launchpad declares it; the two shaders that write it below are ours.
namespace IPC {
texture2D PredicationBuffer { Format = RGBA8; };
}
}
sampler sDLSS5_ProviderMV { Texture = Deferred::MotionVectorsTex; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Point; MagFilter = Point; };
float4 DLSS5_IpcRequestVS(in uint id : SV_VertexID) : SV_Position { return float4(0.0, 0.0, 0.0, 1.0); }
float4 DLSS5_IpcRequestPS(in float4 vpos : SV_Position) : SV_Target0 { return 1.0; }
#define DLSS5_MV_PROVIDER_NAME "Launchpad (Deferred::MotionVectorsTex)"
#define DLSS5_MV_REQUEST_PASS pass IpcRequestOpticalFlow { PrimitiveTopology = POINTLIST; VertexCount = 1; VertexShader = DLSS5_IpcRequestVS; PixelShader = DLSS5_IpcRequestPS; RenderTarget = Deferred::IPC::PredicationBuffer; RenderTargetWriteMask = 4; }
#elif DLSS5_MV_PROVIDER == 2
// VORT (Includes/vort_MotionUtils.fxh, V_MV_MODE 1)
texture2D MotVectTexVort { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RG16F; };
sampler sDLSS5_ProviderMV { Texture = MotVectTexVort; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Point; MagFilter = Point; };
#define DLSS5_MV_PROVIDER_NAME "VORT (MotVectTexVort)"
#elif DLSS5_MV_PROVIDER == 3
// LumeniteFX Kernel (lumenite_Kernel.fx), as lumenite_RTAO/TRAA re-declare it. 1/8 resolution.
namespace Kernel {
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
}
sampler sDLSS5_ProviderMV { Texture = Kernel::tFlow; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Linear; MagFilter = Linear; };
sampler sDLSS5_ProviderMVPoint { Texture = Kernel::tFlow; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Point; MagFilter = Point; };
sampler sDLSS5_ProviderConfidence{ Texture = Kernel::tConfidence; AddressU = Clamp; AddressV = Clamp; };
#define DLSS5_MV_PROVIDER_NAME "LumeniteFX Kernel (Kernel::tFlow, 1/8 res)"
#define DLSS5_MV_LOWRES 1
#elif DLSS5_MV_PROVIDER == 4
// LumeniteFX QuantMotion (lumenite_QuantMotion.fx), as lumenite_QuantAO re-declares it. 1/8 resolution.
namespace QuantMotion {
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
}
sampler sDLSS5_ProviderMV { Texture = QuantMotion::tFlow; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Linear; MagFilter = Linear; };
sampler sDLSS5_ProviderMVPoint { Texture = QuantMotion::tFlow; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Point; MagFilter = Point; };
sampler sDLSS5_ProviderConfidence{ Texture = QuantMotion::tConfidence; AddressU = Clamp; AddressV = Clamp; };
#define DLSS5_MV_PROVIDER_NAME "LumeniteFX QuantMotion (QuantMotion::tFlow, 1/8 res)"
#define DLSS5_MV_LOWRES 1
#else
// The community-standard shared texture (ReshadeMotionEstimation, qUINT, dh_uber_motion, ...)
texture texMotionVectors < pooled = false; > { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RG16F; };
sampler sDLSS5_ProviderMV { Texture = texMotionVectors; AddressU = Clamp; AddressV = Clamp; MipFilter = Point; MinFilter = Point; MagFilter = Point; };
#define DLSS5_MV_PROVIDER_NAME "texMotionVectors (DRME, qUINT, dh_uber_motion, ...)"
#endif
#ifndef DLSS5_MV_LOWRES
#define DLSS5_MV_LOWRES 0
#endif
#ifndef DLSS5_MV_REQUEST_PASS
#define DLSS5_MV_REQUEST_PASS
#endif
// ---------------------------------------------------------------------------------------------
uniform int MV_PROVIDER_INFO <
ui_type = "radio";
ui_label = " ";
ui_text = "Motion vector provider: " DLSS5_MV_PROVIDER_NAME "\n"
"Change it with the DLSS5_MV_PROVIDER preprocessor definition:\n"
" 0 texMotionVectors (DRME, qUINT, dh_uber_motion) 1 Launchpad 2 VORT\n"
" 3 LumeniteFX Kernel 4 LumeniteFX QuantMotion\n"
"Enable that provider's technique ABOVE DLSS 5 Feed.";
>;
#if DLSS5_MV_LOWRES
uniform int MV_LOWRES_FILTER <
ui_type = "combo";
ui_items = "Bilinear\0Point (nearest)\0";
ui_label = "Low-res provider filter";
ui_tooltip = "How the provider's 1/8-resolution flow is brought up to full resolution.\n"
"Bilinear smooths across flow cells; point keeps each 8x8 cell's vector as-is.";
> = 0;
#endif
// ---------------------------------------------------------------------------------------------
// Geometry vectors. A game's motion vectors for static geometry come from camera motion and
// depth, not from pixels. We have depth; the camera motion is fitted each frame from the
// provider's flow over a sparse grid (robust two-pass least squares on a 9-term screen-space
// model: affine + quadratic rotation terms + inverse-depth parallax terms), and every pixel
// then gets the vector that model predicts from its depth -- correct under flicker, correct
// while moving. The provider's flow is only used where it disagrees with the model AND wins a
// structure test: a genuinely moving object. Flames and flicker lose that test and keep the
// geometric vector, so nothing warps.
// ---------------------------------------------------------------------------------------------
uniform bool GEOM_ENABLE <
ui_category = "Geometry vectors (camera model + depth) -- EXPERIMENTAL";
ui_label = "Use geometry vectors (experimental, off by default)";
ui_tooltip = "Fit the camera motion from the provider's flow + depth each frame and derive every static\n"
"pixel's vector from it. The provider is then only consulted for moving objects.\n"
"EXPERIMENTAL: the per-frame fit is still noisy, and anything not part of the 3D world\n"
"(the HUD) gets camera vectors it should not have -- expect jitter there.\n"
"Off = the per-pixel validation below is applied to the provider's flow directly.";
> = false;
uniform float GEOM_PARALLAX <
ui_category = "Geometry vectors (camera model + depth)";
ui_type = "drag"; ui_min = 0.001; ui_max = 0.5; ui_step = 0.001;
ui_label = "Parallax depth scale";
ui_tooltip = "The model's inverse-depth term is s / (depth + s) with linear depth in 0..1. Smaller = more\n"
"parallax resolution near the camera. Usually fine as is.";
> = 0.02;
uniform float GEOM_OUTLIER_PX <
ui_category = "Geometry vectors (camera model + depth)";
ui_type = "drag"; ui_min = 0.5; ui_max = 32.0; ui_step = 0.5;
ui_label = "Fit: outlier rejection (px)";
ui_tooltip = "Second fitting pass ignores samples whose flow is further than this from the first pass's\n"
"prediction -- moving objects, flames, the first-person weapon.";
> = 4.0;
uniform float GEOM_AGREE_PX <
ui_category = "Geometry vectors (camera model + depth)";
ui_type = "drag"; ui_min = 0.0; ui_max = 16.0; ui_step = 0.1;
ui_label = "Agreement (px)";
ui_tooltip = "If the provider's flow is within this many pixels (+10% of the vector) of the model, the\n"
"model's vector is used as-is. Beyond it, the structure test decides moving object vs junk.";
> = 1.5;
uniform float GEOM_DYNAMIC_MARGIN <
ui_category = "Geometry vectors (camera model + depth)";
ui_type = "drag"; ui_min = 0.0; ui_max = 0.9; ui_step = 0.01;
ui_label = "Moving-object margin";
ui_tooltip = "For the provider's flow to override the model on a disagreeing pixel, its reprojection must\n"
"explain the pixel's structure at least this much (relative) better than the model's does.\n"
"Higher = more conservative (fewer things count as moving objects).";
> = 0.25;
uniform float GEOM_MASK_REJECTED <
ui_category = "Geometry vectors (camera model + depth)";
ui_type = "drag"; ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
ui_label = "Mask strength on rejected flow";
ui_tooltip = "Where the provider disagreed with the model but did not win the structure test (fire, smoke,\n"
"flicker), the geometric vector is used; this is how strongly DLSS is additionally asked to\n"
"favour the current frame there. 0 = pure history (smoothest), 1 = mostly current frame.\n\n"
"Also used by the static test's first frame when hysteresis holds its vector back.";
> = 0.35;
uniform bool MV_VALIDATE <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_label = "Validate motion vectors against the previous frame";
ui_tooltip = "Optical-flow providers answer a lighting change (flicker, flames) with a vector that\n"
"points at whatever happened to match. Reprojecting and checking catches those:\n"
"the vector is zeroed and DLSS is told to trust the current frame there (DLSS5_Mask).";
> = true;
uniform bool VALIDATE_STATIC <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_label = "Static-hypothesis test (zeroes the vector, keeps history)";
ui_tooltip = "For each pixel, asks which explains it better: 'did not move' or the provider's vector.\n"
"Both are scored on illumination-normalised 3x3 structure (local mean removed), so a\n"
"flickering light does not count as motion. When 'did not move' wins, the vector is zeroed\n"
"and the pixel is NOT masked -- a static wall wants its full history, which is what smooths\n"
"the flicker. This is the test for the flickering-wall case.";
> = true;
uniform bool STATIC_HYSTERESIS <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_label = "Static test: require two frames in a row";
ui_tooltip = "The static test has no memory: on a low-contrast surface under a slow pan it can win on\n"
"one frame and lose on the next, so the vector alternates between the provider's and zero\n"
"and DLSS alternately reprojects and does not -- a flicker/judder that comes and goes.\n"
"With this on, the vector is only zeroed where the test won on this frame AND the last;\n"
"on the first frame the provider's vector is kept and the pixel is masked instead, so\n"
"DLSS leans on the current frame rather than reprojecting from nowhere.\n"
"Turn it off to compare against the old (per-frame) behaviour.";
> = true;
uniform float STATIC_BIAS <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_type = "drag"; ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
ui_label = "Static bias";
ui_tooltip = "How much worse (relative) the static explanation may score than the vector's and still win.\n"
"0 = the vector must strictly beat 'did not move'. Higher favours zero vectors.";
> = 0.15;
uniform float STATIC_MIN_CONTRAST <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_type = "drag"; ui_min = 0.0; ui_max = 0.1; ui_step = 0.001;
ui_label = "Static test: minimum patch contrast";
ui_tooltip = "Below this 3x3 contrast (mean absolute deviation of luma) a patch has no structure to judge\n"
"motion by, and the test abstains -- the provider's vector stands. Raise it if flat surfaces\n"
"trail while moving (yellow on plain motion in the debug view); lower it if the\n"
"flickering wall stops being caught.";
> = 0.012;
uniform bool VALIDATE_LUMA <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_label = "Luma test (mask only)";
ui_tooltip = "The reprojected previous luma must fall inside the current 3x3 neighbourhood's luma range.\n"
"A failure only raises the mask (DLSS leans on the current frame); it never zeroes the vector,\n"
"because a lighting change does not prove the surface did not move. Off by default: on a\n"
"flickering surface it asks DLSS to drop exactly the history that would smooth the flicker.";
> = false;
uniform float LUMA_TOLERANCE <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_type = "drag"; ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
ui_label = "Luma tolerance";
ui_tooltip = "How far outside the current 3x3 neighbourhood's luma range the reprojected previous luma\n"
"may fall (relative to that range's maximum). Lower = stricter.";
> = 0.25;
uniform bool VALIDATE_DEPTH <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_label = "Depth test (zeroes the vector)";
ui_tooltip = "The reprojected previous linear depth must match the current one: a mismatch means the vector\n"
"points at a different surface (disocclusion), so it is zeroed and masked. Sky is exempt.";
> = true;
uniform float DEPTH_TOLERANCE <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_type = "drag"; ui_min = 0.0; ui_max = 0.5; ui_step = 0.005;
ui_label = "Depth tolerance";
ui_tooltip = "Allowed relative difference between the reprojected previous linear depth and the current one.";
> = 0.10;
uniform bool VALIDATE_MV <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_label = "Consistency test (zeroes the vector)";
ui_tooltip = "This frame's vector must resemble the previous frame's vector at the spot it points to.\n"
"Real motion is smooth frame to frame; optical flow on fire, smoke or a flickering wall is not.\n"
"A failure zeroes the vector and masks the pixel.";
> = true;
uniform float MV_CONSISTENCY <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_type = "drag"; ui_min = 0.0; ui_max = 16.0; ui_step = 0.1;
ui_label = "Vector consistency (px)";
ui_tooltip = "Allowed change, in pixels, between this frame's vector and the previous frame's vector at\n"
"the reprojected spot, plus 50% of the vector length. Raise it if plain camera motion\n"
"shows blue in the 'Validation tests' debug view.";
> = 1.4;
uniform float MASK_STRENGTH <
ui_category = "Validation (flicker / flames / disocclusion)";
ui_type = "drag"; ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
ui_label = "Bias-current-colour mask strength";
ui_tooltip = "How strongly a distrusted pixel asks DLSS to favour the current frame (DLSS5_Mask).\n"
"1 = fully; 0 = only zero the vector, do not mask.";
> = 1.0;
uniform float2 MV_SIGN <
ui_type = "drag";
ui_min = -1.0; ui_max = 1.0; ui_step = 2.0;
ui_label = "Motion vector sign (x, y)";
ui_tooltip = "Flip a component if the DLAA output doubles/smears in that direction while moving.\n"
"Default (1, 1) matches the convention every supported provider uses (prev_uv = uv + mv).";
> = float2(1.0, 1.0);
uniform float MV_SCALE <
ui_type = "drag";
ui_min = 0.0; ui_max = 4.0; ui_step = 0.01;
ui_label = "Motion vector scale";
ui_tooltip = "1.0 = the provider's estimate as-is. Diagnostic only.";
> = 1.0;
uniform int DEBUG_VIEW <
ui_type = "combo";
ui_items = "Motion vectors (colour = direction, brightness = speed)\0"
"Raw depth\0"
"Provider confidence (LumeniteFX only; white = confident)\0"
"Validation mask (white = vector distrusted, DLSS uses current frame)\0"
"Validation mask over the image\0"
"Validation tests over the image (red = luma, green = depth, blue = consistency, yellow = vector zeroed, orange = static held back)\0"
"Geometry model vectors (colour = direction, brightness = speed)\0"
"Geometry decision over the image (green = model, red = provider won as moving object, blue = provider rejected)\0"
"Geometry fit quality (grey = inlier share; top strip = fit error, black 0 px .. white 8 px)\0";
ui_label = "Debug view (DLSS5_Feed_Debug technique)";
> = 0;
// Outputs for the add-on
texture DLSS5_MV { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RG16F; };
texture DLSS5_Depth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R32F; };
texture DLSS5_Mask { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R8; };
sampler sDLSS5_MV { Texture = DLSS5_MV; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_Depth { Texture = DLSS5_Depth; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_Mask { Texture = DLSS5_Mask; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
// Previous-frame history for validation (written at the end of the technique)
texture DLSS5_PrevLuma { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; };
texture DLSS5_PrevDepth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; };
texture DLSS5_PrevMV { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RG16F; };
// Luma may be interpolated (a smooth quantity); depth and vectors must NOT be -- bilinear
// across an object edge mixes two surfaces' values and fails the test on every edge in motion.
sampler sDLSS5_PrevLuma { Texture = DLSS5_PrevLuma; AddressU = Clamp; AddressV = Clamp; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = POINT; };
sampler sDLSS5_PrevDepth { Texture = DLSS5_PrevDepth; AddressU = Clamp; AddressV = Clamp; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_PrevMV { Texture = DLSS5_PrevMV; AddressU = Clamp; AddressV = Clamp; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
// The static-hypothesis decision, this frame and last. The test has no memory of its own,
// so on a low-contrast surface under a slow pan it can win on one frame and lose on the
// next; the vector then alternates between the provider's and zero, and DLSS alternately
// reprojects and does not. That is a flicker/judder that no consumer can smooth out, and
// it comes and goes with the surface (The Surge 2, 2026-09-02). Guides writes StaticNow,
// History copies it into PrevStatic, and the next frame's Guides reads that -- a texture
// cannot be sampled and written in the same pass, which is why it takes two of them.
texture DLSS5_StaticNow { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R8; };
texture DLSS5_PrevStatic { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R8; };
sampler sDLSS5_StaticNow { Texture = DLSS5_StaticNow; AddressU = Clamp; AddressV = Clamp; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_PrevStatic { Texture = DLSS5_PrevStatic; AddressU = Clamp; AddressV = Clamp; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
// Camera-model fit: a sparse sample grid of (x, y, w, valid | u, v), and the solved model as
// six 1x1 RGBA32F texels (18 parameters + fit statistics).
#define DLSS5_FIT_W 40
#define DLSS5_FIT_H 23
texture DLSS5_FitA { Width = DLSS5_FIT_W; Height = DLSS5_FIT_H; Format = RGBA32F; };
texture DLSS5_FitB { Width = DLSS5_FIT_W; Height = DLSS5_FIT_H; Format = RGBA32F; };
sampler sDLSS5_FitA { Texture = DLSS5_FitA; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_FitB { Texture = DLSS5_FitB; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
texture DLSS5_Cam0 { Width = 1; Height = 1; Format = RGBA32F; };
texture DLSS5_Cam1 { Width = 1; Height = 1; Format = RGBA32F; };
texture DLSS5_Cam2 { Width = 1; Height = 1; Format = RGBA32F; };
texture DLSS5_Cam3 { Width = 1; Height = 1; Format = RGBA32F; };
texture DLSS5_Cam4 { Width = 1; Height = 1; Format = RGBA32F; };
texture DLSS5_Cam5 { Width = 1; Height = 1; Format = RGBA32F; };
sampler sDLSS5_Cam0 { Texture = DLSS5_Cam0; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_Cam1 { Texture = DLSS5_Cam1; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_Cam2 { Texture = DLSS5_Cam2; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_Cam3 { Texture = DLSS5_Cam3; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_Cam4 { Texture = DLSS5_Cam4; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
sampler sDLSS5_Cam5 { Texture = DLSS5_Cam5; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; };
// ---------------------------------------------------------------------------------------------
// The selected provider's vector at uv, as delta UV (prev_uv = uv + mv).
float2 ProviderMV(float2 uv)
{
float4 c = float4(uv, 0.0, 0.0);
#if DLSS5_MV_LOWRES
return MV_LOWRES_FILTER == 0 ? tex2Dlod(sDLSS5_ProviderMV, c).xy : tex2Dlod(sDLSS5_ProviderMVPoint, c).xy;
#else
return tex2Dlod(sDLSS5_ProviderMV, c).xy;
#endif
}
float Luma(float2 uv)
{
return dot(tex2Dlod(sDLSS5_ColorInput, float4(uv, 0.0, 0.0)).rgb, float3(0.299, 0.587, 0.114));
}
// Illumination-normalised 3x3 structure difference between the current frame at uv_cur and
// the previous frame at uv_prev: each patch has its own mean removed first, so a brightness
// change (flicker) contributes nothing and only the pattern is compared.
// Also returns the current patch's contrast (mean absolute deviation): a patch with no
// structure cannot decide anything, and the caller must not pretend it can.
float PatchError(float2 uv_cur, float2 uv_prev, out float contrast)
{
const float2 px = BUFFER_PIXEL_SIZE;
float c[9], p[9];
float mc = 0.0, mp = 0.0;
[unroll] for (int i = 0; i < 9; ++i)
{
const float2 o = float2(i % 3 - 1, i / 3 - 1) * px;
c[i] = Luma(uv_cur + o);
p[i] = tex2Dlod(sDLSS5_PrevLuma, float4(uv_prev + o, 0.0, 0.0)).x;
mc += c[i]; mp += p[i];
}
mc /= 9.0; mp /= 9.0;
float err = 0.0;
contrast = 0.0;
[unroll] for (int j = 0; j < 9; ++j)
{
err += abs((c[j] - mc) - (p[j] - mp));
contrast += abs(c[j] - mc);
}
contrast /= 9.0;
return err / 9.0;
}
// Per-test failure (0 = fine, 1 = failed, soft in between): x = luma, y = depth, z = consistency,
// w = the static hypothesis won. Luma failing says "this pixel's appearance changed"; depth or
// consistency failing says "this vector points at the wrong thing"; static winning says "no
// vector explains this pixel better than zero". Only y, z and w justify zeroing the vector, and
// only x, y, z justify asking DLSS to distrust history.
float4 ValidateTests(float2 uv, float2 mv)
{
const float2 puv = uv + mv;
float4 bad = 0.0;
// Reprojecting off-screen: nothing to compare against. Keep the vector (DLSS handles
// it) and let the mask lean on the current frame.
if (any(puv < 0.0) || any(puv > 1.0)) return float4(1.0, 0.0, 0.0, 0.0);
// 0. Static hypothesis: does "did not move" explain this pixel at least as well as the
// vector does? Scored on mean-removed structure, so flicker is not motion. Skipped for
// vectors under half a pixel (nothing to decide).
if (VALIDATE_STATIC && length(mv * BUFFER_SCREEN_SIZE) > 0.5)
{
float sc, unused;
const float es = PatchError(uv, uv, sc);
const float ef = PatchError(uv, puv, unused);
// Only a patch with structure can tell the two apart. Below the contrast floor the
// scores tie for lack of evidence, and a tie must go to the provider (its flow is
// propagated from textured neighbours -- the right guess for a moving flat wall).
// With structure, static wins only if it beats the vector by a share of that contrast.
if (sc >= STATIC_MIN_CONTRAST)
bad.w = es + 0.25 * sc <= ef * (1.0 + STATIC_BIAS) ? 1.0 : 0.0;
}
// 1. Luma: current 3x3 range vs the previous luma at the reprojected spot.
if (VALIDATE_LUMA)
{
const float2 px = BUFFER_PIXEL_SIZE;
float lc = Luma(uv), lmin = lc, lmax = lc;
[unroll] for (int y = -1; y <= 1; ++y)
[unroll] for (int x = -1; x <= 1; ++x)
{
const float l = Luma(uv + float2(x, y) * px);
lmin = min(lmin, l); lmax = max(lmax, l);
}
const float lp = tex2Dlod(sDLSS5_PrevLuma, float4(puv, 0.0, 0.0)).x;
const float margin = LUMA_TOLERANCE * max(lmax, 0.05) + 2.0 / 255.0;
bad.x = saturate(max(lmin - lp, lp - lmax) / margin);
}
// 2. Depth: previous linear depth at the reprojected spot vs the current one (sky exempt).
const float dc = ReShade::GetLinearizedDepth(uv);
if (VALIDATE_DEPTH && dc < 0.999)
{
const float dp = tex2Dlod(sDLSS5_PrevDepth, float4(puv, 0.0, 0.0)).x;
const float tol = DEPTH_TOLERANCE * max(dc, 1e-3);
bad.y = saturate((abs(dp - dc) - tol) / (tol + 1e-5));
}
// 3. Consistency: the previous frame's vector where this pixel came from vs this one.
if (VALIDATE_MV && MV_CONSISTENCY > 0.0)
{
const float2 pmv = tex2Dlod(sDLSS5_PrevMV, float4(puv, 0.0, 0.0)).xy;
const float diff = length((mv - pmv) * BUFFER_SCREEN_SIZE);
const float allow = MV_CONSISTENCY + 0.5 * length(mv * BUFFER_SCREEN_SIZE);
bad.z = saturate((diff - allow) / allow);
}
return bad;
}
// ---------------------------------------------------------------------------------------------
// Camera model. Screen position x, y in -0.5..0.5, inverse-depth term w = s / (depth + s).
// Basis (9 terms): 1, x, y, x^2, xy, y^2, w, xw, yw -- the small-rotation flow field of a
// pinhole camera is quadratic in the image position, and translation adds terms in 1/Z.
// Both flow components share the basis; the fit solves them together (two right-hand sides).
// ---------------------------------------------------------------------------------------------
#define DLSS5_BASIS(B, x, y, w) \
B[0] = 1.0; B[1] = x; B[2] = y; B[3] = x * x; B[4] = x * y; B[5] = y * y; B[6] = w; B[7] = x * w; B[8] = y * w;
float ParallaxW(float d) { return GEOM_PARALLAX / (d + GEOM_PARALLAX); }
// The model's predicted delta-UV at uv for linear depth d.
float2 PredictMV(float2 uv, float d)
{
const float4 c = float4(0.5, 0.5, 0.0, 0.0);
const float4 p0 = tex2Dlod(sDLSS5_Cam0, c), p1 = tex2Dlod(sDLSS5_Cam1, c), p2 = tex2Dlod(sDLSS5_Cam2, c);
const float4 p3 = tex2Dlod(sDLSS5_Cam3, c), p4 = tex2Dlod(sDLSS5_Cam4, c);
const float x = uv.x - 0.5, y = uv.y - 0.5, w = ParallaxW(d);
float B[9]; DLSS5_BASIS(B, x, y, w)
// u: p0.xyzw p1.xyzw p2.x v: p2.yzw p3.xyzw p4.xy
const float u = p0.x * B[0] + p0.y * B[1] + p0.z * B[2] + p0.w * B[3] + p1.x * B[4] + p1.y * B[5] + p1.z * B[6] + p1.w * B[7] + p2.x * B[8];
const float v = p2.y * B[0] + p2.z * B[1] + p2.w * B[2] + p3.x * B[3] + p3.y * B[4] + p3.z * B[5] + p3.w * B[6] + p4.x * B[7] + p4.y * B[8];
return float2(u, v);
}
bool FitIsUsable()
{
const float4 s = tex2Dlod(sDLSS5_Cam5, float4(0.5, 0.5, 0.0, 0.0)); // x = inlier share, y = rms px, z = samples used
return s.z >= 40.0 && s.x >= 0.25;
}
// Pass 1: sample the provider's flow and the depth on a sparse grid.
//
// ReShade cannot skip a whole pass on a uniform, so both fit passes start by checking the
// one uniform that decides whether anything downstream will ever read them. It matters:
// PS_FitSolve is a ONE-pixel shader that runs 2 x 920 iterations with two fetches each and
// a 9x9 Gauss-Jordan solve -- a long serial latency chain on a single lane, every frame,
// for a result only GeometryDecide consumes. GEOM_ENABLE is off by default.
void PS_FitSamples(float4 vpos : SV_Position, float2 uv : TEXCOORD, out float4 A : SV_Target0, out float4 B : SV_Target1)
{
if (!GEOM_ENABLE) { A = 0.0; B = 0.0; return; }
const float2 suv = (floor(vpos.xy) + 0.5) / float2(DLSS5_FIT_W, DLSS5_FIT_H);
const float d = ReShade::GetLinearizedDepth(suv);
const float2 mv = ProviderMV(suv);
const bool valid = d > 0.001 && all(abs(mv * BUFFER_SCREEN_SIZE) < 512.0);
A = float4(suv.x - 0.5, suv.y - 0.5, ParallaxW(d), valid ? 1.0 : 0.0);
B = float4(mv, 0.0, 0.0);
}
// Pass 2 (one pixel): robust least squares. Pass one fits everything; pass two refits on the
// samples the first fit explains to within GEOM_OUTLIER_PX, which drops moving objects,
// flames and the weapon from the camera estimate.
void PS_FitSolve(float4 vpos : SV_Position, float2 uv : TEXCOORD,
out float4 P0 : SV_Target0, out float4 P1 : SV_Target1, out float4 P2 : SV_Target2,
out float4 P3 : SV_Target3, out float4 P4 : SV_Target4, out float4 P5 : SV_Target5)
{
// See PS_FitSamples. P5.z is the sample count FitIsUsable() tests against 40, so a zeroed
// P5 also reads as "no usable fit" for anything that looks at it anyway.
if (!GEOM_ENABLE) { P0 = 0.0; P1 = 0.0; P2 = 0.0; P3 = 0.0; P4 = 0.0; P5 = 0.0; return; }
float p[18];
[unroll] for (int z = 0; z < 18; ++z) p[z] = 0.0;
float inlier = 0.0, rms = 0.0, used = 0.0;
const int total = DLSS5_FIT_W * DLSS5_FIT_H;
[loop] for (int it = 0; it < 2; ++it)
{
float M[45]; // upper triangle of the 9x9 normal matrix
float ru[9], rv[9];
[unroll] for (int z0 = 0; z0 < 45; ++z0) M[z0] = 0.0;
[unroll] for (int z1 = 0; z1 < 9; ++z1) { ru[z1] = 0.0; rv[z1] = 0.0; }
int n = 0;
float se = 0.0;
[loop] for (int s = 0; s < total; ++s)
{
const int2 cell = int2(s % DLSS5_FIT_W, s / DLSS5_FIT_W);
const float4 a = tex2Dfetch(sDLSS5_FitA, cell);
const float4 b = tex2Dfetch(sDLSS5_FitB, cell);
if (a.w < 0.5) continue;
float B[9]; DLSS5_BASIS(B, a.x, a.y, a.z)
if (it > 0)
{
float pu = 0.0, pv = 0.0;
[unroll] for (int i0 = 0; i0 < 9; ++i0) { pu += p[i0] * B[i0]; pv += p[9 + i0] * B[i0]; }
const float r = length((float2(pu, pv) - b.xy) * BUFFER_SCREEN_SIZE);
if (r > GEOM_OUTLIER_PX) continue;
se += r * r;
}
++n;
int k = 0;
[unroll] for (int i = 0; i < 9; ++i)
{
ru[i] += B[i] * b.x;
rv[i] += B[i] * b.y;
[unroll] for (int j = i; j < 9; ++j) { M[k] += B[i] * B[j]; ++k; }
}
}
if (n < 40) break; // not enough evidence: keep whatever the previous pass produced
// Augmented 9 x (9 + 2) system, Gauss-Jordan with partial pivoting, tiny ridge for
// the degenerate cases (flat depth makes w collinear with 1; a still camera makes
// everything zero).
float G[99];
{
int k2 = 0;
[unroll] for (int i = 0; i < 9; ++i)
{
[unroll] for (int j = i; j < 9; ++j) { G[i * 11 + j] = M[k2]; G[j * 11 + i] = M[k2]; ++k2; }
G[i * 11 + i] += 1e-5 * n;
G[i * 11 + 9] = ru[i];
G[i * 11 + 10] = rv[i];
}
}
bool singular = false;
[loop] for (int col = 0; col < 9; ++col)
{
int piv = col;
float best = abs(G[col * 11 + col]);
[loop] for (int r0 = col + 1; r0 < 9; ++r0)
{
const float v0 = abs(G[r0 * 11 + col]);
if (v0 > best) { best = v0; piv = r0; }
}
if (best < 1e-12) { singular = true; break; }
if (piv != col)
[unroll] for (int c0 = 0; c0 < 11; ++c0) { const float t = G[col * 11 + c0]; G[col * 11 + c0] = G[piv * 11 + c0]; G[piv * 11 + c0] = t; }
const float inv = 1.0 / G[col * 11 + col];
[unroll] for (int c1 = 0; c1 < 11; ++c1) G[col * 11 + c1] *= inv;
[loop] for (int r1 = 0; r1 < 9; ++r1)
{
if (r1 == col) continue;
const float f = G[r1 * 11 + col];
if (f == 0.0) continue;
[unroll] for (int c2 = 0; c2 < 11; ++c2) G[r1 * 11 + c2] -= f * G[col * 11 + c2];
}
}
if (singular) break;
[unroll] for (int i2 = 0; i2 < 9; ++i2) { p[i2] = G[i2 * 11 + 9]; p[9 + i2] = G[i2 * 11 + 10]; }
used = n;
inlier = float(n) / float(total);
if (it > 0) rms = sqrt(se / max(n, 1));
}
P0 = float4(p[0], p[1], p[2], p[3]);
P1 = float4(p[4], p[5], p[6], p[7]);
P2 = float4(p[8], p[9], p[10], p[11]);
P3 = float4(p[12], p[13], p[14], p[15]);
P4 = float4(p[16], p[17], 0.0, 0.0);
P5 = float4(inlier, rms, used, 0.0);
}
// Per-pixel decision: x = final delta-UV vector, .z = 0 model / 1 provider (moving object) /
// 2 provider rejected, .w = mask contribution from that decision.
float4 GeometryDecide(float2 uv, float d, float2 flow)
{
const float2 pred = PredictMV(uv, d);
const float r = length((flow - pred) * BUFFER_SCREEN_SIZE);
const float agree = GEOM_AGREE_PX + 0.1 * length(pred * BUFFER_SCREEN_SIZE);
if (r <= agree) return float4(pred, 0.0, 0.0);
float cp, cf;
const float ep = PatchError(uv, uv + pred, cp);
const float ef = PatchError(uv, uv + flow, cf);
const bool dynamic = cp >= STATIC_MIN_CONTRAST && ef <= ep * (1.0 - GEOM_DYNAMIC_MARGIN) - 1.0 / 255.0;
if (dynamic) return float4(flow, 1.0, 0.0);
return float4(pred, 2.0, GEOM_MASK_REJECTED * saturate((r - agree) / (4.0 * agree)));
}
float RawDepth(float2 uv)
{
// Raw hardware depth, exactly as the game wrote it -- the same orientation/offset
// corrections ReShade.fxh applies in GetLinearizedDepth(), minus the linearisation
// (DLSS must receive the raw values; the add-on tells it whether the range is reversed).
float2 t = uv;
#if RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN
t.y = 1.0 - t.y;
#endif
t.x /= RESHADE_DEPTH_INPUT_X_SCALE;
t.y /= RESHADE_DEPTH_INPUT_Y_SCALE;
#if RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET
t.x -= RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET * BUFFER_RCP_WIDTH;
#else
t.x -= RESHADE_DEPTH_INPUT_X_OFFSET / 2.000000001;
#endif
#if RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET
t.y += RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET * BUFFER_RCP_HEIGHT;
#else
t.y += RESHADE_DEPTH_INPUT_Y_OFFSET / 2.000000001;
#endif
return tex2Dlod(ReShade::DepthBuffer, float4(t, 0.0, 0.0)).x;
}
void PS_MotionVectors(float4 vpos : SV_Position, float2 uv : TEXCOORD,
out float2 mv_out : SV_Target0, out float mask : SV_Target1,
out float depth : SV_Target2, out float static_now : SV_Target3)
{
// Providers hand out "delta UV": previous position = uv + mv. DLSS wants the same
// direction, in pixels.
const float2 flow = ProviderMV(uv);
float2 mv = flow;
float distrust = 0.0;
static_now = 0.0; // the geometry path does not run the static test
if (GEOM_ENABLE && FitIsUsable())
{
const float d = ReShade::GetLinearizedDepth(uv);
const float4 g = GeometryDecide(uv, d, flow);
mv = g.xy;
distrust = g.w;
// Disocclusion: the geometric vector on a newly revealed pixel points into the
// occluder's old position; the depth test catches that and asks for the current frame.
if (VALIDATE_DEPTH && d < 0.999)
{
const float2 puv = uv + mv;
if (all(puv >= 0.0) && all(puv <= 1.0))
{
const float dp = tex2Dlod(sDLSS5_PrevDepth, float4(puv, 0.0, 0.0)).x;
const float tol = DEPTH_TOLERANCE * max(d, 1e-3);
distrust = max(distrust, saturate((abs(dp - d) - tol) / (tol + 1e-5)));
}
}
}
else if (MV_VALIDATE)
{
const float4 bad = ValidateTests(uv, flow);
static_now = bad.w; // the raw decision, for next frame's hysteresis
// The static test may only zero the vector once it has won twice in a row. On the
// first win the provider's vector is kept and the pixel is masked instead: "prefer
// the current frame here" is a safe answer either way, where a zeroed vector on a
// pixel that really is moving smears and a full vector on a pixel that really is
// static flickers.
float static_zero = bad.w;
if (STATIC_HYSTERESIS && bad.w > 0.5)
{
const float won_before = tex2Dlod(sDLSS5_PrevStatic, float4(uv, 0.0, 0.0)).x;
if (won_before <= 0.5) { static_zero = 0.0; distrust = max(distrust, GEOM_MASK_REJECTED); }
}
// Hard decision, not a blend: half a vector points at neither where the pixel came
// from nor where it is, so DLSS would warp history in from a place that means
// nothing. The soft scores stay in the mask, which IS a continuous quantity.
const bool zero_vector = max(bad.y, max(bad.z, static_zero)) > 0.5;
distrust = max(distrust, max(bad.x, max(bad.y, bad.z))); // appearance changed / wrong target
mv = zero_vector ? float2(0.0, 0.0) : flow;
}
mv_out = mv * float2(BUFFER_WIDTH, BUFFER_HEIGHT) * MV_SIGN * MV_SCALE;
mask = saturate(distrust) * MASK_STRENGTH;
depth = RawDepth(uv);
}
// End of the technique: this frame becomes next frame's history. The raw provider vector is
// stored (not the validated one), so one distrusted frame does not poison the next test.
// prev_static carries this frame's static decision over to the next (the Guides pass cannot
// both sample and write one texture, so it lands here).
void PS_StoreHistory(float4 vpos : SV_Position, float2 uv : TEXCOORD,
out float luma : SV_Target0, out float depth : SV_Target1, out float2 mv : SV_Target2,
out float prev_static : SV_Target3)
{
luma = Luma(uv);
depth = ReShade::GetLinearizedDepth(uv);
mv = ProviderMV(uv);
prev_static = tex2Dfetch(sDLSS5_StaticNow, int2(vpos.xy)).x;
}
float3 PS_Debug(float4 vpos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
if (DEBUG_VIEW == 1)
{
const float raw_depth = tex2Dlod(sDLSS5_Depth, float4(uv, 0.0, 0.0)).x;
#if RESHADE_DEPTH_INPUT_IS_REVERSED
const float proximity = raw_depth;
#else
const float proximity = 1.0 - raw_depth;
#endif
// Display-only contrast curve: DLSS5_Depth itself remains raw and untouched.
return pow(saturate(proximity), 0.125).xxx;
}
if (DEBUG_VIEW == 2)
{
#if DLSS5_MV_LOWRES
return saturate(tex2Dlod(sDLSS5_ProviderConfidence, float4(uv, 0.0, 0.0)).x).xxx;
#else
return (0.25).xxx; // this provider publishes no confidence map
#endif
}
if (DEBUG_VIEW == 3)
return tex2Dlod(sDLSS5_Mask, float4(uv, 0.0, 0.0)).xxx;
if (DEBUG_VIEW == 4)
{
const float m = tex2Dlod(sDLSS5_Mask, float4(uv, 0.0, 0.0)).x;
const float3 img = tex2Dlod(sDLSS5_ColorInput, float4(uv, 0.0, 0.0)).rgb;
return lerp(img, float3(1.0, 0.2, 0.1), m * 0.75);
}
if (DEBUG_VIEW == 5)
{
// Recomputed here against the same history the feed pass used this frame.
const float2 flow = ProviderMV(uv);
const float4 bad = ValidateTests(uv, flow);
const float3 img = tex2Dlod(sDLSS5_ColorInput, float4(uv, 0.0, 0.0)).rgb * 0.5;
// Yellow is what the feed pass ACTUALLY did, not what the static test proposed: with
// hysteresis on, a test that won only this frame keeps its vector. Watch a slow pan
// over a flat surface -- yellow that blinks on and off frame by frame is the flicker.
const bool moved = length(flow * BUFFER_SCREEN_SIZE) > 0.5;
const bool zeroed = moved && length(tex2Dlod(sDLSS5_MV, float4(uv, 0.0, 0.0)).xy) < 1e-4;
// Dim orange: the static test won this frame but hysteresis kept the vector (masked instead).
const bool held = moved && !zeroed && tex2Dlod(sDLSS5_StaticNow, float4(uv, 0.0, 0.0)).x > 0.5;
return saturate(img + bad.xyz * 0.9 + (zeroed ? float3(0.6, 0.6, 0.0) : float3(0.0, 0.0, 0.0))
+ (held ? float3(0.35, 0.18, 0.0) : float3(0.0, 0.0, 0.0)));
}
if (DEBUG_VIEW == 6)
{
const float2 pv = PredictMV(uv, ReShade::GetLinearizedDepth(uv)) * BUFFER_SCREEN_SIZE;
const float angle = atan2(pv.y, pv.x), speed = length(pv);
const float3 rgb = saturate(3.0 * abs(2.0 * frac(angle / 6.283185 + float3(0.0, -1.0 / 3.0, 1.0 / 3.0)) - 1.0) - 1.0);
return lerp(0.5, rgb, saturate(speed / 16.0));
}
if (DEBUG_VIEW == 7)
{
const float3 img = tex2Dlod(sDLSS5_ColorInput, float4(uv, 0.0, 0.0)).rgb * 0.5;
if (!FitIsUsable()) return img; // no usable fit this frame: nothing to show
const float4 g = GeometryDecide(uv, ReShade::GetLinearizedDepth(uv), ProviderMV(uv));
const float3 tint = g.z < 0.5 ? float3(0.0, 0.5, 0.0) : g.z < 1.5 ? float3(0.9, 0.0, 0.0) : float3(0.0, 0.2, 0.9);
return saturate(img + tint);
}
if (DEBUG_VIEW == 8)
{
const float4 s = tex2Dlod(sDLSS5_Cam5, float4(0.5, 0.5, 0.0, 0.0));
if (uv.y < 0.05) return saturate(s.y / 8.0).xxx; // fit error strip
return s.x.xxx; // inlier share
}
float2 mv = tex2Dlod(sDLSS5_MV, float4(uv, 0.0, 0.0)).xy; // pixels
float angle = atan2(mv.y, mv.x);
float speed = length(mv);
float3 rgb = saturate(3.0 * abs(2.0 * frac(angle / 6.283185 + float3(0.0, -1.0 / 3.0, 1.0 / 3.0)) - 1.0) - 1.0);
return lerp(0.5, rgb, saturate(speed / 16.0)); // 16 px/frame saturates the colour
}
// ---------------------------------------------------------------------------------------------
technique DLSS5_Feed
<
ui_label = "DLSS 5 Feed (place below your motion-vector provider)";
ui_tooltip = "Prepares motion vectors + depth (+ a trust mask) for the DLSS 5 Feed add-on.\n\n"
"Provider: " DLSS5_MV_PROVIDER_NAME "\n"
"Change it with the DLSS5_MV_PROVIDER preprocessor definition (0 texMotionVectors,\n"
"1 Launchpad, 2 VORT, 3 LumeniteFX Kernel, 4 LumeniteFX QuantMotion) and enable\n"
"that provider's technique ABOVE this one.";
>
{
pass FitSamples { VertexShader = PostProcessVS; PixelShader = PS_FitSamples; RenderTarget0 = DLSS5_FitA; RenderTarget1 = DLSS5_FitB; }
pass FitSolve { VertexShader = PostProcessVS; PixelShader = PS_FitSolve; RenderTarget0 = DLSS5_Cam0; RenderTarget1 = DLSS5_Cam1; RenderTarget2 = DLSS5_Cam2; RenderTarget3 = DLSS5_Cam3; RenderTarget4 = DLSS5_Cam4; RenderTarget5 = DLSS5_Cam5; }
pass Guides { VertexShader = PostProcessVS; PixelShader = PS_MotionVectors; RenderTarget0 = DLSS5_MV; RenderTarget1 = DLSS5_Mask; RenderTarget2 = DLSS5_Depth; RenderTarget3 = DLSS5_StaticNow; }
pass History { VertexShader = PostProcessVS; PixelShader = PS_StoreHistory; RenderTarget0 = DLSS5_PrevLuma; RenderTarget1 = DLSS5_PrevDepth; RenderTarget2 = DLSS5_PrevMV; RenderTarget3 = DLSS5_PrevStatic; }
DLSS5_MV_REQUEST_PASS // Launchpad only: ask it to compute optical flow again next frame
}
technique DLSS5_Feed_Debug
<
ui_label = "DLSS 5 Feed - debug view";
ui_tooltip = "Shows the motion vectors / depth / mask the add-on will send to DLSS. Enable only for checking.";
>
{
pass { VertexShader = PostProcessVS; PixelShader = PS_Debug; }
}
@@ -0,0 +1,73 @@
/**
* Daltonization algorithm by daltonize.org
* http://www.daltonize.org/2010/05/lms-daltonization-algorithm.html
* Originally ported to ReShade by IDDQD, modified for ReShade 3.0 by crosire
*/
uniform int Type <
ui_type = "combo";
ui_items = "Protanopia\0Deuteranopia\0Tritanopia\0";
> = 0;
#include "ReShade.fxh"
float3 PS_DaltonizeFXmain(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target
{
float3 input = tex2D(ReShade::BackBuffer, texcoord).rgb;
// RGB to LMS matrix conversion
float OnizeL = (17.8824f * input.r) + (43.5161f * input.g) + (4.11935f * input.b);
float OnizeM = (3.45565f * input.r) + (27.1554f * input.g) + (3.86714f * input.b);
float OnizeS = (0.0299566f * input.r) + (0.184309f * input.g) + (1.46709f * input.b);
// Simulate color blindness
float Daltl, Daltm, Dalts;
if (Type == 0) // Protanopia - reds are greatly reduced (1% men)
{
Daltl = 0.0f * OnizeL + 2.02344f * OnizeM + -2.52581f * OnizeS;
Daltm = 0.0f * OnizeL + 1.0f * OnizeM + 0.0f * OnizeS;
Dalts = 0.0f * OnizeL + 0.0f * OnizeM + 1.0f * OnizeS;
}
else if (Type == 1) // Deuteranopia - greens are greatly reduced (1% men)
{
Daltl = 1.0f * OnizeL + 0.0f * OnizeM + 0.0f * OnizeS;
Daltm = 0.494207f * OnizeL + 0.0f * OnizeM + 1.24827f * OnizeS;
Dalts = 0.0f * OnizeL + 0.0f * OnizeM + 1.0f * OnizeS;
}
else if (Type == 2) // Tritanopia - blues are greatly reduced (0.003% population)
{
Daltl = 1.0f * OnizeL + 0.0f * OnizeM + 0.0f * OnizeS;
Daltm = 0.0f * OnizeL + 1.0f * OnizeM + 0.0f * OnizeS;
Dalts = -0.395913f * OnizeL + 0.801109f * OnizeM + 0.0f * OnizeS;
}
// LMS to RGB matrix conversion
float3 error;
error.r = (0.0809444479f * Daltl) + (-0.130504409f * Daltm) + (0.116721066f * Dalts);
error.g = (-0.0102485335f * Daltl) + (0.0540193266f * Daltm) + (-0.113614708f * Dalts);
error.b = (-0.000365296938f * Daltl) + (-0.00412161469f * Daltm) + (0.693511405f * Dalts);
// Isolate invisible colors to color vision deficiency (calculate error matrix)
error = (input - error);
// Shift colors towards visible spectrum (apply error modifications)
float3 correction;
correction.r = 0; // (error.r * 0.0) + (error.g * 0.0) + (error.b * 0.0);
correction.g = (error.r * 0.7) + (error.g * 1.0); // + (error.b * 0.0);
correction.b = (error.r * 0.7) + (error.b * 1.0); // + (error.g * 0.0);
// Add compensation to original values
correction = input + correction;
return correction;
}
technique Daltonize
{
pass
{
VertexShader = PostProcessVS;
PixelShader = PS_DaltonizeFXmain;
}
}
@@ -0,0 +1,252 @@
/**
* Deband shader by haasn
* https://github.com/haasn/gentoo-conf/blob/xor/home/nand/.mpv/shaders/deband-pre.glsl
*
* Copyright (c) 2015 Niklas Haas
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Modified and optimized for ReShade by JPulowski
* https://reshade.me/forum/shader-presentation/768-deband
*
* Do not distribute without giving credit to the original author(s).
*
* 1.0 - Initial release
* 1.1 - Replaced the algorithm with the one from MPV
* 1.1a - Minor optimizations
* - Removed unnecessary lines and replaced them with ReShadeFX intrinsic counterparts
* 2.0 - Replaced "grain" with CeeJay.dk's ordered dithering algorithm and enabled it by default
* - The configuration is now more simpler and straightforward
* - Some minor code changes and optimizations
* - Improved the algorithm and made it more robust by adding some of the madshi's
* improvements to flash3kyuu_deband which should cause an increase in quality. Higher
* iterations/ranges should now yield higher quality debanding without too much decrease
* in quality.
* - Changed licensing text and original source code URL
* 3.0 - Replaced the entire banding detection algorithm with modified standard deviation and
* Weber ratio analyses which give more accurate and error-free results compared to the
* previous algorithm
* - Added banding map debug view
* - Added and redefined UI categories
* - Added depth detection (credits to spiro) which should be useful when banding only
* occurs in the sky texture for example
* - Fixed a bug in random number generation which was causing artifacts on the upper left
* side of the screen
* - Dithering is now applied only when debanding a pixel as it should be which should
* reduce the overall noise in the final texture
* - Minor code optimizations
* 3.1 - Switched to chroma-based analysis from luma-based analysis which was causing artifacts
* under some scenarios
* - Changed parts of the code which was causing compatibility issues on some renderers
*/
#include "ReShadeUI.fxh"
#include "ReShade.fxh"
uniform bool enable_weber <
ui_category = "Banding analysis";
ui_label = "Weber ratio";
ui_tooltip = "Weber ratio analysis that calculates the ratio of the each local pixel's intensity to average background intensity of all the local pixels.";
ui_type = "radio";
> = true;
uniform bool enable_sdeviation <
ui_category = "Banding analysis";
ui_label = "Standard deviation";
ui_tooltip = "Modified standard deviation analysis that calculates nearby pixels' intensity deviation from the current pixel instead of the mean.";
ui_type = "radio";
> = true;
uniform bool enable_depthbuffer <
ui_category = "Banding analysis";
ui_label = "Depth detection";
ui_tooltip = "Allows depth information to be used when analysing banding, pixels will only be analysed if they are in a certain depth. (e.g. debanding only the sky)";
ui_type = "radio";
> = false;
uniform float t1 <
ui_category = "Banding analysis";
ui_label = "Standard deviation threshold";
ui_max = 0.5;
ui_min = 0.0;
ui_step = 0.001;
ui_tooltip = "Standard deviations lower than this threshold will be flagged as flat regions with potential banding.";
ui_type = "slider";
> = 0.007;
uniform float t2 <
ui_category = "Banding analysis";
ui_label = "Weber ratio threshold";
ui_max = 2.0;
ui_min = 0.0;
ui_step = 0.01;
ui_tooltip = "Weber ratios lower than this threshold will be flagged as flat regions with potential banding.";
ui_type = "slider";
> = 0.04;
uniform float banding_depth <
ui_category = "Banding analysis";
ui_label = "Banding depth";
ui_max = 1.0;
ui_min = 0.0;
ui_step = 0.001;
ui_tooltip = "Pixels under this depth threshold will not be processed and returned as they are.";
ui_type = "slider";
> = 1.0;
uniform float range <
ui_category = "Banding detection & removal";
ui_label = "Radius";
ui_max = 32.0;
ui_min = 1.0;
ui_step = 1.0;
ui_tooltip = "The radius increases linearly for each iteration. A higher radius will find more gradients, but a lower radius will smooth more aggressively.";
ui_type = "slider";
> = 24.0;
uniform int iterations <
ui_category = "Banding detection & removal";
ui_label = "Iterations";
ui_max = 4;
ui_min = 1;
ui_tooltip = "The number of debanding steps to perform per sample. Each step reduces a bit more banding, but takes time to compute.";
ui_type = "slider";
> = 1;
uniform int debug_output <
ui_category = "Debug";
ui_items = "None\0Blurred (LPF) image\0Banding map\0";
ui_label = "Debug view";
ui_tooltip = "Blurred (LPF) image: Useful when tweaking radius and iterations to make sure all banding regions are blurred enough.\nBanding map: Useful when tweaking analysis parameters, continuous green regions indicate flat (i.e. banding) regions.";
ui_type = "combo";
> = 0;
// Reshade uses C rand for random, max cannot be larger than 2^15-1
uniform int drandom < source = "random"; min = 0; max = 32767; >;
float rand(float x)
{
return frac(x / 41.0);
}
float permute(float x)
{
return ((34.0 * x + 1.0) * x) % 289.0;
}
float3 PS_Deband(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target
{
float3 ori = tex2Dlod(ReShade::BackBuffer, float4(texcoord, 0.0, 0.0)).rgb;
if (enable_depthbuffer && (ReShade::GetLinearizedDepth(texcoord) < banding_depth))
return ori;
// Initialize the PRNG by hashing the position + a random uniform
float3 m = float3(texcoord + 1.0, (drandom / 32767.0) + 1.0);
float h = permute(permute(permute(m.x) + m.y) + m.z);
// Compute a random angle
float dir = rand(permute(h)) * 6.2831853;
float2 o;
sincos(dir, o.y, o.x);
// Distance calculations
float2 pt;
float dist;
for (int i = 1; i <= iterations; ++i) {
dist = rand(h) * range * i;
pt = dist * BUFFER_PIXEL_SIZE;
h = permute(h);
}
// Sample at quarter-turn intervals around the source pixel
float3 ref[4] = {
tex2Dlod(ReShade::BackBuffer, float4(mad(pt, o, texcoord), 0.0, 0.0)).rgb, // SE
tex2Dlod(ReShade::BackBuffer, float4(mad(pt, -o, texcoord), 0.0, 0.0)).rgb, // NW
tex2Dlod(ReShade::BackBuffer, float4(mad(pt, float2(-o.y, o.x), texcoord), 0.0, 0.0)).rgb, // NE
tex2Dlod(ReShade::BackBuffer, float4(mad(pt, float2( o.y, -o.x), texcoord), 0.0, 0.0)).rgb // SW
};
// Calculate weber ratio
float3 mean = (ori + ref[0] + ref[1] + ref[2] + ref[3]) * 0.2;
float3 k = abs(ori - mean);
for (int j = 0; j < 4; ++j) {
k += abs(ref[j] - mean);
}
k = k * 0.2 / mean;
// Calculate std. deviation
float3 sd = 0.0;
for (int j = 0; j < 4; ++j) {
sd += pow(ref[j] - ori, 2);
}
sd = sqrt(sd * 0.25);
// Generate final output
float3 output;
if (debug_output == 2)
output = float3(0.0, 1.0, 0.0);
else
output = (ref[0] + ref[1] + ref[2] + ref[3]) * 0.25;
// Generate a binary banding map
bool3 banding_map = true;
if (debug_output != 1) {
if (enable_weber)
banding_map = banding_map && k <= t2 * iterations;
if (enable_sdeviation)
banding_map = banding_map && sd <= t1 * iterations;
}
/*------------------------.
| :: Ordered Dithering :: |
'------------------------*/
//Calculate grid position
float grid_position = frac(dot(texcoord, (BUFFER_SCREEN_SIZE * float2(1.0 / 16.0, 10.0 / 36.0)) + 0.25));
//Calculate how big the shift should be
float dither_shift = 0.25 * (1.0 / (pow(2, BUFFER_COLOR_BIT_DEPTH) - 1.0));
//Shift the individual colors differently, thus making it even harder to see the dithering pattern
float3 dither_shift_RGB = float3(dither_shift, -dither_shift, dither_shift); //subpixel dithering
//modify shift acording to grid position.
dither_shift_RGB = lerp(2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position); //shift acording to grid position.
return banding_map ? output + dither_shift_RGB : ori;
}
technique Deband <
ui_tooltip = "Alleviates color banding by trying to approximate original color values.";
>
{
pass
{
VertexShader = PostProcessVS;
PixelShader = PS_Deband;
}
}
@@ -0,0 +1,427 @@
/*
DisplayDepth by CeeJay.dk (with many updates and additions by the Reshade community)
Visualizes the depth buffer. The distance of pixels determine their brightness.
Close objects are dark. Far away objects are bright.
Use this to configure the depth input preprocessor definitions (RESHADE_DEPTH_INPUT_*).
*/
#include "ReShade.fxh"
// -- Basic options --
#if RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN
#define TEXT_UPSIDE_DOWN "1"
#define TEXT_UPSIDE_DOWN_ALTER "0"
#else
#define TEXT_UPSIDE_DOWN "0"
#define TEXT_UPSIDE_DOWN_ALTER "1"
#endif
#if RESHADE_DEPTH_INPUT_IS_REVERSED
#define TEXT_REVERSED "1"
#define TEXT_REVERSED_ALTER "0"
#else
#define TEXT_REVERSED "0"
#define TEXT_REVERSED_ALTER "1"
#endif
#if RESHADE_DEPTH_INPUT_IS_LOGARITHMIC
#define TEXT_LOGARITHMIC "1"
#define TEXT_LOGARITHMIC_ALTER "0"
#else
#define TEXT_LOGARITHMIC "0"
#define TEXT_LOGARITHMIC_ALTER "1"
#endif
// "ui_text" was introduced in ReShade 4.5, so cannot show instructions in older versions
uniform int iUIPresentType <
ui_label = "Present type";
ui_label_ja_jp = "画面効果";
ui_type = "combo";
ui_items = "Depth map\0Normal map\0Show both (Vertical 50/50)\0";
ui_items_ja_jp = "深度マップ\0法線マップ\0両方を表示 (左右分割)\0";
#if __RESHADE__ < 40500
ui_tooltip =
#else
ui_text =
#endif
"The right settings need to be set in the dialog that opens after clicking the \"Edit global preprocessor definitions\" button above.\n"
"\n"
"RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN is currently set to " TEXT_UPSIDE_DOWN ".\n"
"If the Depth map is shown upside down set it to " TEXT_UPSIDE_DOWN_ALTER ".\n"
"\n"
"RESHADE_DEPTH_INPUT_IS_REVERSED is currently set to " TEXT_REVERSED ".\n"
"If close objects in the Depth map are bright and far ones are dark set it to " TEXT_REVERSED_ALTER ".\n"
"Also try this if you can see the normals, but the depth view is all black.\n"
"\n"
"RESHADE_DEPTH_INPUT_IS_LOGARITHMIC is currently set to " TEXT_LOGARITHMIC ".\n"
"If the Normal map has banding artifacts (extra stripes) set it to " TEXT_LOGARITHMIC_ALTER ".";
ui_text_ja_jp =
#if ADDON_ADJUST_DEPTH
"Adjust Depthアドオンのインストールを検出しました。\n"
"'設定に保存して反映する'ボタンをクリックすると、このエフェクトで調節した全ての変数が共通設定に反映されます。\n"
"または、上の'プリプロセッサの定義を編集'ボタンをクリックした後に開くダイアログで直接編集する事もできます。";
#else
"調節が終わったら、上の'プリプロセッサの定義を編集'ボタンをクリックした後に開くダイアログに入力する必要があります。\n"
"\n"
"RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWNは現在" TEXT_UPSIDE_DOWN "に設定されています。\n"
"深度マップが上下逆さまに表示されている場合は" TEXT_UPSIDE_DOWN_ALTER "に変更して下さい。\n"
"\n"
"RESHADE_DEPTH_INPUT_IS_REVERSEDは現在" TEXT_REVERSED "に設定されています。\n"
"画面効果が深度マップのとき、近くの形状がより白く、遠くの形状がより黒い場合は" TEXT_REVERSED_ALTER "に変更して下さい。\n"
"また、法線マップで形が判別出来るが、深度マップが真っ暗に見えるという場合も、この設定の変更を試して下さい。\n"
"\n"
"RESHADE_DEPTH_INPUT_IS_LOGARITHMICは現在" TEXT_LOGARITHMIC "に設定されています。\n"
"画面効果に実際のレンダリングと合致しない縞模様がある場合は" TEXT_LOGARITHMIC_ALTER "に変更して下さい。";
#endif
ui_tooltip_ja_jp =
"'深度マップ'は、形状の遠近を白黒で表現します。正しい見え方では、近くの形状ほど黒く、遠くの形状ほど白くなります。\n"
"'法線マップ'は、形状を滑らかに表現します。正しい見え方では、全体的に青緑風で、地平線を見たときに地面が緑掛かった色合いになります。\n"
"'両方を表示 (左右分割)'が選択された場合は、左に法線マップ、右に深度マップを表示します。";
> = 2;
uniform bool bUIShowOffset <
ui_label = "Blend Depth map into the image (to help with finding the right offset)";
ui_label_ja_jp = "透かし比較";
ui_tooltip_ja_jp = "補正作業を支援するために、画面効果を半透過で適用します。";
> = false;
uniform bool bUIUseLivePreview <
ui_category = "Preview settings";
ui_category_ja_jp = "基本的な補正";
#if __RESHADE__ <= 50902
ui_category_closed = true;
#elif !ADDON_ADJUST_DEPTH
ui_category_toggle = true;
#endif
ui_label = "Show live preview and ignore preprocessor definitions";
ui_label_ja_jp = "プリプロセッサの定義を無視 (補正プレビューをオン)";
ui_tooltip = "Enable this to preview with the current preset settings instead of the global preprocessor settings.";
ui_tooltip_ja_jp =
"共通設定に保存されたプリプロセッサの定義ではなく、これより下のプレビュー設定を使用するには、これを有効にします。\n"
#if ADDON_ADJUST_DEPTH
"設定の準備が出来たら、'設定に保存して反映する'ボタンをクリックしてから、このチェックボックスをオフにして下さい。"
#else
"設定の準備が出来たら、上の'プリプロセッサの定義を編集'ボタンをクリックした後に開くダイアログに入力して下さい。"
#endif
"\n\n"
"プレビューをオンにした場合と比較して画面効果がまったく同じになれば、正しく設定が反映されています。";
> = false;
#if __RESHADE__ <= 50902
uniform int iUIUpsideDown <
#else
uniform bool iUIUpsideDown <
#endif
ui_category = "Preview settings";
ui_label = "Upside Down";
ui_label_ja_jp = "深度バッファの上下反転を修正";
#if __RESHADE__ <= 50902
ui_type = "combo";
ui_items = "Off\0On\0";
#endif
ui_text_ja_jp =
"\n"
#if ADDON_ADJUST_DEPTH
"項目にカーソルを合わせると、設定が必要な状況の説明が表示されます。"
#else
"項目にカーソルを合わせると、設定が必要な状況の説明と、プリプロセッサの定義が表示されます。"
#endif
;
ui_tooltip_ja_jp =
"深度マップが上下逆さまに表示されている場合は変更して下さい。"
#if !ADDON_ADJUST_DEPTH
"\n\n"
"定義名は次の通りです。文字は完全に一致する必要があり、半角大文字の英字とアンダーバーを用いなければなりません。\n"
"RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN=値\n"
"定義値は次の通りです。オンの場合は1、オフの場合は0を指定して下さい。\n"
"RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN=1\n"
"RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN=0"
#endif
;
> = RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN;
#if __RESHADE__ <= 50902
uniform int iUIReversed <
#else
uniform bool iUIReversed <
#endif
ui_category = "Preview settings";
ui_label = "Reversed";
ui_label_ja_jp = "深度バッファの奥行反転を修正";
#if __RESHADE__ <= 50902
ui_type = "combo";
ui_items = "Off\0On\0";
#endif
ui_tooltip_ja_jp =
"画面効果が深度マップのとき、近くの形状が明るく、遠くの形状が暗い場合は変更して下さい。\n"
"また、法線マップで形が判別出来るが、深度マップが真っ暗に見えるという場合も、この設定の変更を試して下さい。"
#if !ADDON_ADJUST_DEPTH
"\n\n"
"定義名は次の通りです。文字は完全に一致する必要があり、半角大文字の英字とアンダーバーを用いなければなりません。\n"
"RESHADE_DEPTH_INPUT_IS_REVERSED=値\n"
"定義値は次の通りです。オンの場合は1、オフの場合は0を指定して下さい。\n"
"RESHADE_DEPTH_INPUT_IS_REVERSED=1\n"
"RESHADE_DEPTH_INPUT_IS_REVERSED=0"
#endif
;
> = RESHADE_DEPTH_INPUT_IS_REVERSED;
#if __RESHADE__ <= 50902
uniform int iUILogarithmic <
#else
uniform bool iUILogarithmic <
#endif
ui_category = "Preview settings";
ui_label = "Logarithmic";
ui_label_ja_jp = "深度バッファを対数分布として扱うように修正";
#if __RESHADE__ <= 50902
ui_type = "combo";
ui_items = "Off\0On\0";
#endif
ui_tooltip = "Change this setting if the displayed surface normals have stripes in them.";
ui_tooltip_ja_jp =
"画面効果に実際のゲーム画面と合致しない縞模様がある場合は変更して下さい。"
#if !ADDON_ADJUST_DEPTH
"\n\n"
"定義名は次の通りです。文字は完全に一致する必要があり、半角大文字の英字とアンダーバーを用いなければなりません。\n"
"RESHADE_DEPTH_INPUT_IS_LOGARITHMIC=値\n"
"定義値は次の通りです。オンの場合は1、オフの場合は0を指定して下さい。\n"
"RESHADE_DEPTH_INPUT_IS_LOGARITHMIC=1\n"
"RESHADE_DEPTH_INPUT_IS_LOGARITHMIC=0"
#endif
;
> = RESHADE_DEPTH_INPUT_IS_LOGARITHMIC;
// -- Advanced options --
uniform float2 fUIScale <
ui_category = "Preview settings";
ui_label = "Scale";
ui_label_ja_jp = "拡大率";
ui_type = "drag";
ui_text =
"\n"
" * Advanced options\n"
"\n"
"The following settings also need to be set using \"Edit global preprocessor definitions\" above in order to take effect.\n"
"You can preview how they will affect the Depth map using the controls below.\n"
"\n"
"It is rarely necessary to change these though, as their defaults fit almost all games.\n\n";
ui_text_ja_jp =
"\n"
" * その他の補正 (不定形またはその他)\n"
"\n"
"これより下は、深度バッファが不定形など、特別なケース向けの設定です。\n"
"通常はこれより上の'基本的な補正'のみでほとんどのゲームに適合します。\n"
"また、これらの設定は画質の向上にはまったく役に立ちません。\n\n";
ui_tooltip =
"Best use 'Present type'->'Depth map' and enable 'Offset' in the options below to set the scale.\n"
"Use these values for:\nRESHADE_DEPTH_INPUT_X_SCALE=<left value>\nRESHADE_DEPTH_INPUT_Y_SCALE=<right value>\n"
"\n"
"If you know the right resolution of the games depth buffer then this scale value is simply the ratio\n"
"between the correct resolution and the resolution Reshade thinks it is.\n"
"For example:\n"
"If it thinks the resolution is 1920 x 1080, but it's really 1280 x 720 then the right scale is (1.5 , 1.5)\n"
"because 1920 / 1280 is 1.5 and 1080 / 720 is also 1.5, so 1.5 is the right scale for both the x and the y";
ui_tooltip_ja_jp =
"深度バッファの解像度がクライアント解像度と異なる場合に変更して下さい。\n"
"このスケール値は、深度バッファの解像度とクライアント解像度との単純な比率になります。\n"
"深度バッファの解像度が1280×720でクライアント解像度が1920×1080の場合、横の比率が1920÷1280、縦の比率が1080÷720となります。\n"
"計算した結果を設定すると、値はそれぞれX_SCALE=1.5、Y_SCALE=1.5となります。"
#if !ADDON_ADJUST_DEPTH
"\n\n"
"定義名は次の通りです。文字は完全に一致する必要があり、半角大文字の英字とアンダーバーを用いなければなりません。\n"
"RESHADE_DEPTH_INPUT_X_SCALE=横の値\n"
"RESHADE_DEPTH_INPUT_Y_SCALE=縦の値\n"
"定義値は次の通りです。横の値はX_SCALE、縦の値はY_SCALEに指定して下さい。\n"
"RESHADE_DEPTH_INPUT_X_SCALE=1.0\n"
"RESHADE_DEPTH_INPUT_Y_SCALE=1.0"
#endif
;
ui_min = 0.0; ui_max = 2.0;
ui_step = 0.001;
> = float2(RESHADE_DEPTH_INPUT_X_SCALE, RESHADE_DEPTH_INPUT_Y_SCALE);
uniform int2 iUIOffset <
ui_category = "Preview settings";
ui_label = "Offset";
ui_label_ja_jp = "位置オフセット";
ui_type = "slider";
ui_tooltip =
"Best use 'Present type'->'Depth map' and enable 'Offset' in the options below to set the offset in pixels.\n"
"Use these values for:\nRESHADE_DEPTH_INPUT_X_PIXEL_OFFSET=<left value>\nRESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET=<right value>";
ui_tooltip_ja_jp =
"深度バッファにレンダリングされた物体の形状が画面効果と重なり合っていない場合に変更して下さい。\n"
"この値は、ピクセル単位で指定します。"
#if !ADDON_ADJUST_DEPTH
"\n\n"
"定義名は次の通りです。文字は完全に一致する必要があり、半角大文字の英字とアンダーバーを用いなければなりません。\n"
"RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET=横の値\n"
"RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET=縦の値\n"
"定義値は次の通りです。横の値はX_PIXEL_OFFSET、縦の値はY_PIXEL_OFFSETに指定して下さい。\n"
"RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET=0.0\n"
"RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET=0.0"
#endif
;
ui_min = -BUFFER_SCREEN_SIZE;
ui_max = BUFFER_SCREEN_SIZE;
ui_step = 1;
> = int2(RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET, RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET);
uniform float fUIFarPlane <
ui_category = "Preview settings";
ui_label = "Far Plane";
ui_label_ja_jp = "遠点距離";
ui_type = "drag";
ui_tooltip =
"RESHADE_DEPTH_LINEARIZATION_FAR_PLANE=<value>\n"
"Changing this value is not necessary in most cases.";
ui_tooltip_ja_jp =
"深度マップの色合いが距離感と合致しない、法線マップの表面が平面に見える、などの場合に変更して下さい。\n"
"遠点距離を1000に設定すると、ゲームの描画距離が1000メートルであると見なします。\n\n"
"このプレビュー画面はあくまでプレビューであり、ほとんどの場合、深度バッファは深度マップの色数より遥かに高い精度で表現されています。\n"
"例えば、10m前後の距離の形状が純粋な黒に見えるからという理由で値を変更しないで下さい。"
#if !ADDON_ADJUST_DEPTH
"\n\n"
"定義名は次の通りです。文字は完全に一致する必要があり、半角大文字の英字とアンダーバーを用いなければなりません。\n"
"RESHADE_DEPTH_LINEARIZATION_FAR_PLANE=値\n"
"定義値は次の通りです。\n"
"RESHADE_DEPTH_LINEARIZATION_FAR_PLANE=1000.0"
#endif
;
ui_min = 0.0; ui_max = 1000.0;
ui_step = 0.1;
> = RESHADE_DEPTH_LINEARIZATION_FAR_PLANE;
uniform float fUIDepthMultiplier <
ui_category = "Preview settings";
ui_label = "Multiplier";
ui_label_ja_jp = "深度乗数";
ui_type = "drag";
ui_tooltip = "RESHADE_DEPTH_MULTIPLIER=<value>";
ui_tooltip_ja_jp =
"特定のエミュレータソフトウェアにおける深度バッファを修正するため、特別に追加された変数です。\n"
"この値は僅かな変更でも計算式を破壊するため、設定すべき値を知らない場合は変更しないで下さい。"
#if !ADDON_ADJUST_DEPTH
"\n\n"
"定義名は次の通りです。文字は完全に一致する必要があり、半角大文字の英字とアンダーバーを用いなければなりません。\n"
"RESHADE_DEPTH_MULTIPLIER=値\n"
"定義値は次の通りです。\n"
"RESHADE_DEPTH_MULTIPLIER=1.0"
#endif
;
ui_min = 0.0; ui_max = 1000.0;
ui_step = 0.001;
> = RESHADE_DEPTH_MULTIPLIER;
float GetLinearizedDepth(float2 texcoord)
{
if (!bUIUseLivePreview)
{
return ReShade::GetLinearizedDepth(texcoord);
}
else
{
if (iUIUpsideDown) // RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN
texcoord.y = 1.0 - texcoord.y;
texcoord.x /= fUIScale.x; // RESHADE_DEPTH_INPUT_X_SCALE
texcoord.y /= fUIScale.y; // RESHADE_DEPTH_INPUT_Y_SCALE
texcoord.x -= iUIOffset.x * BUFFER_RCP_WIDTH; // RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET
texcoord.y += iUIOffset.y * BUFFER_RCP_HEIGHT; // RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET
float depth = tex2Dlod(ReShade::DepthBuffer, float4(texcoord, 0, 0)).x * fUIDepthMultiplier;
const float C = 0.01;
if (iUILogarithmic) // RESHADE_DEPTH_INPUT_IS_LOGARITHMIC
depth = (exp(depth * log(C + 1.0)) - 1.0) / C;
if (iUIReversed) // RESHADE_DEPTH_INPUT_IS_REVERSED
depth = 1.0 - depth;
const float N = 1.0;
depth /= fUIFarPlane - depth * (fUIFarPlane - N);
return depth;
}
}
float3 GetScreenSpaceNormal(float2 texcoord)
{
float3 offset = float3(BUFFER_PIXEL_SIZE, 0.0);
float2 posCenter = texcoord.xy;
float2 posNorth = posCenter - offset.zy;
float2 posEast = posCenter + offset.xz;
float3 vertCenter = float3(posCenter - 0.5, 1) * GetLinearizedDepth(posCenter);
float3 vertNorth = float3(posNorth - 0.5, 1) * GetLinearizedDepth(posNorth);
float3 vertEast = float3(posEast - 0.5, 1) * GetLinearizedDepth(posEast);
return normalize(cross(vertCenter - vertNorth, vertCenter - vertEast)) * 0.5 + 0.5;
}
void PS_DisplayDepth(in float4 position : SV_Position, in float2 texcoord : TEXCOORD, out float3 color : SV_Target)
{
float3 depth = GetLinearizedDepth(texcoord).xxx;
float3 normal = GetScreenSpaceNormal(texcoord);
// Ordered dithering
#if 1
const float dither_bit = 8.0; // Number of bits per channel. Should be 8 for most monitors.
// Calculate grid position
float grid_position = frac(dot(texcoord, (BUFFER_SCREEN_SIZE * float2(1.0 / 16.0, 10.0 / 36.0)) + 0.25));
// Calculate how big the shift should be
float dither_shift = 0.25 * (1.0 / (pow(2, dither_bit) - 1.0));
// Shift the individual colors differently, thus making it even harder to see the dithering pattern
float3 dither_shift_RGB = float3(dither_shift, -dither_shift, dither_shift); // Subpixel dithering
// Modify shift acording to grid position.
dither_shift_RGB = lerp(2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position);
depth += dither_shift_RGB;
#endif
color = depth;
if (iUIPresentType == 1)
color = normal;
if (iUIPresentType == 2)
color = lerp(normal, depth, step(BUFFER_WIDTH * 0.5, position.x));
if (bUIShowOffset)
{
float3 color_orig = tex2D(ReShade::BackBuffer, texcoord).rgb;
// Blend depth and back buffer color with 'overlay' so the offset is more noticeable
color = lerp(2 * color * color_orig, 1.0 - 2.0 * (1.0 - color) * (1.0 - color_orig), max(color.r, max(color.g, color.b)) < 0.5 ? 0.0 : 1.0);
}
}
technique DisplayDepth <
ui_tooltip =
"This shader helps you set the right preprocessor settings for depth input.\n"
"To set the settings click on 'Edit global preprocessor definitions' and set them there - not in this shader.\n"
"The settings will then take effect for all shaders, including this one.\n"
"\n"
"By default calculated normals and depth are shown side by side.\n"
"Normals (on the left) should look smooth and the ground should be greenish when looking at the horizon.\n"
"Depth (on the right) should show close objects as dark and use gradually brighter shades the further away objects are.\n";
ui_tooltip_ja_jp =
"これは、深度バッファの入力をReShade側の計算式に合わせる調節をするための、設定作業の支援に特化した特殊な扱いのエフェクトです。\n"
"初期状態では「両方を表示」が選択されており、左に法線マップ、右に深度マップが表示されます。\n"
"\n"
"法線マップ(左側)は、形状を滑らかに表現します。正しい設定では、全体的に青緑風で、地平線を見たときに地面が緑を帯びた色になります。\n"
"深度マップ(右側)は、形状の遠近を白黒で表現します。正しい設定では、近くの形状ほど黒く、遠くの形状ほど白くなります。\n"
"\n"
#if ADDON_ADJUST_DEPTH
"設定を完了するには、DisplayDepth.fxエフェクトの変数の一覧にある'設定に保存して反映する'ボタンをクリックして下さい。\n"
#else
"設定を完了するには、エフェクト変数の編集画面にある'プリプロセッサの定義を編集'ボタンをクリックした後に開くダイアログに入力して下さい。\n"
#endif
"すると、インストール先のゲームに対して共通の設定として保存され、他のプリセットでも正しく表示されるようになります。";
>
{
pass
{
VertexShader = PostProcessVS;
PixelShader = PS_DisplayDepth;
}
}
@@ -0,0 +1,228 @@
#ifndef _DRAWTEXT_H_
#define _DRAWTEXT_H_
#define _DRAWTEXT_GRID_X 14.0
#define _DRAWTEXT_GRID_Y 7.0
///////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// DrawText.fxh by kingreic1992 ( update: Sep.28.2019 ) //
// //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
// //
// Available functions: //
// DrawText_String( offset, text size, xy ratio, input coord, string array, array size, output) //
// float2 offset = top left corner of string, screen hight pixel unit. //
// float text size = text size, screen hight pixel unit. //
// float xy ratio = xy ratio of text. //
// float2 input coord = current texture coord. //
// int string array = string data in float2 array format, ex: "Demo Text" //
// int String0[9] = { __D, __e, __m, __o, __Space, __T, __e, __x, __t}; //
// int string size = size of the string array. //
// float output = output. //
// //
// DrawText_Digit( offset, text size, xy ratio, input coord, precision after dot, data, output) //
// float2 offset = same as DrawText_String. //
// float text size = same as DrawText_String. //
// float xy ratio = same as DrawText_String. //
// float2 input coord = same as DrawText_String. //
// int precision = digits after dot. //
// float data = input float. //
// float output = output. //
// //
// float2 DrawText_Shift(offset, shift, text size, xy ratio) //
// float2 offset = same as DrawText_String. //
// float2 shift = shift line(y) and column. //
// float text size = same as DrawText_String. //
// float xy ratio = same as DrawText_String. //
// //
///////////////////////////////////////////////////////////////////////////////////////////////////////
//Sample Usage
/*
#include "DrawText.fxh"
float4 main_fragment( float4 position : POSITION,
float2 txcoord : TEXCOORD) : COLOR {
float res = 0.0;
int line0[9] = { __D, __e, __m, __o, __Space, __T, __e, __x, __t }; //Demo Text
int line1[15] = { __b, __y, __Space, __k, __i, __n, __g, __e, __r, __i, __c, __1, __9, __9, __2 }; //by kingeric1992
int line2[6] = { __S, __i, __z, __e, __Colon, __Space }; // Size: %d.
DrawText_String(float2(100.0 , 100.0), 32, 1, txcoord, line0, 9, res);
DrawText_String(float2(100.0 , 134.0), textSize, 1, txcoord, line1, 15, res);
DrawText_String(DrawText_Shift(float2(100.0 , 134.0), int2(0, 1), textSize, 1), 18, 1, txcoord, line2, 6, res);
DrawText_Digit(DrawText_Shift(DrawText_Shift(float2(100.0 , 134.0), int2(0, 1), textSize, 1), int2(8, 0), 18, 1),
18, 1, txcoord, 0, textSize, res);
return res;
}
*/
//Text display
//Character indexing
#define __Space 0 // (space)
#define __Exclam 1 // !
#define __Quote 2 // "
#define __Pound 3 // #
#define __Dollar 4 // $
#define __Percent 5 // %
#define __And 6 // &
#define __sQuote 7 // '
#define __rBrac_O 8 // (
#define __rBrac_C 9 // )
#define __Asterisk 10 // *
#define __Plus 11 // +
#define __Comma 12 // ,
#define __Minus 13 // -
#define __Dot 14 // .
#define __Slash 15 // /
#define __0 16 // 0
#define __1 17 // 1
#define __2 18 // 2
#define __3 19 // 3
#define __4 20 // 4
#define __5 21 // 5
#define __6 22 // 6
#define __7 23 // 7
#define __8 24 // 8
#define __9 25 // 9
#define __Colon 26 // :
#define __sColon 27 // ;
#define __Less 28 // <
#define __Equals 29 // =
#define __Greater 30 // >
#define __Question 31 // ?
#define __at 32 // @
#define __A 33 // A
#define __B 34 // B
#define __C 35 // C
#define __D 36 // D
#define __E 37 // E
#define __F 38 // F
#define __G 39 // G
#define __H 40 // H
#define __I 41 // I
#define __J 42 // J
#define __K 43 // K
#define __L 44 // L
#define __M 45 // M
#define __N 46 // N
#define __O 47 // O
#define __P 48 // P
#define __Q 49 // Q
#define __R 50 // R
#define __S 51 // S
#define __T 52 // T
#define __U 53 // U
#define __V 54 // V
#define __W 55 // W
#define __X 56 // X
#define __Y 57 // Y
#define __Z 58 // Z
#define __sBrac_O 59 // [
#define __Backslash 60 // \..
#define __sBrac_C 61 // ]
#define __Caret 62 // ^
#define __Underscore 63 // _
#define __Punc 64 // `
#define __a 65 // a
#define __b 66 // b
#define __c 67 // c
#define __d 68 // d
#define __e 69 // e
#define __f 70 // f
#define __g 71 // g
#define __h 72 // h
#define __i 73 // i
#define __j 74 // j
#define __k 75 // k
#define __l 76 // l
#define __m 77 // m
#define __n 78 // n
#define __o 79 // o
#define __p 80 // p
#define __q 81 // q
#define __r 82 // r
#define __s 83 // s
#define __t 84 // t
#define __u 85 // u
#define __v 86 // v
#define __w 87 // w
#define __x 88 // x
#define __y 89 // y
#define __z 90 // z
#define __cBrac_O 91 // {
#define __vBar 92 // |
#define __cBrac_C 93 // }
#define __Tilde 94 // ~
#define __tridot 95 // (...)
#define __empty0 96 // (null)
#define __empty1 97 // (null)
//Character indexing ends
texture Texttex < source = "FontAtlas.png"; > {
Width = 512;
Height = 512;
};
sampler samplerText {
Texture = Texttex;
};
//accomodate for undef array size.
#define DrawText_String( pos, size, ratio, tex, array, arrSize, output ) \
{ float text = 0.0; \
float2 uv = (tex * float2(BUFFER_WIDTH, BUFFER_HEIGHT) - pos) / size; \
uv.y = saturate(uv.y); \
uv.x *= ratio * 2.0; \
float id = array[int(trunc(uv.x))]; \
if(uv.x <= arrSize && uv.x >= 0.0) \
text = tex2D(samplerText, (frac(uv) + float2( id % 14.0, trunc(id / 14.0))) \
/ float2( _DRAWTEXT_GRID_X, _DRAWTEXT_GRID_Y) ).x; \
output += text; }
float2 DrawText_Shift( float2 pos, int2 shift, float size, float ratio ) {
return pos + size * shift * float2(0.5, 1.0) / ratio;
}
void DrawText_Digit( float2 pos, float size, float ratio, float2 tex, int digit, float data, inout float res) {
int digits[13] = {
__0, __1, __2, __3, __4, __5, __6, __7, __8, __9, __Minus, __Space, __Dot
};
float2 uv = (tex * float2(BUFFER_WIDTH, BUFFER_HEIGHT) - pos) / size;
uv.y = saturate(uv.y);
uv.x *= ratio * 2.0;
float t = abs(data);
int radix = floor(t)? ceil(log2(t)/3.32192809):0;
//early exit:
if(uv.x > digit+1 || -uv.x > radix+1) return;
float index = t;
if(floor(uv.x) > 0)
for(int i = ceil(-uv.x); i<0; i++) index *= 10.;
else
for(int i = ceil(uv.x); i<0; i++) index /= 10.;
index = (uv.x >= -radix-!radix)? index%10 : (10+step(0, data)); //adding sign
index = (uv.x > 0 && uv.x < 1)? 12:index; //adding dot
index = digits[(uint)index];
res += tex2D(samplerText, (frac(uv) + float2( index % 14.0, trunc(index / 14.0))) /
float2( _DRAWTEXT_GRID_X, _DRAWTEXT_GRID_Y)).x;
}
#endif
@@ -0,0 +1,80 @@
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ReShade effect file
// visit facebook.com/MartyMcModding for news/updates
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Marty's LUT shader 1.0 for ReShade 3.0
// Copyright © 2008-2016 Marty McFly
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#ifndef fLUT_TextureName
#define fLUT_TextureName "lut.png"
#endif
#ifndef fLUT_TileSizeXY
#define fLUT_TileSizeXY 32
#endif
#ifndef fLUT_TileAmount
#define fLUT_TileAmount 32
#endif
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include "ReShadeUI.fxh"
uniform float fLUT_AmountChroma < __UNIFORM_SLIDER_FLOAT1
ui_min = 0.00; ui_max = 1.00;
ui_label = "LUT chroma amount";
ui_tooltip = "Intensity of color/chroma change of the LUT.";
> = 1.00;
uniform float fLUT_AmountLuma < __UNIFORM_SLIDER_FLOAT1
ui_min = 0.00; ui_max = 1.00;
ui_label = "LUT luma amount";
ui_tooltip = "Intensity of luma change of the LUT.";
> = 1.00;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include "ReShade.fxh"
texture texLUT < source = fLUT_TextureName; > { Width = fLUT_TileSizeXY*fLUT_TileAmount; Height = fLUT_TileSizeXY; Format = RGBA8; };
sampler SamplerLUT { Texture = texLUT; };
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void PS_LUT_Apply(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 res : SV_Target0)
{
float4 color = tex2D(ReShade::BackBuffer, texcoord.xy);
float2 texelsize = 1.0 / fLUT_TileSizeXY;
texelsize.x /= fLUT_TileAmount;
float3 lutcoord = float3((color.xy*fLUT_TileSizeXY-color.xy+0.5)*texelsize.xy,color.z*fLUT_TileSizeXY-color.z);
float lerpfact = frac(lutcoord.z);
lutcoord.x += (lutcoord.z-lerpfact)*texelsize.y;
float3 lutcolor = lerp(tex2D(SamplerLUT, lutcoord.xy).xyz, tex2D(SamplerLUT, float2(lutcoord.x+texelsize.y,lutcoord.y)).xyz,lerpfact);
color.xyz = lerp(normalize(color.xyz), normalize(lutcolor.xyz), fLUT_AmountChroma) *
lerp(length(color.xyz), length(lutcolor.xyz), fLUT_AmountLuma);
res.xyz = color.xyz;
res.w = 1.0;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
technique LUT
{
pass LUT_Apply
{
VertexShader = PostProcessVS;
PixelShader = PS_LUT_Apply;
}
}
@@ -0,0 +1,625 @@
////////////////////////////////////////////////////////////
// BASIC MACROS FOR RESHADE 4 //
// AUTHOR: TREYM //
////////////////////////////////////////////////////////////
// Modified by dddfault //
// //
// Changelogs : //
// Added Sampler texture boundary resolver option //
// Added float2 parameters option //
////////////////////////////////////////////////////////////
// Macros Guide: //
////////////////////////////////////////////////////////////
/* //////////////////////////////////////////////////// *
* //////////////////////////////////////////////////// *
Usage of these macros is very simple once you understand
the syntax and variable names. Let's start with a Simple
integer slider. To begin, type:
UI_INT
Next we need to add _S to indicate that this is a
"slider" widget. Follow the syntax below:
UI_INT_S(INT_NAME, "Label", "Tooltip", 0, 100, 50)
Using just a single line of code, we have created a UI
tweakable integer named INT_NAME with a minimum value of
0, a maximum value of 100, and a default value of 50.
Next, let's create that same widget, but within a UI
category. This time, we'll type:
CAT_INT_S(INT_NAME, "Category", "Label", "Tooltip", 0, 100, 50)
As you can see, the syntax follows the same pattern but
with a new input for "Category"
Below you will find a useful list of examples to get you
started. I hope you find these useful and they help your
workflow. Happy coding!
- TreyM
* //////////////////////////////////////////////////// *
* //////////////////////////////////////////////////// *
Widget Types
Input = _I
Slider = _S
Drag = _D
* //////////////////////////////////////////////////// *
BOOLEAN Macro
UI_BOOL(BOOL_NAME, "Label", "Tooltip", true)
BOOLEAN Categorized Macro
CAT_BOOL(BOOL_NAME, "Category", "Label", "Tooltip", true)
* //////////////////////////////////////////////////// *
INTEGER Combo Widget
UI_COMBO(INT_NAME, "Label", "Tooltip", 0, 2, 0, "Item 1\0Item 2\0Item 3\0")
INTEGER Drag Widget
UI_INT_D(INT_NAME, "Label", "Tooltip", 0, 100, 50)
INTEGER Input Widget
UI_INT_I(INT_NAME, "Label", "Tooltip", 0, 100, 50)
INTEGER Radio Widget
UI_RADIO(INT_NAME, "Label", "Tooltip", 0, 2, 0, " Item 1 \0 Item 2 \0 Item 3\0")
INTEGER Slider Widget
UI_INT_S(INT_NAME, "Label", "Tooltip", 0, 100, 50)
INTEGER Categorized Combo Widget
CAT_COMBO(INT_NAME, "Category", "Label", "Tooltip", 0, 2, 0, " Item 1 \0 Item 2 \0 Item 3\0")
INTEGER Categorized Drag Widget
CAT_INT_D(INT_NAME, "Category", "Label", "Tooltip", 0, 100, 50)
INTEGER Categorized Input Widget
CAT_INT_I(INT_NAME, "Category", "Label", "Tooltip", 0, 100, 50)
INTEGER Categorized Radio Widget
CAT_RADIO(INT_NAME, "Category", "Label", "Tooltip", 0, 2, 0, " Item 1 \0 Item 2 \0 Item 3\0")
INTEGER Categorized Slider Widget
CAT_INT_S(INT_NAME, "Category", "Label", "Tooltip", 0, 100, 50)
* //////////////////////////////////////////////////// *
FLOAT Drag Widget
UI_FLOAT_D(FLOAT_NAME, "Label", "Tooltip", 0.0, 1.0, 0.5)
FLOAT Input Widget
UI_FLOAT_I(FLOAT_NAME, "Label", "Tooltip", 0.0, 1.0, 0.5)
FLOAT Slider Widget
UI_FLOAT_S(FLOAT_NAME, "Label", "Tooltip", 0.0, 1.0, 0.5)
FLOAT Categorized Drag Widget
CAT_FLOAT_D(FLOAT_NAME, "Category", "Label", "Tooltip", 0.0, 1.0, 0.5)
FLOAT Categorized Input Widget
CAT_FLOAT_I(FLOAT_NAME, "Category", "Label", "Tooltip", 0.0, 1.0, 0.5)
FLOAT Categorized Slider Widget
CAT_FLOAT_S(FLOAT_NAME, "Category", "Label", "Tooltip", 0.0, 1.0, 0.5)
FLOAT macro with full control (value after "Tooltip" is ui_step)
UI_FLOAT_FULL(FLOAT_NAME, "ui_type", "Label", "Tooltip", 0.1, 0.0, 1.0, 0.5)
FLOAT Categorized macro with full control (value after "Tooltip" is ui_step)
CAT_FLOAT_FULL(FLOAT_NAME, "ui_type", "Category", "Label", "Tooltip", 0.1, 0.0, 1.0, 0.5)
* //////////////////////////////////////////////////// *
FLOAT2 Drag Widget
UI_FLOAT2_D(FLOAT_NAME, "Label", "Tooltip", 0.0, 1.0, 0.5, 0.5)
FLOAT2 Input Widget
UI_FLOAT2_I(FLOAT_NAME, "Label", "Tooltip", 0.0, 1.0, 0.5, 0.5)
FLOAT2 Slider Widget
UI_FLOAT2_S(FLOAT_NAME, "Label", "Tooltip", 0.0, 1.0, 0.5, 0.5)
FLOAT2 Categorized Drag Widget
CAT_FLOAT2_D(FLOAT_NAME, "Category", "Label", "Tooltip", 0.0, 1.0, 0.5, 0.5)
FLOAT2 Categorized Input Widget
CAT_FLOAT2_I(FLOAT_NAME, "Category", "Label", "Tooltip", 0.0, 1.0, 0.5, 0.5)
FLOAT2 Categorized Slider Widget
CAT_FLOAT2_S(FLOAT_NAME, "Category", "Label", "Tooltip", 0.0, 1.0, 0.5, 0.5)
FLOAT2 macro with full control (value after "Tooltip" is ui_step)
UI_FLOAT2_FULL(FLOAT_NAME, "ui_type", "Label", "Tooltip", 0.1, 0.0, 1.0, 0.5, 0.5)
FLOAT2 Categorized macro with full control (value after "Tooltip" is ui_step)
CAT_FLOAT2_FULL(FLOAT_NAME, "ui_type", "Category", "Label", "Tooltip", 0.1, 0.0, 1.0, 0.5, 0.5)
* //////////////////////////////////////////////////// *
FLOAT3 Drag Widget
UI_FLOAT3_D(FLOAT_NAME, "Label", "Tooltip", 0.5, 0.5, 0.5)
FLOAT3 Input Widget
UI_FLOAT3_I(FLOAT_NAME, "Label", "Tooltip", 0.5, 0.5, 0.5)
FLOAT3 Slider Widget
UI_FLOAT3_S(FLOAT_NAME, "Label", "Tooltip", 0.5, 0.5, 0.5)
FLOAT3 Categorized Drag Widget
CAT_FLOAT3_D(FLOAT_NAME, "Category", "Label", "Tooltip", 0.5, 0.5, 0.5)
FLOAT3 Categorized Input Widget
CAT_FLOAT3_I(FLOAT_NAME, "Category", "Label", "Tooltip", 0.5, 0.5, 0.5)
FLOAT3 Categorized Slider Widget
CAT_FLOAT3_S(FLOAT_NAME, "Category", "Label", "Tooltip", 0.5, 0.5, 0.5)
* //////////////////////////////////////////////////// *
FLOAT3 Color Widget
UI_COLOR(FLOAT_NAME, "Label", "Tooltip", 0.5, 0.5, 0.5)
FLOAT3 Categorized Color Widget
CAT_COLOR(FLOAT_NAME, "Category", "Label", "Tooltip", 0.5, 0.5, 0.5)
* //////////////////////////////////////////////////// *
SAMPLER Macro
SAMPLER(SamplerName, TextureName)
SAMPLER Macro with texture boundary resolver option
SAMPLER_UV(SamplerName, TextureName, ResolverType)
TEXTURE Macro
TEXTURE(TextureName, "TexturePath")
TEXTURE Full Macro
TEXTURE_FULL(TextureName, "TexturePath", Width, Height, Format)
* //////////////////////////////////////////////////// *
TECHNIQUE Macro
TECHNIQUE(TechniqueName, PassMacro)
PASS Macro
PASS(PassID, VertexShader, PixelShader)
PASS Macro with RenderTarget
PASS_RT(PassID, VertexShader, PixelShader, RenderTarget)
////////////////////////////////////////////////////
* //////////////////////////////////////////////////// */
// INTEGER MACROS ////////////////////////////////
#define UI_COMBO(var, label, tooltip, minval, maxval, defval, items) \
uniform int var \
< \
ui_type = "combo"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_items = items; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_COMBO(var, category, label, tooltip, minval, maxval, defval, items) \
uniform int var \
< \
ui_type = "combo"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_items = items; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_INT_I(var, label, tooltip, minval, maxval, defval) \
uniform int var \
< \
ui_type = "input"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_INT_I(var, category, label, tooltip, minval, maxval, defval) \
uniform int var \
< \
ui_type = "input"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_INT_S(var, label, tooltip, minval, maxval, defval) \
uniform int var \
< \
ui_type = "slider"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_INT_S(var, category, label, tooltip, minval, maxval, defval) \
uniform int var \
< \
ui_type = "slider"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_INT_D(var, label, tooltip, minval, maxval, defval) \
uniform int var \
< \
ui_type = "drag"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_INT_D(var, category, label, tooltip, minval, maxval, defval) \
uniform int var \
< \
ui_type = "drag"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_RADIO(var, label, tooltip, minval, maxval, defval, items) \
uniform int var \
< \
ui_type = "radio"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_items = items; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_RADIO(var, category, label, tooltip, minval, maxval, defval, items) \
uniform int var \
< \
ui_type = "radio"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_items = items; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
// BOOL MACROS ///////////////////////////////////
#define UI_BOOL(var, label, tooltip, def) \
uniform bool var \
< \
ui_label = label; \
ui_tooltip = tooltip; \
> = def;
#define CAT_BOOL(var, category, label, tooltip, def) \
uniform bool var \
< \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
> = def;
// FLOAT MACROS //////////////////////////////////
#define UI_FLOAT_D(var, label, tooltip, minval, maxval, defval) \
uniform float var \
< \
ui_type = "drag"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_FLOAT_D(var, category, label, tooltip, minval, maxval, defval) \
uniform float var \
< \
ui_type = "drag"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_FLOAT_FULL(var, uitype, label, tooltip, uistep, minval, maxval, defval) \
uniform float var \
< \
ui_type = uitype; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_step = uistep; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_FLOAT_FULL(var, uitype, category, label, tooltip, uistep, minval, maxval, defval) \
uniform float var \
< \
ui_type = uitype; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_step = uistep; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_FLOAT_I(var, label, tooltip, minval, maxval, defval) \
uniform float var \
< \
ui_type = "input"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_FLOAT_I(var, category, label, tooltip, minval, maxval, defval) \
uniform float var \
< \
ui_type = "input"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_FLOAT_S(var, label, tooltip, minval, maxval, defval) \
uniform float var \
< \
ui_type = "slider"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define CAT_FLOAT_S(var, category, label, tooltip, minval, maxval, defval) \
uniform float var \
< \
ui_type = "slider"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = defval;
#define UI_FLOAT2_D(var, label, tooltip, minval, maxval, defval1, defval2) \
uniform float2 var \
< \
ui_type = "drag"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define CAT_FLOAT2_D(var, category, label, tooltip, minval, maxval, defval1, defval2) \
uniform float2 var \
< \
ui_type = "drag"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define UI_FLOAT2_FULL(var, uitype, label, tooltip, uistep, minval, maxval, defval1, defval2) \
uniform float2 var \
< \
ui_type = uitype; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_step = uistep; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define CAT_FLOAT2_FULL(var, uitype, category, label, tooltip, uistep, minval, defval1, defval2) \
uniform float2 var \
< \
ui_type = uitype; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_step = uistep; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define UI_FLOAT2_I(var, label, tooltip, minval, maxval, defval1, defval2) \
uniform float2 var \
< \
ui_type = "input"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define CAT_FLOAT2_I(var, category, label, tooltip, minval, maxval, defval1, defval2) \
uniform float2 var \
< \
ui_type = "input"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define UI_FLOAT2_S(var, label, tooltip, minval, maxval, defval1, defval2) \
uniform float2 var \
< \
ui_type = "slider"; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define CAT_FLOAT2_S(var, category, label, tooltip, minval, maxval, defval1, defval2) \
uniform float2 var \
< \
ui_type = "slider"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
ui_min = minval; \
ui_max = maxval; \
> = float2(defval1, defval2);
#define UI_FLOAT3_D(var, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "drag"; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
#define CAT_FLOAT3_D(var, category, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "drag"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
#define UI_FLOAT3_I(var, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "input"; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
#define CAT_FLOAT3_I(var, category, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "input"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
#define UI_FLOAT3_S(var, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "slider"; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
#define CAT_FLOAT3_S(var, category, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "slider"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
// COLOR WIDGET MACROS ///////////////////////////
#define UI_COLOR(var, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "color"; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
#define CAT_COLOR(var, category, label, tooltip, defval1, defval2, defval3) \
uniform float3 var \
< \
ui_type = "color"; \
ui_category = category; \
ui_label = label; \
ui_tooltip = tooltip; \
> = float3(defval1, defval2, defval3);
// SAMPLER MACRO /////////////////////////////////
#define SAMPLER(sname, tname) \
sampler sname \
{ \
Texture = tname; \
};
#define SAMPLER_UV(sname, tname, addUVW) \
sampler sname \
{ \
Texture = tname; \
AddressU = addUVW; \
AddressV = addUVW; \
AddressW = addUVW; \
};
// TEXTURE MACROs ////////////////////////////////
#define TEXTURE(tname, src) \
texture tname <source=src;> \
{ \
Width = BUFFER_WIDTH; \
Height = BUFFER_HEIGHT; \
Format = RGBA8; \
};
#define TEXTURE_FULL(tname, src, width, height, fomat) \
texture tname <source=src;> \
{ \
Width = width; \
Height = height; \
Format = fomat; \
};
// TECHNIQUE MACROS //////////////////////////////
#define TECHNIQUE(tname, pass) \
technique tname \
{ \
pass \
}
#define PASS(ID, vs, ps) pass \
{ \
VertexShader = vs; \
PixelShader = ps; \
}
#define PASS_RT(ID, vs, ps, rt) pass \
{ \
VertexShader = vs; \
PixelShader = ps; \
RenderTarget = rt; \
}
@@ -0,0 +1,124 @@
/*
* SPDX-License-Identifier: CC0-1.0
*/
#pragma once
#if !defined(__RESHADE__) || __RESHADE__ < 30000
#error "ReShade 3.0+ is required to use this header file"
#endif
#ifndef RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN
#define RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN 0
#endif
#ifndef RESHADE_DEPTH_INPUT_IS_REVERSED
#define RESHADE_DEPTH_INPUT_IS_REVERSED 1
#endif
#ifndef RESHADE_DEPTH_INPUT_IS_MIRRORED
#define RESHADE_DEPTH_INPUT_IS_MIRRORED 0
#endif
#ifndef RESHADE_DEPTH_INPUT_IS_LOGARITHMIC
#define RESHADE_DEPTH_INPUT_IS_LOGARITHMIC 0
#endif
#ifndef RESHADE_DEPTH_MULTIPLIER
#define RESHADE_DEPTH_MULTIPLIER 1
#endif
#ifndef RESHADE_DEPTH_LINEARIZATION_FAR_PLANE
#define RESHADE_DEPTH_LINEARIZATION_FAR_PLANE 1000.0
#endif
// Above 1 expands coordinates, below 1 contracts and 1 is equal to no scaling on any axis
#ifndef RESHADE_DEPTH_INPUT_Y_SCALE
#define RESHADE_DEPTH_INPUT_Y_SCALE 1
#endif
#ifndef RESHADE_DEPTH_INPUT_X_SCALE
#define RESHADE_DEPTH_INPUT_X_SCALE 1
#endif
// An offset to add to the Y coordinate, (+) = move up, (-) = move down
#ifndef RESHADE_DEPTH_INPUT_Y_OFFSET
#define RESHADE_DEPTH_INPUT_Y_OFFSET 0
#endif
#ifndef RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET
#define RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET 0
#endif
// An offset to add to the X coordinate, (+) = move right, (-) = move left
#ifndef RESHADE_DEPTH_INPUT_X_OFFSET
#define RESHADE_DEPTH_INPUT_X_OFFSET 0
#endif
#ifndef RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET
#define RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET 0
#endif
#define BUFFER_PIXEL_SIZE float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT)
#define BUFFER_SCREEN_SIZE float2(BUFFER_WIDTH, BUFFER_HEIGHT)
#define BUFFER_ASPECT_RATIO (BUFFER_WIDTH * BUFFER_RCP_HEIGHT)
namespace ReShade
{
#if defined(__RESHADE_FXC__)
float GetAspectRatio() { return BUFFER_WIDTH * BUFFER_RCP_HEIGHT; }
float2 GetPixelSize() { return float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT); }
float2 GetScreenSize() { return float2(BUFFER_WIDTH, BUFFER_HEIGHT); }
#define AspectRatio GetAspectRatio()
#define PixelSize GetPixelSize()
#define ScreenSize GetScreenSize()
#else
// These are deprecated and will be removed eventually.
static const float AspectRatio = BUFFER_WIDTH * BUFFER_RCP_HEIGHT;
static const float2 PixelSize = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
static const float2 ScreenSize = float2(BUFFER_WIDTH, BUFFER_HEIGHT);
#endif
// Global textures and samplers
texture BackBufferTex : COLOR;
texture DepthBufferTex : DEPTH;
sampler BackBuffer { Texture = BackBufferTex; };
sampler DepthBuffer { Texture = DepthBufferTex; };
// Helper functions
float GetLinearizedDepth(float2 texcoord)
{
#if RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN
texcoord.y = 1.0 - texcoord.y;
#endif
#if RESHADE_DEPTH_INPUT_IS_MIRRORED
texcoord.x = 1.0 - texcoord.x;
#endif
texcoord.x /= RESHADE_DEPTH_INPUT_X_SCALE;
texcoord.y /= RESHADE_DEPTH_INPUT_Y_SCALE;
#if RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET
texcoord.x -= RESHADE_DEPTH_INPUT_X_PIXEL_OFFSET * BUFFER_RCP_WIDTH;
#else // Do not check RESHADE_DEPTH_INPUT_X_OFFSET, since it may be a decimal number, which the preprocessor cannot handle
texcoord.x -= RESHADE_DEPTH_INPUT_X_OFFSET / 2.000000001;
#endif
#if RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET
texcoord.y += RESHADE_DEPTH_INPUT_Y_PIXEL_OFFSET * BUFFER_RCP_HEIGHT;
#else
texcoord.y += RESHADE_DEPTH_INPUT_Y_OFFSET / 2.000000001;
#endif
float depth = tex2Dlod(DepthBuffer, float4(texcoord, 0, 0)).x * RESHADE_DEPTH_MULTIPLIER;
#if RESHADE_DEPTH_INPUT_IS_LOGARITHMIC
const float C = 0.01;
depth = (exp(depth * log(C + 1.0)) - 1.0) / C;
#endif
#if RESHADE_DEPTH_INPUT_IS_REVERSED
depth = 1.0 - depth;
#endif
const float N = 1.0;
depth /= RESHADE_DEPTH_LINEARIZATION_FAR_PLANE - depth * (RESHADE_DEPTH_LINEARIZATION_FAR_PLANE - N);
return depth;
}
}
// Vertex shader generating a triangle covering the entire screen
// See also https://www.reddit.com/r/gamedev/comments/2j17wk/a_slightly_faster_bufferless_vertex_shader_trick/
void PostProcessVS(in uint id : SV_VertexID, out float4 position : SV_Position, out float2 texcoord : TEXCOORD)
{
texcoord.x = (id == 2) ? 2.0 : 0.0;
texcoord.y = (id == 1) ? 2.0 : 0.0;
position = float4(texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
@@ -0,0 +1,214 @@
#pragma once
#if !defined(__RESHADE__) || __RESHADE__ < 30000
#error "ReShade 3.0+ is required to use this header file"
#endif
#define RESHADE_VERSION(major,minor,build) (10000 * (major) + 100 * (minor) + (build))
#define SUPPORTED_VERSION(major,minor,build) (__RESHADE__ >= RESHADE_VERSION(major,minor,build))
// >= 3.0.0
// Commit current in-game user interface status
// https://github.com/crosire/reshade/commit/302bacc49ae394faedc2e29a296c1cebf6da6bb2#diff-82cf230afdb2a0d5174111e6f17548a5R1183
// Added various GUI related uniform variable annotations
// https://reshade.me/forum/releases/2341-3-0
#define __UNIFORM_INPUT_ANY ui_type = "input";
#define __UNIFORM_INPUT_BOOL1 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_BOOL2 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_BOOL3 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_BOOL4 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_INT1 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_INT2 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_INT3 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_INT4 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_FLOAT1 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_FLOAT2 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_FLOAT3 __UNIFORM_INPUT_ANY
#define __UNIFORM_INPUT_FLOAT4 __UNIFORM_INPUT_ANY
// >= 4.0.1
// Change slider widget to be used with new "slider" instead of a "drag" type annotation
// https://github.com/crosire/reshade/commit/746229f31cd6f311a3e72a543e4f1f23faa23f11#diff-59405a313bd8cbfb0ca6dd633230e504R1701
// Changed slider widget to be used with < ui_type = "slider"; > instead of < ui_type = "drag"; >
// https://reshade.me/forum/releases/4772-4-0
#if SUPPORTED_VERSION(4,0,1)
#define __UNIFORM_DRAG_ANY ui_type = "drag";
// >= 4.0.0
// Rework statistics tab and add drag widgets back
// https://github.com/crosire/reshade/commit/1b2c38795f00efd66c007da1f483f1441b230309
// Changed drag widget to a slider widget (old one is still available via < ui_type = "drag2"; >)
// https://reshade.me/forum/releases/4772-4-0
#elif SUPPORTED_VERSION(4,0,0)
#define __UNIFORM_DRAG_ANY ui_type = "drag2";
// >= 3.0.0
// Commit current in-game user interface status
// https://github.com/crosire/reshade/commit/302bacc49ae394faedc2e29a296c1cebf6da6bb2#diff-82cf230afdb2a0d5174111e6f17548a5R1187
// Added various GUI related uniform variable annotations
// https://reshade.me/forum/releases/2341-3-0
#else
#define __UNIFORM_DRAG_ANY ui_type = "drag";
#endif
#define __UNIFORM_DRAG_BOOL1 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_BOOL2 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_BOOL3 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_BOOL4 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_INT1 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_INT2 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_INT3 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_INT4 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_FLOAT1 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_FLOAT2 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_FLOAT3 __UNIFORM_DRAG_ANY
#define __UNIFORM_DRAG_FLOAT4 __UNIFORM_DRAG_ANY
// >= 4.0.1
// Change slider widget to be used with new "slider" instead of a "drag" type annotation
// https://github.com/crosire/reshade/commit/746229f31cd6f311a3e72a543e4f1f23faa23f11#diff-59405a313bd8cbfb0ca6dd633230e504R1699
// Changed slider widget to be used with < ui_type = "slider"; > instead of < ui_type = "drag"; >
// https://reshade.me/forum/releases/4772-4-0
#if SUPPORTED_VERSION(4,0,1)
#define __UNIFORM_SLIDER_ANY ui_type = "slider";
// >= 4.0.0
// Rework statistics tab and add drag widgets back
// https://github.com/crosire/reshade/commit/1b2c38795f00efd66c007da1f483f1441b230309
// Changed drag widget to a slider widget (old one is still available via < ui_type = "drag2"; >)
// https://reshade.me/forum/releases/4772-4-0
#elif SUPPORTED_VERSION(4,0,0)
#define __UNIFORM_SLIDER_ANY ui_type = "drag";
#else
#define __UNIFORM_SLIDER_ANY __UNIFORM_DRAG_ANY
#endif
#define __UNIFORM_SLIDER_BOOL1 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_BOOL2 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_BOOL3 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_BOOL4 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_INT1 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_INT2 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_INT3 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_INT4 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_FLOAT1 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_FLOAT2 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_FLOAT3 __UNIFORM_SLIDER_ANY
#define __UNIFORM_SLIDER_FLOAT4 __UNIFORM_SLIDER_ANY
// >= 3.0.0
// Add combo box display type for uniform variables and fix displaying of integer variable under Direct3D 9
// https://github.com/crosire/reshade/commit/b025bfae5f7343509ec0cacf6df0cff537c499f2#diff-82cf230afdb2a0d5174111e6f17548a5R1631
// Added various GUI related uniform variable annotations
// https://reshade.me/forum/releases/2341-3-0
#define __UNIFORM_COMBO_ANY ui_type = "combo";
// __UNIFORM_COMBO_BOOL1
#define __UNIFORM_COMBO_BOOL2 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_BOOL3 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_BOOL4 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_INT1 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_INT2 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_INT3 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_INT4 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_FLOAT1 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_FLOAT2 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_FLOAT3 __UNIFORM_COMBO_ANY
#define __UNIFORM_COMBO_FLOAT4 __UNIFORM_COMBO_ANY
// >= 4.0.0
// Add option to display boolean values as combo box instead of checkbox
// https://github.com/crosire/reshade/commit/aecb757c864c9679e77edd6f85a1521c49e489c1#diff-59405a313bd8cbfb0ca6dd633230e504R1147
// https://github.com/crosire/reshade/blob/v4.0.0/source/gui.cpp
// Added option to display boolean values as combo box instead of checkbox (via < ui_type = "combo"; >)
// https://reshade.me/forum/releases/4772-4-0
#define __UNIFORM_COMBO_BOOL1 __UNIFORM_COMBO_ANY
// >= 4.0.0
// Cleanup GUI code and rearrange some widgets
// https://github.com/crosire/reshade/commit/6751f7bd50ea7c0556cf0670f10a4b4ba912ee7d#diff-59405a313bd8cbfb0ca6dd633230e504R1711
// Added radio button widget (via < ui_type = "radio"; ui_items = "Button 1\0Button 2\0...\0"; >)
// https://reshade.me/forum/releases/4772-4-0
#if SUPPORTED_VERSION(4,0,0)
#define __UNIFORM_RADIO_ANY ui_type = "radio";
#else
#define __UNIFORM_RADIO_ANY __UNIFORM_COMBO_ANY
#endif
#define __UNIFORM_RADIO_BOOL1 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_BOOL2 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_BOOL3 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_BOOL4 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_INT1 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_INT2 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_INT3 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_INT4 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_FLOAT1 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_FLOAT2 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_FLOAT3 __UNIFORM_RADIO_ANY
#define __UNIFORM_RADIO_FLOAT4 __UNIFORM_RADIO_ANY
// >= 4.1.0
// Fix floating point uniforms with unknown "ui_type" not showing up in UI
// https://github.com/crosire/reshade/commit/50e5bf44dfc84bc4220c2b9f19d5f50c7a0fda66#diff-59405a313bd8cbfb0ca6dd633230e504R1788
// Fixed floating point uniforms with unknown "ui_type" not showing up in UI
// https://reshade.me/forum/releases/5021-4-1
#define __UNIFORM_COLOR_ANY ui_type = "color";
// >= 3.0.0
// Move technique list to preset configuration file
// https://github.com/crosire/reshade/blob/84bba3aa934c1ebe4c6419b69dfe1690d9ab9d34/source/runtime.cpp#L1328
// Added various GUI related uniform variable annotations
// https://reshade.me/forum/releases/2341-3-0
#define __UNIFORM_COLOR_BOOL1 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_BOOL2 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_BOOL3 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_BOOL4 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_INT1 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_INT2 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_INT3 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_INT4 __UNIFORM_COLOR_ANY
// __UNIFORM_COLOR_FLOAT1
#define __UNIFORM_COLOR_FLOAT2 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_FLOAT3 __UNIFORM_COLOR_ANY
#define __UNIFORM_COLOR_FLOAT4 __UNIFORM_COLOR_ANY
// >= 4.2.0
// Add alpha slider widget for single component uniform variables (#86)
// https://github.com/crosire/reshade/commit/87a740a8e3c4dcda1dd4eeec8d5cff7fa35fe829#diff-59405a313bd8cbfb0ca6dd633230e504R1820
// Added alpha slider widget for single component uniform variables
// https://reshade.me/forum/releases/5150-4-2
#if SUPPORTED_VERSION(4,2,0)
#define __UNIFORM_COLOR_FLOAT1 __UNIFORM_COLOR_ANY
#else
#define __UNIFORM_COLOR_FLOAT1 __UNIFORM_SLIDER_ANY
#endif
// >= 4.3.0
// Add new "list" GUI widget (#103)
// https://github.com/crosire/reshade/commit/515287d20ce615c19cf3d4c21b49f83896f04ddc#diff-59405a313bd8cbfb0ca6dd633230e504R1894
// Added new "list" GUI widget
// https://reshade.me/forum/releases/5417-4-3
#if SUPPORTED_VERSION(4,3,0)
#define __UNIFORM_LIST_ANY ui_type = "list";
#else
#define __UNIFORM_LIST_ANY __UNIFORM_COMBO_ANY
#endif
// __UNIFORM_LIST_BOOL1
#define __UNIFORM_LIST_BOOL2 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_BOOL3 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_BOOL4 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_INT1 __UNIFORM_LIST_ANY // >= 4.3.0
#define __UNIFORM_LIST_INT2 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_INT3 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_INT4 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_FLOAT1 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_FLOAT2 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_FLOAT3 __UNIFORM_LIST_ANY
#define __UNIFORM_LIST_FLOAT4 __UNIFORM_LIST_ANY
// For compatible with 'combo'
#define __UNIFORM_LIST_BOOL1 __UNIFORM_COMBO_ANY
@@ -0,0 +1,73 @@
////////////////////////////////////////////////////////////////////////////////
// Triangular Dither //
// By The Sandvich Maker //
// Ported to ReShade by TreyM //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// //
// Usage: //
// Include this file in your shader like so: #include "TriDither.fx" //
// //
// For shader developers, use this syntax to do a function call in your //
// code as the last thing before exiting a given shader. You should dither //
// anytime data is going to be truncated to a lower bitdepth. Color input //
// must be a float3 value. //
// //
// input.rgb += TriDither(input.rgb, uv, bits); //
// //
// "bits" is an integer number that determines the bit depth //
// being dithered to. Usually 8, sometimes 10 //
// You can automate this by letting Reshade decide like so: //
// //
// input += TriDither(input, uv, BUFFER_COLOR_BIT_DEPTH); //
// //
// Manual setup looks something like this for an 8-bit backbuffer: //
// //
// input.rgb += TriDither(input.rgb, uv, 8); //
// //
////////////////////////////////////////////////////////////////////////////////
uniform float DitherTimer < source = "timer"; >;
#define remap(v, a, b) (((v) - (a)) / ((b) - (a)))
float rand21(float2 uv)
{
float2 noise = frac(sin(dot(uv, float2(12.9898, 78.233) * 2.0)) * 43758.5453);
return (noise.x + noise.y) * 0.5;
}
float rand11(float x)
{
return frac(x * 0.024390243);
}
float permute(float x)
{
return ((34.0 * x + 1.0) * x) % 289.0;
}
float3 TriDither(float3 color, float2 uv, int bits)
{
float bitstep = exp2(bits) - 1.0;
float lsb = 1.0 / bitstep;
float lobit = 0.5 / bitstep;
float hibit = (bitstep - 0.5) / bitstep;
float3 m = float3(uv, rand21(uv + (DitherTimer * 0.001))) + 1.0;
float h = permute(permute(permute(m.x) + m.y) + m.z);
float3 noise1, noise2;
noise1.x = rand11(h); h = permute(h);
noise2.x = rand11(h); h = permute(h);
noise1.y = rand11(h); h = permute(h);
noise2.y = rand11(h); h = permute(h);
noise1.z = rand11(h); h = permute(h);
noise2.z = rand11(h);
float3 lo = saturate(remap(color.xyz, 0.0, lobit));
float3 hi = saturate(remap(color.xyz, 1.0, hibit));
float3 uni = noise1 - 0.5;
float3 tri = noise1 - noise2;
return lerp(uni, tri, min(lo, hi)) * lsb;
}
@@ -0,0 +1,289 @@
/*
Simple UIMask shader by luluco250
I have no idea why this was never ported back to ReShade 3.0 from 2.0,
but if you missed it, here it is.
It doesn't feature the auto mask from the original shader.
It does feature a new multi-channnel masking feature. UI masks can now contain
separate 'modes' within each of the three color channels.
For example, you can have the regular hud on the red channel (the default one),
a mask for an inventory screen on the green channel and a mask for a quest menu
on the blue channel. You can then use keyboard keys to toggle each channel on or off.
Multiple channels can be active at once, they'll just add up to mask the image.
Simple/legacy masks are not affected by this, they'll work just as you'd expect,
so you can still make simple black and white masks that use all color channels, it'll
be no different than just having it on a single channel.
Tips:
--You can adjust how much it will affect your HUD by changing "Mask Intensity".
--You don't actually need to place the UIMask_Bottom technique at the bottom of
your shader pipeline, if you have any effects that don't necessarily affect
the visibility of the HUD you can place it before that.
For instance, if you use color correction shaders like LUT, you might want
to place UIMask_Bottom just before that.
--Preprocessor flags:
--UIMASK_MULTICHANNEL:
Enables having up to three different masks on each color channel.
--Refer to this page for keycodes:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
--To make a custom mask:
1-Take a screenshot of your game with the HUD enabled,
preferrably with any effects disabled for maximum visibility.
2-Open the screenshot with your preferred image editor program, I use GIMP.
3-Make a background white layer if there isn't one already.
Be sure to leave it behind your actual screenshot for the while.
4-Make an empty layer for the mask itself, you can call it "mask".
5-Having selected the mask layer, paint the places where HUD constantly is,
such as health bars, important messages, minimaps etc.
6-Delete or make your screenshot layer invisible.
7-Before saving your mask, let's do some gaussian blurring to improve it's look and feel:
For every step of blurring you want to do, make a new layer, such as:
Mask - Blur16x16
Mask - Blur8x8
Mask - Blur4x4
Mask - Blur2x2
Mask - NoBlur
You should use your image editor's default gaussian blurring filter, if there is one.
This avoids possible artifacts and makes the mask blend more easily on the eyes.
You may not need this if your mask is accurate enough and/or the HUD is simple enough.
8-Now save the final image with a unique name such as "MyUIMask.png" in your textures folder.
9-Set the preprocessor definition UIMASK_TEXTURE to the unique name of your image, with quotes.
You're done!
MIT Licensed:
Copyright (c) 2017 Lucas Melo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
//#region Preprocessor
#include "ReShade.fxh"
#include "ReShadeUI.fxh"
#ifndef UIMASK_MULTICHANNEL
#define UIMASK_MULTICHANNEL 0
#endif
#if !UIMASK_MULTICHANNEL
#define TEXFORMAT R8
#else
#define TEXFORMAT RGBA8
#endif
#ifndef UIMASK_TEXTURE
#define UIMASK_TEXTURE "UIMask.png"
#endif
//#endregion
namespace UIMask
{
//#region Uniforms
uniform int _Help
<
ui_label = " ";
ui_text =
"For more detailed instructions, see the text at the top of this "
"effect's shader file (UIMask.fx).\n"
"\n"
"Available preprocessor definitions:\n"
" UIMASK_MULTICHANNEL:\n"
" If set to 1, each of the RGB color channels in the texture is "
"treated as a separate mask.\n"
"\n"
"How to create a mask:\n"
"\n"
"1. Take a screenshot with the game's UI appearing.\n"
"2. Open the screenshot in an image editor, GIMP or Photoshop are "
"recommended.\n"
"3. Create a new layer over the screenshot layer, fill it with black.\n"
"4. Reduce the layer opacity so you can see the screenshot layer "
"below.\n"
"5. Cover the UI with white to mask it from effects. The stronger the "
"mask white color, the more opaque the mask will be.\n"
"6. Set the mask layer opacity back to 100%.\n"
"7. Save the image in one of your texture folders, making sure to "
"use a unique name such as: \"MyUIMask.png\"\n"
"8. Set the preprocessor definition UIMASK_TEXTURE to the name of "
"your image, with quotes: \"MyUIMask.png\"\n"
;
ui_category = "Help";
ui_category_closed = true;
ui_type = "radio";
>;
uniform float fMask_Intensity
<
__UNIFORM_SLIDER_FLOAT1
ui_label = "Mask Intensity";
ui_tooltip =
"How much to mask effects from affecting the original image.\n"
"\nDefault: 1.0";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 1.0;
uniform bool bDisplayMask <
ui_label = "Display Mask";
ui_tooltip =
"Display the mask texture.\n"
"Useful for testing multiple channels or simply the mask itself.\n"
"\nDefault: Off";
> = false;
#if UIMASK_MULTICHANNEL
uniform bool bToggleRed <
ui_label = "Toggle Red Channel";
ui_tooltip = "Toggle UI masking for the red channel.\n"
"Right click to assign a hotkey.\n"
"\nDefault: On";
> = true;
uniform bool bToggleGreen <
ui_label = "Toggle Green Channel";
ui_tooltip = "Toggle UI masking for the green channel.\n"
"Right click to assign a hotkey."
"\nDefault: On";
> = true;
uniform bool bToggleBlue <
ui_label = "Toggle Blue Channel";
ui_tooltip = "Toggle UI masking for the blue channel.\n"
"Right click to assign a hotkey."
"\nDefault: On";
> = true;
#endif
//#endregion
//#region Textures
texture BackupTex
{
Width = BUFFER_WIDTH;
Height = BUFFER_HEIGHT;
};
sampler Backup
{
Texture = BackupTex;
};
texture MaskTex <source=UIMASK_TEXTURE;>
{
Width = BUFFER_WIDTH;
Height = BUFFER_HEIGHT;
Format = TEXFORMAT;
};
sampler Mask
{
Texture = MaskTex;
};
//#endregion
//#region Shaders
float4 BackupPS(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
return tex2D(ReShade::BackBuffer, uv);
}
float4 MainPS(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target {
float4 color = tex2D(ReShade::BackBuffer, uv);
float4 backup = tex2D(Backup, uv);
#if !UIMASK_MULTICHANNEL
float mask = tex2D(Mask, uv).r;
#else
float3 mask_rgb = tex2D(Mask, uv).rgb;
// This just works, it basically adds masking with each channel that has
// been toggled.
float mask = saturate(
1.0 - dot(1.0 - mask_rgb,
float3(bToggleRed, bToggleGreen, bToggleBlue)));
#endif
color = lerp(color, backup, mask * fMask_Intensity);
color = bDisplayMask ? mask : color;
return color;
}
//#endregion
//#region Techniques
technique UIMask_Top
<
ui_tooltip = "Place this *above* the effects to be masked.";
>
{
pass
{
VertexShader = PostProcessVS;
PixelShader = BackupPS;
RenderTarget = BackupTex;
}
}
technique UIMask_Bottom
<
ui_tooltip =
"Place this *below* the effects to be masked.\n"
"If you want to add a toggle key for the effect, set it to this one.";
>
{
pass
{
VertexShader = PostProcessVS;
PixelShader = MainPS;
}
}
//#endregion
} // Namespace.
@@ -0,0 +1,252 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_ColorManagement.fxh
Version : 2026.05.05
Author : Afzaal (Kaidō)
Description: Provides color management including color space detection,
color space transfers and tonemapping.
Supported colorbuffers:
- SDR (sRGB)
- HDR (scRGB / Linear)
- HDR (PQ / ST.2084)
- HDR (HLG)
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
#pragma once
/*-------------------.
| :: PREPROCESSOR :: |
'-------------------*/
#ifndef HDR_WHITELEVEL
#define HDR_WHITELEVEL 203
#endif
#if BUFFER_COLOR_SPACE > 0
//already defined by ReShade
#else
#if BUFFER_COLOR_BIT_DEPTH == 8
#undef BUFFER_COLOR_SPACE
#define BUFFER_COLOR_SPACE 1 //sRGB
#elif BUFFER_COLOR_BIT_DEPTH == 16
#undef BUFFER_COLOR_SPACE
#define BUFFER_COLOR_SPACE 2 //scRGB
#elif __RENDERER__ < 0xb000
#undef BUFFER_COLOR_SPACE
#define BUFFER_COLOR_SPACE 1 //D3D9/10 usually SDR
#endif
#endif
/*------------------.
| :: UI UNIFORMS :: |
'------------------*/
// uniform int SHOW_COLOR_SPACE <
// ui_category = "Color Management";
// ui_type = "combo";
// ui_label = "Colorspace";
// ui_tooltip = "Shows the detected color space.\n1=sRGB, 2=scRGB, 3=PQ, 4=HLG";
// hidden = true;
// #if BUFFER_COLOR_SPACE == 1
// ui_items = "sRGB (Detected)\0";
// #elif BUFFER_COLOR_SPACE == 2
// ui_items = "scRGB (Detected)\0";
// #elif BUFFER_COLOR_SPACE == 3
// ui_items = "PQ / ST.2084 (Detected)\0";
// #elif BUFFER_COLOR_SPACE == 4
// ui_items = "HLG (Detected)\0";
// #else
// ui_items = "Unknown (Defaulting to sRGB)\0";
// #endif
// > = 0;
#if BUFFER_COLOR_BIT_DEPTH > 8 || BUFFER_COLOR_SPACE > 1
#define COLORSPACE_CONVERSION 1 //use approx. transfer function; 0 for accurate
#else
#define COLORSPACE_CONVERSION 2 //N/A for 8-bit
#endif
#if BUFFER_COLOR_SPACE == 1
#define TONEMAPPER 1 //reinhard tonemapper workflow for SDR (sRGB) colorbuffer; 0 for None
#else
#define TONEMAPPER 0
#endif
/*-------------------------.
| :: TRANSFER FUNCTIONS :: |
'-------------------------*/
//sRGB
float3 sRGBtoLinearAccurate(float3 r) {
return (r <= 0.04045) ? (r / 12.92) : pow(abs(r + 0.055) / 1.055, 2.4);
}
float3 sRGBtoLinearFast(float3 r) {
return max(r / 12.92, r * r); //gamma 2.0 approx
}
float3 sRGBtoLinear(float3 r) {
if (COLORSPACE_CONVERSION == 1) return sRGBtoLinearFast(r);
else return sRGBtoLinearAccurate(r);
}
float3 linearToSRGBAccurate(float3 r) {
return (r <= 0.0031308) ? (r * 12.92) : (1.055 * pow(abs(r), 1.0 / 2.4) - 0.055);
}
float3 linearToSRGBFast(float3 r) {
return min(r * 12.92, sqrt(r)); //gamma 2.0 approx
}
float3 linearToSRGB(float3 r) {
if (COLORSPACE_CONVERSION == 1) return linearToSRGBFast(r);
else return linearToSRGBAccurate(r);
}
//PQ (ST.2084)
float3 PQtoLinearAccurate(float3 r) {
const float m1 = 1305.0/8192.0;
const float m2 = 2523.0/32.0;
const float c1 = 107.0/128.0;
const float c2 = 2413.0/128.0;
const float c3 = 2392.0/128.0;
float3 powr = pow(max(r, 0), 1.0/m2);
r = pow(max(max(powr - c1, 0) / (c2 - c3 * powr), 0), 1.0/m1);
//scale 10,000 nits down so Paper White (HDR_WHITELEVEL) maps to 1.0
return r * 10000.0 / HDR_WHITELEVEL;
}
float3 PQtoLinearFast(float3 r) {
float3 square = r * r;
float3 quad = square * square;
float3 oct = quad * quad;
r = max(max(square / 340.0, quad / 6.0), oct);
return r * 10000.0 / HDR_WHITELEVEL;
}
float3 PQtoLinear(float3 r) {
if (COLORSPACE_CONVERSION == 1) return PQtoLinearFast(r);
else return PQtoLinearAccurate(r);
}
float3 linearToPQAccurate(float3 r) {
const float m1 = 1305.0/8192.0;
const float m2 = 2523.0/32.0;
const float c1 = 107.0/128.0;
const float c2 = 2413.0/128.0;
const float c3 = 2392.0/128.0;
r = r * (HDR_WHITELEVEL / 10000.0); //rescale 1.0 back to nits
float3 powr = pow(max(r, 0), m1);
r = pow(max((c1 + c2 * powr) / (1 + c3 * powr), 0), m2);
return r;
}
float3 linearToPQFast(float3 r) {
r = r * (HDR_WHITELEVEL / 10000.0);
float3 squareroot = sqrt(r);
float3 quadroot = sqrt(squareroot);
float3 octroot = sqrt(quadroot);
r = min(octroot, min(sqrt(sqrt(6.0))*quadroot, sqrt(340.0)*squareroot));
return r;
}
float3 linearToPQ(float3 r) {
if (COLORSPACE_CONVERSION == 1) return linearToPQFast(r);
else return linearToPQAccurate(r);
}
//HLG (Hybrid Log Gamma)
float3 linearToHLG(float3 r) {
r = r * HDR_WHITELEVEL / 1000.0;
const float a = 0.17883277;
const float b = 0.28466892;
const float c = 0.55991073;
float3 s = sqrt(3 * r);
return (s < 0.5) ? s : (log(12 * r - b) * a + c);
}
float3 HLGtoLinear(float3 r) {
const float a = 0.17883277;
const float b = 0.28466892;
const float c = 0.55991073;
r = (r < 0.5) ? (r * r / 3.0) : ((exp((r - c) / a) + b) / 12.0);
return r * 1000.0 / HDR_WHITELEVEL;
}
//YCoCg
float3 linearToYCoCg(float3 r) {
float y = (r.r + 2.0 * r.g + r.b) * 0.25;
float co = (r.r - r.b) * 0.5;
float cg = (r.g - (r.r + r.b) * 0.5) * 0.5;
return float3(y, co, cg);
}
float3 YCoCgToLinear(float3 r) {
float y = r.x;
float co = r.y;
float cg = r.z;
float g = y + cg;
float rOut = y + co - cg;
float b = y - co - cg;
return float3(rOut, g, b);
}
/*--------------.
| :: HELPERS :: |
'--------------*/
float3 ToLinearColorspace(float3 r, bool tonemap) {
if (BUFFER_COLOR_SPACE == 2) r = r * (80.0 / HDR_WHITELEVEL); //scRGB
else if (BUFFER_COLOR_SPACE == 3) r = PQtoLinear(r);
else if (BUFFER_COLOR_SPACE == 4) r = HLGtoLinear(r);
else {
r = sRGBtoLinear(r);
if (TONEMAPPER == 1 && tonemap) r = r / max(1.0 - r, 0.001); //inverse reinhard
}
return r;
}
float3 ToOutputColorspace(float3 r, bool tonemap) {
if (BUFFER_COLOR_SPACE == 2) r = r * (HDR_WHITELEVEL / 80.0); //scRGB
else if (BUFFER_COLOR_SPACE == 3) r = linearToPQ(r);
else if (BUFFER_COLOR_SPACE == 4) r = linearToHLG(r);
else {
if (TONEMAPPER == 1 && tonemap) r = r / (1.0 + r); //forward reinhard
r = linearToSRGB(r);
}
return r;
}
//read the theoretical max value of the buffer (in linear scale)
float GetMaxColorValue() {
if (BUFFER_COLOR_SPACE == 4) return 1000.0 / HDR_WHITELEVEL;
if (BUFFER_COLOR_SPACE >= 2) return 10000.0 / HDR_WHITELEVEL;
return 1.0;
}
float GetLuminance(float3 color)
{
return dot(color, float3(0.2126, 0.7152, 0.0722));
}
float3 GetLinearColor(float2 uv, bool tonemap)
{
float3 color = tex2Dlod(ReShade::BackBuffer, float4(uv, 0, 0)).rgb;
return ToLinearColorspace(color, tonemap);
}
@@ -0,0 +1,54 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_Compute.fxh
Version : 2026.05.09
Author : Afzaal (Kaidō)
Description: Header file for supporting compute enabled platforms.
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
#pragma once
#include "ReShade.fxh"
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define D3D9 0x9000
#define D3D10 0xa000
#define D3D11 0xb000
#define D3D12 0xc000
#define OPENGL 0x10000
#define VULKAN 0x20000
#if __RENDERER__ >= D3D11
#define _COMPUTE_ENABLED_ 1
#else
#define _COMPUTE_ENABLED_ 0
#endif
struct CSInput
{
uint3 dispatchID : SV_DispatchThreadID; //global pixel coord (x, y, 0)
uint3 groupID : SV_GroupID; //which tile/group in grid
uint3 localID : SV_GroupThreadID; //thread inside group [0..CS_W-1]
uint flatIndex : SV_GroupIndex; //localID flattened: y*CS_W + x
};
@@ -0,0 +1,94 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_Helpers.fxh
Version : 2026.05.30
Author : Afzaal (Kaidō)
Description: Helper functions for Lumenite shaders.
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
#pragma once
#include "ReShade.fxh"
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define PI 3.14159265359
#define EPSILON 1e-6
//R2 sequence constants
static const float PHI_2 = 1.324717957244746;
static const float2 R2_CONSTANT = float2(1.0/PHI_2, 1.0/(PHI_2*PHI_2));
/*--------------.
| :: UNIFORMS ::|
'--------------*/
uniform float TIMER < source = "timer"; >; //ms since launch
uniform float FRAME_TIME < source = "frametime"; >; //ms last frame
uniform uint FRAME_COUNT < source = "framecount"; >;
uniform float2 MOUSE_POS < source = "mousepoint"; >; //in screen px
uniform bool MOUSE_DOWN < source = "mousebutton"; min = 0; max = 0; >;
/*--------------.
| :: HELPERS :: |
'--------------*/
bool CheckerboardSkip(uint2 currentPos, float scale)
{
//map current buffer pixel to full screen pixel.
//floor() to ensure we snap to the integer grid of the full screen
uint2 fullScreenPos = uint2(floor(currentPos.x * scale), floor(currentPos.y * scale));
return (((fullScreenPos.x + fullScreenPos.y + (FRAME_COUNT & 1)) & 1) == 1);
}
float GetDepth(float2 uv)
{
return ReShade::GetLinearizedDepth(uv);
}
bool IsOOB(float2 uv) {
return any(uv < 0.0) || any(uv > 1.0);
}
//QUASI-MONTE CARLO SEQUENCE
//fast Hilbert curve math (a 1D index from 2D coords)
uint HilbertIndex(uint x, uint y) {
uint index = 0;
[unroll] for (uint s = 64 / 2; s > 0; s /= 2) {
uint rx = (x & s) > 0;
uint ry = (y & s) > 0;
index += s * s * ((3 * rx) ^ ry);
if (ry == 0) {
if (rx == 1) {
x = 64 - 1 - x;
y = 64 - 1 - y;
}
uint t = x; x = y; y = t;
}
}
return index;
}
float2 GetStratifiedNoise(float2 vpos) {
uint2 screenPos = uint2(vpos.xy) % 64; //64x64 tiled pixel coords
uint hIndex = HilbertIndex(screenPos.x, screenPos.y); //Hilbert index (spatial)
uint totalIndex = hIndex + (uint(FRAME_COUNT % 64) * 288); //temporal offset: 288 (same as Intel XeGTAO implementation)
return frac(float(totalIndex) * R2_CONSTANT);
}
@@ -0,0 +1,96 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_Projections.fxh
Version : 2026.04.11
Author : Afzaal (Kaidō)
Description: Camera projection functions for Lumenite shaders.
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
#pragma once
#include "ReShade.fxh"
/*--------------.
| :: HELPERS :: |
'--------------*/
//VERTEX SHADER
struct VSOUT
{
float4 vpos : SV_Position;
float2 uv : TEXCOORD0;
float tan_half_fov_x : TEXCOORD1;
float tan_half_fov_y : TEXCOORD2;
float inv_tan_half_fov_x : TEXCOORD3;
float inv_tan_half_fov_y : TEXCOORD4;
float near_ratio : TEXCOORD5;
float diff_ratio : TEXCOORD6;
};
#define TAN_HALF_FOV_Y tan(radians(FOV * 0.5))
#define ASPECT_RATIO_X_OVER_Y ((float)BUFFER_WIDTH / (float)BUFFER_HEIGHT)
#define TAN_HALF_FOV_X TAN_HALF_FOV_Y * ASPECT_RATIO_X_OVER_Y
#define INV_TAN_HALF_FOV_X rcp(TAN_HALF_FOV_X)
#define INV_TAN_HALF_FOV_Y rcp(TAN_HALF_FOV_Y)
VSOUT VS(uint id : SV_VertexID)
{
VSOUT o;
o.uv.x = (id == 2) ? 2.0 : 0.0;
o.uv.y = (id == 1) ? 2.0 : 0.0;
o.vpos = float4(mad(o.uv.x, 2.0, -1.0), mad(o.uv.y, -2.0, 1.0), 0.0, 1.0);
o.tan_half_fov_x = TAN_HALF_FOV_X;
o.tan_half_fov_y = TAN_HALF_FOV_Y;
o.inv_tan_half_fov_x = INV_TAN_HALF_FOV_X;
o.inv_tan_half_fov_y = INV_TAN_HALF_FOV_Y;
o.near_ratio = NEAR_PLANE / RESHADE_DEPTH_LINEARIZATION_FAR_PLANE;
o.diff_ratio = 1.0 - o.near_ratio; //lerp(a,b,t) = (a+t * (b-a)), precompute (b-a) or (1.0-near_ratio) here
return o;
}
//PROJECTION FUNCTIONS
//normalized frustum
//left-handed viewspace
//normals point outwards
//Z+ goes into the screen
float3 UVToViewSpace(float2 uv, float linear_depth_vs, VSOUT ps_input)
{
float projection_scale = mad(linear_depth_vs, ps_input.diff_ratio, ps_input.near_ratio); //faster lerp: a+t * diff
float3 view_pos;
float ndc_x = mad(uv.x, 2.0, -1.0);
float ndc_y = mad(uv.y, -2.0, 1.0);
view_pos.x = ndc_x * ps_input.tan_half_fov_x * projection_scale;
view_pos.y = ndc_y * ps_input.tan_half_fov_y * projection_scale;
view_pos.z = linear_depth_vs;
return view_pos;
}
float2 ViewSpaceToUV(float3 view_pos, VSOUT ps_input)
{
float inv_projection_scale = rcp(mad(view_pos.z, ps_input.diff_ratio, ps_input.near_ratio));
float2 ndc;
ndc.x = view_pos.x * ps_input.inv_tan_half_fov_x * inv_projection_scale;
ndc.y = view_pos.y * ps_input.inv_tan_half_fov_y * inv_projection_scale;
float2 uv;
uv.x = mad(ndc.x, 0.5, 0.5);
uv.y = mad(ndc.y, -0.5, 0.5);
return uv;
}
@@ -0,0 +1,511 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_AnamorphicBloom.fx
Version : 2026.06.09
Author : Afzaal (Kaidō)
Description: Artistic bloom approximating the Anamorphic lens aesthetic.
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#ifndef ANAMORPHIC_BLOOM
#define ANAMORPHIC_BLOOM 1
#endif
#ifndef ANAMORPHIC_STREAKS
#define ANAMORPHIC_STREAKS 0
#endif
#ifndef COLOR_FRINGING
#define COLOR_FRINGING 0
#endif
#define BLOOM_THRESHOLD_SCALER 10.0
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
#include "./include/lumenite_ColorManagement.fxh"
#include "./include/lumenite_Helpers.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
#if ANAMORPHIC_BLOOM
uniform bool BLOOM_SKIP_SKYBOX <
ui_type = "radio";
ui_label = "Exclude Skybox (Bloom)";
ui_tooltip = "Prevents sky pixels from contributing to bloom.";
ui_category = "Anamorphic Bloom";
> = false;
uniform bool BLOOM_SHARP <
ui_type = "radio";
ui_label = "Add More Definition to Bloom Shape (Experimental)";
ui_tooltip = "Enables a sharper 1D horizontal kernel. May flicker with camera movement.";
ui_category = "Anamorphic Bloom";
> = false;
uniform float BLOOM_INTENSITY <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
ui_label = "Bloom Intensity";
ui_tooltip = "Scales the intensity of the Bloom effect.";
ui_category = "Anamorphic Bloom";
> = 1.0;
uniform float BLOOM_THRESHOLD <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
ui_label = "Bloom Threshold";
ui_tooltip = "Higher values bloom more of the scene.";
ui_category = "Anamorphic Bloom";
> = 0.7;
uniform float BLOOM_STRETCH <
ui_type = "drag";
ui_min = 0.0; ui_max = 7.5; ui_step = 0.01;
ui_label = "Bloom Stretch";
ui_tooltip = "Adjusts the horizontal elongation of the Bloom effect.";
ui_category = "Anamorphic Bloom";
> = 7.5;
#if COLOR_FRINGING
uniform float BLOOM_CA <
ui_type = "drag";
ui_min = 0.0; ui_max = 10.0; ui_step = 0.01;
ui_label = "Bloom Chromatic Shift";
ui_tooltip = "Shifts R/B channels within the bloom passes.";
ui_category = "Anamorphic Bloom";
> = 10.0;
#endif
#endif
#if ANAMORPHIC_STREAKS
uniform bool STREAK_SKIP_SKYBOX <
ui_type = "radio";
ui_label = "Exclude Skybox (Streaks)";
ui_tooltip = "Prevents sky pixels from contributing to light streaks.";
ui_category = "Anamorphic Streaks";
> = false;
uniform float STREAK_INTENSITY <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
ui_label = "Streak Intensity";
ui_tooltip = "Scales the intensity of the light streaks.";
ui_category = "Anamorphic Streaks";
> = 1.0;
uniform float STREAK_THRESHOLD <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
ui_label = "Streak Threshold";
ui_tooltip = "Higher values considers more of the scene.";
ui_category = "Anamorphic Streaks";
> = 0.5;
uniform float STREAK_STRETCH <
ui_type = "drag";
ui_min = 0.0; ui_max = 10.0; ui_step = 0.01;
ui_label = "Streak Stretch";
ui_tooltip = "Adjusts the horizontal elongation of the light streaks.";
ui_category = "Anamorphic Streaks";
> = 10.0;
uniform float3 STREAK_TINT <
ui_type = "color";
ui_label = "Tint";
ui_tooltip = "Tints the light streaks with chosen color. Set to white (1, 1, 1) for pass-through.";
ui_category = "Anamorphic Streaks";
> = float3(0.55, 0.55, 1.0);
#if COLOR_FRINGING
uniform float STREAK_CA <
ui_type = "drag";
ui_min = 0.0; ui_max = 10.0; ui_step = 0.01;
ui_label = "Streak Chromatic Shift";
ui_tooltip = "Shifts R/B channels of the light streaks.";
ui_category = "Anamorphic Streaks";
> = 10.0;
#endif
#endif
uniform int USER_GUIDE <
ui_type = "radio";
ui_category = "";
ui_label = " ";
ui_text = "Exclude Skybox: Requires access to properly configured depth buffer.";
>;
namespace LumeniteAnamorphicBloom {
/*-------------.
| :: MACROS :: |
'-------------*/
#if ANAMORPHIC_BLOOM
#define BLOOM_SHIFT (float2(BLOOM_CA * BUFFER_PIXEL_SIZE.x, 0.0)) //once per pass
#if COLOR_FRINGING
#define SAMPLE_BLOOM_TEX(s, uv) float3( \
tex2D(s, (uv) - BLOOM_SHIFT).r, \
tex2D(s, (uv)).g, \
tex2D(s, (uv) + BLOOM_SHIFT).b \
)
#else
#define SAMPLE_BLOOM_TEX(s, uv) tex2D(s, uv).rgb
#endif
#endif
#if ANAMORPHIC_STREAKS
#define STREAK_SHIFT (STREAK_CA * BUFFER_PIXEL_SIZE.x)
#if COLOR_FRINGING
#define SAMPLE_STREAK_TEX(s, uv, o) float3( \
tex2D(s, uv + float2(o - STREAK_SHIFT, 0.0)).r, \
tex2D(s, uv + float2(o, 0.0)).g, \
tex2D(s, uv + float2(o + STREAK_SHIFT, 0.0)).b \
)
#else
#define SAMPLE_STREAK_TEX(s, uv, o) tex2D(s, uv + float2(o, 0.0)).rgb
#endif
#endif
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
texture2D tUnpackedColor { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler2D sUnpackedColor { Texture = tUnpackedColor; };
#if ANAMORPHIC_BLOOM
texture2D tBloomDown0 { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sBloomDown0 { Texture = tBloomDown0; };
texture2D tBloomDown1 { Width = BUFFER_WIDTH/4; Height = BUFFER_HEIGHT/4; Format = RGBA16F; };
sampler2D sBloomDown1 { Texture = tBloomDown1; };
texture2D tBloomDown2 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RGBA16F; };
sampler2D sBloomDown2 { Texture = tBloomDown2; };
texture2D tBloomDown3 { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RGBA16F; };
sampler2D sBloomDown3 { Texture = tBloomDown3; };
texture2D tBloomDown4 { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = RGBA16F; };
sampler2D sBloomDown4 { Texture = tBloomDown4; };
texture2D tBloomUp3 { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RGBA16F; };
sampler2D sBloomUp3 { Texture = tBloomUp3; };
texture2D tBloomUp2 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RGBA16F; };
sampler2D sBloomUp2 { Texture = tBloomUp2; };
texture2D tBloomUp1 { Width = BUFFER_WIDTH/4; Height = BUFFER_HEIGHT/4; Format = RGBA16F; };
sampler2D sBloomUp1 { Texture = tBloomUp1; };
texture2D tBloomUp0 { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sBloomUp0 { Texture = tBloomUp0; };
texture2D tBloomUp4 { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler2D sBloomUp4 { Texture = tBloomUp4; };
#endif
#if ANAMORPHIC_STREAKS
texture2D tStreakDown0 { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakDown0 { Texture = tStreakDown0; };
texture2D tStreakDown1 { Width = BUFFER_WIDTH/4; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakDown1 { Texture = tStreakDown1; };
texture2D tStreakDown2 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakDown2 { Texture = tStreakDown2; };
texture2D tStreakDown3 { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakDown3 { Texture = tStreakDown3; };
texture2D tStreakDown4 { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakDown4 { Texture = tStreakDown4; };
texture2D tStreakUp3 { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakUp3 { Texture = tStreakUp3; };
texture2D tStreakUp2 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakUp2 { Texture = tStreakUp2; };
texture2D tStreakUp1 { Width = BUFFER_WIDTH/4; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakUp1 { Texture = tStreakUp1; };
texture2D tStreakUp0 { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler2D sStreakUp0 { Texture = tStreakUp0; };
#endif
/*--------------.
| :: HELPERS :: |
'--------------*/
#if ANAMORPHIC_BLOOM
float3 TentFilter13Anisotropic(sampler2D src, float2 uv, float2 radius)
{
float dx = radius.x;
float dy = radius.y;
[branch] if (BLOOM_SHARP)
{
float3 center = SAMPLE_BLOOM_TEX(src, uv);
float3 innerLeft = SAMPLE_BLOOM_TEX(src, uv + float2(-dx, 0));
float3 innerRight = SAMPLE_BLOOM_TEX(src, uv + float2( dx, 0));
float3 outerLeft = SAMPLE_BLOOM_TEX(src, uv + float2(-2*dx, 0));
float3 outerRight = SAMPLE_BLOOM_TEX(src, uv + float2( 2*dx, 0));
return center * 0.25 + (innerLeft + innerRight) * 0.25 + (outerLeft + outerRight) * 0.125;
}
float3 a = SAMPLE_BLOOM_TEX(src, uv + float2(-2*dx, 2*dy)).rgb;
float3 b = SAMPLE_BLOOM_TEX(src, uv + float2( 0, 2*dy)).rgb;
float3 c = SAMPLE_BLOOM_TEX(src, uv + float2( 2*dx, 2*dy)).rgb;
float3 d = SAMPLE_BLOOM_TEX(src, uv + float2(-2*dx, 0)).rgb;
float3 e = SAMPLE_BLOOM_TEX(src, uv + float2( 0, 0)).rgb;
float3 f = SAMPLE_BLOOM_TEX(src, uv + float2( 2*dx, 0)).rgb;
float3 g = SAMPLE_BLOOM_TEX(src, uv + float2(-2*dx, -2*dy)).rgb;
float3 h = SAMPLE_BLOOM_TEX(src, uv + float2( 0, -2*dy)).rgb;
float3 i = SAMPLE_BLOOM_TEX(src, uv + float2( 2*dx, -2*dy)).rgb;
float3 j = SAMPLE_BLOOM_TEX(src, uv + float2(-dx, dy)).rgb;
float3 k = SAMPLE_BLOOM_TEX(src, uv + float2( dx, dy)).rgb;
float3 l = SAMPLE_BLOOM_TEX(src, uv + float2(-dx, -dy)).rgb;
float3 m = SAMPLE_BLOOM_TEX(src, uv + float2( dx, -dy)).rgb;
return e * 0.125 + (a + c + g + i) * 0.03125 + (b + d + f + h) * 0.0625 + (j + k + l + m) * 0.125;
}
float3 TentFilter9Anisotropic(sampler2D src, float2 uv, float2 radius)
{
float dx = radius.x;
float dy = radius.y;
[branch] if (BLOOM_SHARP)
{
float3 center = SAMPLE_BLOOM_TEX(src, uv);
float3 left = SAMPLE_BLOOM_TEX(src, uv + float2(-dx, 0));
float3 right = SAMPLE_BLOOM_TEX(src, uv + float2( dx, 0));
return center * 0.5 + (left + right) * 0.25;
}
float3 a = SAMPLE_BLOOM_TEX(src, uv + float2(-dx, dy)).rgb;
float3 b = SAMPLE_BLOOM_TEX(src, uv + float2( 0, dy)).rgb;
float3 c = SAMPLE_BLOOM_TEX(src, uv + float2( dx, dy)).rgb;
float3 d = SAMPLE_BLOOM_TEX(src, uv + float2(-dx, 0)).rgb;
float3 e = SAMPLE_BLOOM_TEX(src, uv + float2( 0, 0)).rgb;
float3 f = SAMPLE_BLOOM_TEX(src, uv + float2( dx, 0)).rgb;
float3 g = SAMPLE_BLOOM_TEX(src, uv + float2(-dx, -dy)).rgb;
float3 h = SAMPLE_BLOOM_TEX(src, uv + float2( 0, -dy)).rgb;
float3 i = SAMPLE_BLOOM_TEX(src, uv + float2( dx, -dy)).rgb;
return (e * 4.0 + (b + d + f + h) * 2.0 + (a + c + g + i)) * 0.0625;
}
#endif
#if ANAMORPHIC_STREAKS
float3 StreakFilter(sampler2D src, float2 uv, float radius)
{
float dx = BUFFER_PIXEL_SIZE.x * radius;
return SAMPLE_STREAK_TEX(src, uv, -dx * 2.0) * 0.1 +
SAMPLE_STREAK_TEX(src, uv, -dx) * 0.25 +
SAMPLE_STREAK_TEX(src, uv, 0.0) * 0.3 +
SAMPLE_STREAK_TEX(src, uv, dx) * 0.25 +
SAMPLE_STREAK_TEX(src, uv, dx * 2.0) * 0.1;
}
#endif
/*--------------.
| :: SHADERS :: |
'--------------*/
float4 PS_StoreUnpackedColor(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return float4(GetLinearColor(uv, false), 1);
}
#if ANAMORPHIC_BLOOM
//downsample with anisotropic blur (13-tap)
float4 PS_BloomDownsample0(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 1.0) * BUFFER_PIXEL_SIZE;
float3 downsample = TentFilter13Anisotropic(sUnpackedColor, uv, radius);
if (BLOOM_SKIP_SKYBOX) downsample *= (GetDepth(uv) < 1.0);
downsample = downsample * smoothstep(0.0, max(1.0 - BLOOM_THRESHOLD, 0.07)*BLOOM_THRESHOLD_SCALER, GetLuminance(downsample));
return float4(downsample, 1);
}
float4 PS_BloomDownsample1(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 1.0) * BUFFER_PIXEL_SIZE * 2.0;
return float4(TentFilter13Anisotropic(sBloomDown0, uv, radius), 1);
}
float4 PS_BloomDownsample2(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 1.0) * BUFFER_PIXEL_SIZE * 4.0;
return float4(TentFilter13Anisotropic(sBloomDown1, uv, radius), 1);
}
float4 PS_BloomDownsample3(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 1.0) * BUFFER_PIXEL_SIZE * 8.0;
return float4(TentFilter13Anisotropic(sBloomDown2, uv, radius), 1);
}
float4 PS_BloomDownsample4(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 1.0) * BUFFER_PIXEL_SIZE * 16.0;
return float4(TentFilter13Anisotropic(sBloomDown3, uv, radius), 1);
}
//upsample with anisotropic blur (9-tap)
float4 PS_BloomUpsample0(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 0.0) * BUFFER_PIXEL_SIZE * 32.0 * float2(1.0, rcp(BUFFER_ASPECT_RATIO));
float3 upsample = TentFilter9Anisotropic(sBloomDown4, uv, radius);
float3 previous = tex2D(sBloomDown3, uv).rgb;
return float4(upsample + previous, 1);
}
float4 PS_BloomUpsample1(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 0.0) * BUFFER_PIXEL_SIZE * 16.0 * float2(1.0, rcp(BUFFER_ASPECT_RATIO));
float3 upsample = TentFilter9Anisotropic(sBloomUp3, uv, radius);
float3 previous = tex2D(sBloomDown2, uv).rgb;
return float4(upsample + previous, 1);
}
float4 PS_BloomUpsample2(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 0.0) * BUFFER_PIXEL_SIZE * 8.0 * float2(1.0, rcp(BUFFER_ASPECT_RATIO));
float3 upsample = TentFilter9Anisotropic(sBloomUp2, uv, radius);
float3 previous = tex2D(sBloomDown1, uv).rgb;
return float4(upsample + previous, 1);
}
float4 PS_BloomUpsample3(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 0.0) * BUFFER_PIXEL_SIZE * 4.0 * float2(1.0, rcp(BUFFER_ASPECT_RATIO));
float3 upsample = TentFilter9Anisotropic(sBloomUp1, uv, radius);
float3 previous = tex2D(sBloomDown0, uv).rgb;
return float4(upsample + previous, 1);
}
float4 PS_BloomUpsample4(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 radius = float2(BLOOM_STRETCH, 0.0) * BUFFER_PIXEL_SIZE * 2.0 * float2(1.0, rcp(BUFFER_ASPECT_RATIO));
float3 upsample = TentFilter9Anisotropic(sBloomUp0, uv, radius);
float3 previous = tex2D(sBloomDown0, uv).rgb;
return float4(upsample + previous, 1);
}
#endif
#if ANAMORPHIC_STREAKS
//thresholding pass
float4 PS_Prefilter(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 color = tex2D(sUnpackedColor, uv).rgb;
if (STREAK_SKIP_SKYBOX) color *= (GetDepth(uv) < 1.0);
float br = max(color.r, max(color.g, color.b));
float nm = max(0.0, br - (1.0 - STREAK_THRESHOLD));
return float4(color * (nm / max(br, 0.0001)), 1.0);
}
float4 PS_StreakDownsample0(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakDown0, uv, 1.0 * STREAK_STRETCH), 1); }
float4 PS_StreakDownsample1(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakDown1, uv, 2.0 * STREAK_STRETCH), 1); }
float4 PS_StreakDownsample2(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakDown2, uv, 4.0 * STREAK_STRETCH), 1); }
float4 PS_StreakDownsample3(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakDown3, uv, 8.0 * STREAK_STRETCH), 1); }
float4 PS_StreakUpsample0(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakUp3, uv, 8.0 * STREAK_STRETCH) + tex2D(sStreakDown2, uv).rgb, 1); }
float4 PS_StreakUpsample1(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakUp2, uv, 4.0 * STREAK_STRETCH) + tex2D(sStreakDown1, uv).rgb, 1); }
float4 PS_StreakUpsample2(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakUp1, uv, 2.0 * STREAK_STRETCH) + tex2D(sStreakDown0, uv).rgb, 1); }
float4 PS_StreakUpsample3(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { return float4(StreakFilter(sStreakDown4, uv, 16.0 * STREAK_STRETCH) + tex2D(sStreakDown3, uv).rgb, 1); }
#endif
float4 PS_ToDisplay(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 unpackedColor = tex2D(sUnpackedColor, uv).rgb;
float3 light = 0;
#if ANAMORPHIC_BLOOM
light = tex2D(sBloomUp4, uv).rgb * BLOOM_INTENSITY;
#endif
#if ANAMORPHIC_STREAKS
light = max(light, tex2D(sStreakUp0, uv).rgb * STREAK_TINT * STREAK_INTENSITY);
#endif
float3 toDisplay;
#if (BUFFER_COLOR_SPACE == 1)
//sRGB colorspace
toDisplay = 1.0 - (1.0 - unpackedColor) * (1.0 - light);
#else
toDisplay = unpackedColor + light;
#endif
toDisplay = ToOutputColorspace(toDisplay, false);
return float4(toDisplay, 1);
}
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique Lumenite_AnamorphicBloom <
ui_label = "LUMENITE: AnamorphicBloom";
ui_tooltip = "Artistic bloom & Lens Flare approximating the Anamorphic lens aesthetic.";
>
{
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreUnpackedColor; RenderTarget = tUnpackedColor; }
//bloom pyramid
#if ANAMORPHIC_BLOOM
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomDownsample0; RenderTarget = tBloomDown0; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomDownsample1; RenderTarget = tBloomDown1; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomDownsample2; RenderTarget = tBloomDown2; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomDownsample3; RenderTarget = tBloomDown3; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomDownsample4; RenderTarget = tBloomDown4; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomUpsample0; RenderTarget = tBloomUp3; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomUpsample1; RenderTarget = tBloomUp2; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomUpsample2; RenderTarget = tBloomUp1; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomUpsample3; RenderTarget = tBloomUp0; }
pass { VertexShader = PostProcessVS; PixelShader = PS_BloomUpsample4; RenderTarget = tBloomUp4; }
#endif
//streak pyramid
#if ANAMORPHIC_STREAKS
pass { VertexShader = PostProcessVS; PixelShader = PS_Prefilter; RenderTarget = tStreakDown0; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakDownsample0; RenderTarget = tStreakDown1; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakDownsample1; RenderTarget = tStreakDown2; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakDownsample2; RenderTarget = tStreakDown3; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakDownsample3; RenderTarget = tStreakDown4; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakUpsample3; RenderTarget = tStreakUp3; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakUpsample0; RenderTarget = tStreakUp2; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakUpsample1; RenderTarget = tStreakUp1; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StreakUpsample2; RenderTarget = tStreakUp0; }
#endif
pass { VertexShader = PostProcessVS; PixelShader = PS_ToDisplay; }
}
}
@@ -0,0 +1,993 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_Kernel.fx
Version : 2026.09.06
Author : Afzaal (Kaidō)
Description: Pre-effect for various LumeniteFX shaders.
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define FOV 60.0
#define NEAR_PLANE 0.01
#ifndef IMAGE_SPACE
#define IMAGE_SPACE 0
#endif
#ifndef DEBUG_KERNEL
#define DEBUG_KERNEL 0
#endif
#ifndef SMOOTH_NORMALS
#define SMOOTH_NORMALS 0
#endif
#define RES_SCALE ((BUFFER_HEIGHT) / 2160.0) //DO NOT modify this
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
// #if DEBUG_KERNEL
// #include "DrawText.fxh"
// #endif
#include "./include/lumenite_Projections.fxh"
#include "./include/lumenite_Helpers.fxh"
#include "./include/lumenite_Compute.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
#if DEBUG_KERNEL
uniform int DEBUG_VIEW <
ui_type = "combo";
ui_items = "Split View\0"
"Normals/Depth\0"
"Optical Flow\0"
"Motion Vectors\0"
"Motion Confidence\0"
;
ui_label = "Debug View";
ui_category = "Kernel";
> = 0;
#endif
#if IMAGE_SPACE == 0
#if SMOOTH_NORMALS
uniform float LUMA_DETAIL <
ui_type = "drag";
ui_min = -2.0; ui_max = 2.0;
ui_label = "Surface Relief";
ui_tooltip = "How much texture gets carved into smoothed normals. sign inverts the relief.";
> = 0.0;
uniform int LUMA_DETAIL_LOD <
ui_type = "slider";
ui_min = 0; ui_max = 4; ui_step = 1;
ui_label = "Texture LOD";
ui_tooltip = "1 = finest carving, 2 = fine relief, 4 = broad folds";
> = 2;
#endif
#endif
namespace Kernel {
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sConfidence { Texture = tConfidence; };
texture tNormals { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; MipLevels = 4; };
sampler sNormals { Texture = tNormals; };
#if IMAGE_SPACE == 0
#if SMOOTH_NORMALS
texture tGuideNormals { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sGuideNormals { Texture = tGuideNormals; };
texture texHRAN_H0 { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler sHRAN_H0 { Texture = texHRAN_H0; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture texHRAN_HA { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler sHRAN_HA { Texture = texHRAN_HA; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture texHRAN_HB { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = RGBA16F; };
sampler sHRAN_HB { Texture = texHRAN_HB; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
#endif
#endif
texture2D tDepth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 4; };
sampler2D sDepth { Texture = tDepth; };
texture2D tCurrLuma { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 8; };
sampler2D sCurrLuma { Texture = tCurrLuma; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tPrevLuma { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 8; };
sampler2D sPrevLuma { Texture = tPrevLuma; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow128 { Width = BUFFER_WIDTH/128; Height = BUFFER_HEIGHT/128; Format = RG16F; };
sampler2D sFlow128 { Texture = tFlow128; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow64A { Width = BUFFER_WIDTH/64; Height = BUFFER_HEIGHT/64; Format = RG16F; };
sampler2D sFlow64A { Texture = tFlow64A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow64B { Width = BUFFER_WIDTH/64; Height = BUFFER_HEIGHT/64; Format = RG16F; };
sampler2D sFlow64B { Texture = tFlow64B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow32A { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = RG16F; };
sampler2D sFlow32A { Texture = tFlow32A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow32B { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = RG16F; };
sampler2D sFlow32B { Texture = tFlow32B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow16A { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RG16F; };
sampler2D sFlow16A { Texture = tFlow16A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow16B { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RG16F; };
sampler2D sFlow16B { Texture = tFlow16B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow8 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow8 { Texture = tFlow8; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tPrevFrameFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sPrevFrameFlow { Texture = tPrevFrameFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tPrevConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sPrevConfidence { Texture = tPrevConfidence; };
/*--------------.
| :: HELPERS :: |
'--------------*/
float3 GetColor(float2 uv)
{
return tex2Dlod(ReShade::BackBuffer, float4(uv, 0, 0)).rgb;
}
float3 DepthGradient(float t, float2 uv)
{
//grayscale: close=dark, far=bright
float3 depth = saturate(t).xxx;
const float ditherBit = 8.0;
float gridPos = frac(dot(uv, (BUFFER_SCREEN_SIZE * float2(1.0 / 16.0, 10.0 / 36.0)) + 0.25));
float ditherShift = 0.25 * (1.0 / (pow(2.0, ditherBit) - 1.0));
float3 ditherShiftRGB = float3(ditherShift, -ditherShift, ditherShift); //subpixel dithering
ditherShiftRGB = lerp(2.0 * ditherShiftRGB, -2.0 * ditherShiftRGB, gridPos);
return depth + ditherShiftRGB;
}
float3 MotionToColor(float2 motion)
{
float angle = atan2(-motion.y, -motion.x) / 6.283 + 0.5;
float rawLength = length(motion) / (15.0 * BUFFER_PIXEL_SIZE.x);
float compressed = rawLength / (1.0 + rawLength * 1.4); //asymptotic squash
float boosted = pow(compressed, 0.5); //lift shadows
float magnitude = saturate(lerp(compressed, boosted, saturate(rawLength * 3.0)));
float3 hsv = float3(angle, 1, magnitude);
float4 K = float4(1, 2/3.0, 1/3.0, 3);
float3 p = abs(frac(hsv.xxx + K.xyz) * 6 - K.www);
return hsv.z * lerp(K.xxx, clamp(p - K.xxx, 0, 1), hsv.y) + 0.1;
}
float SegmentDist(float2 p, float2 a, float2 b) //anti-aliased distance from point p to segment a-b
{
float2 pa = p - a;
float2 ba = b - a;
float h = saturate(dot(pa, ba) / (dot(ba, ba) + EPSILON));
return length(pa - ba * h);
}
float4 DrawMotionVectors(float2 uv)
{
static const int GATHER = 2; //cell radius searched (5x5); always MAX_LENGTH <= GATHER*GRID_SPACING
static const float GRID_SPACING = 16.0; //px between grid nodes
static const float DOT_RADIUS = 2.0; //px radius of node dots
static const float GRID_OPACITY = 0.20; //0..1 lattice visibility
static const float3 GRID_TINT = float3(0.55, 0.55, 0.60);
static const float SHAFT_THICKNESS = 1.5; //px half-width of shaft (larger)
static const float HEAD_LENGTH = 6.0; //px length of arrowhead (larger)
static const float HEAD_HALF_WIDTH = 4.0; //px half-width of head base (larger)
static const float MIN_LENGTH = 7.0; //px shortest arrow
static const float MAX_LENGTH = 30.0; //px longest arrow (<= GATHER*GRID_SPACING)
static const float LENGTH_SCALE = 2.5; //arrow px per motion px (elongation gain)
static const float AA = 0.9; //px edge softness
float3 baseColor = GetColor(uv);
float2 pixelPos = uv * BUFFER_SCREEN_SIZE;
//dotted grid
float2 g = pixelPos / GRID_SPACING;
float2 nearest = round(g) * GRID_SPACING; //nearest node centre, px
float dDot = length(pixelPos - nearest); //px distance to that node
float gridCov = (1.0 - smoothstep(DOT_RADIUS - AA, DOT_RADIUS + AA, dDot)) * GRID_OPACITY;
float bestCov = 0.0;
float3 bestColor = float3(0.0, 0.0, 0.0);
//union of arrows from the (2*GATHER+1)^2 nearest nodes (roots on grid crossings)
float2 baseNode = round(g);
[unroll] for (int ny = -GATHER; ny <= GATHER; ny++)
[unroll] for (int nx = -GATHER; nx <= GATHER; nx++)
{
float2 rootPx = (baseNode + float2(nx, ny)) * GRID_SPACING; //node sits on a crossing
float2 rootUV = rootPx * BUFFER_PIXEL_SIZE;
float2 motion = tex2Dlod(sFlow, float4(rootUV, 0, 0)).xy;
float2 motionPx = motion * BUFFER_SCREEN_SIZE;
float magPx = length(motionPx);
bool valid = (magPx >= 0.4) && (tex2Dlod(sDepth, float4(rootUV, 0, 0)).r < 0.999);
float len = clamp(magPx * LENGTH_SCALE, MIN_LENGTH, MAX_LENGTH); //elongates with this node's motion
float2 fwd = -motionPx / (magPx + EPSILON); //negate for forward motion
float2 tip = rootPx + fwd * len;
float2 perp = float2(-fwd.y, fwd.x);
//shaft
float2 shaftEnd = rootPx + fwd * max(len - HEAD_LENGTH, 0.0);
float dShaft = SegmentDist(pixelPos, rootPx, shaftEnd);
float covShaft = 1.0 - smoothstep(SHAFT_THICKNESS - AA, SHAFT_THICKNESS + AA, dShaft);
//head
float2 toTip = pixelPos - tip;
float along = dot(toTip, -fwd);
float side = abs(dot(toTip, perp));
float halfW = HEAD_HALF_WIDTH * saturate(along / HEAD_LENGTH);
float covAlong = smoothstep(-AA, AA, along) * (1.0 - smoothstep(HEAD_LENGTH - AA, HEAD_LENGTH + AA, along));
float covHead = covAlong * (1.0 - smoothstep(halfW - AA, halfW + AA, side));
float cov = max(covShaft, covHead) * (valid ? 1.0 : 0.0);
if (cov > bestCov) { bestCov = cov; bestColor = MotionToColor(motion); }
}
float3 outColor = lerp(baseColor, GRID_TINT, gridCov); //lattice underneath
outColor = lerp(outColor, bestColor, bestCov); //arrows on top
return float4(outColor, 1.0);
}
float ZMSAD(sampler2D currLumaSrc, sampler2D prevLumaSrc, float2 posA, float2 posB, float2 texelSize, uint mip)
{
static const int2 offsets[9] = {
int2(0, 3),
int2(0, 1),
int2(-3,0), int2(-1,0), int2(0, 0), int2(1,0), int2(3,0),
int2(0,-1),
int2(0,-3)
};
//gather samples and calculate the mean for each patch
float samplesA[9], samplesB[9];
float meanA = 0.0, meanB = 0.0;
[unroll] for(int i = 0; i < 9; i++) {
float2 offset = float2(offsets[i]) * texelSize;
samplesA[i] = tex2Dlod(currLumaSrc, float4(posA + offset, 0, mip)).r;
samplesB[i] = tex2Dlod(prevLumaSrc, float4(posB + offset, 0, mip)).r;
meanA += samplesA[i];
meanB += samplesB[i];
}
meanA /= 9.0;
meanB /= 9.0;
//SAD on the normalized samples
float err = 0.0;
[unroll] for(int i = 0; i < 9; i++)
err += abs((samplesA[i] - meanA) - (samplesB[i] - meanB));
return ((err / 9.0) + EPSILON);
}
float2 Median9(sampler2D flowSrc, float2 uv, float2 texelSize, uint mip)
{
float2 v[9];
int idx = 0;
[unroll] for(int dy = -1; dy <= 1; dy++) for(int dx = -1; dx <= 1; dx++)
v[idx++] = tex2Dlod(flowSrc, float4(uv + float2(dx, dy) * texelSize, 0, mip)).xy;
//bubble sort ensures the Median lands in v[4], only needs 5 passes
//indices 4,5,6,7,8 contain the 5 largest items, so v[4] is the median
[unroll] for(int k = 0; k < 5; k++) for(int i = 0; i < 8 - k; i++) { //checks decrease as right side gets sorted
float2 a = v[i];
float2 b = v[i+1];
v[i] = min(a, b);
v[i+1] = max(a, b);
}
return v[4];
}
float2 BilateralMedian9(sampler2D flowSrc, float2 uv, float2 texelSize, uint mip)
{
static const int2 DENSE_3X3[9] = {
int2(-1,-1), int2(0,-1), int2(1,-1),
int2(-1, 0), int2(0, 0), int2(1, 0),
int2(-1, 1), int2(0, 1), int2(1, 1)
};
float lumaC = tex2Dlod(sCurrLuma, float4(uv, 0, mip)).x;
float lumaW = tex2Dlod(sCurrLuma, float4(uv + float2(-1.0, 0.0) * texelSize, 0, mip)).x;
float lumaE = tex2Dlod(sCurrLuma, float4(uv + float2( 1.0, 0.0) * texelSize, 0, mip)).x;
float lumaN = tex2Dlod(sCurrLuma, float4(uv + float2( 0.0,-1.0) * texelSize, 0, mip)).x;
float lumaS = tex2Dlod(sCurrLuma, float4(uv + float2( 0.0, 1.0) * texelSize, 0, mip)).x;
//central-difference gradient, wider baseline than quad ddx/ddy, derived from real samples
float dxLuma = (lumaE - lumaW) * 0.5;
float dyLuma = (lumaS - lumaN) * 0.5;
float2 v[9];
uint validCount = 0;
[unroll] for (int i = 0; i < 9; i++) {
int2 off = DENSE_3X3[i];
float2 sampleUV = uv + float2(off) * texelSize;
//cardinals + center use sampled luma; diagonals get linear prediction
float sampleLuma = lumaC; //covers (0,0)
if (off.x == -1 && off.y == 0) sampleLuma = lumaW;
else if (off.x == 1 && off.y == 0) sampleLuma = lumaE;
else if (off.x == 0 && off.y == -1) sampleLuma = lumaN;
else if (off.x == 0 && off.y == 1) sampleLuma = lumaS;
else if (off.x != 0 && off.y != 0) sampleLuma = lumaC + float(off.x) * dxLuma + float(off.y) * dyLuma;
bool isValid = abs(lumaC - sampleLuma) <= 0.05;
v[i] = isValid ? tex2Dlod(flowSrc, float4(sampleUV, 0, 0)).xy : float2(1e38, 1e38);
validCount += uint(isValid);
}
if(validCount < 3u) return v[4];
//right-to-left bubble: smallest reaches v[0] per pass; after 5 passes, v[0..4] sorted ascending
[unroll] for(int k = 0; k < 5; k++) for(int j = 7; j >= k; j--) {
float2 a = v[j];
float2 b = v[j+1];
v[j] = min(a, b);
v[j+1] = max(a, b);
}
uint medianIdx = validCount / 2u;
float2 result = v[1]; //fallback for validCount == 3 (medianIdx 1)
if (medianIdx == 2u) result = v[2];
if (medianIdx == 3u) result = v[3];
if (medianIdx == 4u) result = v[4];
return result;
}
float2 ATrousFilter(sampler2D motionSrc, float2 uv, uint dilation, uint mip)
{
static const int2 offsets[8] = { int2(-1,-1), int2(0,-1), int2(1,-1),
int2(-1, 0), int2(1, 0),
int2(-1, 1), int2(0, 1), int2(1, 1) };
float centerLuma = tex2Dlod(sCurrLuma, float4(uv, 0, mip)).r;
#if IMAGE_SPACE == 0
float centerDepth = tex2Dlod(sDepth, float4(uv, 0, mip)).r;
#endif
float2 centerFlow = tex2Dlod(motionSrc, float4(uv, 0, 0)).xy;
float centerConf = max(tex2Dlod(sConfidence, float4(uv, 0, 0)).r, 0.01); //0.01 floor prevents NaN if conf hits 0
float2 sum = centerFlow * centerConf;
float totalWeight = centerConf;
[unroll] for (int i = 0; i < 8; i++) {
float2 sampleUV = uv + float2(offsets[i]) * dilation * BUFFER_PIXEL_SIZE * 8.0; //*8 = stride of flow grid
float2 sampleFlow = tex2Dlod(motionSrc, float4(sampleUV, 0, 0)).xy;
float sampleConf = tex2Dlod(sConfidence, float4(sampleUV, 0, 0)).r;
float confWeight = pow(sampleConf, 3.0);
float discontinuityGate;
#if IMAGE_SPACE == 0
float sampleDepth = tex2Dlod(sDepth, float4(sampleUV, 0, mip)).r;
float absDepthDiff = abs(centerDepth - sampleDepth);
float depthWeight = (absDepthDiff < 0.003) ? 1.0 : 0.0;
discontinuityGate = depthWeight;
#else
float2 flowDeltaPx = (sampleFlow - centerFlow) * BUFFER_SCREEN_SIZE; //measure flow disagreement in full-res px
float rawMotionGate = exp2(-dot(flowDeltaPx, flowDeltaPx) / (0.01 + EPSILON));
float motionGate = lerp(1.0, rawMotionGate, saturate(centerConf)); //if center flow is unreliable; relax gate so confident neighbors repair it
discontinuityGate = motionGate;
#endif
float sampleLuma = tex2Dlod(sCurrLuma, float4(sampleUV, 0, mip)).r;
float absLumaDiff = abs(centerLuma - sampleLuma);
float lumaWeight = saturate(1.0 - absLumaDiff * 10.0); //10.0: scale, 4.0: sharpness
float weight = confWeight * lumaWeight * discontinuityGate;
sum += sampleFlow * weight;
totalWeight += weight;
}
return sum / (totalWeight + EPSILON);
}
float2 UpscaleFlow(sampler2D coarseSrc, sampler2D currLumaSrc, sampler2D prevLumaSrc, float2 uv, float2 texelSize, uint mip)
{
if(FRAME_COUNT == 0) return float2(0, 0);
float2 coarseTexelSize = rcp(float2(tex2Dsize(coarseSrc, 0)));
//pool candidates for tournament selection. order matters here
float2 candidates[10];
candidates[0] = tex2D(coarseSrc, uv).xy ;
candidates[1] = tex2D(coarseSrc, uv + float2(0, -coarseTexelSize.y)).xy ;
candidates[2] = tex2D(coarseSrc, uv + float2(0, coarseTexelSize.y)).xy ;
candidates[3] = tex2D(coarseSrc, uv - float2(coarseTexelSize.x, 0)).xy ;
candidates[4] = tex2D(coarseSrc, uv + float2(coarseTexelSize.x, 0)).xy ;
candidates[5] = tex2D(coarseSrc, uv + float2(-coarseTexelSize.x, -coarseTexelSize.y)).xy ;
candidates[6] = tex2D(coarseSrc, uv + float2( coarseTexelSize.x, -coarseTexelSize.y)).xy ;
candidates[7] = tex2D(coarseSrc, uv + float2(-coarseTexelSize.x, coarseTexelSize.y)).xy ;
candidates[8] = tex2D(coarseSrc, uv + float2(coarseTexelSize.x, coarseTexelSize.y)).xy ;
candidates[9] = tex2D(sPrevFrameFlow, uv).xy;
float minCost = 1e6;
float2 prediction = candidates[0];
[loop] for (int i = 0; i < 10; i++) {
float cost = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + candidates[i], texelSize, mip);
if (cost < minCost) {
minCost = cost;
prediction = candidates[i];
}
}
//refinement with parabolic fitting
float costLeft = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction - float2(texelSize.x, 0), texelSize, mip);
float costRight = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction + float2(texelSize.x, 0), texelSize, mip);
float costDown = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction - float2(0, texelSize.y), texelSize, mip);
float costUp = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction + float2(0, texelSize.y), texelSize, mip);
//sub-pixel offset (parabolic fitting)
float2 subpixelOffset;
subpixelOffset.x = (costLeft - costRight) / (4.0 * (costLeft + costRight - 2.0 * minCost) + EPSILON); //EPSILON for flat surface handling
subpixelOffset.y = (costDown - costUp) / (4.0 * (costDown + costUp - 2.0 * minCost) + EPSILON);
//clamp offset to a reasonable range
subpixelOffset = clamp(subpixelOffset, -0.5, 0.5);
return (prediction+subpixelOffset*texelSize);
}
/*--------------.
| :: SHADERS :: |
'--------------*/
float PS_PackFeatures(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 color = GetColor(uv);
float luma = dot(color, float3(0.2126, 0.7152, 0.0722));
return luma * rcp(1.0 + luma);
}
#if IMAGE_SPACE == 0
void PS_ReconstructNormals(VSOUT input, out float4 gbuffer : SV_Target0, out float depthC : SV_Target1)
{
depthC = GetDepth(input.uv);
const float2 offsetX = float2(BUFFER_PIXEL_SIZE.x, 0);
const float2 offsetY = float2(0, BUFFER_PIXEL_SIZE.y);
float3 pC = UVToViewSpace(input.uv, depthC, input);
float3 pL = UVToViewSpace(input.uv - offsetX, GetDepth(input.uv - offsetX), input);
float3 pR = UVToViewSpace(input.uv + offsetX, GetDepth(input.uv + offsetX), input);
float3 pT = UVToViewSpace(input.uv - offsetY, GetDepth(input.uv - offsetY), input);
float3 pB = UVToViewSpace(input.uv + offsetY, GetDepth(input.uv + offsetY), input);
float3 diffX2 = pR - pC;
float3 diffX1 = pC - pL;
float3 diffY2 = pB - pC;
float3 diffY1 = pC - pT;
float lenSqX2 = dot(diffX2, diffX2);
float lenSqX1 = dot(diffX1, diffX1);
float lenSqY2 = dot(diffY2, diffY2);
float lenSqY1 = dot(diffY1, diffY1);
float3 ddx = lenSqX2 < lenSqX1 ? diffX2 : diffX1;
float3 ddy = lenSqY2 < lenSqY1 ? diffY2 : diffY1;
float3 geoNormal = normalize(cross(ddx, ddy));
gbuffer = float4(geoNormal, depthC);
}
#if SMOOTH_NORMALS
static const float2 HRAN_SIZE = float2(BUFFER_WIDTH/2, BUFFER_HEIGHT/2);
static const float2 HRAN_PX = float2(2.0, 2.0) / float2(BUFFER_WIDTH, BUFFER_HEIGHT);
static const float HRAN_TOL_SLOPE = 0.005 / RES_SCALE;
static const float HRAN_TOL_FLOOR = 0.00005 / RES_SCALE;
static const float CURV_LO = 0.0;
static const float CURV_HI = 0.475;
static const float MERGE_CENTER_WEIGHT = 1.0;
static const float CURV_OPEN_WINDOW = 1.3333333;
static const float COHERENCE_LO = 0.86;
static const float COHERENCE_HI = 0.85;
static const float STRIDE_NEAR_REF = 0.12 * RES_SCALE;
static const float STRIDE_MAX = 24.0 * RES_SCALE;
static const float DETAIL_GAIN = 1.25 * RES_SCALE;
static const float DEPTH_TOL_SLOPE = 0.005;
float4 PS_HRAN_Half(VSOUT input) : SV_Target
{
float2 hc = floor(input.uv * HRAN_SIZE);
float2 uvC = (hc * 2.0 + 0.5) * BUFFER_PIXEL_SIZE;
float zC = GetDepth(uvC);
if (zC >= 0.999) return float4(0.0, 0.0, -1.0, zC); //sky
float b = clamp(STRIDE_NEAR_REF * rcp(max(zC, 1e-5)), RES_SCALE, STRIDE_MAX) * 2.0; //half-px -> full px
//border guard
float2 pxPos = uvC * BUFFER_SCREEN_SIZE;
float bx = max(min(floor(b + 0.5), min(pxPos.x, BUFFER_SCREEN_SIZE.x - 1.0 - pxPos.x)), 1.0);
float by = max(min(floor(b + 0.5), min(pxPos.y, BUFFER_SCREEN_SIZE.y - 1.0 - pxPos.y)), 1.0);
float2 offX = float2(bx, 0.0) * BUFFER_PIXEL_SIZE.x;
float2 offY = float2(0.0, by) * BUFFER_PIXEL_SIZE.y;
float3 pC = UVToViewSpace(uvC, zC, input);
float3 pL = UVToViewSpace(uvC - offX, GetDepth(uvC - offX), input);
float3 pR = UVToViewSpace(uvC + offX, GetDepth(uvC + offX), input);
float3 pT = UVToViewSpace(uvC - offY, GetDepth(uvC - offY), input);
float3 pB = UVToViewSpace(uvC + offY, GetDepth(uvC + offY), input);
//best-fit selection
float3 dX2 = pR - pC, dX1 = pC - pL;
float3 dY2 = pB - pC, dY1 = pC - pT;
float3 ddxV = dot(dX2, dX2) < dot(dX1, dX1) ? dX2 : dX1;
float3 ddyV = dot(dY2, dY2) < dot(dY1, dY1) ? dY2 : dY1;
float3 n = cross(ddxV, ddyV);
n *= rsqrt(max(dot(n, n), 1e-30)); //scale-safe normalize
return float4(n, zC);
}
float4 ATrousNormalsH(sampler2D gbufferSrc, float2 uv, uint dilation)
{
static const int2 offsets[4] = { int2(0,-1),
int2(-1, 0), int2(1, 0),
int2(0, 1) };
float4 centerGeo = tex2Dlod(gbufferSrc, float4(uv, 0, 0));
float detail = saturate(dot(fwidth(centerGeo.rgb), float3(1,1,1)) * DETAIL_GAIN); //0 = facet interior/flat, 1 = dense variation
if (centerGeo.a >= 0.999) return centerGeo; //sky/far
float strideScale = clamp(STRIDE_NEAR_REF * exp2(-detail) / max(centerGeo.a, 1e-5), RES_SCALE, STRIDE_MAX); //world-locked footprint
float ringR = dilation * strideScale; //hoist: shared by rotation gate and every tap
float rotAng = (ringR > 1.5) ? frac(GetStratifiedNoise(uv * HRAN_SIZE).x + float(dilation) * 0.6180339887) * 1.5707963268 : 0.0; //rotate cross: scrambles band phase into grain the next pass averages away
float rotS, rotC; sincos(rotAng, rotS, rotC);
float invDepthTol = 1.0 / (centerGeo.a * DEPTH_TOL_SLOPE * sqrt(strideScale / RES_SCALE) + EPSILON); //slope term ~ z*sqrt(stride) covers curvature headroom
float invDepthTolL2 = invDepthTol * 1.4426950408; //log2(e) prefold: exp(-x)==exp2(-x*log2e)
float3 armSum = 0.0; //arms accumulate FIRST; the centre's weight is decided after,
float armWeight = 0.0; //once we know how many of them actually survived the gates
float4 geo[4];
[unroll] for (int i = 0; i < 2; i++) {
float2 offIdeal = float2(offsets[i]) * ringR; //full-res grid stride, depth-adaptive
float2 offPx = float2(offIdeal.x * rotC - offIdeal.y * rotS, offIdeal.x * rotS + offIdeal.y * rotC);
offPx = floor(offPx + 0.5); //texel snap
//border guard: scale the MIRRORED pair down so both taps stay on the half grid
float2 hcPos = uv * HRAN_SIZE;
float2 avail = max(min(hcPos, HRAN_SIZE - 1.0 - hcPos), 0.0);
float bsc = min(1.0, min(avail.x / max(abs(offPx.x), 1e-3), avail.y / max(abs(offPx.y), 1e-3)));
offPx = floor(offPx * bsc + 0.5);
float2 sampleUV = uv + offPx * HRAN_PX;
geo[i] = tex2Dlod(gbufferSrc, float4(sampleUV, 0, 0)); //one fetch = signal + both guides
geo[3 - i] = tex2Dlod(gbufferSrc, float4(uv - offPx * HRAN_PX, 0, 0)); //mirrored partner, same snapped offset
}
//curvature consistency
float3 d2x = geo[1].rgb + geo[2].rgb - 2.0 * centerGeo.rgb;
float3 d2y = geo[0].rgb + geo[3].rgb - 2.0 * centerGeo.rgb;
float curv = (length(d2x) + length(d2y)) * 0.5;
bool ringValid = (geo[0].a < 0.999) && (geo[1].a < 0.999) && (geo[2].a < 0.999) && (geo[3].a < 0.999);
float curvGate = ringValid ? (1.0 - smoothstep(CURV_LO, CURV_HI, curv)) : 0.0;
float tapWindow = lerp(1.3333333, CURV_OPEN_WINDOW, curvGate);
//planar depth prediction
float dzdxF = geo[2].a - centerGeo.a, dzdxB = centerGeo.a - geo[1].a; //E-C, C-W
float dzdyF = geo[3].a - centerGeo.a, dzdyB = centerGeo.a - geo[0].a; //S-C, C-N
float dzdx = abs(dzdxF) < abs(dzdxB) ? dzdxF : dzdxB;
float dzdy = abs(dzdyF) < abs(dzdyB) ? dzdyF : dzdyB;
float gradCap = 6.0 / invDepthTol;
dzdx = clamp(dzdx, -gradCap, gradCap);
dzdy = clamp(dzdy, -gradCap, gradCap);
[unroll] for (int i = 0; i < 4; i++) {
float4 sampleGeo = geo[i];
float planeResid = sampleGeo.a - (centerGeo.a + dzdx * float(offsets[i].x) + dzdy * float(offsets[i].y));
float depthWeight = exp2(-abs(planeResid) * invDepthTolL2); //point-to-plane: slanted floors and gentle kinks pass, depth discontinuities fail
float nAlign = saturate(dot(centerGeo.rgb, sampleGeo.rgb));
float normalWeight = saturate(nAlign * tapWindow - (tapWindow - 1.0)); //angular window
float weight = depthWeight * normalWeight * 2.0; //uniform arm weight
weight = sampleGeo.a >= 0.999 ? 0.0 : weight; //skip skylines
armSum += sampleGeo.rgb * weight;
armWeight += weight;
}
//adaptive center weight to the flicker on small geometry
float armConf = saturate(armWeight * 0.125); //8.0 = four arms x 2.0 max
float centerW = lerp(4.0, MERGE_CENTER_WEIGHT, armConf);
float3 sum = centerGeo.rgb * centerW + armSum;
float totalWeight = centerW + armWeight;
float filteredLen = length(sum);
float3 mergedDir = (filteredLen > EPSILON) ? sum / filteredLen : centerGeo.rgb;
//coherence gate
float coherence = filteredLen / max(totalWeight, EPSILON);
float coherenceGate = smoothstep(COHERENCE_LO, COHERENCE_HI, coherence);
float mergeStrength = max(coherenceGate, curvGate);
float3 filtered = normalize(lerp(centerGeo.rgb, mergedDir, mergeStrength));
return float4(filtered, centerGeo.a); //depth rides through untouched
}
float4 PS_HRAN_A(float4 vp : SV_Position, float2 uv : TEXCOORD) : SV_Target { return ATrousNormalsH(sHRAN_H0, uv, 2); }
float4 PS_HRAN_B(float4 vp : SV_Position, float2 uv : TEXCOORD) : SV_Target { return ATrousNormalsH(sHRAN_HA, uv, 4); }
float4 PS_HRAN_C(float4 vp : SV_Position, float2 uv : TEXCOORD) : SV_Target { return ATrousNormalsH(sHRAN_HB, uv, 8); }
float4 PS_HRAN_Up(float4 vp : SV_Position, float2 uv : TEXCOORD) : SV_Target
{ //joint-bilateral upsample
float4 g = tex2Dlod(sGuideNormals, float4(uv, 0, 0));
if (g.a >= 0.999) return g; //sky/far
//center-relative one-sided guide slopes at FULL res (min-mag guard: silhouette on one side can't poison the other)
float zE = tex2Dlod(sGuideNormals, float4(uv + float2(BUFFER_PIXEL_SIZE.x, 0), 0, 0)).a;
float zW = tex2Dlod(sGuideNormals, float4(uv - float2(BUFFER_PIXEL_SIZE.x, 0), 0, 0)).a;
float zS = tex2Dlod(sGuideNormals, float4(uv + float2(0, BUFFER_PIXEL_SIZE.y), 0, 0)).a;
float zN = tex2Dlod(sGuideNormals, float4(uv - float2(0, BUFFER_PIXEL_SIZE.y), 0, 0)).a;
float dxF = zE - g.a, dxB = g.a - zW;
float dzdx = abs(dxF) < abs(dxB) ? dxF : dxB;
float dyF = zS - g.a, dyB = g.a - zN;
float dzdy = abs(dyF) < abs(dyB) ? dyF : dyB;
float invTol = 1.0 / (g.a * HRAN_TOL_SLOPE + HRAN_TOL_FLOOR); //span ~1-2 full px -> no stride coupling needed
float invTolL2 = invTol * 1.4426950408; //log2(e) prefold
float gradCap = 3.0 / invTol; //cut-poisoned-fit cap, same job as always
dzdx = clamp(dzdx, -gradCap, gradCap);
dzdy = clamp(dzdy, -gradCap, gradCap);
float2 hc = uv * HRAN_SIZE - 0.5;
float2 hb = min(max(floor(hc), 0.0), HRAN_SIZE - 2.0); //border guard: the 2x2 always reads real texels
float2 fr = hc - hb;
float2 frw = smoothstep(0.0, 1.0, fr);
float2 base = (hb + 0.5) * HRAN_PX;
float3 nsum = 0.0; float ws = 0.0;
[unroll] for (int j = 0; j < 2; j++)
[unroll] for (int i = 0; i < 2; i++) {
float4 hg = tex2Dlod(sHRAN_HA, float4(base + float2(i, j) * HRAN_PX, 0, 0));
float2 dFull = 2.0 * (float2(i, j) - fr); //tap offset in FULL-res px (one half-px = two full-px)
float resid = hg.a - (g.a + dzdx * dFull.x + dzdy * dFull.y); //point-to-plane vs the pristine full-res guide
float bi = (i == 0 ? 1.0 - frw.x : frw.x) * (j == 0 ? 1.0 - frw.y : frw.y);
float w = exp2(-abs(resid) * invTolL2) * saturate(saturate(dot(g.rgb, hg.rgb)) * 1.3333333 - 0.3333333) * bi; //75deg window
w = hg.a >= 0.999 ? 0.0 : w;
nsum += hg.rgb * w; ws += w;
}
if (ws < 1e-4) return g; //never blend on the wrong-side, let raw normal through, unsmoothed but correct here
float3 n = nsum / ws; float len = length(n);
float3 outN = (len > EPSILON) ? n / len : g.rgb;
//luma micro-relief
[branch] if (abs(LUMA_DETAIL) > 1e-4)
{
float lodEff = LUMA_DETAIL_LOD + log2(RES_SCALE);
float lodPx = exp2(lodEff);
float2 lr = BUFFER_PIXEL_SIZE * lodPx;
float lE = tex2Dlod(sCurrLuma, float4(uv + float2(lr.x, 0), 0, lodEff)).r;
float lW = tex2Dlod(sCurrLuma, float4(uv - float2(lr.x, 0), 0, lodEff)).r;
float lS = tex2Dlod(sCurrLuma, float4(uv + float2(0, lr.y), 0, lodEff)).r;
float lN = tex2Dlod(sCurrLuma, float4(uv - float2(0, lr.y), 0, lodEff)).r;
float2 lg = float2(lE - lW, lS - lN) * 0.5;
lg = sign(lg) * min(abs(lg), 0.08); //cap: residual hard edges emboss boundedly
outN = normalize(outN + float3(-lg.x, -lg.y, 0.0) * (LUMA_DETAIL * 8.0));
}
return float4(outN, g.a); //full-res depth rides through
}
#endif
#endif
float2 PS_ComputeFlow128(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
if(FRAME_COUNT == 0) return float2(0, 0);
static const int SEARCH_RADIUS = 3;
static const uint mip = 5;
float2 texelSize = BUFFER_PIXEL_SIZE * exp2(mip);
//candidate seeds for the coarsest level for tournament selection
float2 prevSeed = tex2D(sPrevFrameFlow, uv).xy;
float2 zeroSeed = float2(0, 0);
float prevCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + prevSeed, texelSize, mip);
float zeroCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + zeroSeed, texelSize, mip);
float2 seed = (zeroCost < prevCost) ? zeroSeed : prevSeed; //pick better candidate as seed
float2 bestFlow = seed;
float minCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv+seed, texelSize, mip);
//search in a grid AROUND the seed
for (int y = -SEARCH_RADIUS; y <= SEARCH_RADIUS; ++y) for (int x = -SEARCH_RADIUS; x <= SEARCH_RADIUS; ++x) {
if (x == 0 && y == 0) continue;
float2 candidateFlow = seed + float2(x, y) * texelSize;
float cost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + candidateFlow, texelSize, mip);
if (cost < minCost) {
minCost = cost;
bestFlow = candidateFlow;
if (minCost < 0.01) //near-perfect match found
return bestFlow;
}
}
return bestFlow;
}
float2 PS_UpscaleFlow64(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow128, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*16.0, 4);
}
float2 PS_MedianPass64(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return Median9(sFlow64A, uv, BUFFER_PIXEL_SIZE*64.0, 6);
}
float2 PS_UpscaleFlow32(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow64B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*8.0, 3);
}
float2 PS_MedianPass32(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return Median9(sFlow32A, uv, BUFFER_PIXEL_SIZE*32.0, 5);
}
float2 PS_UpscaleFlow16(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow32B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*4.0, 2);
}
float2 PS_MedianPass16(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return Median9(sFlow16A, uv, BUFFER_PIXEL_SIZE*16.0, 4);
}
float2 PS_UpscaleFlow8(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow16B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*2.0, 1);
}
float2 PS_MedianPass8A(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return BilateralMedian9(sFlow, uv, BUFFER_PIXEL_SIZE*8.0, 3);
}
float2 PS_MedianPass8B(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return BilateralMedian9(sFlow8, uv, BUFFER_PIXEL_SIZE*8.0, 3);
}
float2 PS_ATrousPassA(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target //stride 1
{
return ATrousFilter(sFlow, uv, 2, 3);
}
float2 PS_ATrousPassB(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target //stride 2
{
float2 flow = ATrousFilter(sFlow8, uv, 4, 1);
//kill sub-pixel noise
float flowPixelMag = length(flow / BUFFER_PIXEL_SIZE);
float gate = saturate(1.0 - pow(1.0 - saturate(saturate(flowPixelMag) - 0.2), 10.0)); //SNAP TO REALITY
return flow*gate;
}
float PS_Confidence(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
if(FRAME_COUNT == 0) return 0.0; //no confidence
float2 flow = tex2D(sFlow, uv).xy;
float2 prevUV = uv + flow; //warp prev frame forward
if(IsOOB(prevUV)) return 0.0;
//look at local contrast for pattern confidence
float sumX = 0, sumX2 = 0, sumY = 0, sumY2 = 0;
float2 lumaTexSize = BUFFER_PIXEL_SIZE * 4.0;
static const float2 offsets[5] = {
float2(0, 1),
float2(-1,0), float2(0, 0), float2(1,0),
float2(0,-1)
};
[unroll] for(int i = 0; i < 5; i++) {
float valCurr = tex2Dlod(sCurrLuma, float4(uv + offsets[i] * lumaTexSize, 0, 2)).r;
float valPrev = tex2Dlod(sPrevLuma, float4(prevUV + offsets[i] * lumaTexSize, 0, 2)).r;
sumX += valCurr; sumX2 += valCurr * valCurr;
sumY += valPrev; sumY2 += valPrev * valPrev;
}
float varCurr = max(0.0, (sumX2 / 5.0) - (sumX / 5.0 * sumX / 5.0));
float varPrev = max(0.0, (sumY2 / 5.0) - (sumY / 5.0 * sumY / 5.0));
float patternConf = 1.0 - saturate(abs(sqrt(varCurr) - sqrt(varPrev)) / (sqrt(varCurr) + 0.01));
//look at neighborhood for flow consistency
float flowMagnitude = length(flow);
float2 flowTexelSize = BUFFER_PIXEL_SIZE * 8.0;
float2 flowN = tex2Dlod(sFlow, float4(uv + float2(0, -flowTexelSize.y), 0, 0)).xy;
float2 flowS = tex2Dlod(sFlow, float4(uv + float2(0, flowTexelSize.y), 0, 0)).xy;
float2 flowE = tex2Dlod(sFlow, float4(uv + float2( flowTexelSize.x, 0), 0, 0)).xy;
float2 flowW = tex2Dlod(sFlow, float4(uv + float2(-flowTexelSize.x, 0), 0, 0)).xy;
float2 avgNeighborFlow = (flowN + flowS + flowE + flowW) * 0.25;
float spatialDiff = distance(flow, avgNeighborFlow);
float spatialThreshold = flowMagnitude * 0.5 + BUFFER_PIXEL_SIZE.x;
float spatialConfidence = saturate(1.0 - (spatialDiff / (spatialThreshold + EPSILON)));
//motion length penalty
float subpixelThreshold = length(BUFFER_PIXEL_SIZE);
float lengthConfidence = (flowMagnitude <= subpixelThreshold) ? 1.0 : rcp((flowMagnitude / subpixelThreshold) * 0.05 + 1.0);
//float panThreshold = BUFFER_PIXEL_SIZE.x * 30.0;
//float lengthConfidence = (flowMagnitude <= panThreshold) ? 1.0 : rcp(((flowMagnitude - panThreshold) / panThreshold) * 0.1 + 1.0);
//current frame final confidence
float currentConf = spatialConfidence * lengthConfidence * patternConf;
//temporal filter
float historyConf = tex2D(sPrevConfidence, prevUV).r;
//DEPRECATED: linear EMA (a=0.15) 15% new + 85% history every frame
//unbiased (settles at the true mean), very stable but distrusts a real drop only as slowly as it trusts a rise
//return lerp(historyConf, currentConf, 0.15); //higher makes it react to changes quickly
//Asymmetric EMA; a=0.5 only on a genuine drop (>0.05 below history) fast distrust, else a reasonable a=0.1
//0.05 deadband keeps calm-region jitter on 0.1; only true occlusion/disocclusion bleeds confidence fast
float alpha = (currentConf < historyConf - 0.05) ? 0.5 : 0.1;
return lerp(historyConf, currentConf, alpha);
}
void PS_StoreFlow(float4 pos : SV_Position, float2 uv : TEXCOORD, out float2 flow : SV_Target0, out float confidence : SV_Target1)
{
flow = tex2D(sFlow, uv).xy;
confidence = tex2D(sConfidence, uv).r;
}
float PS_StoreLuma(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return tex2D(sCurrLuma, uv).r;
}
#if DEBUG_KERNEL
float4 PS_Debug(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 sceneColor = GetColor(uv);
switch(DEBUG_VIEW)
{
case 0: {
static const float LINE_PX = 1.5; //divider half-width, px
static const float3 LINE_TINT = float3(0.0, 0.0, 0.0);
static const float2 BOX_HALF = float2(0.16, 0.18); //centre inset half-extents, uv
float2 pixelPos = uv * BUFFER_SCREEN_SIZE;
float2 centrePx = BUFFER_SCREEN_SIZE * 0.5;
float2 boxHalfPx = BOX_HALF * BUFFER_SCREEN_SIZE;
//axis-aligned box distance
float2 dd = abs(pixelPos - centrePx) - boxHalfPx;
float boxSDF = length(max(dd, 0.0)) + min(max(dd.x, dd.y), 0.0);
float3 view;
if (boxSDF < 0.0)
{
float2 boxUV = (uv - (0.5 - BOX_HALF)) / (2.0 * BOX_HALF); //full frame mapped into inset
view = DrawMotionVectors(boxUV).rgb; //centre: motion vectors
}
else
{
float2 quadUV = frac(uv * 2.0); //flow/confidence remap to full [0,1] frame
if (uv.y < 0.5)
view = (uv.x < 0.5)
? tex2Dlod(sNormals, float4(uv, 0, 0)).rgb * 0.5 + 0.5 //TL: normals (spatial, raw uv)
: DepthGradient(tex2Dlod(sDepth, float4(uv, 0, 0)).r, uv); //TR: depth (spatial, raw uv)
else if (uv.x < 0.5)
view = MotionToColor(tex2Dlod(sFlow, float4(quadUV, 0, 0)).xy); //BL: optical flow field
else
{
float confidence = tex2Dlod(sConfidence, float4(quadUV, 0, 0)).x; //BR: motion confidence field
float3 confidenceColor = (confidence < 0.5)
? lerp(float3(1.0, 0.0, 0.0), float3(1.0, 1.0, 0.0), confidence * 2.0)
: lerp(float3(1.0, 1.0, 0.0), float3(0.0, 1.0, 0.0), (confidence - 0.5) * 2.0);
view = lerp(GetColor(quadUV), confidenceColor, 0.9);
}
//black dividers
float dCross = min(abs(pixelPos.x - centrePx.x), abs(pixelPos.y - centrePx.y));
view = lerp(view, LINE_TINT, 1.0 - smoothstep(LINE_PX - 0.9, LINE_PX + 0.9, dCross));
}
//centre inset border
view = lerp(view, LINE_TINT, 1.0 - smoothstep(LINE_PX - 0.9, LINE_PX + 0.9, abs(boxSDF)));
//window labels
// float2 texcoord = uv; //alias: the DrawText macro declares its own internal 'uv'
// float labelMask = 0.0;
// float labelSize = max(BUFFER_HEIGHT * 0.025, 12.0); //label height, px
// int lblNormals[21] = { __R, __e, __c, __o, __n, __s, __t, __r, __u, __c, __t, __e, __d, __Space, __N, __o, __r, __m, __a, __l, __s };
// int lblDepth[16] = { __L, __i, __n, __e, __a, __r, __i, __z, __e, __d, __Space, __D, __e, __p, __t, __h };
// int lblFlow[10] = { __F, __l, __o, __w, __Space, __F, __i, __e, __l, __d };
// int lblConfidence[16] = { __C, __o, __n, __f, __i, __d, __e, __n, __c, __e, __Space, __F, __i, __e, __l, __d };
// int lblVectors[14] = { __M, __o, __t, __i, __o, __n, __Space, __V, __e, __c, __t, __o, __r, __s };
//
// labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.25 - 21.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.03), labelSize, 1.0, texcoord, lblNormals, 21, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //TL white
// labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.75 - 16.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.03), labelSize, 1.0, texcoord, lblDepth, 16, labelMask); view = lerp(view, float3(0.55, 0.85, 1.00), saturate(labelMask)); //TR blue
// labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.25 - 10.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.53), labelSize, 1.0, texcoord, lblFlow, 10, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //BL white
// labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.75 - 16.0 * labelSize * 0.25, BUFFER_HEIGHT * 0.53), labelSize, 1.0, texcoord, lblConfidence, 16, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //BR white
// labelMask = 0.0; DrawText_String(float2(BUFFER_WIDTH * 0.50 - 14.0 * labelSize * 0.25, BUFFER_HEIGHT * (0.5 - BOX_HALF.y) + 8.0), labelSize, 1.0, texcoord, lblVectors, 14, labelMask); view = lerp(view, float3(1.00, 1.00, 1.00), saturate(labelMask)); //centre white
//
// view = lerp(view, float3(1.0, 1.0, 1.0), saturate(labelMask)); //white labels
return float4(view, 1.0);
}
case 1: {
float4 gbuffer = tex2D(sNormals, uv);
float3 normal = gbuffer.rgb;
float depth = gbuffer.a;
bool isLeftHalf = uv.x < 0.5;
float4 dbg;
if (isLeftHalf)
dbg = float4(normal * 0.5 + 0.5, 1.0); //left: normals
else
dbg = float4(DepthGradient(depth, uv), 1.0); //right: depth gradient
return dbg;
}
case 2: return float4(MotionToColor(tex2D(sFlow, uv).xy), 1);
case 3: return DrawMotionVectors(uv);
case 4:
{
float confidence = tex2D(sConfidence, uv).x;
float3 confidenceColor;
if (confidence < 0.5)
confidenceColor = lerp(float3(1.0, 0.0, 0.0), float3(1.0, 1.0, 0.0), confidence * 2.0);
else
confidenceColor = lerp(float3(1.0, 1.0, 0.0), float3(0.0, 1.0, 0.0), (confidence - 0.5) * 2.0);
return float4(lerp(sceneColor, confidenceColor, 0.9), 1.0);
}
default: return float4(sceneColor, 1.0);
}
}
#endif
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique Lumenite_Kernel <
ui_label = "LUMENITE: Kernel 2.0";
ui_tooltip = "Pre-effect for LumeniteFX shaders.";
>
{
//features
pass { VertexShader = PostProcessVS; PixelShader = PS_PackFeatures; RenderTarget = tCurrLuma; }
//normals
#if IMAGE_SPACE == 0
#if SMOOTH_NORMALS == 0
pass { VertexShader = VS; PixelShader = PS_ReconstructNormals; RenderTarget0 = tNormals; RenderTarget1 = tDepth; }
#else
pass { VertexShader = VS; PixelShader = PS_ReconstructNormals; RenderTarget0 = tGuideNormals; RenderTarget1 = tDepth; }
pass { VertexShader = VS; PixelShader = PS_HRAN_Half; RenderTarget = texHRAN_H0; }
pass { VertexShader = PostProcessVS; PixelShader = PS_HRAN_A; RenderTarget = texHRAN_HA; }
pass { VertexShader = PostProcessVS; PixelShader = PS_HRAN_B; RenderTarget = texHRAN_HB; }
pass { VertexShader = PostProcessVS; PixelShader = PS_HRAN_C; RenderTarget = texHRAN_HA; }
pass { VertexShader = PostProcessVS; PixelShader = PS_HRAN_Up; RenderTarget = tNormals; }
#endif
#endif
//optical flow
pass { VertexShader = PostProcessVS; PixelShader = PS_ComputeFlow128; RenderTarget = tFlow128; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow64; RenderTarget = tFlow64A; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass64; RenderTarget = tFlow64B; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow32; RenderTarget = tFlow32A; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass32; RenderTarget = tFlow32B; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow16; RenderTarget = tFlow16A; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass16; RenderTarget = tFlow16B; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow8; RenderTarget = tFlow; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass8A; RenderTarget = tFlow8; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass8B; RenderTarget = tFlow; }
pass { VertexShader = PostProcessVS; PixelShader = PS_Confidence; RenderTarget = tConfidence; }
pass { VertexShader = PostProcessVS; PixelShader = PS_ATrousPassA; RenderTarget = tFlow8; }
pass { VertexShader = PostProcessVS; PixelShader = PS_ATrousPassB; RenderTarget = tFlow; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreFlow; RenderTarget0 = tPrevFrameFlow; RenderTarget1 = tPrevConfidence; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreLuma; RenderTarget = tPrevLuma; }
//debug views
#if DEBUG_KERNEL
pass { VertexShader = PostProcessVS; PixelShader = PS_Debug; }
#endif
}
}
@@ -0,0 +1,368 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_LSAO.fx
Version : 2026.06.09
Author : Afzaal (Kaidō)
Description: Large-Scale Ray Traced Ambient Occlusion (Screen Space).
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define FOV 60.0
#define NEAR_PLANE 0.01
#define AO_MAX_MARCH_STEPS 100
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
#include "./include/lumenite_Projections.fxh"
#include "./include/lumenite_Helpers.fxh"
#include "./include/lumenite_ColorManagement.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
uniform bool DEBUG_VIEW <
ui_label = "Show AO Mask";
ui_tooltip = "Debug view for the AO. Shows raw AO.";
ui_category = "Ambient Occlusion";
> = 0;
uniform float DEPTH_BOUNDARY <
ui_type = "slider";
ui_min = 0.001; ui_max = 0.999; ui_step = 0.001;
ui_label = "AO Range";
ui_tooltip = "The Z+ range/depth in which the effect is applied.";
ui_category = "Ambient Occlusion";
hidden = false;
> = 0.6;
uniform float DEPTH_FADE_START <
ui_type = "slider";
ui_min = 0.1; ui_max = 1.0; ui_step = 0.01;
ui_label = "Z+ Fade Start (%)";
ui_tooltip = "Z+ fraction where effect starts fading out (relative to AO Range)";
ui_category = "Ambient Occlusion";
hidden = true;
> = 0.75;
uniform float AO_INTENSITY <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0;
ui_label = "AO Strength";
ui_tooltip = "Controls the intensity of the ambient occlusion effect.";
ui_category = "Ambient Occlusion";
> = 1.0;
/*--------------.
| :: IMPORTS :: |
'--------------*/
namespace Kernel {
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sConfidence { Texture = tConfidence; };
texture tNormals { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; MipLevels = 4; };
sampler sNormals { Texture = tNormals; };
texture2D tDepth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 4; };
sampler2D sDepth { Texture = tDepth; };
}
namespace LumeniteLSAO {
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
texture tAOTrace { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = R16F; };
sampler sAOTrace { Texture = tAOTrace; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture tAO1 { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sAO1 { Texture = tAO1; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture tAO2 { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sAO2 { Texture = tAO2; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
sampler sAO2Linear { Texture = tAO2; AddressU = CLAMP; AddressV = CLAMP; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; };
texture tPrevAO { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sPrevAO { Texture = tPrevAO; AddressU = CLAMP; AddressV = CLAMP; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; };
//HiZ mipchain
texture tHiZMip0 { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; };
texture tHiZMip1 { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = R16F; };
texture tHiZMip2 { Width = BUFFER_WIDTH/4; Height = BUFFER_HEIGHT/4; Format = R16F; };
texture tHiZMip3 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
texture tHiZMip4 { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = R16F; };
texture tHiZMip5 { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = R16F; };
sampler sHiZMip0 { Texture = tHiZMip0; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip1 { Texture = tHiZMip1; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip2 { Texture = tHiZMip2; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip3 { Texture = tHiZMip3; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip4 { Texture = tHiZMip4; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip5 { Texture = tHiZMip5; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
/*--------------.
| :: HELPERS :: |
'--------------*/
void BuildOrthonormalBasis(float3 n, out float3 b1, out float3 b2)
{
if (n.z < -0.9999999) {
b1 = float3(0.0, -1.0, 0.0);
b2 = float3(-1.0, 0.0, 0.0);
} else {
float a = rcp(1.0 + n.z);
float b = -n.x * n.y * a;
b1 = float3(mad(-n.x * n.x, a, 1.0), b, -n.x);
b2 = float3(b, mad(-n.y * n.y, a, 1.0), -n.y);
}
}
float3 GenerateHemisphereDirection(float3 normal, float2 rand, float3 tangent, float3 bitangent)
{
float phi = rand.x * 6.28318530718; //2.0*PI as constant
float sinPhi, cosPhi;
sincos(phi, sinPhi, cosPhi);
float cosTheta = sqrt(1.0 - rand.y);
float sinTheta = sqrt(rand.y);
float3 result = normal * cosTheta;
result = mad(bitangent, sinTheta * sinPhi, result);
result = mad(tangent, sinTheta * cosPhi, result);
return result;
}
float CalculateDepthFade(float depth)
{
float fadeStartDepth = DEPTH_BOUNDARY * DEPTH_FADE_START;
float fadeRange = DEPTH_BOUNDARY - fadeStartDepth;
return 1.0 - saturate((depth - fadeStartDepth) / fadeRange);
}
float2 ATrousFilter(sampler SourceSampler, float2 uv, uint dilation, bool adaptiveDilation)
{
float4 gbuffer = tex2D(Kernel::sNormals, uv);
if (gbuffer.a == 0 || gbuffer.a >= DEPTH_BOUNDARY) return float2(1.0, 0.0);
[branch] if (adaptiveDilation) {
float confidence = tex2Dlod(Kernel::sConfidence, float4(uv, 0, 0)).r;
dilation += uint(round((1.0 - confidence) * 2.0)); //scale filter kernel w. motion by up to a factor of 2
}
float2 centerData = tex2Dlod(SourceSampler, float4(uv, 0, 0)).rg;
float variance = max(0.0, centerData.g - (centerData.r * centerData.r)); //Moment - AO^2
variance = max(variance, 0.0001);
float2 sum = centerData;
float totalWeight = 1.0;
for (int y = -1; y <= 1; y++) for (int x = -1; x <= 1; x++) {
if (x == 0 && y == 0) continue;
float2 sampleUV = uv + float2(x, y) * dilation * (BUFFER_PIXEL_SIZE * 2.0); //don't forget the x2.0 to properly step half-res grid!
float2 sampleData = tex2Dlod(SourceSampler, float4(sampleUV, 0, 0)).rg;
float4 sampleGeo = tex2Dlod(Kernel::sNormals, float4(sampleUV, 0, 0));
float depthWeight = exp(-abs(gbuffer.a - sampleGeo.a) / (gbuffer.a * 0.02 + 0.001));
float normalWeight = pow(saturate(dot(gbuffer.rgb, sampleGeo.rgb)), 50.0);
float aoDiff = centerData.r - sampleData.r;
float aoWeight = exp(-(aoDiff * aoDiff) / (variance + 0.0001));
float weight = depthWeight * normalWeight * aoWeight;
sum += sampleData * weight;
totalWeight += weight;
}
return sum / (totalWeight + EPSILON);
}
float SamplePrevHiZ(float2 centerUV, sampler srcSampler, int srcMipLvl) {
float2 srcTexelSize = BUFFER_PIXEL_SIZE * pow(2, srcMipLvl);
float2 off[4] = { float2(-0.5, -0.5), float2(0.5, -0.5), float2(-0.5, 0.5), float2(0.5, 0.5) };
float minDepth = 1.0;
[unroll] for(int i=0; i<4; i++)
minDepth = min(minDepth, tex2D(srcSampler, centerUV + off[i] * srcTexelSize).r);
return minDepth;
}
/*--------------.
| :: SHADERS :: |
'--------------*/
float PS_GenerateMip0(VSOUT input) : SV_Target
{
float2 blockOriginUV = floor(input.uv / (BUFFER_PIXEL_SIZE * 2.0)) * (BUFFER_PIXEL_SIZE * 2.0);
float2 uvs[4] = { blockOriginUV + BUFFER_PIXEL_SIZE * float2(0.5, 0.5),
blockOriginUV + BUFFER_PIXEL_SIZE * float2(1.5, 0.5),
blockOriginUV + BUFFER_PIXEL_SIZE * float2(0.5, 1.5),
blockOriginUV + BUFFER_PIXEL_SIZE * float2(1.5, 1.5) };
float d0 = tex2D(Kernel::sDepth, uvs[0]).r;
float d1 = tex2D(Kernel::sDepth, uvs[1]).r;
float d2 = tex2D(Kernel::sDepth, uvs[2]).r;
float d3 = tex2D(Kernel::sDepth, uvs[3]).r;
return min(min(d0, d1), min(d2, d3));
}
float PS_ReduceMip1 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip0, 0); }
float PS_ReduceMip2 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip1, 1); }
float PS_ReduceMip3 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip2, 2); }
float PS_ReduceMip4 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip3, 3); }
float PS_ReduceMip5 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip4, 4); }
float PS_TraceAO(VSOUT input) : SV_Target
{
float4 gbuffer = tex2D(Kernel::sNormals, input.uv);
float3 normal = gbuffer.rgb;
float depth = gbuffer.a;
if (depth == 0 || depth >= DEPTH_BOUNDARY) discard;
float3 startPos = UVToViewSpace(input.uv, depth, input);
float3 tangent, bitangent;
BuildOrthonormalBasis(normal, tangent, bitangent);
float2 noise = GetStratifiedNoise(input.vpos.xy);
float3 rayDir = GenerateHemisphereDirection(normal, noise, tangent, bitangent);
float totalRayLength = 0.7 * depth;
float baseStepSize = totalRayLength / (float)AO_MAX_MARCH_STEPS;
float stepSize = baseStepSize;
float3 currentPos = startPos + rayDir * stepSize;
float occlusion = 0.0;
float t = stepSize;
[loop]
for (int step = 0; step < AO_MAX_MARCH_STEPS; step++) {
if (t >= totalRayLength) break;
float2 hitPos = ViewSpaceToUV(currentPos, input);
if (IsOOB(hitPos)) break;
//select the appropriate Mip Level
float2 ray_screen_velocity = abs(rayDir.xy / currentPos.z) * float2(BUFFER_WIDTH, BUFFER_HEIGHT);
float footprint = max(ray_screen_velocity.x, ray_screen_velocity.y) * max(stepSize / currentPos.z, 1.0);
int mip = clamp(int(log2(max(footprint, 1.0))), 0, 5);
float HiZDepth;
if (mip==5) HiZDepth = tex2Dlod(sHiZMip5, float4(hitPos,0,0)).r;
else if (mip==4) HiZDepth = tex2Dlod(sHiZMip4, float4(hitPos,0,0)).r;
else if (mip==3) HiZDepth = tex2Dlod(sHiZMip3, float4(hitPos,0,0)).r;
else if (mip==2) HiZDepth = tex2Dlod(sHiZMip2, float4(hitPos,0,0)).r;
else if (mip==1) HiZDepth = tex2Dlod(sHiZMip1, float4(hitPos,0,0)).r;
else HiZDepth = tex2Dlod(sHiZMip0, float4(hitPos,0,0)).r;
//skip some empty space
float currentStepSize = stepSize * max(1.0, float(mip) * 0.5); //stepsize mip scaling
if (currentPos.z < HiZDepth && (HiZDepth - currentPos.z) > currentStepSize) {
float leap = max(currentStepSize, (HiZDepth - currentPos.z) * 0.065);
currentPos += rayDir * leap;
t += leap;
continue;
}
//hit test
float sceneDepth = tex2Dlod(Kernel::sDepth, float4(hitPos, 0, 0)).r;
float depthDiff = currentPos.z - sceneDepth;
float maxThickness = currentPos.z * 0.6;
if (depthDiff > (currentPos.z * 0.0001) && depthDiff < maxThickness) {
float3 scenePos = UVToViewSpace(hitPos, sceneDepth, input);
float hitDistance = length(scenePos - startPos);
float normalizedDist = hitDistance / totalRayLength;
occlusion = 1.0 - saturate(depthDiff / maxThickness);
occlusion = occlusion * occlusion;
occlusion = saturate(pow(saturate(1.0 - normalizedDist), 1.2) * occlusion * 1.4);
break;
}
currentPos += rayDir * stepSize;
t += stepSize;
}
float aoFactor = 1.0 - saturate(occlusion * AO_INTENSITY);
return aoFactor;
}
float2 PS_TemporalFilter(VSOUT input) : SV_Target
{
float depth = tex2D(Kernel::sDepth, input.uv).r;
//overwrite noise at boundary with clean White, prevents gaps
if (depth >= DEPTH_BOUNDARY) return float2(1.0, 1.0); //1.0 AO, 1.0 Moment
if (depth == 0) discard;
float ao = tex2D(sAOTrace, input.uv).r;
ao = lerp(1.0, ao, CalculateDepthFade(depth));
float moment = ao * ao;
float2 flow = tex2D(Kernel::sFlow, input.uv).xy;
float confidence = tex2D(Kernel::sConfidence, input.uv).x;
confidence = saturate(confidence + log2(2.0 - confidence) * 0.6); //boost confidence
float2 rawHistory = tex2D(sPrevAO, input.uv + flow).rg; //history stores "1.0 - AO". 0.0 (Black Texture) -> Reads as 1.0 (White)
float prevAO = 1.0 - rawHistory.r;
float prevMoment = 1.0 - rawHistory.g;
float alpha = confidence * 0.98;
ao = lerp(ao, prevAO, alpha);
moment = lerp(moment, prevMoment, alpha);
//max(..., 0.001) to ensure we NEVER write exactly 0.0 again
//this tells the next frame "I contain data"
return float2(max(ao, 0.001), max(moment, 0.001));
}
float2 PS_StoreAO(VSOUT input) : SV_Target
{
//must prevent history collision here
//if we store exactly 0.0 (means White), the next frame's blend pass thinks
//history is empty and resets it, causing shimmer
//so clamp to 0.0001 so the system knows "This is valid history data"
float2 data = tex2D(sAO1, input.uv).rg;
return float2(max(1.0 - data.r, 0.0001), max(1.0 - data.g, 0.0001)); //store inverted
}
float2 PS_ATrousPass1(VSOUT input) : SV_Target { return ATrousFilter(sAO1, input.uv, 2, false); }
float4 PS_ToDisplay(VSOUT input) : SV_Target
{
float depth = tex2D(Kernel::sDepth, input.uv).r;
float ao = ATrousFilter(sAO2Linear, input.uv, 4, true).r; //stable AO mask (fades to 1.0)
if (DEBUG_VIEW) {
#if BUFFER_COLOR_SPACE > 1
return float4(ToOutputColorspace(ao.xxx, true), 1.0);
#else
return float4(ao.xxx, 1.0);
#endif
}
if (depth == 0 || depth >= DEPTH_BOUNDARY) discard;
float3 base = GetLinearColor(input.uv, true);
base *= ao;
return float4(ToOutputColorspace(base, true), 1.0);
}
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique Lumenite_LSAO <
ui_label = "LUMENITE: LSAO";
ui_tooltip = "Large-Scale Ray Traced Ambient Occlusion (Screen Space).";
>
{
pass { VertexShader = VS; PixelShader = PS_GenerateMip0; RenderTarget = tHiZMip0; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip1; RenderTarget = tHiZMip1; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip2; RenderTarget = tHiZMip2; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip3; RenderTarget = tHiZMip3; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip4; RenderTarget = tHiZMip4; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip5; RenderTarget = tHiZMip5; }
pass { VertexShader = VS; PixelShader = PS_TraceAO; RenderTarget = tAOTrace; }
pass { VertexShader = VS; PixelShader = PS_TemporalFilter; RenderTarget = tAO1; }
pass { VertexShader = VS; PixelShader = PS_StoreAO; RenderTarget = tPrevAO; }
pass { VertexShader = VS; PixelShader = PS_ATrousPass1; RenderTarget = tAO2; }
pass { VertexShader = VS; PixelShader = PS_ToDisplay; }
}
}
@@ -0,0 +1,403 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_QuantAO.fx
Version : 2026.06.09
Author : Afzaal (Kaidō)
Description: Fast Ambient Occlusion (Screen Space).
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define FOV 60.0
#define NEAR_PLANE 0.01
#define AO_MAX_MARCH_STEPS 48
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
#include "./include/lumenite_Projections.fxh"
#include "./include/lumenite_Helpers.fxh"
#include "./include/lumenite_ColorManagement.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
uniform bool DEBUG_VIEW <
ui_label = "Show AO Mask";
ui_tooltip = "Debug view for the AO. Shows raw AO.";
ui_category = "Ambient Occlusion";
> = 0;
uniform float DEPTH_BOUNDARY <
ui_type = "slider";
ui_min = 0.001; ui_max = 0.999; ui_step = 0.001;
ui_label = "AO Range";
ui_tooltip = "The Z+ range/depth in which the effect is applied.";
ui_category = "Ambient Occlusion";
hidden = false;
> = 0.6;
uniform float DEPTH_FADE_START <
ui_type = "slider";
ui_min = 0.1; ui_max = 1.0; ui_step = 0.01;
ui_label = "Z+ Fade Start (%)";
ui_tooltip = "Z+ fraction where effect starts fading out (relative to AO Range)";
ui_category = "Ambient Occlusion";
hidden = true;
> = 0.75;
uniform float AO_INTENSITY <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0;
ui_label = "AO Strength";
ui_tooltip = "Controls the intensity of the ambient occlusion effect.";
ui_category = "Ambient Occlusion";
> = 1.0;
/*--------------.
| :: IMPORTS :: |
'--------------*/
namespace QuantMotion {
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sConfidence { Texture = tConfidence; };
}
namespace LumeniteQuantAO {
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
texture tNormals { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RGBA16F; };
sampler sNormals { Texture = tNormals; };
texture2D tDepth { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = R16F; };
sampler2D sDepth { Texture = tDepth; };
texture tAOTrace { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = R16F; };
sampler sAOTrace { Texture = tAOTrace; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture tAO1 { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sAO1 { Texture = tAO1; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture tAO2 { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sAO2 { Texture = tAO2; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture tAO3 { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sAO3Linear { Texture = tAO3; AddressU = CLAMP; AddressV = CLAMP; MagFilter = LINEAR; MinFilter = LINEAR; };
texture tPrevAO { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sPrevAO { Texture = tPrevAO; AddressU = CLAMP; AddressV = CLAMP; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; };
//HiZ mipchain
texture tHiZMip0 { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; };
texture tHiZMip1 { Width = BUFFER_WIDTH/2; Height = BUFFER_HEIGHT/2; Format = R16F; };
texture tHiZMip2 { Width = BUFFER_WIDTH/4; Height = BUFFER_HEIGHT/4; Format = R16F; };
texture tHiZMip3 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
texture tHiZMip4 { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = R16F; };
texture tHiZMip5 { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = R16F; };
sampler sHiZMip0 { Texture = tHiZMip0; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip1 { Texture = tHiZMip1; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip2 { Texture = tHiZMip2; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip3 { Texture = tHiZMip3; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip4 { Texture = tHiZMip4; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
sampler sHiZMip5 { Texture = tHiZMip5; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; };
/*--------------.
| :: HELPERS :: |
'--------------*/
void BuildOrthonormalBasis(float3 n, out float3 b1, out float3 b2)
{
if (n.z < -0.9999999) {
b1 = float3(0.0, -1.0, 0.0);
b2 = float3(-1.0, 0.0, 0.0);
} else {
float a = rcp(1.0 + n.z);
float b = -n.x * n.y * a;
b1 = float3(mad(-n.x * n.x, a, 1.0), b, -n.x);
b2 = float3(b, mad(-n.y * n.y, a, 1.0), -n.y);
}
}
float3 GenerateHemisphereDirection(float3 normal, float2 rand, float3 tangent, float3 bitangent)
{
float phi = rand.x * 6.28318530718; //2.0*PI as constant
float sinPhi, cosPhi;
sincos(phi, sinPhi, cosPhi);
float cosTheta = sqrt(1.0 - rand.y);
float sinTheta = sqrt(rand.y);
float3 result = normal * cosTheta;
result = mad(bitangent, sinTheta * sinPhi, result);
result = mad(tangent, sinTheta * cosPhi, result);
return result;
}
float CalculateDepthFade(float depth)
{
float fadeStartDepth = DEPTH_BOUNDARY * DEPTH_FADE_START;
float fadeRange = DEPTH_BOUNDARY - fadeStartDepth;
return 1.0 - saturate((depth - fadeStartDepth) / fadeRange);
}
float2 ATrousFilter(sampler SourceSampler, float2 uv, uint dilation, bool adaptiveDilation)
{
float4 gbuffer = tex2D(sNormals, uv);
if (gbuffer.a == 0 || gbuffer.a >= DEPTH_BOUNDARY) return float2(1.0, 0.0);
[branch] if (adaptiveDilation) {
float confidence = tex2Dlod(QuantMotion::sConfidence, float4(uv, 0, 0)).r;
dilation += uint(round((1.0 - confidence) * 2.0)); //scale filter kernel w. motion by up to a factor of 2
}
float2 centerData = tex2Dlod(SourceSampler, float4(uv, 0, 0)).rg;
float variance = max(0.0, centerData.g - (centerData.r * centerData.r)); //Moment - AO^2
variance = max(variance, 0.0001);
float2 sum = centerData;
float totalWeight = 1.0;
for (int y = -1; y <= 1; y++) for (int x = -1; x <= 1; x++) {
if (x == 0 && y == 0) continue;
float2 sampleUV = uv + float2(x, y) * dilation * (BUFFER_PIXEL_SIZE * 2.0); //don't forget the x2.0 to properly step half-res grid!
float2 sampleData = tex2Dlod(SourceSampler, float4(sampleUV, 0, 0)).rg;
float4 sampleGeo = tex2Dlod(sNormals, float4(sampleUV, 0, 0));
float depthWeight = exp(-abs(gbuffer.a - sampleGeo.a) / (gbuffer.a * 0.02 + 0.001));
float normalWeight = pow(saturate(dot(gbuffer.rgb, sampleGeo.rgb)), 50.0);
float aoDiff = centerData.r - sampleData.r;
float aoWeight = exp(-(aoDiff * aoDiff) / (variance + 0.0001));
float weight = depthWeight * normalWeight * aoWeight;
sum += sampleData * weight;
totalWeight += weight;
}
return sum / (totalWeight + EPSILON);
}
float SamplePrevHiZ(float2 centerUV, sampler srcSampler, int srcMipLvl) {
float2 srcTexelSize = BUFFER_PIXEL_SIZE * pow(2, srcMipLvl);
float2 off[4] = { float2(-0.5, -0.5), float2(0.5, -0.5), float2(-0.5, 0.5), float2(0.5, 0.5) };
float minDepth = 1.0;
[unroll] for(int i=0; i<4; i++)
minDepth = min(minDepth, tex2D(srcSampler, centerUV + off[i] * srcTexelSize).r);
return minDepth;
}
/*--------------.
| :: SHADERS :: |
'--------------*/
void PS_ReconstructNormals(VSOUT input, out float4 gbuffer : SV_Target0, out float depthC : SV_Target1)
{
depthC = GetDepth(input.uv);
const float2 offsetX = float2(BUFFER_PIXEL_SIZE.x, 0);
const float2 offsetY = float2(0, BUFFER_PIXEL_SIZE.y);
float3 pC = UVToViewSpace(input.uv, depthC, input);
float3 pL = UVToViewSpace(input.uv - offsetX, GetDepth(input.uv - offsetX), input);
float3 pR = UVToViewSpace(input.uv + offsetX, GetDepth(input.uv + offsetX), input);
float3 pT = UVToViewSpace(input.uv - offsetY, GetDepth(input.uv - offsetY), input);
float3 pB = UVToViewSpace(input.uv + offsetY, GetDepth(input.uv + offsetY), input);
float3 diffX2 = pR - pC;
float3 diffX1 = pC - pL;
float3 diffY2 = pB - pC;
float3 diffY1 = pC - pT;
float lenSqX2 = dot(diffX2, diffX2);
float lenSqX1 = dot(diffX1, diffX1);
float lenSqY2 = dot(diffY2, diffY2);
float lenSqY1 = dot(diffY1, diffY1);
float3 ddx = lenSqX2 < lenSqX1 ? diffX2 : diffX1;
float3 ddy = lenSqY2 < lenSqY1 ? diffY2 : diffY1;
float3 geoNormal = normalize(cross(ddx, ddy));
gbuffer = float4(geoNormal, depthC);
}
float PS_GenerateMip0(VSOUT input) : SV_Target
{
float2 blockOriginUV = floor(input.uv / (BUFFER_PIXEL_SIZE * 2.0)) * (BUFFER_PIXEL_SIZE * 2.0);
float2 uvs[4] = { blockOriginUV + BUFFER_PIXEL_SIZE * float2(0.5, 0.5),
blockOriginUV + BUFFER_PIXEL_SIZE * float2(1.5, 0.5),
blockOriginUV + BUFFER_PIXEL_SIZE * float2(0.5, 1.5),
blockOriginUV + BUFFER_PIXEL_SIZE * float2(1.5, 1.5) };
float d0 = tex2D(sDepth, uvs[0]).r;
float d1 = tex2D(sDepth, uvs[1]).r;
float d2 = tex2D(sDepth, uvs[2]).r;
float d3 = tex2D(sDepth, uvs[3]).r;
return min(min(d0, d1), min(d2, d3));
}
float PS_ReduceMip1 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip0, 0); }
float PS_ReduceMip2 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip1, 1); }
float PS_ReduceMip3 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip2, 2); }
float PS_ReduceMip4 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip3, 3); }
float PS_ReduceMip5 (VSOUT input) : SV_Target { return SamplePrevHiZ(input.uv, sHiZMip4, 4); }
float PS_TraceAO(VSOUT input) : SV_Target
{
float4 gbuffer = tex2D(sNormals, input.uv);
float3 normal = gbuffer.rgb;
float depth = gbuffer.a;
if (depth == 0 || depth >= DEPTH_BOUNDARY) discard;
float3 startPos = UVToViewSpace(input.uv, depth, input);
float3 tangent, bitangent;
BuildOrthonormalBasis(normal, tangent, bitangent);
float2 noise = GetStratifiedNoise(input.vpos.xy);
float3 rayDir = GenerateHemisphereDirection(normal, noise, tangent, bitangent);
float totalRayLength = 0.7 * depth;
float baseStepSize = totalRayLength / (float)AO_MAX_MARCH_STEPS;
float stepSize = baseStepSize;
float3 currentPos = startPos + rayDir * stepSize;
float occlusion = 0.0;
float t = stepSize;
[loop]
for (int step = 0; step < AO_MAX_MARCH_STEPS; step++) {
if (t >= totalRayLength) break;
float2 hitPos = ViewSpaceToUV(currentPos, input);
if (IsOOB(hitPos)) break;
//select the appropriate Mip Level
float2 ray_screen_velocity = abs(rayDir.xy / currentPos.z) * float2(BUFFER_WIDTH, BUFFER_HEIGHT);
float footprint = max(ray_screen_velocity.x, ray_screen_velocity.y) * max(stepSize / currentPos.z, 1.0);
int mip = clamp(int(log2(max(footprint, 1.0))), 0, 5);
float HiZDepth;
if (mip==5) HiZDepth = tex2Dlod(sHiZMip5, float4(hitPos,0,0)).r;
else if (mip==4) HiZDepth = tex2Dlod(sHiZMip4, float4(hitPos,0,0)).r;
else if (mip==3) HiZDepth = tex2Dlod(sHiZMip3, float4(hitPos,0,0)).r;
else if (mip==2) HiZDepth = tex2Dlod(sHiZMip2, float4(hitPos,0,0)).r;
else if (mip==1) HiZDepth = tex2Dlod(sHiZMip1, float4(hitPos,0,0)).r;
else HiZDepth = tex2Dlod(sHiZMip0, float4(hitPos,0,0)).r;
//skip some empty space
float currentStepSize = stepSize * max(1.0, float(mip) * 0.5); //stepsize mip scaling
if (currentPos.z < HiZDepth && (HiZDepth - currentPos.z) > currentStepSize) {
float leap = max(currentStepSize, (HiZDepth - currentPos.z) * 0.065);
currentPos += rayDir * leap;
t += leap;
continue;
}
//hit test
float sceneDepth = tex2Dlod(sDepth, float4(hitPos, 0, 0)).r;
float depthDiff = currentPos.z - sceneDepth;
float maxThickness = currentPos.z * 0.6;
if (depthDiff > (currentPos.z * 0.0001) && depthDiff < maxThickness) {
float3 scenePos = UVToViewSpace(hitPos, sceneDepth, input);
float hitDistance = length(scenePos - startPos);
float normalizedDist = hitDistance / totalRayLength;
occlusion = 1.0 - saturate(depthDiff / maxThickness);
occlusion = occlusion * occlusion;
occlusion = saturate(pow(saturate(1.0 - normalizedDist), 1.2) * occlusion * 1.4);
break;
}
currentPos += rayDir * stepSize;
t += stepSize;
}
float aoFactor = 1.0 - saturate(occlusion * AO_INTENSITY);
return aoFactor;
}
float2 PS_TemporalFilter(VSOUT input) : SV_Target
{
float depth = tex2D(sDepth, input.uv).r;
//overwrite noise at boundary with clean White, prevents gaps
if (depth >= DEPTH_BOUNDARY) return float2(1.0, 1.0); //1.0 AO, 1.0 Moment
if (depth == 0) discard;
float ao = tex2D(sAOTrace, input.uv).r;
ao = lerp(1.0, ao, CalculateDepthFade(depth));
float moment = ao * ao;
float2 flow = tex2D(QuantMotion::sFlow, input.uv).xy;
float confidence = tex2D(QuantMotion::sConfidence, input.uv).x;
confidence = saturate(confidence + log2(2.0 - confidence) * 0.55); //boost confidence
float2 rawHistory = tex2D(sPrevAO, input.uv + flow).rg; //history stores "1.0 - AO". 0.0 (Black Texture) -> Reads as 1.0 (White)
float prevAO = 1.0 - rawHistory.r;
float prevMoment = 1.0 - rawHistory.g;
float alpha = confidence * 0.98;
ao = lerp(ao, prevAO, alpha);
moment = lerp(moment, prevMoment, alpha);
//max(..., 0.001) to ensure we NEVER write exactly 0.0 again
//this tells the next frame "I contain data"
return float2(max(ao, 0.001), max(moment, 0.001));
}
float2 PS_StoreAO(VSOUT input) : SV_Target
{
//must prevent history collision here
//if we store exactly 0.0 (means White), the next frame's blend pass thinks
//history is empty and resets it, causing shimmer
//so clamp to 0.0001 so the system knows "This is valid history data"
float2 data = tex2D(sAO1, input.uv).rg;
return float2(max(1.0 - data.r, 0.0001), max(1.0 - data.g, 0.0001)); //store inverted
}
float2 PS_ATrousPass1(VSOUT input) : SV_Target { return ATrousFilter(sAO1, input.uv, 2, false); }
float2 PS_ATrousPass2(VSOUT input) : SV_Target { return ATrousFilter(sAO2, input.uv, 4, true); }
float4 PS_ToDisplay(VSOUT input) : SV_Target
{
float depth = tex2D(sDepth, input.uv).r;
float ao = tex2D(sAO3Linear, input.uv).r;
if (DEBUG_VIEW) {
#if BUFFER_COLOR_SPACE > 1
return float4(ToOutputColorspace(ao.xxx, true), 1.0);
#else
return float4(ao.xxx, 1.0);
#endif
}
if (depth == 0 || depth >= DEPTH_BOUNDARY) discard;
float3 base = GetLinearColor(input.uv, true);
base *= ao;
return float4(ToOutputColorspace(base, true), 1.0);
}
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique Lumenite_QuantAO <
ui_label = "LUMENITE: QuantAO";
ui_tooltip = "Fast Ambient Occlusion (Screen Space).";
>
{
pass { VertexShader = VS; PixelShader = PS_ReconstructNormals; RenderTarget0 = tNormals; RenderTarget1 = tDepth; }
pass { VertexShader = VS; PixelShader = PS_GenerateMip0; RenderTarget = tHiZMip0; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip1; RenderTarget = tHiZMip1; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip2; RenderTarget = tHiZMip2; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip3; RenderTarget = tHiZMip3; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip4; RenderTarget = tHiZMip4; }
pass { VertexShader = VS; PixelShader = PS_ReduceMip5; RenderTarget = tHiZMip5; }
pass { VertexShader = VS; PixelShader = PS_TraceAO; RenderTarget = tAOTrace; }
pass { VertexShader = VS; PixelShader = PS_TemporalFilter; RenderTarget = tAO1; }
pass { VertexShader = VS; PixelShader = PS_StoreAO; RenderTarget = tPrevAO; }
pass { VertexShader = VS; PixelShader = PS_ATrousPass1; RenderTarget = tAO2; }
pass { VertexShader = VS; PixelShader = PS_ATrousPass2; RenderTarget = tAO3; }
pass { VertexShader = VS; PixelShader = PS_ToDisplay; }
}
}
@@ -0,0 +1,444 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : QuantMotion.fx
Version : 2026.06.16
Author : Afzaal (Kaidō)
Description: Superfast motion vectors for low-end hardware.
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define EPSILON 1e-6
#ifndef DEBUG_FLOW
#define DEBUG_FLOW 0
#endif
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
uniform uint FRAME_COUNT < source = "framecount"; >;
namespace QuantMotion {
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sConfidence { Texture = tConfidence; };
texture2D tCurrLuma { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 8; };
sampler2D sCurrLuma { Texture = tCurrLuma; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tPrevLuma { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 8; };
sampler2D sPrevLuma { Texture = tPrevLuma; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow128 { Width = BUFFER_WIDTH/128; Height = BUFFER_HEIGHT/128; Format = RG16F; };
sampler2D sFlow128 { Texture = tFlow128; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow64A { Width = BUFFER_WIDTH/64; Height = BUFFER_HEIGHT/64; Format = RG16F; };
sampler2D sFlow64A { Texture = tFlow64A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow64B { Width = BUFFER_WIDTH/64; Height = BUFFER_HEIGHT/64; Format = RG16F; };
sampler2D sFlow64B { Texture = tFlow64B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow32A { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = RG16F; };
sampler2D sFlow32A { Texture = tFlow32A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow32B { Width = BUFFER_WIDTH/32; Height = BUFFER_HEIGHT/32; Format = RG16F; };
sampler2D sFlow32B { Texture = tFlow32B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow16A { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RG16F; };
sampler2D sFlow16A { Texture = tFlow16A; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow16B { Width = BUFFER_WIDTH/16; Height = BUFFER_HEIGHT/16; Format = RG16F; };
sampler2D sFlow16B { Texture = tFlow16B; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tFlow8 { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow8 { Texture = tFlow8; MagFilter = POINT; MinFilter = POINT; AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; };
texture2D tPrevFrameFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sPrevFrameFlow { Texture = tPrevFrameFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tPrevConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sPrevConfidence { Texture = tPrevConfidence; };
/*--------------.
| :: HELPERS :: |
'--------------*/
bool IsOOB(float2 uv) {
return any(uv < 0.0) || any(uv > 1.0);
}
float3 GetColor(float2 uv)
{
return tex2Dlod(ReShade::BackBuffer, float4(uv, 0, 0)).rgb;
}
float3 MotionToColor(float2 motion)
{
float angle = atan2(-motion.y, -motion.x) / 6.283 + 0.5;
float rawLength = length(motion) / (15.0 * BUFFER_PIXEL_SIZE.x);
float compressed = rawLength / (1.0 + rawLength * 1.4); //asymptotic squash
float boosted = pow(compressed, 0.5); //lift shadows
float magnitude = saturate(lerp(compressed, boosted, saturate(rawLength * 3.0)));
float3 hsv = float3(angle, 1, magnitude);
float4 K = float4(1, 2/3.0, 1/3.0, 3);
float3 p = abs(frac(hsv.xxx + K.xyz) * 6 - K.www);
return hsv.z * lerp(K.xxx, clamp(p - K.xxx, 0, 1), hsv.y) + 0.1;
}
float ZMSAD(sampler2D currLumaSrc, sampler2D prevLumaSrc, float2 posA, float2 posB, float2 texelSize, uint mip)
{
static const int2 offsets[9] = {
int2(0, 3),
int2(0, 1),
int2(-3,0), int2(-1,0), int2(0, 0), int2(1,0), int2(3,0),
int2(0,-1),
int2(0,-3)
};
//gather samples and calculate the mean for each patch
float samplesA[9], samplesB[9];
float meanA = 0.0, meanB = 0.0;
[unroll] for(int i = 0; i < 9; i++) {
float2 offset = float2(offsets[i]) * texelSize;
samplesA[i] = tex2Dlod(currLumaSrc, float4(posA + offset, 0, mip)).r;
samplesB[i] = tex2Dlod(prevLumaSrc, float4(posB + offset, 0, mip)).r;
meanA += samplesA[i];
meanB += samplesB[i];
}
meanA /= 9.0;
meanB /= 9.0;
//SAD on the normalized samples
float err = 0.0;
[unroll] for(int i = 0; i < 9; i++)
err += abs((samplesA[i] - meanA) - (samplesB[i] - meanB));
return ((err / 9.0) + EPSILON);
}
float2 Median9(sampler2D flowSrc, float2 uv, float2 texelSize, uint mip)
{
float2 v[9];
int idx = 0;
[unroll] for(int dy = -1; dy <= 1; dy++) for(int dx = -1; dx <= 1; dx++)
v[idx++] = tex2Dlod(flowSrc, float4(uv + float2(dx, dy) * texelSize, 0, mip)).xy;
//bubble sort ensures the Median lands in v[4], only needs 5 passes
//indices 4,5,6,7,8 contain the 5 largest items, so v[4] is the median
[unroll] for(int k = 0; k < 5; k++) for(int i = 0; i < 8 - k; i++) { //checks decrease as right side gets sorted
float2 a = v[i];
float2 b = v[i+1];
v[i] = min(a, b);
v[i+1] = max(a, b);
}
return v[4];
}
float2 BilateralMedian9(sampler2D flowSrc, float2 uv, float2 texelSize, uint mip)
{
static const int2 DENSE_3X3[9] = {
int2(-1,-1), int2(0,-1), int2(1,-1),
int2(-1, 0), int2(0, 0), int2(1, 0),
int2(-1, 1), int2(0, 1), int2(1, 1)
};
float lumaC = tex2Dlod(sCurrLuma, float4(uv, 0, 0)).x;
float lumaW = tex2Dlod(sCurrLuma, float4(uv + float2(-1.0, 0.0) * texelSize, 0, 0)).x;
float lumaE = tex2Dlod(sCurrLuma, float4(uv + float2( 1.0, 0.0) * texelSize, 0, 0)).x;
float lumaN = tex2Dlod(sCurrLuma, float4(uv + float2( 0.0,-1.0) * texelSize, 0, 0)).x;
float lumaS = tex2Dlod(sCurrLuma, float4(uv + float2( 0.0, 1.0) * texelSize, 0, 0)).x;
//central-difference gradient, wider baseline than quad ddx/ddy, derived from real samples
float dxLuma = (lumaE - lumaW) * 0.5;
float dyLuma = (lumaS - lumaN) * 0.5;
float2 v[9];
uint validCount = 0;
[unroll] for (int i = 0; i < 9; i++) {
int2 off = DENSE_3X3[i];
float2 sampleUV = uv + float2(off) * texelSize;
//cardinals + center use sampled luma; diagonals get linear prediction
float sampleLuma = lumaC; //covers (0,0)
if (off.x == -1 && off.y == 0) sampleLuma = lumaW;
else if (off.x == 1 && off.y == 0) sampleLuma = lumaE;
else if (off.x == 0 && off.y == -1) sampleLuma = lumaN;
else if (off.x == 0 && off.y == 1) sampleLuma = lumaS;
else if (off.x != 0 && off.y != 0) sampleLuma = lumaC + float(off.x) * dxLuma + float(off.y) * dyLuma;
bool isValid = abs(lumaC - sampleLuma) <= 0.05;
v[i] = isValid ? tex2Dlod(flowSrc, float4(sampleUV, 0, mip)).xy : float2(1e38, 1e38);
validCount += uint(isValid);
}
if(validCount < 3u) return v[4];
//right-to-left bubble: smallest reaches v[0] per pass; after 5 passes, v[0..4] sorted ascending
[unroll] for(int k = 0; k < 5; k++) for(int j = 7; j >= k; j--) {
float2 a = v[j];
float2 b = v[j+1];
v[j] = min(a, b);
v[j+1] = max(a, b);
}
uint medianIdx = validCount / 2u;
float2 result = v[1]; //fallback for validCount == 3 (medianIdx 1)
if (medianIdx == 2u) result = v[2];
if (medianIdx == 3u) result = v[3];
if (medianIdx == 4u) result = v[4];
return result;
}
float2 ATrousFilter(sampler2D motionSrc, float2 uv, uint dilation, uint mip)
{
static const int2 offsets[8] = { int2(-1,-1), int2(0,-1), int2(1,-1),
int2(-1, 0), int2(1, 0),
int2(-1, 1), int2(0, 1), int2(1, 1) };
float2 centerFlow = tex2Dlod(motionSrc, float4(uv, 0, 0)).xy;
float centerConf = max(tex2Dlod(sConfidence, float4(uv, 0, 0)).r, 0.01); //0.01 floor prevents NaN if conf hits 0
float2 sum = centerFlow * centerConf;
float totalWeight = centerConf;
[unroll] for (int i = 0; i < 8; i++) {
float2 sampleUV = uv + float2(offsets[i]) * dilation * BUFFER_PIXEL_SIZE * 8.0; //*8 = stride of flow grid
float2 sampleFlow = tex2Dlod(motionSrc, float4(sampleUV, 0, 0)).xy;
float2 flowDelta = (sampleFlow - centerFlow) / BUFFER_PIXEL_SIZE * 8.0;
float flowWeight = exp(-dot(flowDelta, flowDelta) * 0.125);
float weight = flowWeight;
sum += sampleFlow * weight;
totalWeight += weight;
}
return sum / (totalWeight + EPSILON);
}
float2 UpscaleFlow(sampler2D coarseSrc, sampler2D currLumaSrc, sampler2D prevLumaSrc, float2 uv, float2 texelSize, uint mip)
{
if(FRAME_COUNT == 0) return float2(0, 0);
float2 coarseTexelSize = rcp(float2(tex2Dsize(coarseSrc, 0)));
//pool candidates for tournament selection. order matters here
float2 candidates[6];
candidates[0] = tex2D(coarseSrc, uv).xy ;
candidates[1] = tex2D(coarseSrc, uv + float2(0, -coarseTexelSize.y)).xy ;
candidates[2] = tex2D(coarseSrc, uv + float2(0, coarseTexelSize.y)).xy ;
candidates[3] = tex2D(coarseSrc, uv - float2(coarseTexelSize.x, 0)).xy ;
candidates[4] = tex2D(coarseSrc, uv + float2(coarseTexelSize.x, 0)).xy ;
candidates[5] = tex2D(sPrevFrameFlow, uv).xy;
float minCost = 1e6;
float2 prediction = candidates[0];
[loop] for (int i = 0; i < 6; i++) {
float cost = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + candidates[i], texelSize, mip);
if (cost < minCost) {
minCost = cost;
prediction = candidates[i];
}
}
//refinement with parabolic fitting
float costLeft = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction - float2(texelSize.x, 0), texelSize, mip);
float costRight = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction + float2(texelSize.x, 0), texelSize, mip);
float costDown = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction - float2(0, texelSize.y), texelSize, mip);
float costUp = ZMSAD(currLumaSrc, prevLumaSrc, uv, uv + prediction + float2(0, texelSize.y), texelSize, mip);
//sub-pixel offset (parabolic fitting)
float2 subpixelOffset;
subpixelOffset.x = (costLeft - costRight) / (4.0 * (costLeft + costRight - 2.0 * minCost) + EPSILON); //EPSILON for flat surface handling
subpixelOffset.y = (costDown - costUp) / (4.0 * (costDown + costUp - 2.0 * minCost) + EPSILON);
//clamp offset to a reasonable range
subpixelOffset = clamp(subpixelOffset, -0.5, 0.5);
return (prediction+subpixelOffset*texelSize);
}
/*--------------.
| :: SHADERS :: |
'--------------*/
float PS_PackFeatures(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float luma = dot(GetColor(uv), float3(0.2126, 0.7152, 0.0722));
return luma * rcp(1.0 + luma);
}
float2 PS_ComputeFlow128(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
if(FRAME_COUNT == 0) return float2(0, 0);
static const int SEARCH_RADIUS = 3;
static const uint mip = 5;
float2 texelSize = BUFFER_PIXEL_SIZE * exp2(mip);
//candidate seeds for the coarsest level for tournament selection
float2 prevSeed = tex2D(sPrevFrameFlow, uv).xy;
float2 zeroSeed = float2(0, 0);
float prevCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + prevSeed, texelSize, mip);
float zeroCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + zeroSeed, texelSize, mip);
float2 seed = (zeroCost < prevCost) ? zeroSeed : prevSeed; //pick better candidate as seed
float2 bestFlow = seed;
float minCost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv+seed, texelSize, mip);
//search in a grid AROUND the seed
for (int y = -SEARCH_RADIUS; y <= SEARCH_RADIUS; ++y) for (int x = -SEARCH_RADIUS; x <= SEARCH_RADIUS; ++x) {
if (x == 0 && y == 0) continue;
float2 candidateFlow = seed + float2(x, y) * texelSize;
float cost = ZMSAD(sCurrLuma, sPrevLuma, uv, uv + candidateFlow, texelSize, mip);
if (cost < minCost) {
minCost = cost;
bestFlow = candidateFlow;
if (minCost < 0.01) //near-perfect match found
return bestFlow;
}
}
return bestFlow;
}
float2 PS_UpscaleFlow64(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow128, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*16.0, 4);
}
float2 PS_MedianPass64(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return Median9(sFlow64A, uv, BUFFER_PIXEL_SIZE*64.0, 6);
}
float2 PS_UpscaleFlow32(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow64B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*8.0, 3);
}
float2 PS_MedianPass32(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return Median9(sFlow32A, uv, BUFFER_PIXEL_SIZE*32.0, 5);
}
float2 PS_UpscaleFlow16(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow32B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*4.0, 2);
}
float2 PS_MedianPass16(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return Median9(sFlow16A, uv, BUFFER_PIXEL_SIZE*16.0, 4);
}
float2 PS_UpscaleFlow8(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return UpscaleFlow(sFlow16B, sCurrLuma, sPrevLuma, uv, BUFFER_PIXEL_SIZE*2.0, 1);
}
float2 PS_MedianPass8(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return BilateralMedian9(sFlow8, uv, BUFFER_PIXEL_SIZE*8.0, 3);
}
float2 PS_ATrousPassA(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target //stride 1
{
return ATrousFilter(sFlow, uv, 2, 3);
}
float2 PS_ATrousPassB(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target //stride 2
{
float2 flow = ATrousFilter(sFlow8, uv, 4, 1);
//kill sub-pixel noise
float flowPixelMag = length(flow / BUFFER_PIXEL_SIZE);
float gate = saturate(1.0 - pow(1.0 - saturate(saturate(flowPixelMag) - 0.2), 10.0)); //SNAP TO REALITY
return flow*gate;
}
float PS_Confidence(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
if(FRAME_COUNT == 0) return 0.0; //no confidence
float2 flow = tex2D(sFlow, uv).xy;
float2 prevUV = uv + flow; //warp prev frame forward
if(IsOOB(prevUV)) return 0.0;
float currLuma = tex2Dlod(sCurrLuma, float4(uv, 0, 3)).r;
float prevLuma = tex2Dlod(sPrevLuma, float4(prevUV, 0, 3)).r;
float lumaError = abs(currLuma - prevLuma);
if(lumaError > 0.1) return 0.0; //no confidence
float subpixelThreshold = length(BUFFER_PIXEL_SIZE);
float flowMagnitude = length(flow);
if (flowMagnitude <= subpixelThreshold) return 0.9; //if flow is subpixel, high confidence
float motionPenalty = flowMagnitude / subpixelThreshold;
float lengthConfidence = rcp(motionPenalty * 0.07 + 1.0);
float photometricConfidence = exp(-lumaError * 8.0 * lengthConfidence);
//current frame final confidence
float currentConf = lengthConfidence * photometricConfidence;
//temporal filter
float historyConf = tex2D(sPrevConfidence, prevUV).r;
float alpha = (currentConf < historyConf - 0.05) ? 0.5 : 0.1; //drop fast (kill speckles promptly), regain slowly (stay stable)
return lerp(historyConf, currentConf, alpha);
}
void PS_StoreFlow(float4 pos : SV_Position, float2 uv : TEXCOORD, out float2 flow : SV_Target0, out float confidence : SV_Target1)
{
flow = tex2D(sFlow, uv).xy;
confidence = tex2D(sConfidence, uv).r;
}
float PS_StoreLuma(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return tex2D(sCurrLuma, uv).r;
}
#if DEBUG_FLOW
float4 PS_Debug(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
return float4(MotionToColor(tex2D(sFlow, uv).xy), 1);
}
#endif
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique Lumenite_QuantMotion <
ui_label = "LUMENITE: QuantMotion";
ui_tooltip = "Superfast motion vectors for ReShade.";
>
{
//optical flow
pass { VertexShader = PostProcessVS; PixelShader = PS_PackFeatures; RenderTarget = tCurrLuma; }
pass { VertexShader = PostProcessVS; PixelShader = PS_ComputeFlow128; RenderTarget = tFlow128; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow64; RenderTarget = tFlow64A; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass64; RenderTarget = tFlow64B; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow32; RenderTarget = tFlow32A; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass32; RenderTarget = tFlow32B; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow16; RenderTarget = tFlow16A; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass16; RenderTarget = tFlow16B; }
pass { VertexShader = PostProcessVS; PixelShader = PS_UpscaleFlow8; RenderTarget = tFlow8; }
pass { VertexShader = PostProcessVS; PixelShader = PS_MedianPass8; RenderTarget = tFlow; }
pass { VertexShader = PostProcessVS; PixelShader = PS_Confidence; RenderTarget = tConfidence; }
pass { VertexShader = PostProcessVS; PixelShader = PS_ATrousPassA; RenderTarget = tFlow8; }
pass { VertexShader = PostProcessVS; PixelShader = PS_ATrousPassB; RenderTarget = tFlow; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreFlow; RenderTarget0 = tPrevFrameFlow; RenderTarget1 = tPrevConfidence; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreLuma; RenderTarget = tPrevLuma; }
//debug views
#if DEBUG_FLOW
pass { VertexShader = PostProcessVS; PixelShader = PS_Debug; }
#endif
}
}
@@ -0,0 +1,300 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_RTAO.fx
Version : 2026.05.30
Author : Afzaal (Kaidō)
Description: Ray Traced Ambient Occlusion (Screen Space).
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define FOV 60.0
#define NEAR_PLANE 0.01
#define INITIAL_STEP_SCALE 0.9
#define STEP_GROWTH_FACTOR 1.2
#define AO_MAX_MARCH_STEPS 15
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
#include "./include/lumenite_Projections.fxh"
#include "./include/lumenite_Helpers.fxh"
#include "./include/lumenite_ColorManagement.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
uniform bool DEBUG_VIEW <
ui_label = "Show AO Mask";
ui_tooltip = "Debug view for the AO. Shows raw AO.";
ui_category = "Ambient Occlusion";
> = 0;
uniform float DEPTH_BOUNDARY <
ui_type = "slider";
ui_min = 0.001; ui_max = 0.999; ui_step = 0.001;
ui_label = "AO Range";
ui_tooltip = "The Z+ range/depth in which the effect is applied.";
ui_category = "Ambient Occlusion";
hidden = false;
> = 0.6;
uniform float DEPTH_FADE_START <
ui_type = "slider";
ui_min = 0.1; ui_max = 1.0; ui_step = 0.01;
ui_label = "Z+ Fade Start (%)";
ui_tooltip = "Z+ fraction where effect starts fading out (relative to AO Range)";
ui_category = "Ambient Occlusion";
hidden = true;
> = 0.75;
uniform float AO_INTENSITY <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0;
ui_label = "AO Strength";
ui_tooltip = "Controls the intensity of the ambient occlusion effect.";
ui_category = "Ambient Occlusion";
> = 1.0;
//deprecated
// uniform int USER_GUIDE <
// ui_type = "radio";
// ui_category = "";
// ui_label = " ";
// ui_text = "RESOLUTION_SCALING:\n0: Renders AO at full-resolution.\n1: Renders AO at half-resolution.";
// >;
/*--------------.
| :: IMPORTS :: |
'--------------*/
namespace Kernel {
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sConfidence { Texture = tConfidence; };
texture tNormals { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; MipLevels = 4; };
sampler sNormals { Texture = tNormals; };
texture2D tDepth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 4; };
sampler2D sDepth { Texture = tDepth; };
}
namespace LumeniteRTAO {
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
texture tAOTrace { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = R16F; };
sampler sAOTrace { Texture = tAOTrace; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
texture tAO1 { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sAO1 { Texture = tAO1; AddressU = CLAMP; AddressV = CLAMP; MagFilter = POINT; MinFilter = POINT; MipFilter = POINT; };
sampler sAO1Linear { Texture = tAO1; AddressU = CLAMP; AddressV = CLAMP; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; };
texture tPrevAO { Width = BUFFER_WIDTH / 2; Height = BUFFER_HEIGHT / 2; Format = RG16F; };
sampler sPrevAO { Texture = tPrevAO; AddressU = CLAMP; AddressV = CLAMP; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; };
/*--------------.
| :: HELPERS :: |
'--------------*/
void BuildOrthonormalBasis(float3 n, out float3 b1, out float3 b2)
{
if (n.z < -0.9999999) {
b1 = float3(0.0, -1.0, 0.0);
b2 = float3(-1.0, 0.0, 0.0);
} else {
float a = rcp(1.0 + n.z);
float b = -n.x * n.y * a;
b1 = float3(mad(-n.x * n.x, a, 1.0), b, -n.x);
b2 = float3(b, mad(-n.y * n.y, a, 1.0), -n.y);
}
}
float3 GenerateHemisphereDirection(float3 normal, float2 rand, float3 tangent, float3 bitangent)
{
float phi = rand.x * 6.28318530718; //2.0*PI as constant
float sinPhi, cosPhi;
sincos(phi, sinPhi, cosPhi);
float cosTheta = sqrt(1.0 - rand.y);
float sinTheta = sqrt(rand.y);
float3 result = normal * cosTheta;
result = mad(bitangent, sinTheta * sinPhi, result);
result = mad(tangent, sinTheta * cosPhi, result);
return result;
}
float CalculateDepthFade(float depth)
{
float fadeStartDepth = DEPTH_BOUNDARY * DEPTH_FADE_START;
float fadeRange = DEPTH_BOUNDARY - fadeStartDepth;
return 1.0 - saturate((depth - fadeStartDepth) / fadeRange);
}
float2 ATrousFilter(sampler SourceSampler, float2 uv, uint dilation)
{
float4 gbuffer = tex2D(Kernel::sNormals, uv);
if (gbuffer.a == 0 || gbuffer.a >= DEPTH_BOUNDARY) return float2(1.0, 0.0);
float confidence = tex2Dlod(Kernel::sConfidence, float4(uv, 0, 0)).r;
dilation += uint(round((1.0 - confidence))); //scale filter radius with motion
float2 centerData = tex2Dlod(SourceSampler, float4(uv, 0, 0)).rg;
float variance = max(0.0, centerData.g - (centerData.r * centerData.r)); //Moment - AO^2
variance = max(variance, 0.0001);
float2 sum = centerData;
float totalWeight = 1.0;
for (int y = -1; y <= 1; y++) for (int x = -1; x <= 1; x++) {
if (x == 0 && y == 0) continue;
float2 sampleUV = uv + float2(x, y) * dilation * (BUFFER_PIXEL_SIZE * 2.0); //don't forget the x2.0 to properly step half-res grid!
float2 sampleData = tex2Dlod(SourceSampler, float4(sampleUV, 0, 0)).rg;
float4 sampleGeo = tex2Dlod(Kernel::sNormals, float4(sampleUV, 0, 0));
float depthWeight = exp(-abs(gbuffer.a - sampleGeo.a) / (gbuffer.a * 0.02 + 0.001));
float normalWeight = pow(saturate(dot(gbuffer.rgb, sampleGeo.rgb)), 50.0);
float aoDiff = centerData.r - sampleData.r;
float aoWeight = exp(-(aoDiff * aoDiff) / (variance + 0.0001));
float weight = depthWeight * normalWeight * aoWeight;
sum += sampleData * weight;
totalWeight += weight;
}
return sum / (totalWeight + EPSILON);
}
/*--------------.
| :: SHADERS :: |
'--------------*/
float PS_TraceAO(VSOUT input) : SV_Target
{
//deprecated
// if (CHECKERBOARD_RENDERING) {
// #if RESOLUTION_SCALING
// if(CheckerboardSkip(uint2(input.vpos.xy), 2.0)) discard;
// #else
// if(CheckerboardSkip(uint2(input.vpos.xy), 1.0)) discard;
// #endif
// }
float4 gbuffer = tex2D(Kernel::sNormals, input.uv);
float3 normal = gbuffer.rgb;
float depth = gbuffer.a;
if (depth == 0 || depth >= DEPTH_BOUNDARY) discard;
float3 startPos = UVToViewSpace(input.uv, depth, input);
float3 tangent, bitangent;
BuildOrthonormalBasis(normal, tangent, bitangent);
float2 noise = GetStratifiedNoise(input.vpos.xy);
float3 rayDir = GenerateHemisphereDirection(normal, noise, tangent, bitangent);
float invDepth = rcp(depth);
float totalRayLength = 0.02 * depth;
float initialStepScale = INITIAL_STEP_SCALE * rcp((float)AO_MAX_MARCH_STEPS);
float stepSize = totalRayLength * initialStepScale;
float3 rayPos = mad(rayDir, stepSize * 0.5, startPos);
rayPos += normal * depth * 0.0005; //push ray slightly OUTWARD along the normal; clears staircase artifacts
float occlusion = 0.0;
[loop]
for (int step = 0; step < AO_MAX_MARCH_STEPS; step++) {
float2 sampleUV = ViewSpaceToUV(rayPos, input);
float sceneDepth = GetDepth(sampleUV);
float depthDiff = rayPos.z - sceneDepth;
[branch]
if (depthDiff > 0.0 && depthDiff < rayPos.z) {
float3 scenePos = UVToViewSpace(sampleUV, sceneDepth, input);
float hitDistance = length(scenePos - startPos);
float normalizedDistance = hitDistance * invDepth;
occlusion = exp(-normalizedDistance * 15.0);
break;
}
stepSize *= STEP_GROWTH_FACTOR;
rayPos = mad(rayDir, stepSize, rayPos);
}
float aoFactor = 1.0 - saturate(occlusion * AO_INTENSITY);
return aoFactor;
}
float2 PS_TemporalFilter(VSOUT input) : SV_Target
{
float depth = tex2D(Kernel::sDepth, input.uv).r;
//overwrite noise at boundary with clean White, prevents gaps
if (depth >= DEPTH_BOUNDARY) return float2(1.0, 1.0); //1.0 AO, 1.0 Moment
if (depth == 0) discard;
float ao = tex2D(sAOTrace, input.uv).r;
ao = lerp(1.0, ao, CalculateDepthFade(depth));
float moment = ao * ao;
float2 flow = tex2D(Kernel::sFlow, input.uv).xy;
float confidence = tex2D(Kernel::sConfidence, input.uv).x;
confidence = saturate(confidence + log2(2.0 - confidence) * 0.6); //boost confidence
float2 rawHistory = tex2D(sPrevAO, input.uv + flow).rg; //history stores "1.0 - AO". 0.0 (Black Texture) -> Reads as 1.0 (White)
float prevAO = 1.0 - rawHistory.r;
float prevMoment = 1.0 - rawHistory.g;
float alpha = confidence * 0.98;
ao = lerp(ao, prevAO, alpha);
moment = lerp(moment, prevMoment, alpha);
//max(..., 0.001) to ensure we NEVER write exactly 0.0 again
//this tells the next frame "I contain data"
return float2(max(ao, 0.001), max(moment, 0.001));
}
float2 PS_StoreAO(VSOUT input) : SV_Target
{
//must prevent history collision here
//if we store exactly 0.0 (means White), the next frame's blend pass thinks
//history is empty and resets it, causing shimmer
//so clamp to 0.0001 so the system knows "This is valid history data"
float2 data = tex2D(sAO1, input.uv).rg;
return float2(max(1.0 - data.r, 0.0001), max(1.0 - data.g, 0.0001)); //store inverted
}
float4 PS_ToDisplay(VSOUT input) : SV_Target
{
float depth = tex2D(Kernel::sDepth, input.uv).r;
float ao = ATrousFilter(sAO1Linear, input.uv, 2).r; //stable AO mask (fades to 1.0)
if (DEBUG_VIEW) {
#if BUFFER_COLOR_SPACE > 1
return float4(ToOutputColorspace(ao.xxx, true), 1.0);
#else
return float4(ao.xxx, 1.0);
#endif
}
if (depth == 0 || depth >= DEPTH_BOUNDARY) discard;
float3 base = GetLinearColor(input.uv, true);
base *= ao;
return float4(ToOutputColorspace(base, true), 1.0);
}
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique Lumenite_RTAO <
ui_label = "LUMENITE: RTAO";
ui_tooltip = "Ray Traced Ambient Occlusion (Screen Space).";
>
{
pass { VertexShader = VS; PixelShader = PS_TraceAO; RenderTarget = tAOTrace; }
pass { VertexShader = VS; PixelShader = PS_TemporalFilter; RenderTarget = tAO1; }
pass { VertexShader = VS; PixelShader = PS_StoreAO; RenderTarget = tPrevAO; }
pass { VertexShader = VS; PixelShader = PS_ToDisplay; }
}
}
@@ -0,0 +1,360 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_SSSR.fx
Version : 2026.09.06
Author : Afzaal (Kaidō)
Description: Stochastic Screen Space Reflections.
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#define FOV 70.0
#define NEAR_PLANE 0.5
#define RAY_LENGTH_SCALE 9.0
#define RAY_ORIGIN_BIAS -0.0004
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
#include "./include/lumenite_Projections.fxh"
#include "./include/lumenite_Helpers.fxh"
#include "./include/lumenite_ColorManagement.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
uniform bool SMOOTH_SHADING <
ui_label = "Smooth Shading";
ui_tooltip = "Slightly smoothens the raw normals. Turn OFF if SMOOTH_NORMALS is enabled in Kernel.";
> = 1;
uniform float DEPTH_BOUNDARY <
ui_type = "slider";
ui_min = 0.001; ui_max = 0.999; ui_step = 0.001;
ui_label = "SSSR Range";
ui_tooltip = "The Z+ range/depth in which the effect is applied.";
ui_category = "";
hidden = false;
> = 0.85;
uniform float DEPTH_FADE_START <
ui_type = "slider";
ui_min = 0.1; ui_max = 1.0; ui_step = 0.01;
ui_label = "Z+ Fade Start (%)";
ui_tooltip = "Z+ fraction where effect starts fading out (relative to Z+ boundary)";
ui_category = "";
hidden = true;
> = 0.75;
uniform int MAX_STEPS <
ui_type = "drag";
ui_min = 1; ui_max = 32; ui_step = 1;
ui_label = "Ray Resolution";
ui_category = "";
ui_tooltip = "";
> = 32;
uniform int BINARY_SEARCH_STEPS <
ui_type = "drag";
ui_min = 1; ui_max = 8; ui_step = 1;
ui_label = "Hit Refinement";
ui_category = "";
ui_tooltip = "";
> = 4;
uniform float F0 <
ui_type = "drag";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.001;
ui_label = "Base Reflectivity (F0)";
ui_category = "";
ui_tooltip = "";
> = 1.0;
uniform float ROUGHNESS <
ui_type = "drag";
ui_min = 0.0; ui_max = 0.3; ui_step = 0.001;
ui_label = "Roughness";
ui_category = "";
ui_tooltip = "";
> = 0.1;
uniform float BUMP_SCALE <
ui_type = "drag";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.001;
ui_label = "Bump Detail";
ui_tooltip = "Scale of the extracted bump details. Lower = finer bumps.";
> = 0.5;
uniform float TAIL_FEATHERING <
ui_type = "drag";
ui_min = 0.0; ui_max = 5.0; ui_step = 0.001;
ui_label = "Tail Feathering";
ui_category = "";
ui_tooltip = "";
> = 0.0;
/*--------------.
| :: IMPORTS :: |
'--------------*/
namespace Kernel {
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sConfidence { Texture = tConfidence; };
texture tNormals { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; MipLevels = 4; };
sampler sNormals { Texture = tNormals; };
texture2D tDepth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 4; };
sampler2D sDepth { Texture = tDepth; };
}
namespace LumeniteSSSR {
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
texture tSpec1 { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sSpec1 { Texture = tSpec1; AddressU = CLAMP; AddressV = CLAMP; };
texture tSpec2 { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sSpec2 { Texture = tSpec2; AddressU = CLAMP; AddressV = CLAMP; };
texture tPrevSpec { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sPrevSpec { Texture = tPrevSpec; AddressU = CLAMP; AddressV = CLAMP; };
/*--------------.
| :: HELPERS :: |
'--------------*/
float CalculateDepthFade(float depth)
{
float fadeStartDepth = DEPTH_BOUNDARY * DEPTH_FADE_START;
float fadeRange = DEPTH_BOUNDARY - fadeStartDepth;
return 1.0 - saturate((depth - fadeStartDepth) / fadeRange);
}
float3 CalculateSmoothNormal(float2 uv, float4 gbuffer, int dilation, sampler SrcSampler)
{
float3 normal = gbuffer.rgb;
float depth = gbuffer.a;
float3 normalSum = normal;
float weightSum = 1.0;
[unroll] for(int dy = -4; dy <= 4; dy++) for (int dx = -4; dx <= 4; dx++) {
float2 sampleUV = uv + float2(dx, dy) * ReShade::PixelSize * dilation;
float4 neighborData = tex2Dlod(SrcSampler, float4(sampleUV, 0, 0));
float depthDiff = abs(neighborData.a - depth);
float normalDot = max(dot(normal, neighborData.rgb), 0.0);
float weight = exp(-depthDiff * 300.0) * pow(normalDot, 20.0);
normalSum += neighborData.rgb * weight;
weightSum += weight;
}
return normalize(normalSum / weightSum);
}
float3 GetBackBuffer(float2 uv)
{
return tex2Dlod(ReShade::BackBuffer, float4(uv,0,0)).rgb;
}
float3 CalculateBumpyNormal(float2 uv, float3 geoNormal)
{
float2 texelSize = BUFFER_PIXEL_SIZE * BUMP_SCALE;
float3 lumaWeights = float3(0.299, 0.587, 0.114);
//use gamma space intentionally
float lumaCenter = dot(GetBackBuffer(uv), lumaWeights);
float lumaRight = dot(GetBackBuffer(uv + float2(texelSize.x, 0.0)), lumaWeights);
float lumaBottom = dot(GetBackBuffer(uv + float2(0.0, texelSize.y)), lumaWeights);
//luma gradients
float dx = (lumaRight - lumaCenter) * 2.5;
float dy = (lumaBottom - lumaCenter) * 2.5;
//orthogonal tangent basis around macro geometry normal
float3 up = abs(geoNormal.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
float3 tangent = normalize(cross(up, geoNormal));
float3 bitangent = cross(geoNormal, tangent);
//2D bump gradient to 3D tangent-space normal
float3 bumpVec = normalize(float3(-dx, -dy, 1.0));
return normalize(tangent * bumpVec.x + bitangent * bumpVec.y + geoNormal * bumpVec.z);
}
/*--------------.
| :: SHADERS :: |
'--------------*/
float4 PS_TraceSpecular(VSOUT input) : SV_Target
{
float4 gbuffer = tex2D(Kernel::sNormals, input.uv);
float3 normal = gbuffer.rgb;
float depth = gbuffer.a;
if (depth <= 0.0 || depth > DEPTH_BOUNDARY) return float4(0, 0, 0, 1);
//process normals
if (SMOOTH_SHADING) normal = CalculateSmoothNormal(input.uv, gbuffer, 3, Kernel::sNormals);
if (BUMP_SCALE > 0.0) normal = CalculateBumpyNormal(input.uv, normal);
float3 StartPos = UVToViewSpace(input.uv, depth, input);
float3 viewDir = normalize(-StartPos);
float dynamicRayLengthNormalized = min(RAY_LENGTH_SCALE * depth, 1.0-depth);
float stepSize = dynamicRayLengthNormalized / float(MAX_STEPS);
float3 mirrorDir = reflect(-viewDir, normal);
if (dot(mirrorDir, mirrorDir) < 0.001) { //mirror reflection validation chck
return float4(0, 0, 0, 1);
}
float2 noise = GetStratifiedNoise(input.vpos.xy);
float3 jitterN = normalize(normal + float3((noise * 2.0 - 1.0) * ROUGHNESS * 0.2, 0.0));
float3 rayDir = reflect(-viewDir, jitterN);
if (dot(rayDir, normal) < 0.0) rayDir = mirrorDir; //prevent jitter from pushing ray inside the geometry
float biasedOffset = RAY_ORIGIN_BIAS + (depth * RAY_ORIGIN_BIAS * 0.01); //intentional -ve origin bias
float3 biasedStartPos = StartPos - (normal * biasedOffset); //deliberately pushes the ray slightly into the floor, makes it immediately collide with the floor's depth, killing "joined reflections"
float t = stepSize * noise.x;
float3 spec = float3(0.0, 0.0, 0.0);
bool hitFound = false;
float distanceRatio = 0.0;
float2 finalUV = 0.0;
for (int i = 0; i < MAX_STEPS; i++)
{
if (t >= dynamicRayLengthNormalized)
break;
float3 currentPos = biasedStartPos + rayDir * t;
float2 hitUV = ViewSpaceToUV(currentPos, input);
if (IsOOB(hitUV))
break;
float sceneDepth = tex2Dlod(Kernel::sDepth, float4(hitUV, 0, 0)).r;
if (sceneDepth > DEPTH_BOUNDARY) {
t += stepSize;
continue;
}
float3 scenePos = UVToViewSpace(hitUV, sceneDepth, input);
float depthDiff = currentPos.z - scenePos.z;
if (depthDiff > 0.0) //passed behind surface
{
float gateThreshold = dynamicRayLengthNormalized * 0.1; //initially, a broad thickness check
float extraThickness = (t > gateThreshold) ? 0.01 : 0.0; //if t is past the threshold, add extra thickness
float dynamicThickness = (currentPos.z * 0.05) + extraThickness;
if (depthDiff < dynamicThickness)
{
//binary search refinement
float binarySearchT = t;
float binarySearchStep = stepSize;
float3 binarySearchCurrentPos = currentPos;
float2 binarySearchUV = hitUV;
float3 binarySearchScenePos = scenePos;
for (int j = 0; j < BINARY_SEARCH_STEPS; j++) {
binarySearchStep *= 0.5;
binarySearchT += (binarySearchCurrentPos.z > binarySearchScenePos.z) ? -binarySearchStep : binarySearchStep; //move backwards if behind the surface, otherwise forwards
binarySearchCurrentPos = biasedStartPos + rayDir * binarySearchT;
binarySearchUV = ViewSpaceToUV(binarySearchCurrentPos, input);
float binarySearchSceneDepth = tex2Dlod(Kernel::sDepth, float4(binarySearchUV, 0, 0)).r;
binarySearchScenePos = UVToViewSpace(binarySearchUV, binarySearchSceneDepth, input);
}
float finalDepthDiff = binarySearchCurrentPos.z - binarySearchScenePos.z;
if (abs(finalDepthDiff) < (binarySearchCurrentPos.z * 0.01 + 0.01)) { //tighter thickness tolerance on the final refined hit to discard empty space behind thin grass
hitFound = true;
distanceRatio = binarySearchT / dynamicRayLengthNormalized;
finalUV = binarySearchUV;
break;
}
}
}
t += stepSize * noise.y;
}
if (hitFound) {
float3 hitColor = GetLinearColor(finalUV, false);
float2 edgeFadeUV = abs(finalUV * 2.0 - 1.0);
float edgeFade = saturate(1.0 - max(edgeFadeUV.x, edgeFadeUV.y));
edgeFade = smoothstep(0.0, 0.05, edgeFade);
float maxDistFade = pow(saturate(1.0 - distanceRatio), TAIL_FEATHERING + EPSILON);
spec = hitColor * maxDistFade * edgeFade;
}
return float4(spec, 1.0);
}
float4 PS_TemporalBlend(VSOUT input) : SV_Target
{
float depth = tex2D(Kernel::sDepth, input.uv).r;
if (depth >= DEPTH_BOUNDARY) return float4(0, 0, 0, 0);
float3 spec = tex2D(sSpec1, input.uv).rgb;
float2 flow = tex2D(Kernel::sFlow, input.uv).xy;
float confidence = tex2D(Kernel::sConfidence, input.uv).x;
confidence = saturate(confidence + log2(2.0 - confidence) * 0.5);
float3 prevSpec = tex2D(sPrevSpec, input.uv + flow).rgb;
float historyMax = max(prevSpec.r, max(prevSpec.g, prevSpec.b));
float blendWeight = (historyMax < 0.00001) ? 0.0 : (confidence * 0.98);
float3 blended = lerp(spec, prevSpec, blendWeight);
return float4(blended, 1.0);
}
float4 PS_StoreHistory(VSOUT input) : SV_Target
{
float depth = tex2D(Kernel::sDepth, input.uv).r;
if (depth >= DEPTH_BOUNDARY) return float4(0, 0, 0, 0); //if past boundary, store 0.0 to 'clear' history for next frame
return float4(max(tex2D(sSpec2, input.uv).rgb, 0.0001), 1.0); //clamp to 0.0001 so it knows 'valid hist data', prevents shimmer at depth boundary edges
}
float4 PS_ToDisplay(VSOUT input) : SV_Target
{
float3 base = GetLinearColor(input.uv, false);
float4 gbuffer = tex2D(Kernel::sNormals, input.uv);
float3 normal = gbuffer.rgb;
float depth = gbuffer.a;
float3 surfacePos = UVToViewSpace(input.uv, depth, input);
float depthFade = CalculateDepthFade(depth);
float3 viewDir = normalize(-surfacePos);
float NdotV = saturate(dot(normal, viewDir));
float fresnel = F0 + (1.0 - F0) * pow(1.0 - NdotV, 5.0); //schlick's approximation
float3 spec = tex2D(sSpec2, input.uv).rgb;
spec *= depthFade;
spec *= fresnel;
float reflectionMask = saturate(length(spec) + fresnel * 0.5);
float3 conservationBase = base * (1.0 - reflectionMask * 0.7 * depthFade);
return float4(ToOutputColorspace(conservationBase + spec, false), 1.0);
}
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique LUMENITE_SSSR <
ui_label = "LUMENITE: SSSR";
ui_tooltip = "Stochastic Screen Space Reflections.";
>
{
pass { VertexShader = VS; PixelShader = PS_TraceSpecular; RenderTarget = tSpec1; }
pass { VertexShader = VS; PixelShader = PS_TemporalBlend; RenderTarget = tSpec2; }
pass { VertexShader = VS; PixelShader = PS_ToDisplay; }
pass { VertexShader = VS; PixelShader = PS_StoreHistory; RenderTarget = tPrevSpec; }
}
}
@@ -0,0 +1,525 @@
/*
========================================================================
Copyright (c) Afzaal. All rights reserved.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
GitHub : https://github.com/umar-afzaal/LumeniteFX
Discord : https://discord.gg/deXJrW2dx6
Filename : lumenite_TRAA.fx
Version : 2026.07.28
Author : Afzaal (Kaidō)
Description: Temporal Reprojection Anti-Aliasing
License : AGNYA License (https://github.com/nvb-uy/AGNYA-License)
========================================================================
*/
/*------------------.
| :: DEFINITIONS :: |
'------------------*/
#ifndef ENABLE_DLAA
#define ENABLE_DLAA 1
#endif
/*--------------.
| :: HEADERS :: |
'--------------*/
#include "ReShade.fxh"
#include "./include/lumenite_ColorManagement.fxh"
#include "./include/lumenite_Helpers.fxh"
/*---------------.
| :: UNIFORMS :: |
'---------------*/
uniform int SHOW_STATUS <
ui_type = "radio";
ui_label = " ";
#if ENABLE_DLAA
ui_text = "DLAA Prepass: Enabled.";
#else
ui_text = "DLAA Prepass: Disabled.";
#endif
>;
#if ENABLE_DLAA
uniform bool DEBUG_EDGES <
ui_label = "Show Edge Mask";
ui_tooltip = "Paints the detected edge mask over black background.";
> = false;
uniform int EDGE_MODE <
ui_type = "combo";
ui_label = "Edge Detection";
ui_items = "Luma\0Geometric\0";
ui_tooltip = "Luma: shading and texture edges as well; the classic DLAA mask.\n"
"Geometric: silhouettes only, ignores flat UI.";
> = 0;
#endif
uniform float HISTORY_BLEND <
ui_type = "slider";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
ui_label = "Temporal Blend";
hidden = false;
> = 0.9;
uniform float SHARP_STRENGTH <
ui_type = "drag";
ui_min = 0; ui_max = 2.0; ui_step = 0.05;
ui_label = "Adaptive Sharpen";
hidden = false;
> = 1.0;
uniform float MAX_SHARP_DIFF <
ui_type = "drag";
ui_min = 0.05; ui_max = 0.25; ui_step = 0.01;
ui_label = "Sharpen Guard";
ui_tooltip = "Higher = more aggressive sharpening allowed.\nLower = tighter anti-ringing clamp.";
hidden = true;
> = 0.1;
uniform float HFI_INTENSITY <
ui_type = "drag";
ui_min = 0.0; ui_max = 0.1; ui_step = 0.001;
ui_label = "High-Frequency Injection";
ui_tooltip = "Re-injects detail lost during Temporal blend.";
> = 0.01;
/*--------------.
| :: IMPORTS :: |
'--------------*/
namespace Kernel {
texture2D tFlow { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = RG16F; };
sampler2D sFlow { Texture = tFlow; MagFilter = POINT; MinFilter = POINT; };
texture2D tConfidence { Width = BUFFER_WIDTH/8; Height = BUFFER_HEIGHT/8; Format = R16F; };
sampler2D sConfidence { Texture = tConfidence; };
texture tNormals { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; MipLevels = 4; };
sampler sNormals { Texture = tNormals; };
texture2D tDepth { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = R16F; MipLevels = 4; };
sampler2D sDepth { Texture = tDepth; };
}
namespace LumeniteTRAA {
/*---------------------.
| :: RENDER TARGETS :: |
'---------------------*/
#if ENABLE_DLAA
texture tDLAAPreFilter { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sDLAAPreFilter { Texture = tDLAAPreFilter; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = LINEAR; };
texture tDLAAPrePass { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sDLAAPrePass { Texture = tDLAAPrePass; MagFilter = POINT; MinFilter = POINT; };
#endif
texture tCurrHistory { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sCurrHistory { Texture = tCurrHistory; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; };
texture tPrevHistory { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA16F; };
sampler sPrevHistory { Texture = tPrevHistory; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = LINEAR; };
/*--------------.
| :: HELPERS :: |
'--------------*/
//5-Tap 2D Catmull-Rom Filter (Brian Karis / Unreal Engine)
float3 SampleCatmullRom5Tap(sampler tex, float2 uv) {
float2 pos = uv * float2(BUFFER_WIDTH, BUFFER_HEIGHT);
float2 centerPos = floor(pos - 0.5) + 0.5;
float2 f = pos - centerPos;
//1D Catmull-Rom weights
float2 f2 = f * f;
float2 f3 = f2 * f;
float2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f));
float2 w1 = 1.0 + f2 * (-2.5 + 1.5 * f);
float2 w2 = f * (0.5 + f * (2.0 - 1.5 * f));
float2 w3 = f2 * (-0.5 + 0.5 * f);
//group the inner positive lobes (w1, w2) for bilinear hardware
float2 w12 = w1 + w2;
float2 offset12 = w2 / (w12 + 0.00001); // Prevent div by zero
//5-tap texture fetch in a cross pattern
float2 texCoord0 = (centerPos - float2(1.0, 0.0) + float2(0.0, offset12.y)) * BUFFER_PIXEL_SIZE; //left
float2 texCoord1 = (centerPos + float2(2.0, 0.0) + float2(0.0, offset12.y)) * BUFFER_PIXEL_SIZE; //right
float2 texCoord2 = (centerPos + float2(offset12.x, -1.0)) * BUFFER_PIXEL_SIZE; //top
float2 texCoord3 = (centerPos + float2(offset12.x, 2.0)) * BUFFER_PIXEL_SIZE; //bottom
float2 texCoord4 = (centerPos + offset12) * BUFFER_PIXEL_SIZE; //center
//final 2D weights for the 5 taps
float weight0 = w0.x * w12.y; //left
float weight1 = w3.x * w12.y; //right
float weight2 = w12.x * w0.y; //top
float weight3 = w12.x * w3.y; //bottom
float weight4 = w12.x * w12.y; //center
//normalize weights because we dropped the 4 corner taps (sum would be slightly < 1.0)
float weightSum = weight0 + weight1 + weight2 + weight3 + weight4;
weightSum = max(weightSum, 0.0001);
weight0 /= weightSum;
weight1 /= weightSum;
weight2 /= weightSum;
weight3 /= weightSum;
weight4 /= weightSum;
//sample w. hw bilinear filtering (offsets take care of the interpolation)
float3 color0 = tex2Dlod(tex, float4(texCoord0, 0, 0)).rgb;
float3 color1 = tex2Dlod(tex, float4(texCoord1, 0, 0)).rgb;
float3 color2 = tex2Dlod(tex, float4(texCoord2, 0, 0)).rgb;
float3 color3 = tex2Dlod(tex, float4(texCoord3, 0, 0)).rgb;
float3 color4 = tex2Dlod(tex, float4(texCoord4, 0, 0)).rgb;
float3 result = color0 * weight0 + color1 * weight1 + color2 * weight2 + color3 * weight3 + color4 * weight4;
//anti-ringing clamp
float3 minColor = min(min(min(color0, color1), min(color2, color3)), color4);
float3 maxColor = max(max(max(color0, color1), max(color2, color3)), color4);
return clamp(result, minColor, maxColor);
}
//if ray misses the bounding box (tEnter > tExit), returning t = 1.0 is the safe fallback
float3 YCoCgLineBoxClip(float3 historyYCoCg, float3 meanYCoCg, float3 colorMin, float3 colorMax) {
float3 rayDir = meanYCoCg - historyYCoCg;
rayDir = abs(rayDir) < 0.0001 ? float3(0.0001, 0.0001, 0.0001) : rayDir; //avoid div by zero
//compute t for intersection with min and max bounds per-channel
float3 tMin = (colorMin - historyYCoCg) / rayDir;
float3 tMax = (colorMax - historyYCoCg) / rayDir;
float3 t1 = min(tMin, tMax);
float3 t2 = max(tMin, tMax);
tMin = t1;
tMax = t2;
//entry and exit points for ray-box intersection
float tEnter = max(max(tMin.x, tMin.y), tMin.z);
float tExit = min(min(tMax.x, tMax.y), tMax.z);
//if ray misses the box; fallback to 1.0 (mean) to discard history
//else, clamp the entry point to [0, 1] to clip exactly at the box edge
float t = tEnter > tExit ? 1.0 : clamp(tEnter, 0.0, 1.0);
return historyYCoCg + rayDir * t;
}
/*--------------.
| :: SHADERS :: |
'--------------*/
#if ENABLE_DLAA
float4 PS_DLAAPreFilter(float4 vpos : SV_Position, float2 uv : TexCoord) : SV_Target {
float3 center = sqrt(max(GetLinearColor(uv, false), 0.0));
float edge;
if (!EDGE_MODE) {
//luma edge in perceptual space; the extra sqrt fattens the mask
float2 px = float2(BUFFER_PIXEL_SIZE.x, 0.0);
float2 py = float2(0.0, BUFFER_PIXEL_SIZE.y);
float3 left = sqrt(max(GetLinearColor(uv - px, false), 0.0));
float3 right = sqrt(max(GetLinearColor(uv + px, false), 0.0));
float3 top = sqrt(max(GetLinearColor(uv - py, false), 0.0));
float3 bottom = sqrt(max(GetLinearColor(uv + py, false), 0.0));
float3 edges = 4.0 * abs((left + right + top + bottom) - 4.0 * center);
edge = GetLuminance(sqrt(max(edges, 0.0))); //recursive gamma compression: do another sqrt(), fattens the edge mask
} else {
float4 s0 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2(-1,-1), 0, 0));
float4 s1 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2( 0,-1), 0, 0));
float4 s2 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2( 1,-1), 0, 0));
float4 s3 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2(-1, 0), 0, 0));
float4 s4 = tex2Dlod(Kernel::sNormals, float4(uv, 0, 0));
float4 s5 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2( 1, 0), 0, 0));
float4 s6 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2(-1, 1), 0, 0));
float4 s7 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2( 0, 1), 0, 0));
float4 s8 = tex2Dlod(Kernel::sNormals, float4(uv + BUFFER_PIXEL_SIZE * float2( 1, 1), 0, 0));
//3x3 depth Sobel
float dC = s4.a;
float sxD = -s0.a + s2.a - 2.0 * s3.a + 2.0 * s5.a - s6.a + s8.a;
float syD = -s0.a - 2.0 * s1.a - s2.a + s6.a + 2.0 * s7.a + s8.a;
float depthEdge = saturate(sqrt(sxD * sxD + syD * syD) / (dC + 1e-5));
//3x3 normal Sobel
float3 sxN = -s0.xyz + s2.xyz - 2.0 * s3.xyz + 2.0 * s5.xyz - s6.xyz + s8.xyz;
float3 syN = -s0.xyz - 2.0 * s1.xyz - s2.xyz + s6.xyz + 2.0 * s7.xyz + s8.xyz;
float normalEdge = saturate(length(sxN) + length(syN));
edge = max(depthEdge, normalEdge);
}
return float4(center, edge);
}
#define SAMPLE_G(uv, dx, dy) tex2Dlod(sDLAAPreFilter, float4((uv) + float2(dx, dy) * BUFFER_PIXEL_SIZE, 0, 0))
float4 PS_DLAA(float4 vpos : SV_Position, float2 uv : TexCoord) : SV_Target {
float4 center = SAMPLE_G(uv, 0.0, 0.0);
float4 left01 = SAMPLE_G(uv, -1.5, 0.0);
float4 right01 = SAMPLE_G(uv, 1.5, 0.0);
float4 top01 = SAMPLE_G(uv, 0.0, -1.5);
float4 bottom01 = SAMPLE_G(uv, 0.0, 1.5);
//flat-region early exit
float localEdges = max(center.a, max(max(left01.a, right01.a), max(top01.a, bottom01.a)));
if (localEdges < 0.05) return float4(center.xyz * center.xyz, 1.0);
float4 wH = 2.0 * (left01 + right01);
float4 wV = 2.0 * (top01 + bottom01);
float4 edgeH = abs(wH - 4.0 * center) / 4.0;
float4 edgeV = abs(wV - 4.0 * center) / 4.0;
float4 blurredH = (wH + 2.0 * center) / 6.0;
float4 blurredV = (wV + 2.0 * center) / 6.0;
float edgeHLum = GetLuminance(edgeH.xyz);
float edgeVLum = GetLuminance(edgeV.xyz);
float blurredHLum = GetLuminance(blurredH.xyz);
float blurredVLum = GetLuminance(blurredV.xyz);
const float kLambda = 3.0;
const float kEpsilon = 0.1;
float edgeMaskH = saturate((kLambda * edgeHLum - kEpsilon) / (blurredVLum + 1e-5));
float edgeMaskV = saturate((kLambda * edgeVLum - kEpsilon) / (blurredHLum + 1e-5));
float gate = (!EDGE_MODE) ? 1.0 : center.a;
edgeMaskH *= gate;
edgeMaskV *= gate;
float4 clr = center;
clr = lerp(clr, blurredH, edgeMaskV);
clr = lerp(clr, blurredV, edgeMaskH * 0.5);
//skip unnecessary work on long-edges
if (localEdges > 0.5) {
float4 h0 = right01;
float4 h1 = SAMPLE_G(uv, 3.5, 0.0);
float4 h2 = SAMPLE_G(uv, 5.5, 0.0);
float4 h3 = SAMPLE_G(uv, 7.5, 0.0);
float4 h4 = left01;
float4 h5 = SAMPLE_G(uv, -3.5, 0.0);
float4 h6 = SAMPLE_G(uv, -5.5, 0.0);
float4 h7 = SAMPLE_G(uv, -7.5, 0.0);
float4 v0 = bottom01;
float4 v1 = SAMPLE_G(uv, 0.0, 3.5);
float4 v2 = SAMPLE_G(uv, 0.0, 5.5);
float4 v3 = SAMPLE_G(uv, 0.0, 7.5);
float4 v4 = top01;
float4 v5 = SAMPLE_G(uv, 0.0, -3.5);
float4 v6 = SAMPLE_G(uv, 0.0, -5.5);
float4 v7 = SAMPLE_G(uv, 0.0, -7.5);
float longEdgeMaskH = (h0.a + h1.a + h2.a + h3.a + h4.a + h5.a + h6.a + h7.a) / 8.0;
float longEdgeMaskV = (v0.a + v1.a + v2.a + v3.a + v4.a + v5.a + v6.a + v7.a) / 8.0;
longEdgeMaskH = saturate(longEdgeMaskH * 2.0 - 1.0);
longEdgeMaskV = saturate(longEdgeMaskV * 2.0 - 1.0);
if (abs(longEdgeMaskH - longEdgeMaskV) > 0.2) {
float4 left = SAMPLE_G(uv, -1.0, 0.0);
float4 right = SAMPLE_G(uv, 1.0, 0.0);
float4 top = SAMPLE_G(uv, 0.0, -1.0);
float4 bottom = SAMPLE_G(uv, 0.0, 1.0);
float4 longBlurredH = (h0 + h1 + h2 + h3 + h4 + h5 + h6 + h7) / 8.0;
float4 longBlurredV = (v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7) / 8.0;
float lbHLum = GetLuminance(longBlurredH.xyz);
float lbVLum = GetLuminance(longBlurredV.xyz);
float centerLum = GetLuminance(center.xyz);
float leftLum = GetLuminance(left.xyz);
float rightLum = GetLuminance(right.xyz);
float topLum = GetLuminance(top.xyz);
float bottomLum = GetLuminance(bottom.xyz);
float4 clrV = center;
float4 clrH = center;
float hx = saturate(0.0 + (lbHLum - topLum) / (centerLum - topLum + 1e-6));
float hy = saturate(1.0 + (lbHLum - centerLum) / (centerLum - bottomLum + 1e-6));
float vx = saturate(0.0 + (lbVLum - leftLum) / (centerLum - leftLum + 1e-6));
float vy = saturate(1.0 + (lbVLum - centerLum) / (centerLum - rightLum + 1e-6));
float4 vhxy = float4(vx, vy, hx, hy);
vhxy.x = (vhxy.x == 0.0) ? 1.0 : vhxy.x;
vhxy.y = (vhxy.y == 0.0) ? 1.0 : vhxy.y;
vhxy.z = (vhxy.z == 0.0) ? 1.0 : vhxy.z;
vhxy.w = (vhxy.w == 0.0) ? 1.0 : vhxy.w;
clrV = lerp(left, clrV, vhxy.x);
clrV = lerp(right, clrV, vhxy.y);
clrH = lerp(top, clrH, vhxy.z);
clrH = lerp(bottom, clrH, vhxy.w);
clr = lerp(clr, clrV, longEdgeMaskV);
clr = lerp(clr, clrH, longEdgeMaskH);
}
}
//highlight protection
float4 r0 = SAMPLE_G(uv, -1.5, -1.5);
float4 r1 = SAMPLE_G(uv, 1.5, -1.5);
float4 r2 = SAMPLE_G(uv, -1.5, 1.5);
float4 r3 = SAMPLE_G(uv, 1.5, 1.5);
float4 r = (4.0 * (r0 + r1 + r2 + r3) + center + top01 + bottom01 + left01 + right01) / 25.0;
float mask = saturate(r.a * 3.0 - 2.0);
clr = lerp(clr, center, mask);
return float4(clr.xyz * clr.xyz, 1.0); //store linear color here!
}
#endif
float4 PS_TRAA(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target {
//3x3 neighborhood from DLAA prepass
static const float2 offsets[9] = {
float2(-1, -1), float2(0, -1), float2(1, -1),
float2(-1, 0) , float2(0, 0) , float2(1, 0),
float2(-1, 1) , float2(0, 1) , float2(1, 1)
};
float3 samples[9];
float3 samplesYCoCg[9];
float3 meanYCoCg = float3(0, 0, 0);
for (int i = 0; i < 9; i++) {
float2 samplePos = texcoord + BUFFER_PIXEL_SIZE * offsets[i];
#if ENABLE_DLAA
samples[i] = tex2Dlod(sDLAAPrePass, float4(samplePos, 0, 0)).rgb;
#else
samples[i] = GetLinearColor(samplePos, false);
#endif
samplesYCoCg[i] = linearToYCoCg(samples[i]);
meanYCoCg += samplesYCoCg[i];
}
meanYCoCg /= 9.0;
//standard deviation per channel
float3 stddev = float3(0, 0, 0);
for (int i = 0; i < 9; i++) {
float3 diff = samplesYCoCg[i] - meanYCoCg;
stddev += diff * diff;
}
stddev = sqrt(stddev / 9.0);
//variance-scaled bounding box in YCoCg
float3 colorMin = meanYCoCg - stddev * 1.25;
float3 colorMax = meanYCoCg + stddev * 1.25;
float2 flow = tex2D(Kernel::sFlow, texcoord).xy;
float confidence = tex2D(Kernel::sConfidence, texcoord).x;
confidence = saturate(confidence + 0.11 * 4.0 * confidence * (1.0 - confidence));
float2 historyUV = texcoord + flow;
historyUV = clamp(historyUV, BUFFER_PIXEL_SIZE, 1.0 - BUFFER_PIXEL_SIZE);
float3 historyRGB = SampleCatmullRom5Tap(sPrevHistory, historyUV);
float3 historyYCoCg = linearToYCoCg(historyRGB);
//clip history to current neighborhood bounds via line-box intersection
float3 clippedHistoryYCoCg = YCoCgLineBoxClip(historyYCoCg, meanYCoCg, colorMin, colorMax);
//re-inject current pixel's detail into clipped history
float3 centerYCoCg = samplesYCoCg[4]; //blend against the center pixel (index 4 of the 3x3 grid)
float3 injectedHistory = clippedHistoryYCoCg + (centerYCoCg - meanYCoCg) * HFI_INTENSITY;
//blend clipped history with current in YCoCg space
float blendVal = min(0.98, HISTORY_BLEND);
float3 blendedYCoCg = lerp(centerYCoCg, injectedHistory, confidence * blendVal);
float3 output = YCoCgToLinear(blendedYCoCg);
return float4(output, 1.0);
}
float4 PS_ToDisplay(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target {
#if ENABLE_DLAA
if (DEBUG_EDGES) {
float edgeDbg = tex2D(sDLAAPreFilter, texcoord).a;
static const float3 edgeTint = float3(0.125, 0.698, 0.667) * float3(0.125, 0.698, 0.667); //target color squared so lands on the real hue
return float4(ToOutputColorspace(edgeTint * saturate(edgeDbg), false), 1.0);
}
#endif
float3 c = tex2D(sCurrHistory, texcoord).rgb;
float3 sharpened = c;
if (SHARP_STRENGTH > 0) {
float2 off = BUFFER_PIXEL_SIZE * 0.5;
float3 ne = tex2D(sCurrHistory, texcoord + float2( off.x, off.y)).rgb;
float3 sw = tex2D(sCurrHistory, texcoord + float2(-off.x, -off.y)).rgb;
float3 se = tex2D(sCurrHistory, texcoord + float2( off.x, -off.y)).rgb;
float3 nw = tex2D(sCurrHistory, texcoord + float2(-off.x, off.y)).rgb;
//bounds for the neighborhood
float3 local_min = min(min(min(ne, nw), min(se, sw)), c);
float3 local_max = max(max(max(ne, nw), max(se, sw)), c);
//high-pass
float3 diag_max = max(max(ne, nw), max(se, sw));
float3 diag_min = min(min(ne, nw), min(se, sw));
float3 diff_rgb = 2.0 * c + (ne + nw + se + sw) - 3.0 * (diag_max + diag_min);
static const float3 luma_weight = float3(0.2126, 0.7152, 0.0722);
float luma_c = dot(c, luma_weight);
float luma_diff = dot(diff_rgb, luma_weight);
//rational limit
float max_allowed = MAX_SHARP_DIFF * (luma_c + 0.1);
luma_diff = luma_diff / (rcp(SHARP_STRENGTH) + abs(luma_diff) / max(max_allowed, 0.001));
//lower epsilon (0.005) for more dark-area detail
float ratio = (luma_c + luma_diff) / max(luma_c, 0.005);
//allow up to 3x brightness for extreme highlights
ratio = clamp(ratio, 0.3, 3.0);
sharpened = c * ratio;
//anti-ringing
//instead of clamping strictly to min/max, we allow a 20% overshoot
//perceived "sharpness" while capping fireflies
float3 overshoot_min = local_min * 0.8;
float3 overshoot_max = local_max * 1.2;
sharpened = clamp(sharpened, overshoot_min, overshoot_max);
}
return float4(ToOutputColorspace(sharpened, false), 1.0);
}
float4 PS_StoreHistory(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target {
float3 taaResult = tex2D(sCurrHistory, texcoord).rgb;
return float4(taaResult, 1.0);
}
/*----------------.
| :: TECHNIQUE :: |
'----------------*/
technique Lumenite_TRAA <
ui_label = "LUMENITE: TRAA";
ui_tooltip = "Temporal Reprojection Anti-Aliasing.";
>
{
#if ENABLE_DLAA
pass { VertexShader = PostProcessVS; PixelShader = PS_DLAAPreFilter; RenderTarget = tDLAAPreFilter; }
pass { VertexShader = PostProcessVS; PixelShader = PS_DLAA; RenderTarget = tDLAAPrePass; } //spatial filter
#endif
pass { VertexShader = PostProcessVS; PixelShader = PS_TRAA; RenderTarget = tCurrHistory; } //temporal filter
pass { VertexShader = PostProcessVS; PixelShader = PS_ToDisplay; }
pass { VertexShader = PostProcessVS; PixelShader = PS_StoreHistory; RenderTarget = tPrevHistory; }
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

+13
View File
@@ -0,0 +1,13 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
gradlePluginPortal()
}
}
// Should match your modid
rootProject.name = 'wpywdlss'
@@ -0,0 +1,150 @@
package dev.wpyw.dlss;
import com.mojang.blaze3d.platform.InputConstants;
import dev.wpyw.dlss.cfg.MasterSwitch;
import dev.wpyw.dlss.gui.DlssSetupScreen;
import dev.wpyw.dlss.gui.VideoSettingsHook;
import dev.wpyw.dlss.install.DlssStackInstaller;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import org.lwjgl.glfw.GLFW;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Client entrypoint.
*
* <p>This mod is the installer, the control panel and the diagnostics for the DLSS stack - it is
* not the thing that runs DLSS. The renderer is ReShade plus its add-ons, which is why the single
* most important fact about this mod is that <b>it cannot activate ReShade in the current
* process</b>: ReShade is a proxy DLL for {@code opengl32.dll}, and Windows resolved that import
* before any Fabric code ran. Installing therefore always ends with "restart the game", and the
* GUI says so rather than pretending otherwise.
*
* <p>Everything the player needs is reachable three ways, in decreasing order of discoverability:
* the buttons this mod injects into <b>Options - Video Settings</b>, the {@code /dlss} command,
* and an (unbound by default) key binding.
*/
public class DlssClientMod implements ClientModInitializer {
public static final String MOD_ID = "wpywdlss";
public static final Logger LOG = LoggerFactory.getLogger(MOD_ID);
private static KeyMapping openGuiKey;
@Override
public void onInitializeClient() {
LOG.info("Wpyw DLSS initialising (client)");
openGuiKey = KeyBindingHelper.registerKeyBinding(new KeyMapping(
"key.wpywdlss.settings",
InputConstants.Type.KEYSYM,
GLFW.GLFW_KEY_UNKNOWN, // unbound by default; the video settings button is the way in
"Wpyw DLSS"));
ClientTickEvents.END_CLIENT_TICK.register(client -> {
while (openGuiKey.consumeClick()) {
openPanel();
}
});
// The entry point the user actually asked for: two buttons inside Options - Video Settings.
VideoSettingsHook.register();
registerCommands();
// Report the state once at startup, because the interesting question at this point is
// always "did ReShade actually load this launch?".
DlssStackInstaller.Report r = DlssStackInstaller.analyze();
LOG.info("stack target : {}", r.target());
LOG.info("stack status : {}", r.status());
LOG.info("payload files : {}", r.present().size());
if (!r.ngxMissing().isEmpty()) {
LOG.warn("NVIDIA runtime(s) missing: {}", r.ngxMissing());
}
Path reshadeLog = r.target().resolve("ReShade.log");
if (r.status() == DlssStackInstaller.Status.INSTALLED
&& !Files.isRegularFile(reshadeLog)) {
LOG.warn("installed, but no ReShade.log yet - restart Minecraft to let the proxy load");
}
LOG.info("game dir : {}", FabricLoader.getInstance().getGameDir());
LOG.info("open the panel with /dlss, or Options - Video Settings");
}
private void registerCommands() {
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) ->
dispatcher.register(ClientCommandManager.literal("dlss")
.executes(ctx -> {
openPanel();
return 1;
})
.then(ClientCommandManager.literal("status")
.executes(ctx -> {
DlssStackInstaller.Report r = DlssStackInstaller.analyze();
LOG.info("\n{}", r.describe());
return 1;
}))
.then(ClientCommandManager.literal("install")
.executes(ctx -> {
try {
DlssStackInstaller.Report r = DlssStackInstaller.install();
LOG.info("installed: {}\n{}", r.status(), r.describe());
} catch (Exception e) {
LOG.error("install failed", e);
}
VideoSettingsHook.invalidate();
return 1;
}))
.then(ClientCommandManager.literal("uninstall")
.executes(ctx -> {
try {
DlssStackInstaller.Report r = DlssStackInstaller.uninstall();
LOG.info("uninstalled: {}", r.status());
} catch (Exception e) {
LOG.error("uninstall failed", e);
}
VideoSettingsHook.invalidate();
return 1;
}))
.then(ClientCommandManager.literal("on")
.executes(ctx -> {
setMasterSwitch(true);
return 1;
}))
.then(ClientCommandManager.literal("off")
.executes(ctx -> {
setMasterSwitch(false);
return 1;
}))
));
}
private static void setMasterSwitch(boolean on) {
MasterSwitch.State s = MasterSwitch.write(DlssStackInstaller.jvmBinDir(), on);
if (!s.note().isEmpty()) {
LOG.warn("DLSS {} failed: {}", on ? "on" : "off", s.note());
}
LOG.info("DLSS is now {}", s.on() ? "ON" : "OFF");
VideoSettingsHook.invalidate();
}
/**
* Opens the panel over whatever is on screen now, so closing it returns the player there
* instead of dumping them back into the world. Screens may only be replaced on the client
* thread, so hop there first when called from a command.
*/
private static void openPanel() {
Minecraft mc = Minecraft.getInstance();
Screen parent = mc.screen;
mc.execute(() -> mc.setScreen(new DlssSetupScreen(parent)));
}
}
@@ -0,0 +1,117 @@
package dev.wpyw.dlss;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
/**
* Thin Java facade over the native bridge DLL.
*
* <p>The bridge does the things Java cannot: create a Vulkan device, share textures with
* Minecraft's OpenGL context through Win32 external memory, and (later) drive NVIDIA
* Streamline / NGX.
*
* <p>The DLL is loaded by extracting it from this mod's own jar to a temp directory, because
* {@code System.loadLibrary} cannot read inside a jar.
*/
public final class NativeBridge {
private static final String[] RESOURCE_CANDIDATES = {
"/assets/wpywdlss/native/windows-x86_64/wpywdlss_bridge.dll",
"/assets/wpywdlss/native/windows-x86_64/wpywdlss_bridge_debug.dll",
};
private static boolean attempted;
private static boolean loaded;
private static String loadError = "not attempted";
private static Path loadedFrom;
private NativeBridge() {
}
public static synchronized boolean isLoaded() {
return loaded;
}
public static synchronized String loadError() {
return loadError;
}
public static synchronized Path loadedFrom() {
return loadedFrom;
}
/** Loads the bridge exactly once. Returns true on success. Never throws. */
public static synchronized boolean load() {
if (attempted) {
return loaded;
}
attempted = true;
try {
// Dev override: point straight at a build output so a rebuild needs no repackaging.
String override = System.getProperty("wpywdlss.bridgePath");
if (override != null && !override.isBlank()) {
Path p = Path.of(override);
if (Files.isRegularFile(p)) {
System.load(p.toAbsolutePath().toString());
loaded = true;
loadedFrom = p;
loadError = null;
return true;
}
loadError = "wpywdlss.bridgePath set but not a file: " + override;
return false;
}
Path dir = Files.createTempDirectory("wpywdlss-bridge");
dir.toFile().deleteOnExit();
for (String resource : RESOURCE_CANDIDATES) {
try (InputStream in = NativeBridge.class.getResourceAsStream(resource)) {
if (in == null) {
continue;
}
String fileName = resource.substring(resource.lastIndexOf('/') + 1);
Path target = dir.resolve(fileName);
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
target.toFile().deleteOnExit();
System.load(target.toAbsolutePath().toString());
loaded = true;
loadedFrom = target;
loadError = null;
return true;
}
}
loadError = "no bridge DLL found in jar resources (looked for "
+ String.join(", ", RESOURCE_CANDIDATES) + ")";
} catch (IOException | UnsatisfiedLinkError | SecurityException e) {
loadError = e.getClass().getSimpleName() + ": " + e.getMessage();
}
return false;
}
// ---- native surface -------------------------------------------------
/** Bridge build identifier, e.g. "0.1.0-p0". */
public static native String nativeVersion();
/** Runs the phase-0 Vulkan / interop capability probe and returns a report. */
public static native String nativeProbe();
/**
* Creates a hidden WGL context and reports the OpenGL side of the interop story:
* whether GL_EXT_memory_object_win32 / GL_EXT_semaphore_win32 are present and
* whether the interop entry points resolve. This is the go/no-go check for every
* AI-upscaling route, because OpenGL cannot export memory -- a Vulkan/D3D12 device
* allocates and exports, and GL imports.
*/
public static native String nativeProbeOpenGL();
/** True once a Vulkan device is up and the interop entry points resolved. */
public static native boolean nativeIsReady();
/** Destroys the Vulkan device and instance. */
public static native void nativeShutdown();
}
@@ -0,0 +1,199 @@
package dev.wpyw.dlss.cfg;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Line-preserving editor for the flat {@code key=value} files this stack uses.
*
* <p>Both {@code dlss5-feed.cfg} and {@code deep-fried-chicken.cfg} are completely flat
* (no sections, 600+ keys each) and, crucially, <b>re-read while the game runs</b> - the
* DLSS5-Feeder documentation says so explicitly. That means a Minecraft GUI can change most
* settings live, without a restart, by rewriting the file.
*
* <p>These files belong to other programs, so edits are as narrow as they can be:
* <ul>
* <li>edits are applied per line and unknown lines are kept verbatim - rebuilding the file
* from a parsed map would drop the hundreds of keys this mod does not know about and
* reorder everything;</li>
* <li>the original <b>line ending is preserved</b>. Both live files are CRLF; writing them
* back with LF would rewrite all 666 lines of a file Deep Fried Chicken owns, to no
* benefit, and risks churn against whatever that add-on does when it saves.</li>
* </ul>
*/
public final class FlatCfg {
private final Path path;
private final List<String> lines = new ArrayList<>();
private final String eol;
private FlatCfg(Path path, List<String> lines, String eol) {
this.path = path;
this.lines.addAll(lines);
this.eol = eol;
}
/** Reads an existing file, or starts an empty one if it does not exist. */
public static FlatCfg load(Path path) throws IOException {
if (Files.isRegularFile(path)) {
byte[] raw = Files.readAllBytes(path);
return new FlatCfg(path, decodeLines(raw), detectEol(raw));
}
// A file this mod creates should look native on the host platform.
return new FlatCfg(path, new ArrayList<>(), System.lineSeparator());
}
/** The line terminator that will be used on {@link #save()}, as read from disk. */
public String lineEnding() {
return eol;
}
public Path path() {
return path;
}
/** Snapshot of the current key/value pairs, in file order. Only simple {@code k=v} lines. */
public Map<String, String> asMap() {
Map<String, String> m = new LinkedHashMap<>();
for (String line : lines) {
int eq = line.indexOf('=');
if (eq <= 0) {
continue;
}
String k = line.substring(0, eq).trim();
if (!k.isEmpty() && !k.startsWith("#") && !k.startsWith(";")) {
m.put(k, line.substring(eq + 1).trim());
}
}
return m;
}
public String get(String key, String fallback) {
return asMap().getOrDefault(key, fallback);
}
public int getInt(String key, int fallback) {
try {
return Integer.parseInt(get(key, Integer.toString(fallback)).trim());
} catch (NumberFormatException e) {
return fallback;
}
}
public double getDouble(String key, double fallback) {
try {
return Double.parseDouble(get(key, Double.toString(fallback)).trim());
} catch (NumberFormatException e) {
return fallback;
}
}
public boolean getBool(String key, boolean fallback) {
String v = get(key, fallback ? "1" : "0").trim();
return "1".equals(v) || "true".equalsIgnoreCase(v);
}
/**
* Sets a key in place, or appends it if absent. Returns {@code true} when the value actually
* changed, so callers can avoid pointless disk writes.
*/
public boolean set(String key, String value) {
String wanted = key + "=" + value;
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
int eq = line.indexOf('=');
if (eq > 0 && line.substring(0, eq).trim().equals(key)) {
if (line.equals(wanted)) {
return false;
}
lines.set(i, wanted);
return true;
}
}
lines.add(wanted);
return true;
}
public boolean set(String key, int value) {
return set(key, Integer.toString(value));
}
public boolean set(String key, double value) {
// The cfg files use plain decimal notation; keep it stable to avoid needless rewrites.
return set(key, String.format(java.util.Locale.ROOT, "%.3f", value));
}
public boolean set(String key, boolean value) {
return set(key, value ? "1" : "0");
}
/** Writes the file, creating parent directories, using the line ending it was read with. */
public void save() throws IOException {
Path parent = path.getParent();
if (parent != null) {
Files.createDirectories(parent);
}
Files.write(path, (String.join(eol, lines) + eol).getBytes(StandardCharsets.UTF_8));
}
// ------------------------------------------------------------------ line handling
/**
* Splits on LF, CRLF and lone CR, with the same shape as {@code Files.readAllLines}: no
* trailing empty element for a final terminator, but empty elements kept for blank lines.
*/
private static List<String> decodeLines(byte[] raw) {
String text = new String(raw, StandardCharsets.UTF_8);
List<String> out = new ArrayList<>();
int start = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c != '\n' && c != '\r') {
continue;
}
out.add(text.substring(start, i));
if (c == '\r' && i + 1 < text.length() && text.charAt(i + 1) == '\n') {
i++;
}
start = i + 1;
}
if (start < text.length()) {
out.add(text.substring(start));
}
return out;
}
private static String detectEol(byte[] raw) {
int crlf = 0;
int bareLf = 0;
int bareCr = 0;
for (int i = 0; i < raw.length; i++) {
byte b = raw[i];
if (b == '\n') {
if (i > 0 && raw[i - 1] == '\r') {
crlf++;
} else {
bareLf++;
}
} else if (b == '\r' && (i + 1 >= raw.length || raw[i + 1] != '\n')) {
bareCr++;
}
}
if (crlf > 0 && crlf >= bareLf && crlf >= bareCr) {
return "\r\n";
}
if (bareCr > 0 && bareCr > bareLf) {
return "\r";
}
if (bareLf > 0) {
return "\n";
}
return System.lineSeparator();
}
}
@@ -0,0 +1,261 @@
package dev.wpyw.dlss.cfg;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Line-preserving, section-aware editor for ReShade's ini files.
*
* <p>Two shapes have to be handled, and they are different:
* <ul>
* <li>{@code ReShade.ini} is a normal ini - every key lives under a {@code [Section]}.</li>
* <li>{@code ReShadePreset.ini} puts {@code Techniques=} and {@code TechniqueSorting=} in the
* file's <b>preamble</b>, before the first {@code [Effect.fx]} section. Those two keys are
* the ones that matter most: {@code Techniques=} says what is enabled, and
* {@code TechniqueSorting=} defines execution order - which is what decides whether the
* motion-vector provider runs before the shader that consumes it.</li>
* </ul>
* A {@code null} section therefore means "the preamble". Replacing either file wholesale would
* destroy state we do not own (there are hundreds of keys here, and ReShade regenerates defaults
* on exit), so edits are applied line by line and everything unknown is preserved verbatim.
*/
public final class IniFile {
private final Path path;
private final List<String> lines = new ArrayList<>();
private IniFile(Path path, List<String> lines) {
this.path = path;
this.lines.addAll(lines);
}
public static IniFile load(Path path) throws IOException {
if (Files.isRegularFile(path)) {
return new IniFile(path, Files.readAllLines(path, StandardCharsets.UTF_8));
}
return new IniFile(path, new ArrayList<>());
}
public Path path() {
return path;
}
public boolean isNew() {
return lines.isEmpty();
}
private static boolean isHeader(String line) {
String t = line.trim();
return t.length() >= 3 && t.startsWith("[") && t.endsWith("]");
}
/** Index of the given section's header line, or -1. */
private int headerIndex(String section) {
if (section == null || section.isEmpty()) {
return -1;
}
String want = "[" + section + "]";
for (int i = 0; i < lines.size(); i++) {
if (lines.get(i).trim().equalsIgnoreCase(want)) {
return i;
}
}
return -1;
}
/** Exclusive end of a section body (the next header, or the end of file). */
private int bodyEnd(int headerIdx) {
for (int i = headerIdx + 1; i < lines.size(); i++) {
if (isHeader(lines.get(i))) {
return i;
}
}
return lines.size();
}
/** For the preamble: [first, last) index range that may contain keys. */
private int preambleEnd() {
for (int i = 0; i < lines.size(); i++) {
if (isHeader(lines.get(i))) {
return i;
}
}
return lines.size();
}
private int findKey(String section, String key) {
int from;
int to;
if (section == null || section.isEmpty()) {
from = 0;
to = preambleEnd();
} else {
int h = headerIndex(section);
if (h < 0) {
return -1;
}
from = h + 1;
to = bodyEnd(h);
}
for (int i = from; i < to; i++) {
String line = lines.get(i);
int eq = line.indexOf('=');
if (eq > 0 && line.substring(0, eq).trim().equalsIgnoreCase(key)) {
return i;
}
}
return -1;
}
public String get(String section, String key, String fallback) {
int i = findKey(section, key);
if (i < 0) {
return fallback;
}
String line = lines.get(i);
return line.substring(line.indexOf('=') + 1).trim();
}
/** Sets a key, creating the section (or preamble entry) when needed. */
public boolean set(String section, String key, String value) {
String wanted = key + "=" + value;
int i = findKey(section, key);
if (i >= 0) {
if (lines.get(i).equals(wanted)) {
return false;
}
lines.set(i, wanted);
return true;
}
if (section == null || section.isEmpty()) {
// Insert at the end of the preamble, i.e. immediately before the first header.
lines.add(preambleEnd(), wanted);
return true;
}
int h = headerIndex(section);
if (h < 0) {
if (!lines.isEmpty() && !lines.get(lines.size() - 1).isBlank()) {
lines.add("");
}
lines.add("[" + section + "]");
lines.add(wanted);
return true;
}
lines.add(bodyEnd(h), wanted);
return true;
}
/**
* Adds or updates one element of a comma-separated list value, preserving every other element
* and their order. Used for {@code PreprocessorDefinitions} and {@code [ADDON] LoadFromDllMain},
* where other tools legitimately own entries too.
*
* @return true when something changed
*/
public boolean addToListElement(String section, String key, String element) {
String current = get(section, key, "");
List<String> parts = new ArrayList<>();
for (String p : current.split(",")) {
String t = p.trim();
if (!t.isEmpty()) {
parts.add(t);
}
}
String wantedName = element.contains("=") ? element.substring(0, element.indexOf('=')).trim() : element;
for (int k = 0; k < parts.size(); k++) {
String p = parts.get(k);
String name = p.contains("=") ? p.substring(0, p.indexOf('=')).trim() : p;
if (name.equalsIgnoreCase(wantedName)) {
if (p.equals(element)) {
return false;
}
parts.set(k, element);
return set(section, key, String.join(",", parts));
}
}
parts.add(element);
return set(section, key, String.join(",", parts));
}
/** Moves an element to the front of a comma-separated list. Used for TechniqueSorting. */
public boolean moveToFront(String section, String key, String element) {
String current = get(section, key, "");
if (current.isEmpty()) {
return false;
}
List<String> parts = new ArrayList<>();
for (String p : current.split(",")) {
String t = p.trim();
if (!t.isEmpty()) {
parts.add(t);
}
}
int idx = parts.indexOf(element);
if (idx == 0) {
return false;
}
if (idx > 0) {
parts.remove(idx);
}
parts.add(0, element);
return set(section, key, String.join(",", parts));
}
public void removeKey(String section, String key) {
int i = findKey(section, key);
if (i >= 0) {
lines.remove(i);
}
}
/** All {@code key=value} pairs of a section (or of the preamble when section is null). */
public Map<String, String> section(String section) {
Map<String, String> m = new LinkedHashMap<>();
int from;
int to;
if (section == null || section.isEmpty()) {
from = 0;
to = preambleEnd();
} else {
int h = headerIndex(section);
if (h < 0) {
return m;
}
from = h + 1;
to = bodyEnd(h);
}
for (int i = from; i < to; i++) {
String line = lines.get(i);
int eq = line.indexOf('=');
if (eq > 0) {
m.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim());
}
}
return m;
}
public List<String> rawLines() {
return List.copyOf(lines);
}
public void save() throws IOException {
Path parent = path.getParent();
if (parent != null) {
Files.createDirectories(parent);
}
List<String> out = new ArrayList<>(lines);
while (!out.isEmpty() && out.get(out.size() - 1).isBlank()) {
out.remove(out.size() - 1);
}
// ReShade writes CRLF; match it so its own diffs stay clean.
Files.write(path, (String.join("\r\n", out) + "\r\n").getBytes(StandardCharsets.UTF_8));
}
}
@@ -0,0 +1,97 @@
package dev.wpyw.dlss.cfg;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* The one switch a player actually wants: "is DLSS on right now?".
*
* <p>There is no single flag for that. Two independent ReShade add-ons have to be enabled -
* DLSS5-Feeder, which turns each frame into a DLSS request, and Deep Fried Chicken, which
* is the thing that performs the neural render - and each keeps its own {@code enabled} key
* in its own flat cfg file. A half-on stack is a real and confusing failure mode (the feeder
* requests frames nobody renders), so both are always read and written together.
*
* <p>Both files are re-read by their add-ons while the game runs, so this is a live switch:
* flipping it does not require a restart. Installing the stack does, because ReShade is a
* proxy DLL and Windows resolved {@code opengl32.dll} before any mod code ran.
*/
public final class MasterSwitch {
public static final String FEED_CFG = "dlss5-feed.cfg";
public static final String CHICKEN_CFG = "deep-fried-chicken.cfg";
private static final String KEY = "enabled";
/**
* @param on both cfg files exist and both say {@code enabled=1}
* @param feedPresent {@code dlss5-feed.cfg} is on disk
* @param chickenPresent {@code deep-fried-chicken.cfg} is on disk
* @param note empty unless the last operation failed, then the reason
*/
public record State(boolean on, boolean feedPresent, boolean chickenPresent, String note) {
/** Both add-on cfgs are present, so there is something to switch. */
public boolean switchable() {
return this.feedPresent && this.chickenPresent;
}
}
private MasterSwitch() {
}
/** Reads the current state. Never throws; a read failure is reported as {@code on=false}. */
public static State read(Path binDir) {
Path feed = binDir.resolve(FEED_CFG);
Path chicken = binDir.resolve(CHICKEN_CFG);
boolean feedPresent = Files.isRegularFile(feed);
boolean chickenPresent = Files.isRegularFile(chicken);
if (!feedPresent || !chickenPresent) {
// Nothing to read. Deliberately not "on": a missing cfg is an uninstalled stack,
// not an enabled one, and reporting it as enabled would hide the real problem.
return new State(false, feedPresent, chickenPresent, "");
}
try {
boolean on = FlatCfg.load(feed).getBool(KEY, false)
&& FlatCfg.load(chicken).getBool(KEY, false);
return new State(on, true, true, "");
} catch (IOException e) {
return new State(false, true, true, "read failed: " + e.getMessage());
}
}
/**
* Writes {@code enabled} to both cfgs. Returns the state that is actually on disk
* afterwards, which is what the caller should display - not what it asked for.
*/
public static State write(Path binDir, boolean enabled) {
Path feed = binDir.resolve(FEED_CFG);
Path chicken = binDir.resolve(CHICKEN_CFG);
boolean feedPresent = Files.isRegularFile(feed);
boolean chickenPresent = Files.isRegularFile(chicken);
if (!feedPresent || !chickenPresent) {
return new State(false, feedPresent, chickenPresent,
"add-on cfg missing - install the stack first");
}
try {
// Feeder first, deliberately. If the second write fails we are left with feeder=off,
// chicken=on - which is inert (nothing is asking for neural frames) rather than the
// other way round, which would leave the feeder requesting frames nobody renders.
for (Path p : new Path[]{feed, chicken}) {
FlatCfg cfg = FlatCfg.load(p);
// Only touch the file when the value really changes, so a no-op toggle does
// not rewrite 666 lines and risk racing the add-on's own reader.
if (cfg.set(KEY, enabled)) {
cfg.save();
}
}
} catch (IOException e) {
State now = read(binDir);
return new State(now.on(), feedPresent, chickenPresent, "write failed: " + e.getMessage());
}
return read(binDir);
}
}
@@ -0,0 +1,104 @@
package dev.wpyw.dlss.diag;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
/**
* Reads the tail of the stack's log files so the in-game GUI can show what is actually happening
* without the player alt-tabbing to a text editor.
*
* <p>Three logs matter and each answers a different question:
* <ul>
* <li>{@code ReShade.log} - did ReShade load at all, and did it register the add-ons?</li>
* <li>{@code dlss5-feed.log} - did the feeder open its transport, create the DLSS feature, and
* is it getting motion vectors and depth?</li>
* <li>{@code deep-fried-chicken.log} - did the neural pass run, and how many frames succeeded?</li>
* </ul>
*/
public final class LogTail {
private LogTail() {
}
public static final String RESHADE = "ReShade.log";
public static final String FEEDER = "dlss5-feed.log";
public static final String CHICKEN = "deep-fried-chicken.log";
/** Last {@code maxLines} lines of a log, or an explanatory single line when unusable. */
public static List<String> tail(Path logFile, int maxLines) {
if (!Files.isRegularFile(logFile)) {
return List.of("(no " + logFile.getFileName() + " - the stack has not run yet)");
}
Deque<String> ring = new ArrayDeque<>();
try {
for (String line : Files.readAllLines(logFile, StandardCharsets.UTF_8)) {
ring.addLast(stripAnsi(line));
if (ring.size() > maxLines) {
ring.removeFirst();
}
}
} catch (IOException e) {
return List.of("(cannot read " + logFile.getFileName() + ": " + e.getMessage() + ")");
}
return new ArrayList<>(ring);
}
/** Greps a log for the lines that carry a verdict, rather than dumping everything. */
public static List<String> highlights(Path logFile, int maxLines) {
if (!Files.isRegularFile(logFile)) {
return List.of();
}
List<String> hits = new ArrayList<>();
try {
for (String line : Files.readAllLines(logFile, StandardCharsets.UTF_8)) {
String l = stripAnsi(line);
if (l.contains("Registered add-on")
|| l.contains("neural frame succeeded")
|| l.contains("feature ready")
|| l.contains("feature 18 created")
|| l.contains("session ready")
|| l.contains("unified neural frame accepted")
|| l.contains("MV probe")
|| l.contains("Depth probe")
|| l.contains("WARN")
|| l.contains("ERROR")) {
hits.add(l);
}
}
} catch (IOException e) {
return List.of("(cannot read " + logFile.getFileName() + ": " + e.getMessage() + ")");
}
if (hits.size() > maxLines) {
return new ArrayList<>(hits.subList(hits.size() - maxLines, hits.size()));
}
return hits;
}
/** ReShade colourises its log; those escapes render as garbage in a plain text widget. */
private static String stripAnsi(String s) {
return s.replaceAll("\u001B\\[[0-9;]*[A-Za-z]", "");
}
/** One short status word for a log, for the GUI's summary line. */
public static String verdict(Path logFile, String successMarker) {
if (!Files.isRegularFile(logFile)) {
return "absent";
}
try {
for (String line : Files.readAllLines(logFile, StandardCharsets.UTF_8)) {
if (line.contains(successMarker)) {
return "ok";
}
}
} catch (IOException ignored) {
return "unreadable";
}
return "no match";
}
}
@@ -0,0 +1,176 @@
package dev.wpyw.dlss.gui;
import dev.wpyw.dlss.diag.LogTail;
import dev.wpyw.dlss.install.DlssStackInstaller;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
/**
* Shows the verdict lines from the three logs, in game.
*
* <p>The point is to remove the alt-tab: whether the stack is actually working is answered by
* specific log lines, not by how the picture looks. For example a working setup prints
* {@code Registered add-on}, {@code session ready}, {@code feature ready ... DLAA} and
* {@code standalone neural frame succeeded}, while the characteristic failure prints
* {@code depth is FLAT while the scene moves}.
*
* <p>Scrolling uses buttons rather than the mouse wheel on purpose: the wheel handler's signature
* changed between 1.20.1 and later versions, and getting it wrong breaks the build for no benefit.
*/
public class DlssLogScreen extends Screen {
private static final int WHITE = 0xFFFFFFFF;
private static final int GREY = 0xFFAAAAAA;
private static final int GREEN = 0xFF55FF55;
private static final int ORANGE = 0xFFFFAA55;
private final Screen parent;
private record Tab(String label, String fileName, String successMarker) {
}
private static final Tab[] TABS = {
new Tab("ReShade", LogTail.RESHADE, "Registered add-on"),
new Tab("Feeder", LogTail.FEEDER, "session ready"),
new Tab("Chicken", LogTail.CHICKEN, "neural frame succeeded"),
};
private int tab = 0;
private int scroll = 0;
private List<String> lines = new ArrayList<>();
private String verdict = "";
private Button tabButton;
private Button upButton;
private Button downButton;
public DlssLogScreen(Screen parent) {
super(Component.literal("Wpyw DLSS - 日志与诊断"));
this.parent = parent;
}
@Override
protected void init() {
int cx = this.width / 2;
tabButton = Button.builder(Component.literal(tabLabel()), b -> {
tab = (tab + 1) % TABS.length;
scroll = 0;
b.setMessage(Component.literal(tabLabel()));
load();
updateScrollButtons();
}).bounds(cx - 235, 34, 200, 20).build();
addRenderableWidget(tabButton);
addRenderableWidget(Button.builder(Component.literal("只看关键行 / 看全部"), b -> {
showAll = !showAll;
scroll = 0;
load();
updateScrollButtons();
}).bounds(cx - 27, 34, 140, 20).build());
addRenderableWidget(Button.builder(Component.literal("复制到剪贴板"), b -> {
this.minecraft.keyboardHandler.setClipboard(String.join("\n", lines));
}).bounds(cx + 121, 34, 114, 20).build());
upButton = Button.builder(Component.literal("▲"), b -> {
scroll = Math.max(0, scroll - 12);
updateScrollButtons();
}).bounds(cx + 243, 34 + 30, 20, 20).build();
addRenderableWidget(upButton);
downButton = Button.builder(Component.literal("▼"), b -> {
scroll = Math.min(maxScroll(), scroll + 12);
updateScrollButtons();
}).bounds(cx + 243, this.height - 74, 20, 20).build();
addRenderableWidget(downButton);
addRenderableWidget(Button.builder(Component.literal("返回"), b -> this.onClose())
.bounds(cx - 76, this.height - 48, 152, 20).build());
load();
updateScrollButtons();
}
private boolean showAll = false;
private String tabLabel() {
return "日志: " + TABS[tab].label() + "(点击切换)";
}
private void load() {
Path dir = DlssStackInstaller.jvmBinDir();
List<String> raw = showAll
? LogTail.tail(dir.resolve(TABS[tab].fileName()), 400)
: LogTail.highlights(dir.resolve(TABS[tab].fileName()), 400);
lines = raw.isEmpty() ? List.of("(没有匹配的关键行 - 勾选「看全部」,或该日志尚未生成)") : raw;
verdict = LogTail.verdict(dir.resolve(TABS[tab].fileName()), TABS[tab].successMarker());
}
private int visibleRows() {
return Math.max(1, (this.height - 120) / 10);
}
private int maxScroll() {
return Math.max(0, lines.size() - visibleRows());
}
private void updateScrollButtons() {
if (upButton != null) {
upButton.active = scroll > 0;
}
if (downButton != null) {
downButton.active = scroll < maxScroll();
}
}
@Override
public void render(GuiGraphics g, int mouseX, int mouseY, float partialTick) {
renderBackground(g);
g.drawCenteredString(this.minecraft.font, this.title, this.width / 2, 14, WHITE);
int verdictColour = switch (verdict) {
case "ok" -> GREEN;
case "absent" -> GREY;
default -> ORANGE;
};
String v = "判据 " + TABS[tab].successMarker() + " -> " + verdict;
g.drawString(this.minecraft.font, v, this.width / 2 - 235, 62, verdictColour);
int y = 78;
int rows = visibleRows();
int end = Math.min(lines.size(), scroll + rows);
for (int i = scroll; i < end; i++) {
String line = lines.get(i);
int colour = GREY;
if (line.contains("ERROR") || line.contains("FAIL")) {
colour = ORANGE;
} else if (line.contains("Registered add-on") || line.contains("neural frame succeeded")
|| line.contains("session ready") || line.contains("feature ready")) {
colour = GREEN;
} else if (line.contains("WARN")) {
colour = ORANGE;
}
g.drawString(this.minecraft.font, this.minecraft.font.plainSubstrByWidth(line, this.width - 96),
this.width / 2 - 235, y, colour);
y += 10;
}
g.drawString(this.minecraft.font,
String.format("%d 行,显示 %d-%d", lines.size(), scroll + 1, end),
this.width / 2 - 235, this.height - 78, GREY);
super.render(g, mouseX, mouseY, partialTick);
}
@Override
public void onClose() {
this.minecraft.setScreen(parent);
}
}
@@ -0,0 +1,275 @@
package dev.wpyw.dlss.gui;
import dev.wpyw.dlss.cfg.FlatCfg;
import dev.wpyw.dlss.install.DlssStackInstaller;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractSliderButton;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Live quality settings.
*
* <p>Everything here is written into the two {@code .cfg} files that sit beside the add-ons. That
* is worth explaining, because it is why this screen can work at all: the DLSS5-Feeder README
* states that {@code dlss5-feed.cfg} is "re-read while the game runs, if you prefer editing the
* file directly", and Deep Fried Chicken behaves the same way. So most knobs take effect within a
* second or two without a restart - the notable exception being {@code arm}, which is
* restart-only.
*
* <p>The defaults are already sane (one neural pass at 100% work scale), so this screen exists
* mainly for two things: trading the neural pass off against frame rate, and fixing the HUD if
* the neural pass starts eating the hotbar.
*/
public class DlssSettingsScreen extends Screen {
private static final Logger LOG = LoggerFactory.getLogger("wpywdlss/gui");
private static final int WHITE = 0xFFFFFFFF;
private static final int GREY = 0xFFAAAAAA;
private static final int YELLOW = 0xFFFFD166;
private static final int GREEN = 0xFF55FF55;
private final Screen parent;
private FlatCfg feed;
private FlatCfg chicken;
private final List<String> notes = new ArrayList<>();
private int left;
private int right;
public DlssSettingsScreen(Screen parent) {
super(Component.literal("Wpyw DLSS - 画质设置"));
this.parent = parent;
}
private Path bin() {
return DlssStackInstaller.jvmBinDir();
}
@Override
protected void init() {
notes.clear();
try {
feed = FlatCfg.load(bin().resolve("dlss5-feed.cfg"));
} catch (Exception e) {
notes.add("读不到 dlss5-feed.cfg: " + e.getMessage());
}
try {
chicken = FlatCfg.load(bin().resolve("deep-fried-chicken.cfg"));
} catch (Exception e) {
notes.add("读不到 deep-fried-chicken.cfg: " + e.getMessage());
}
left = this.width / 2 - 235;
right = this.width / 2 + 5;
int y = 44;
final int row = 24;
// ---- Feed side (left column): the DLSS transport itself ----
addRenderableWidget(Button.builder(toggleLabel("Feeder 总开关", feed, "enabled"), b -> {
toggle(feed, "enabled");
b.setMessage(toggleLabel("Feeder 总开关", feed, "enabled"));
}).bounds(left, y, 230, 20).build());
y += row;
addRenderableWidget(new DoubleSlider(left, y, 230, 20, "锐化 (work_sharpness)", 0.0, 1.0, 0.01,
() -> feed == null ? 0.30 : feed.getDouble("work_sharpness", 0.30),
v -> setDouble(feed, "work_sharpness", v)));
y += row;
addRenderableWidget(new DoubleSlider(left, y, 230, 20, "创建延迟 (create_delay, 帧)", 0, 240, 1,
() -> feed == null ? 60 : feed.getInt("create_delay", 60),
v -> setInt(feed, "create_delay", (int) Math.round(v))));
y += row;
addRenderableWidget(new DoubleSlider(left, y, 230, 20, "卡顿阈值 (stall_log_ms)", 0, 200, 5,
() -> feed == null ? 50 : feed.getInt("stall_log_ms", 50),
v -> setInt(feed, "stall_log_ms", (int) Math.round(v))));
y += row;
addRenderableWidget(new DoubleSlider(left, y, 230, 20, "运动矢量放大 (mv_scale)", 0.5, 2.0, 0.05,
() -> feed == null ? 1.0 : feed.getDouble("mv_scale_x", 1.0),
v -> {
setDouble(feed, "mv_scale_x", v);
setDouble(feed, "mv_scale_y", v);
}));
// ---- Chicken side (right column): the neural pass ----
y = 44;
addRenderableWidget(Button.builder(toggleLabel("神经渲染", chicken, "enabled"), b -> {
toggle(chicken, "enabled");
b.setMessage(toggleLabel("神经渲染", chicken, "enabled"));
}).bounds(right, y, 230, 20).build());
y += row;
addRenderableWidget(new DoubleSlider(right, y, 230, 20, "神经 pass 数", 1, 10, 0.1,
() -> chicken == null ? 1.0 : chicken.getDouble("passes", 1.0),
v -> {
setDouble(chicken, "passes", v);
setInt(chicken, "layers", Math.max(1, (int) Math.ceil(v)));
}));
y += row;
addRenderableWidget(new DoubleSlider(right, y, 230, 20, "神经工作分辨率 %", 25, 150, 5,
() -> chicken == null ? 100 : chicken.getInt("neural_work_percent", 100),
v -> {
setInt(chicken, "neural_work_percent", (int) Math.round(v));
// >100% is neural supersampling; <100% can look soft. Both are documented trade-offs.
}));
y += row;
addRenderableWidget(new DoubleSlider(right, y, 230, 20, "强度 (layer_1_intensity)", 0.0, 3.0, 0.05,
() -> chicken == null ? 1.0 : chicken.getDouble("layer_1_intensity", 1.0),
v -> setDouble(chicken, "layer_1_intensity", v)));
y += row;
addRenderableWidget(Button.builder(toggleLabel("HUD 修正 (layer_1_ui_correction)", chicken, "layer_1_ui_correction"), b -> {
toggle(chicken, "layer_1_ui_correction");
b.setMessage(toggleLabel("HUD 修正 (layer_1_ui_correction)", chicken, "layer_1_ui_correction"));
}).bounds(right, y, 230, 20).build());
y += row;
addRenderableWidget(Button.builder(toggleLabel("保留原版色调 Native Look", chicken, "preserve_native_tone_color"), b -> {
toggle(chicken, "preserve_native_tone_color");
b.setMessage(toggleLabel("保留原版色调 Native Look", chicken, "preserve_native_tone_color"));
}).bounds(right, y, 230, 20).build());
// ---- bottom row ----
int by = this.height - 48;
addRenderableWidget(Button.builder(Component.literal("重载配置"), b -> {
this.rebuildWidgets();
}).bounds(this.width / 2 - 155, by, 150, 20).build());
addRenderableWidget(Button.builder(Component.literal("返回"), b -> this.onClose())
.bounds(this.width / 2 + 5, by, 150, 20).build());
}
private Component toggleLabel(String label, FlatCfg cfg, String key) {
if (cfg == null) {
return Component.literal(label + ": ?");
}
return Component.literal(label + ": " + (cfg.getBool(key, false) ? "开" : "关"));
}
private void toggle(FlatCfg cfg, String key) {
if (cfg == null) {
return;
}
boolean now = cfg.getBool(key, false);
cfg.set(key, !now);
flush(cfg, key);
}
private void setInt(FlatCfg cfg, String key, int v) {
if (cfg != null && cfg.set(key, v)) {
flush(cfg, key);
}
}
private void setDouble(FlatCfg cfg, String key, double v) {
if (cfg != null && cfg.set(key, v)) {
// Sliders fire continuously; writing on every pixel of drag would be wasteful.
// The value is committed on release (see DoubleSlider.onRelease).
pendingFlush = cfg;
}
}
private FlatCfg pendingFlush;
private void flush(FlatCfg cfg, String key) {
try {
cfg.save();
LOG.info("wpywdlss cfg write: {} -> {}", key, cfg.get(key, "?"));
} catch (Exception e) {
notes.add("写入失败: " + e.getMessage());
LOG.warn("cfg write failed", e);
}
}
@Override
public void render(GuiGraphics g, int mouseX, int mouseY, float partialTick) {
renderBackground(g);
g.drawCenteredString(this.minecraft.font, this.title, this.width / 2, 14, WHITE);
g.drawString(this.minecraft.font, "DLSS5-Feeder 传输", left, 32, GREY);
g.drawString(this.minecraft.font, "Deep Fried Chicken 神经渲染", right, 32, GREY);
int y = this.height - 78;
g.drawString(this.minecraft.font,
"多数项即时生效(cfg 在游戏运行时被重读);arm / early_load 类改动需重启。",
24, y, GREY);
y += 12;
g.drawString(this.minecraft.font,
"神经工作分辨率 >100% 是超采样(更清晰、更慢),<100% 会变软。",
24, y, GREY);
y += 12;
for (String n : notes) {
g.drawString(this.minecraft.font, n, 24, y, YELLOW);
y += 12;
}
super.render(g, mouseX, mouseY, partialTick);
}
@Override
public void onClose() {
if (pendingFlush != null) {
flush(pendingFlush, "(slider release)");
pendingFlush = null;
}
this.minecraft.setScreen(parent);
}
/** Slider bound to a getter/setter pair so it always reflects the file on disk. */
private class DoubleSlider extends AbstractSliderButton {
private final String label;
private final double min;
private final double max;
private final double step;
private final java.util.function.DoubleSupplier getter;
private final java.util.function.DoubleConsumer setter;
DoubleSlider(int x, int y, int w, int h, String label, double min, double max, double step,
java.util.function.DoubleSupplier getter, java.util.function.DoubleConsumer setter) {
super(x, y, w, h, Component.literal(label), normalise(getter.getAsDouble(), min, max));
this.label = label;
this.min = min;
this.max = max;
this.step = step;
this.getter = getter;
this.setter = setter;
updateMessage();
}
private static double normalise(double v, double min, double max) {
return max <= min ? 0.0 : Math.max(0.0, Math.min(1.0, (v - min) / (max - min)));
}
private double actual() {
double raw = min + this.value * (max - min);
if (step > 0) {
raw = Math.round(raw / step) * step;
}
return raw;
}
@Override
protected void updateMessage() {
setMessage(Component.literal(String.format(Locale.ROOT, "%s: %.2f", label, actual())));
}
@Override
protected void applyValue() {
setter.accept(actual());
}
}
}
@@ -0,0 +1,204 @@
package dev.wpyw.dlss.gui;
import dev.wpyw.dlss.diag.LogTail;
import dev.wpyw.dlss.install.DlssStackInstaller;
import dev.wpyw.dlss.install.NgxRuntimeLocator;
import net.minecraft.Util;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
/**
* Main screen: shows whether the stack is installed, offers install / repair / uninstall, and
* surfaces the verdict lines from the three logs so the player can see what actually happened
* without leaving the game.
*/
public class DlssSetupScreen extends Screen {
private static final Logger LOG = LoggerFactory.getLogger("wpywdlss/gui");
private static final int GREEN = 0xFF55FF55;
private static final int YELLOW = 0xFFFFD166;
private static final int RED = 0xFFFF6B6B;
private static final int GREY = 0xFFAAAAAA;
private static final int WHITE = 0xFFFFFFFF;
private final Screen parent;
private DlssStackInstaller.Report report;
private List<String> statusLines = new ArrayList<>();
private int statusColour = GREY;
private String actionMessage = "";
public DlssSetupScreen(Screen parent) {
super(Component.literal("Wpyw DLSS - 状态与安装"));
this.parent = parent;
}
@Override
protected void init() {
refresh();
int cx = this.width / 2;
int y = this.height - 58;
int gap = 6;
Button installBtn = Button.builder(Component.literal(installLabel()), b -> {
try {
DlssStackInstaller.Report r = DlssStackInstaller.install();
actionMessage = "安装完成:" + r.present().size() + " 个文件就位"
+ (r.ngxMissing().isEmpty() ? "" : ",但缺 " + r.ngxMissing().size() + " 个 NVIDIA 运行时");
LOG.info("install finished: {}", r.status());
LOG.info("\n{}", r.describe());
} catch (Exception e) {
actionMessage = "安装失败:" + e.getClass().getSimpleName() + ": " + e.getMessage();
LOG.error("install failed", e);
}
this.rebuildWidgets();
}).bounds(cx - 235, y, 150, 20).build();
Button settingsBtn = Button.builder(Component.literal("画质设置"), b ->
this.minecraft.setScreen(new DlssSettingsScreen(this))).bounds(cx - 79, y, 150, 20).build();
Button logsBtn = Button.builder(Component.literal("日志与诊断"), b ->
this.minecraft.setScreen(new DlssLogScreen(this))).bounds(cx + 77, y, 150, 20).build();
Button uninstallBtn = Button.builder(Component.literal("卸载"), b -> {
try {
DlssStackInstaller.Report r = DlssStackInstaller.uninstall();
actionMessage = "已卸载(" + r.notes().get(r.notes().size() - 1) + ")";
} catch (Exception e) {
actionMessage = "卸载失败:" + e.getMessage();
}
this.rebuildWidgets();
}).bounds(cx - 235, y + 24, 150, 20).build();
Button openBtn = Button.builder(Component.literal("打开安装目录"), b -> {
try {
Util.getPlatform().openFile(DlssStackInstaller.jvmBinDir().toFile());
} catch (Exception e) {
actionMessage = "无法打开目录:" + e.getMessage();
}
}).bounds(cx - 79, y + 24, 150, 20).build();
Button doneBtn = Button.builder(Component.literal("完成"), b -> this.onClose())
.bounds(cx + 77, y + 24, 150, 20).build();
addRenderableWidget(installBtn);
addRenderableWidget(settingsBtn);
addRenderableWidget(logsBtn);
addRenderableWidget(uninstallBtn);
addRenderableWidget(openBtn);
addRenderableWidget(doneBtn);
// Only an unsupported target is a dead end; otherwise let the user re-verify at will.
installBtn.active = report.status() != DlssStackInstaller.Status.UNSUPPORTED;
}
private String installLabel() {
if (report == null) {
return "安装 / 修复";
}
return switch (report.status()) {
case NOT_INSTALLED -> "安装";
case NEEDS_REPAIR -> "修复";
case INSTALLED -> "重新校验 / 修复运行时";
case UNSUPPORTED -> "无法安装";
};
}
private void refresh() {
report = DlssStackInstaller.analyze();
statusLines = buildStatusLines();
// The video settings buttons cache their probe; anything that happens here can change
// the answer they show, so drop that cache on every entry into this panel.
VideoSettingsHook.invalidate();
}
private List<String> buildStatusLines() {
List<String> out = new ArrayList<>();
Path target = report.target();
statusColour = switch (report.status()) {
case INSTALLED -> GREEN;
case NEEDS_REPAIR -> YELLOW;
default -> RED;
};
out.add("状态: " + report.status());
out.add("安装目标: " + target);
out.add("载荷: " + report.present().size() + " 个文件已就位"
+ (report.missing().isEmpty() ? "" : ",缺 " + report.missing().size())
+ (report.modified().isEmpty() ? "" : ",被改动 " + report.modified().size()));
for (NgxRuntimeLocator.Found f : report.ngx()) {
out.add("NVIDIA: " + f.name() + " " + String.format("%,d B", f.size())
+ (f.sizeMatchesNominal() ? "" : " (与验证过的 SDK 大小不同)"));
}
for (String m : report.ngxMissing()) {
out.add("NVIDIA: 缺失 " + m + " —— 需要放在 javaw.exe 旁");
}
boolean runningNow = Files.isRegularFile(target.resolve("opengl32.dll"));
if (report.status() == DlssStackInstaller.Status.INSTALLED) {
out.add("");
out.add("ReShade 是代理 DLL,只在 javaw.exe 启动时加载。");
out.add("本次启动是否已生效,取决于启动时它是否已经就位:");
Path reshadeLog = target.resolve(LogTail.RESHADE);
String rv = LogTail.verdict(reshadeLog, "Registered add-on");
out.add(" ReShade.log: " + (rv.equals("ok") ? "已加载并注册 add-on" : "尚未生效 —— 请重启游戏"));
}
if (!report.notes().isEmpty()) {
out.add("");
for (String n : report.notes()) {
out.add("· " + n);
}
}
return out;
}
@Override
public void render(GuiGraphics g, int mouseX, int mouseY, float partialTick) {
renderBackground(g);
g.drawCenteredString(this.minecraft.font, this.title, this.width / 2, 14, WHITE);
int y = 38;
int left = 24;
int maxWidth = this.width - 48;
int colour = statusColour;
for (String line : statusLines) {
if (line.isEmpty()) {
y += 6;
colour = GREY;
continue;
}
for (var wrapped : this.minecraft.font.split(Component.literal(line), maxWidth)) {
g.drawString(this.minecraft.font, wrapped, left, y, colour);
y += 11;
}
y += 1;
colour = GREY; // only the status line is coloured
}
if (!actionMessage.isEmpty()) {
g.drawString(this.minecraft.font, actionMessage, left, this.height - 74, YELLOW);
}
super.render(g, mouseX, mouseY, partialTick);
}
@Override
public void onClose() {
this.minecraft.setScreen(parent);
}
}
@@ -0,0 +1,205 @@
package dev.wpyw.dlss.gui;
import dev.wpyw.dlss.cfg.MasterSwitch;
import dev.wpyw.dlss.install.DlssStackInstaller;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.fabric.api.client.screen.v1.Screens;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.VideoSettingsScreen;
import net.minecraft.network.chat.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
/**
* Puts the DLSS controls into <b>Options - Video Settings</b>, which is where a player looks
* for anything that changes how the game is drawn.
*
* <p>Two buttons flank the vanilla "Done" button:
* <ul>
* <li><b>left</b> - the master on/off switch (only shown once the stack is installed, because
* before that there is nothing to switch);</li>
* <li><b>right</b> - opens the DLSS panel, and its label carries the live install state so the
* answer to "is it actually working" is visible without clicking anything.</li>
* </ul>
*
* <h2>Why this is safe to do from an event instead of a mixin</h2>
* {@code Screens.getButtons(screen)} is a {@code ButtonList} built over the screen's own
* {@code drawables}, {@code selectables} and {@code children} lists, and its {@code add} inserts
* into all three. That is exactly what {@code Screen.addRenderableWidget} does, so an injected
* button renders, takes clicks and takes keyboard focus on vanilla's normal code path - no
* accessor, no {@code @Invoker}, and nothing to break when the screen is resized.
*
* <h2>Geometry</h2>
* Taken from the 1.20.1 bytecode rather than guessed: {@code VideoSettingsScreen.init()} builds
* {@code new OptionsList(minecraft, width, height, 32, height - 32, 25)} and then adds the Done
* button at {@code (width/2 - 100, height - 27, 200, 20)}. The option rows stop at
* {@code height - 32}, so the strip the Done button lives in is free on both sides of it - which
* is where these two go. When the window is too narrow to fit anything beside Done, nothing is
* injected; {@code /dlss} always works.
*/
public final class VideoSettingsHook {
private static final Logger LOG = LoggerFactory.getLogger("wpywdlss/gui");
// Vanilla geometry, verified against the compiled class.
private static final int DONE_WIDTH = 200;
private static final int ROW_HEIGHT = 20;
private static final int DONE_Y_FROM_BOTTOM = 27;
private static final int GAP = 6;
private static final int EDGE_INSET = 4;
private static final int MIN_WIDTH = 64;
private static final int MAX_WIDTH = 170;
/** Probing means hashing ~10 MB of payload; do not do it on every screen open. */
private static final long CACHE_TTL_MS = 1500L;
private static DlssStackInstaller.Status cachedStatus = DlssStackInstaller.Status.UNSUPPORTED;
private static String cachedDetail = "";
private static boolean cachedSwitchable;
private static boolean cachedOn;
private static long cachedAt;
private VideoSettingsHook() {
}
public static void register() {
ScreenEvents.AFTER_INIT.register((client, screen, width, height) -> {
if (!(screen instanceof VideoSettingsScreen)) {
return;
}
try {
inject(screen, width, height);
} catch (Throwable t) {
// A broken button must never take the vanilla video settings screen down with it.
LOG.warn("could not inject the DLSS buttons into the video settings screen", t);
}
});
}
private static void inject(Screen screen, int width, int height) {
int side = width / 2 - DONE_WIDTH / 2 - GAP - EDGE_INSET;
int buttonWidth = Math.min(MAX_WIDTH, side);
if (buttonWidth < MIN_WIDTH) {
LOG.info("window too narrow ({}x{}) for the DLSS buttons in video settings; use /dlss",
width, height);
return;
}
refresh();
int y = height - DONE_Y_FROM_BOTTOM;
int leftX = width / 2 - DONE_WIDTH / 2 - GAP - buttonWidth;
int rightX = width / 2 + DONE_WIDTH / 2 + GAP;
if (cachedSwitchable) {
Screens.getButtons(screen).add(switchButton(leftX, y, buttonWidth));
}
Screens.getButtons(screen).add(panelButton(screen, rightX, y, buttonWidth));
}
// ------------------------------------------------------------------ buttons
private static Button switchButton(int x, int y, int width) {
return Button.builder(Component.literal(switchLabel(cachedOn)), b -> {
Path bin = DlssStackInstaller.jvmBinDir();
MasterSwitch.State before = MasterSwitch.read(bin);
MasterSwitch.State after = MasterSwitch.write(bin, !before.on());
b.setMessage(Component.literal(switchLabel(after.on())));
cachedOn = after.on();
cachedAt = 0L; // force a re-probe the next time the screen opens
if (!after.note().isEmpty()) {
LOG.warn("master switch: {}", after.note());
} else {
LOG.info("DLSS master switch -> {}", after.on() ? "on" : "off");
}
}).bounds(x, y, width, ROW_HEIGHT)
.tooltip(Tooltip.create(Component.literal(
"DLSS 总开关\n"
+ "同时写入 DLSS5-Feeder 与 Deep Fried Chicken 两个 cfg 的 enabled。\n"
+ "两个 add-on 都会在游戏运行时重读自己的 cfg,所以这是即时生效的开关,不需要重启。\n"
+ "分开开关会出现「半开」状态:Feeder 在请求神经帧,但没人渲染它们。")))
.build();
}
private static Button panelButton(Screen parent, int x, int y, int width) {
return Button.builder(Component.literal(panelLabel(cachedStatus)), b ->
Minecraft.getInstance().setScreen(new DlssSetupScreen(parent)))
.bounds(x, y, width, ROW_HEIGHT)
.tooltip(Tooltip.create(Component.literal(
"Wpyw DLSS —— 状态、安装、画质设置、日志诊断\n\n" + cachedDetail)))
.build();
}
private static String switchLabel(boolean on) {
return on ? "DLSS: 开" : "DLSS: 关";
}
private static String panelLabel(DlssStackInstaller.Status status) {
return switch (status) {
case INSTALLED -> "DLSS · 已安装";
case NOT_INSTALLED -> "DLSS · 未安装";
case NEEDS_REPAIR -> "DLSS · 需修复";
case UNSUPPORTED -> "DLSS · 不可用";
};
}
// ------------------------------------------------------------------ probe
private static void refresh() {
long now = System.currentTimeMillis();
if (cachedAt != 0L && now - cachedAt < CACHE_TTL_MS) {
return;
}
cachedAt = now;
try {
DlssStackInstaller.Report r = DlssStackInstaller.analyze();
MasterSwitch.State sw = MasterSwitch.read(DlssStackInstaller.jvmBinDir());
cachedStatus = r.status();
cachedSwitchable = sw.switchable();
cachedOn = sw.on();
cachedDetail = describe(r);
} catch (Throwable t) {
cachedStatus = DlssStackInstaller.Status.UNSUPPORTED;
cachedSwitchable = false;
cachedOn = false;
cachedDetail = "状态探测失败: " + t;
LOG.warn("probe failed", t);
}
}
private static String describe(DlssStackInstaller.Report r) {
StringBuilder sb = new StringBuilder();
sb.append("状态: ").append(switch (r.status()) {
case INSTALLED -> "已安装";
case NOT_INSTALLED -> "未安装";
case NEEDS_REPAIR -> "需要修复";
case UNSUPPORTED -> "目标不可用";
}).append('\n');
sb.append("安装目标: ").append(r.target()).append('\n');
sb.append("载荷: ").append(r.present().size()).append(" 个文件已就位");
if (!r.missing().isEmpty()) {
sb.append(",缺 ").append(r.missing().size());
}
if (!r.modified().isEmpty()) {
sb.append(",被改动 ").append(r.modified().size());
}
sb.append('\n');
if (!r.ngxMissing().isEmpty()) {
sb.append("NVIDIA 运行时缺失: ").append(String.join(", ", r.ngxMissing())).append('\n');
}
sb.append("\n命令行同样可用: /dlss");
return sb.toString();
}
/** Drops the cached probe so the next screen open re-reads the disk. */
public static void invalidate() {
cachedAt = 0L;
}
}
@@ -0,0 +1,602 @@
package dev.wpyw.dlss.install;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import dev.wpyw.dlss.cfg.IniFile;
import net.fabricmc.loader.api.FabricLoader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
/**
* Installs the DLSS stack into the running JVM so that Minecraft's OpenGL renderer picks it up.
*
* <h2>Why the JVM's own {@code bin/} directory</h2>
* ReShade is not a library the game links against - it is a <b>proxy DLL</b>. Windows resolves
* {@code opengl32.dll} by searching the directory of the running executable first, so the proxy
* must sit next to {@code javaw.exe}, and ReShade then loads its add-ons from that same directory.
* The NGX runtimes must be there too, for the same reason (the feeder and Deep Fried Chicken both
* look beside the executable).
*
* <h2>Why a restart is always required</h2>
* A Fabric mod initialises long after {@code javaw.exe} has resolved its imports. By the time this
* code runs, {@code opengl32.dll} has already been loaded - the stock system one, if ReShade was
* not there at process start. Nothing can retro-fit a proxy DLL into a running process, so the
* only honest design is: install now, activate on the next launch. Deep Fried Chicken's own
* installer reaches the same conclusion in its logs
* ("will only prepare the next launch").
*/
public final class DlssStackInstaller {
private static final String PAYLOAD_ROOT = "/assets/wpywdlss/payload";
private static final String MANIFEST_RES = PAYLOAD_ROOT + "/payload-manifest.txt";
private static final String RECORD_NAME = "wpywdlss-install.json";
private static final String BACKUP_DIR_NAME = "wpywdlss-backup";
/** Payload paths are stored relative to the payload root, prefixed with the target subtree. */
private static final String TARGET_SUBTREE = "jvm/";
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private DlssStackInstaller() {
}
// ------------------------------------------------------------------ model
public enum Status {
/** Nothing of ours is present. */
NOT_INSTALLED,
/** Every payload file is present with the expected hash. */
INSTALLED,
/** Partially installed, or files were changed/removed since install. */
NEEDS_REPAIR,
/** Blocked: the target does not look like a writable 64-bit JVM directory. */
UNSUPPORTED
}
public record Entry(String relPath, String sha256, long size) {
}
public record Report(
Status status,
Path target,
List<Entry> present,
List<Entry> missing,
List<Entry> modified,
List<NgxRuntimeLocator.Found> ngx,
List<String> ngxMissing,
Path foreignProxyBackup,
List<String> notes,
boolean restartRequired) {
public boolean ok() {
return status == Status.INSTALLED;
}
/** Multi-line human-readable summary, used by the GUI and the log. */
public String describe() {
StringBuilder sb = new StringBuilder();
sb.append("target : ").append(target).append('\n');
sb.append("status : ").append(status).append('\n');
sb.append("payload files : ").append(present.size()).append(" ok");
if (!missing.isEmpty()) {
sb.append(", ").append(missing.size()).append(" missing");
}
if (!modified.isEmpty()) {
sb.append(", ").append(modified.size()).append(" modified");
}
sb.append('\n');
for (NgxRuntimeLocator.Found f : ngx) {
sb.append("ngx runtime : ").append(f.name())
.append(" ").append(String.format("%,d B", f.size()))
.append(f.sizeMatchesNominal() ? "" : " (size differs from verified SDK)")
.append('\n');
}
for (String m : ngxMissing) {
sb.append("ngx runtime : MISSING ").append(m).append('\n');
}
if (foreignProxyBackup != null) {
sb.append("previous opengl32.dll backed up to: ").append(foreignProxyBackup).append('\n');
}
for (String n : notes) {
sb.append("note : ").append(n).append('\n');
}
if (restartRequired) {
sb.append("action : RESTART Minecraft - ReShade only loads at process start\n");
}
return sb.toString();
}
}
// ------------------------------------------------------------------ paths
/** The directory holding {@code java.exe} / {@code javaw.exe} for the running JVM. */
public static Path jvmBinDir() {
return Path.of(System.getProperty("java.home"), "bin");
}
public static Path installRecordPath() {
return jvmBinDir().resolve(RECORD_NAME);
}
private static boolean looksLikeJvmBin(Path bin) {
return Files.isRegularFile(bin.resolve("java.exe")) || Files.isRegularFile(bin.resolve("javaw.exe"));
}
// ------------------------------------------------------------------ manifest
/** Parses the SHA-256 manifest generated by Gradle at build time. */
public static List<Entry> readManifest() throws IOException {
try (InputStream in = DlssStackInstaller.class.getResourceAsStream(MANIFEST_RES)) {
if (in == null) {
throw new IOException("payload manifest missing from the jar (" + MANIFEST_RES + ")");
}
List<Entry> out = new ArrayList<>();
for (String line : new String(in.readAllBytes(), StandardCharsets.UTF_8).split("\n")) {
String t = line.trim();
if (t.isEmpty() || t.startsWith("#")) {
continue;
}
String[] parts = t.split("\\s{2,}");
if (parts.length < 2) {
continue;
}
long size = parts.length > 2 ? Long.parseLong(parts[2].trim()) : -1L;
out.add(new Entry(parts[1].trim(), parts[0].trim(), size));
}
out.sort(Comparator.comparing(Entry::relPath));
return out;
}
}
private static String targetRelative(String payloadRelPath) {
return payloadRelPath.startsWith(TARGET_SUBTREE)
? payloadRelPath.substring(TARGET_SUBTREE.length())
: payloadRelPath;
}
// ------------------------------------------------------------------ analyze
/** Read-only inspection. Never writes anything. */
public static Report analyze() {
Path target = jvmBinDir();
List<String> notes = new ArrayList<>();
List<Entry> present = new ArrayList<>();
List<Entry> missing = new ArrayList<>();
List<Entry> modified = new ArrayList<>();
List<NgxRuntimeLocator.Found> ngx = new ArrayList<>();
List<String> ngxMissing = new ArrayList<>();
if (!looksLikeJvmBin(target)) {
notes.add("java.home does not point at a JVM bin directory containing java.exe/javaw.exe");
return new Report(Status.UNSUPPORTED, target, present, missing, modified, ngx, ngxMissing,
null, notes, false);
}
List<Entry> manifest;
try {
manifest = readManifest();
} catch (IOException e) {
notes.add("cannot read payload manifest: " + e.getMessage());
return new Report(Status.UNSUPPORTED, target, present, missing, modified, ngx, ngxMissing,
null, notes, false);
}
if (manifest.isEmpty()) {
notes.add("the jar contains no payload - build with the payload directory present");
}
for (Entry e : manifest) {
Path p = target.resolve(targetRelative(e.relPath()));
if (!Files.isRegularFile(p)) {
missing.add(e);
continue;
}
String actual = sha256(p);
if (actual != null && actual.equalsIgnoreCase(e.sha256())) {
present.add(e);
} else {
modified.add(e);
}
}
NgxRuntimeLocator loc = new NgxRuntimeLocator(target,
FabricLoader.getInstance().getGameDir(),
FabricLoader.getInstance().getConfigDir());
for (NgxRuntimeLocator.Found f : loc.locateAll()) {
ngx.add(f);
}
for (String name : NgxRuntimeLocator.runtimeNames()) {
boolean found = ngx.stream().anyMatch(f -> f.name().equals(name));
if (!found) {
ngxMissing.add(name);
}
}
Status status;
if (present.isEmpty() && missing.size() == manifest.size()) {
status = Status.NOT_INSTALLED;
} else if (missing.isEmpty() && modified.isEmpty()) {
status = Status.INSTALLED;
} else {
status = Status.NEEDS_REPAIR;
}
// A foreign proxy (a ReShade build we did not put there, or another injector) matters.
Path proxy = target.resolve("opengl32.dll");
boolean proxyIsOurs = present.stream().anyMatch(e -> targetRelative(e.relPath()).equalsIgnoreCase("opengl32.dll"));
if (Files.isRegularFile(proxy) && !proxyIsOurs) {
notes.add("a different opengl32.dll is already present; installing will back it up");
}
return new Report(status, target, present, missing, modified, ngx, ngxMissing, null, notes, false);
}
// ------------------------------------------------------------------ install
/**
* Copies the payload into the JVM bin directory and writes the ReShade configuration.
* Idempotent: re-running repairs missing or altered files and leaves everything else alone.
*/
public static Report install() throws IOException {
Path target = jvmBinDir();
List<String> notes = new ArrayList<>();
List<Entry> present = new ArrayList<>();
List<Entry> missing = new ArrayList<>();
List<Entry> modified = new ArrayList<>();
List<NgxRuntimeLocator.Found> ngx = new ArrayList<>();
List<String> ngxMissing = new ArrayList<>();
if (!looksLikeJvmBin(target)) {
notes.add("refusing to install: " + target + " is not a JVM bin directory");
return new Report(Status.UNSUPPORTED, target, present, missing, modified, ngx, ngxMissing,
null, notes, false);
}
Files.createDirectories(target);
Path backupDir = target.resolve(BACKUP_DIR_NAME);
Path foreignProxyBackup = null;
List<Entry> manifest = readManifest();
if (manifest.isEmpty()) {
notes.add("nothing to install: the jar contains no payload");
return new Report(Status.UNSUPPORTED, target, present, missing, modified, ngx, ngxMissing,
null, notes, false);
}
// --- 1. preserve any opengl32.dll we did not put there -----------------
boolean payloadOwnsProxy = manifest.stream()
.anyMatch(e -> targetRelative(e.relPath()).equalsIgnoreCase("opengl32.dll"));
Path proxy = target.resolve("opengl32.dll");
if (payloadOwnsProxy && Files.isRegularFile(proxy)) {
String existing = sha256(proxy);
Entry expected = manifest.stream()
.filter(e -> targetRelative(e.relPath()).equalsIgnoreCase("opengl32.dll"))
.findFirst().orElse(null);
if (expected != null && existing != null && !existing.equalsIgnoreCase(expected.sha256())) {
Files.createDirectories(backupDir);
Path bak = backupDir.resolve("opengl32.dll." + stamp() + ".bak");
Files.copy(proxy, bak, StandardCopyOption.REPLACE_EXISTING);
foreignProxyBackup = bak;
notes.add("existing opengl32.dll was not ours and has been backed up");
}
}
// --- 2. extract / repair every payload file ---------------------------
long copied = 0;
long bytes = 0;
for (Entry e : manifest) {
Path dest = target.resolve(targetRelative(e.relPath()));
boolean needWrite = true;
if (Files.isRegularFile(dest)) {
String actual = sha256(dest);
if (actual != null && actual.equalsIgnoreCase(e.sha256())) {
needWrite = false;
present.add(e);
} else {
modified.add(e);
}
} else {
missing.add(e);
}
if (!needWrite) {
continue;
}
try (InputStream in = DlssStackInstaller.class.getResourceAsStream(PAYLOAD_ROOT + "/" + e.relPath())) {
if (in == null) {
notes.add("payload resource missing from jar: " + e.relPath());
continue;
}
Files.createDirectories(dest.getParent());
Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING);
copied++;
bytes += Files.size(dest);
present.add(e);
missing.remove(e);
modified.remove(e);
}
}
notes.add(String.format("wrote %d file(s), %,.2f MB", copied, bytes / 1048576.0));
// --- 3. NVIDIA NGX runtimes ------------------------------------------
NgxRuntimeLocator loc = new NgxRuntimeLocator(target,
FabricLoader.getInstance().getGameDir(),
FabricLoader.getInstance().getConfigDir());
for (NgxRuntimeLocator.Found f : loc.locateAll()) {
Path dest = target.resolve(f.name());
if (!dest.equals(f.path())) {
Files.copy(f.path(), dest, StandardCopyOption.REPLACE_EXISTING);
notes.add("copied " + f.name() + " from " + f.path());
}
ngx.add(new NgxRuntimeLocator.Found(f.name(), dest, Files.size(dest),
f.sizeMatchesNominal(), f.origin()));
}
for (String name : NgxRuntimeLocator.runtimeNames()) {
if (ngx.stream().noneMatch(f -> f.name().equals(name))) {
ngxMissing.add(name);
notes.add("NVIDIA runtime not found anywhere: " + name
+ " - place an official copy next to javaw.exe, or rebuild with -PbundleNvidia=true");
}
}
// --- 4. ReShade configuration ----------------------------------------
writeReshadeIni(target, backupDir, notes);
writeReshadePreset(target, backupDir, notes);
// --- 5. install record ------------------------------------------------
writeRecord(target, manifest, ngx);
Status status = (missing.isEmpty() && modified.isEmpty())
? Status.INSTALLED
: Status.NEEDS_REPAIR;
return new Report(status, target, present, missing, modified, ngx, ngxMissing,
foreignProxyBackup, notes, true);
}
// ------------------------------------------------------------------ ini writers
private static final String DEFAULT_RESHADE_INI = """
[DEPTH]
DepthCopyAtClearIndex=0
DepthCopyBeforeClears=1
DrawStatsHeuristic=0
UseAspectRatioHeuristics=1
[GENERAL]
EffectSearchPaths=.\\reshade-shaders\\Shaders\\**
NoDebugInfo=1
NoEffectCache=0
NoReloadOnInit=0
PerformanceMode=0
PresetPath=.\\ReShadePreset.ini
PreprocessorDefinitions=RESHADE_DEPTH_LINEARIZATION_FAR_PLANE=1000.0,RESHADE_DEPTH_INPUT_IS_UPSIDE_DOWN=0,RESHADE_DEPTH_INPUT_IS_REVERSED=0,RESHADE_DEPTH_INPUT_IS_LOGARITHMIC=0,DLSS5_MV_PROVIDER=3
TextureSearchPaths=.\\reshade-shaders\\Textures\\**
[INPUT]
GamepadNavigation=0
KeyOverlay=36,0,0,0
[PROXY]
EnableProxyLibrary=0
ProxyLibrary=
""";
private static void writeReshadeIni(Path target, Path backupDir, List<String> notes) throws IOException {
Path ini = target.resolve("ReShade.ini");
boolean existed = Files.isRegularFile(ini);
IniFile f = IniFile.load(ini);
if (!existed || f.isNew()) {
Files.writeString(ini, DEFAULT_RESHADE_INI, StandardCharsets.UTF_8);
notes.add("wrote a fresh ReShade.ini");
return;
}
// Merge rather than replace: ReShade and Deep Fried Chicken both own keys in this file.
Files.createDirectories(backupDir);
Files.copy(ini, backupDir.resolve("ReShade.ini." + stamp() + ".bak"), StandardCopyOption.REPLACE_EXISTING);
boolean changed = false;
// The motion-vector provider is what makes DLSS5_Feed.fx able to find its input.
changed |= f.addToListElement("GENERAL", "PreprocessorDefinitions", "DLSS5_MV_PROVIDER=3");
// Minecraft's depth is not reversed - confirmed by direct observation in-game.
changed |= f.addToListElement("GENERAL", "PreprocessorDefinitions", "RESHADE_DEPTH_INPUT_IS_REVERSED=0");
changed |= f.set("GENERAL", "EffectSearchPaths", ".\\reshade-shaders\\Shaders\\**");
changed |= f.set("GENERAL", "TextureSearchPaths", ".\\reshade-shaders\\Textures\\**");
changed |= f.set("GENERAL", "PresetPath", ".\\ReShadePreset.ini");
changed |= f.set("DEPTH", "UseAspectRatioHeuristics", "1");
changed |= f.set("INPUT", "KeyOverlay", "36,0,0,0");
if (changed) {
f.save();
notes.add("merged required keys into the existing ReShade.ini (backed up)");
} else {
notes.add("existing ReShade.ini already had every required key");
}
}
private static final String PROVIDER = "Lumenite_Kernel@lumenite_Kernel.fx";
private static final String FEEDER = "DLSS5_Feed@DLSS5_Feed.fx";
private static void writeReshadePreset(Path target, Path backupDir, List<String> notes) throws IOException {
Path preset = target.resolve("ReShadePreset.ini");
boolean existed = Files.isRegularFile(preset);
IniFile f = IniFile.load(preset);
if (!existed || f.isNew()) {
Files.writeString(preset,
"# Managed by wpywdlss.\n"
+ "# Techniques= lists what is ENABLED; TechniqueSorting= defines EXECUTION ORDER,\n"
+ "# and the motion-vector provider must run before the consumer that reads it.\n"
+ "Techniques=" + PROVIDER + "," + FEEDER + "\n"
+ "TechniqueSorting=" + PROVIDER + "," + FEEDER + "\n",
StandardCharsets.UTF_8);
notes.add("wrote a fresh ReShadePreset.ini with the provider ordered before the feeder");
return;
}
Files.createDirectories(backupDir);
Files.copy(preset, backupDir.resolve("ReShadePreset.ini." + stamp() + ".bak"), StandardCopyOption.REPLACE_EXISTING);
boolean changed = false;
// Enable both, without disabling whatever else the user had enabled.
// null section = the file's preamble, which is where these two keys live.
changed |= f.addToListElement(null, "Techniques", PROVIDER);
changed |= f.addToListElement(null, "Techniques", FEEDER);
// The provider must execute before the consumer that reads its texture.
changed |= f.moveToFront(null, "TechniqueSorting", PROVIDER);
if (changed) {
f.save();
notes.add("merged the motion-vector provider and feeder into the existing preset (backed up)");
}
}
// ------------------------------------------------------------------ record / uninstall
private static void writeRecord(Path target, List<Entry> manifest,
List<NgxRuntimeLocator.Found> ngx) throws IOException {
JsonObject root = new JsonObject();
root.addProperty("schema", 1);
root.addProperty("installedUtc", Instant.now().toString());
root.addProperty("target", target.toString());
root.addProperty("javaHome", System.getProperty("java.home"));
root.addProperty("javaVersion", System.getProperty("java.version"));
JsonArray files = new JsonArray();
for (Entry e : manifest) {
JsonObject o = new JsonObject();
o.addProperty("path", targetRelative(e.relPath()));
o.addProperty("sha256", e.sha256());
o.addProperty("size", e.size());
files.add(o);
}
root.add("files", files);
JsonArray runtimes = new JsonArray();
for (NgxRuntimeLocator.Found f : ngx) {
JsonObject o = new JsonObject();
o.addProperty("name", f.name());
o.addProperty("origin", f.origin());
o.addProperty("source", f.path().toString());
runtimes.add(o);
}
root.add("ngxRuntimes", runtimes);
Files.writeString(target.resolve(RECORD_NAME), GSON.toJson(root), StandardCharsets.UTF_8);
}
/** Reads back the install record, or null when there is none / it is unreadable. */
public static JsonObject readRecord() {
Path p = installRecordPath();
if (!Files.isRegularFile(p)) {
return null;
}
try {
return GSON.fromJson(Files.readString(p, StandardCharsets.UTF_8), JsonObject.class);
} catch (Exception e) {
return null;
}
}
/**
* Removes only the files this installer recorded, and restores any proxy it displaced.
* Files it does not own (ReShade's own later additions, other tools) are left alone.
*/
public static Report uninstall() throws IOException {
Path target = jvmBinDir();
List<String> notes = new ArrayList<>();
List<Entry> removed = new ArrayList<>();
JsonObject rec = readRecord();
if (rec == null) {
notes.add("no install record found - nothing to remove");
return new Report(Status.NOT_INSTALLED, target, List.of(), List.of(), List.of(),
List.of(), List.of(), null, notes, true);
}
if (rec.has("files")) {
for (var el : rec.getAsJsonArray("files")) {
String rel = el.getAsJsonObject().get("path").getAsString();
Path p = target.resolve(rel);
if (Files.isRegularFile(p) && Files.deleteIfExists(p)) {
removed.add(new Entry(rel, "", 0));
}
}
}
Files.deleteIfExists(target.resolve(RECORD_NAME));
// Restore a displaced proxy, if we made a backup.
Path backupDir = target.resolve(BACKUP_DIR_NAME);
if (Files.isDirectory(backupDir)) {
try (Stream<Path> s = Files.list(backupDir)) {
Path newest = s.filter(p -> p.getFileName().toString().startsWith("opengl32.dll."))
.max(Comparator.comparing(p -> p.getFileName().toString()))
.orElse(null);
if (newest != null) {
Files.copy(newest, target.resolve("opengl32.dll"), StandardCopyOption.REPLACE_EXISTING);
notes.add("restored the previous opengl32.dll from " + newest.getFileName());
}
}
}
notes.add("removed " + removed.size() + " file(s); ReShade's own later additions were left alone");
return new Report(Status.NOT_INSTALLED, target, List.of(), List.of(), List.of(),
List.of(), List.of(), null, notes, true);
}
// ------------------------------------------------------------------ util
public static String sha256(Path p) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (InputStream in = Files.newInputStream(p)) {
byte[] buf = new byte[1 << 16];
int n;
while ((n = in.read(buf)) > 0) {
md.update(buf, 0, n);
}
}
StringBuilder sb = new StringBuilder();
for (byte b : md.digest()) {
sb.append(String.format("%02X", b));
}
return sb.toString();
} catch (Exception e) {
return null;
}
}
private static String stamp() {
return java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss")
.withZone(java.time.ZoneId.systemDefault())
.format(Instant.now());
}
/** Convenience for the GUI: a short one-line state. */
public static String shortState() {
Report r = analyze();
if (r.status() == Status.INSTALLED) {
return "installed";
}
if (r.status() == Status.NOT_INSTALLED) {
return "not installed";
}
if (r.status() == Status.UNSUPPORTED) {
return "unsupported";
}
Map<String, Integer> counts = new LinkedHashMap<>();
counts.put("missing", r.missing().size());
counts.put("modified", r.modified().size());
return "needs repair (" + counts.get("missing") + " missing, " + counts.get("modified") + " modified)";
}
}
@@ -0,0 +1,212 @@
package dev.wpyw.dlss.install;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
/**
* Locates the NVIDIA NGX runtimes this stack needs.
*
* <p>Both files must end up <b>next to the game executable</b> - for Minecraft Java that is the
* JVM's own {@code bin/} directory, because that is where {@code java.exe}/{@code javaw.exe}
* live. Deep Fried Chicken's installer says the same thing in its own words ("Copy these beside
* the actual game executable"), and it is why its host helper looks for the runtimes next to
* itself rather than in the game folder.
*
* <p>We deliberately do not ship these DLLs by default: together they are ~225 MB, they are
* NVIDIA proprietary binaries, and on any machine that has already run this stack once they are
* sitting in the target directory already. When they are missing this class searches a bounded
* set of plausible roots, and the installer copies whatever it finds.
*
* <p>The nominal sizes come from the SDK that was actually verified on this machine, so a
* wildly different file is rejected rather than copied.
*/
public final class NgxRuntimeLocator {
/** name -> {minimum plausible size, nominal size seen in the verified SDK} */
private static final Object[][] KNOWN = {
{"nvngx_dlss.dll", 20L * 1024 * 1024, 58_956_912L},
{"nvngx_dlssnr.dll", 40L * 1024 * 1024, 165_840_496L},
};
/** A located runtime: where it is and whether it is trustworthy enough to copy. */
public record Found(String name, Path path, long size, boolean sizeMatchesNominal, String origin) {
public String describe() {
return String.format("%s %,d B%s [%s]%n %s",
name, size, sizeMatchesNominal ? "" : " (size differs from verified SDK)", origin, path);
}
}
private final Path target;
private final List<Path> extraRoots = new ArrayList<>();
public NgxRuntimeLocator(Path targetBinDir, Path gameDir, Path modConfigDir) {
this.target = targetBinDir;
if (gameDir != null) {
extraRoots.add(gameDir);
}
if (modConfigDir != null) {
extraRoots.add(modConfigDir);
}
}
/** Adds a user-configured extra search root. */
public void addRoot(Path root) {
if (root != null) {
extraRoots.add(root);
}
}
public static List<String> runtimeNames() {
List<String> names = new ArrayList<>();
for (Object[] row : KNOWN) {
names.add((String) row[0]);
}
return names;
}
/** The path a runtime must occupy for the feeder and DFC to load it. */
public Path expectedPath(String name) {
return target.resolve(name);
}
public boolean isInPlace(String name) {
return verify(expectedPath(name)) != null;
}
/** Returns null when the file is unusable, otherwise a short verdict string. */
public static String verify(Path p) {
if (p == null || !Files.isRegularFile(p)) {
return null;
}
long size;
try {
size = Files.size(p);
} catch (IOException e) {
return null;
}
String name = p.getFileName().toString();
long min = 1L * 1024 * 1024;
for (Object[] row : KNOWN) {
if (row[0].equals(name)) {
min = (Long) row[1];
}
}
if (size < min) {
return "too small (" + size + " B)";
}
if (!PeProbe.isX64Pe(p)) {
return "not a 64-bit PE image";
}
return String.format("%,d B, x64 PE", size);
}
/**
* Finds a usable copy of every runtime, preferring the target directory (already correct),
* then the extra roots, then a bounded scan of each root.
*/
public List<Found> locateAll() {
List<Found> out = new ArrayList<>();
for (Object[] row : KNOWN) {
String name = (String) row[0];
long nominal = (Long) row[2];
Path inPlace = expectedPath(name);
if (Files.isRegularFile(inPlace)) {
long sz = sizeOf(inPlace);
out.add(new Found(name, inPlace, sz, sz == nominal, "already in the target directory"));
continue;
}
Found found = null;
for (Path root : searchRoots()) {
Path direct = root.resolve(name);
if (Files.isRegularFile(direct)) {
found = new Found(name, direct, sizeOf(direct), sizeOf(direct) == nominal, "direct hit");
break;
}
Path scanned = scanFor(root, name);
if (scanned != null) {
found = new Found(name, scanned, sizeOf(scanned), sizeOf(scanned) == nominal,
"found by bounded scan under " + root);
break;
}
}
if (found != null) {
out.add(found);
}
}
return out;
}
private Set<Path> searchRoots() {
// Ordered, de-duplicated. The target comes first so an existing correct install wins.
Set<Path> roots = new LinkedHashSet<>();
roots.add(target);
for (Path p : extraRoots) {
if (p != null && Files.isDirectory(p)) {
roots.add(p);
}
}
return roots;
}
private static long sizeOf(Path p) {
try {
return Files.size(p);
} catch (IOException e) {
return -1;
}
}
/**
* Depth-bounded recursive search. Minecraft mod folders contain a lot of jars, and the whole
* point is to stay fast, so this stays shallow.
*/
private static Path scanFor(Path root, String name) {
try (Stream<Path> s = Files.walk(root, 4)) {
return s.filter(Files::isRegularFile)
.filter(p -> p.getFileName().toString().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
} catch (IOException e) {
return null;
}
}
/** Minimal PE header probe: DOS magic, PE signature, COFF machine == AMD64. */
public static final class PeProbe {
private PeProbe() {
}
public static boolean isX64Pe(Path p) {
try (InputStream in = Files.newInputStream(p)) {
byte[] head = in.readNBytes(0x1000);
if (head.length < 0x40 || head[0] != 'M' || head[1] != 'Z') {
return false;
}
int peOff = u32(head, 0x3C);
if (peOff < 0 || peOff + 6 > head.length) {
return false;
}
if (head[peOff] != 'P' || head[peOff + 1] != 'E' || head[peOff + 2] != 0 || head[peOff + 3] != 0) {
return false;
}
int machine = (head[peOff + 4] & 0xFF) | ((head[peOff + 5] & 0xFF) << 8);
return machine == 0x8664; // IMAGE_FILE_MACHINE_AMD64
} catch (IOException e) {
return false;
}
}
private static int u32(byte[] b, int off) {
return (b[off] & 0xFF) | ((b[off + 1] & 0xFF) << 8) | ((b[off + 2] & 0xFF) << 16) | ((b[off + 3] & 0xFF) << 24);
}
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"schemaVersion": 1,
"id": "wpywdlss",
"version": "${version}",
"name": "Wpyw DLSS",
"description": "Adds NVIDIA DLSS to Minecraft Java by installing and driving a ReShade + DLSS5-Feeder + Deep Fried Chicken stack: a DLSS on/off switch and a control panel inside Options - Video Settings, plus /dlss for install, status and settings. The renderer is ReShade, so installing requires one game restart. NVIDIA runtime binaries are NOT bundled and are located on disk by the installer.",
"authors": [
"wpy"
],
"contact": {},
"license": "MIT",
"environment": "client",
"entrypoints": {
"client": [
"dev.wpyw.dlss.DlssClientMod"
]
},
"mixins": [
{
"config": "wpywdlss.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.14.21",
"minecraft": "~1.20.1",
"java": ">=17",
"fabric-api": "*"
},
"suggests": {
"sodium": "*",
"iris": "*"
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"required": true,
"minVersion": "0.8",
"package": "dev.wpyw.dlss.mixin",
"compatibilityLevel": "JAVA_17",
"client": [
],
"injectors": {
"defaultRequire": 1
}
}
+40
View File
@@ -0,0 +1,40 @@
import dev.wpyw.dlss.NativeBridge;
/**
* Standalone harness for the native bridge, so capability results can be verified
* WITHOUT launching Minecraft.
*
* run:
* $env:JAVA_HOME='C:\Program Files\Java\jdk-21.0.10'
* java -cp "build\classes\java\main" `
* -Dwpywdlss.bridgePath=native\build\wpywdlss_bridge.dll `
* tools\ProbeRunner.java
*/
public class ProbeRunner {
public static void main(String[] args) {
System.out.println("=== wpywdlss native bridge probe ===");
System.out.println();
if (!NativeBridge.load()) {
System.out.println("[FAIL] could not load bridge DLL");
System.out.println(" " + NativeBridge.loadError());
System.exit(1);
}
System.out.println("bridge loaded from : " + NativeBridge.loadedFrom());
System.out.println("bridge version : " + NativeBridge.nativeVersion());
System.out.println();
// ---- Vulkan side -------------------------------------------------
System.out.println(NativeBridge.nativeProbe());
boolean vkReady = NativeBridge.nativeIsReady();
System.out.println("nativeIsReady() = " + vkReady);
System.out.println();
// ---- OpenGL side -------------------------------------------------
System.out.println(NativeBridge.nativeProbeOpenGL());
NativeBridge.nativeShutdown();
System.out.println("shutdown ok");
System.exit(vkReady ? 0 : 2);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
{
"file_format_version": "1.0.0",
"layer": {
"name": "VK_LAYER_reshade",
"type": "GLOBAL",
"library_path": ".\\ReShade64.dll",
"api_version": "1.3.268",
"implementation_version": "1",
"description": "crosire's ReShade post-processing injector for 64-bit",
"device_extensions": [
{
"name": "VK_EXT_tooling_info",
"spec_version": "1",
"entrypoints": [ "vkGetPhysicalDeviceToolPropertiesEXT" ]
}
],
"disable_environment": {
"DISABLE_VK_LAYER_reshade_1": "1"
}
}
}
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
{
"file_format_version": "1.2.0",
"layer": {
"name": "VK_LAYER_feed_vk",
"type": "GLOBAL",
"library_path": ".\VkLayer_feed_vk.dll",
"api_version": "1.3.280",
"implementation_version": "1",
"description": "DLSS5-Feeder: enables the KHR external-interop extensions its Vulkan transport needs",
"functions": {
"vkNegotiateLoaderLayerInterfaceVersion": "vkNegotiateLoaderLayerInterfaceVersion"
},
"disable_environment": {
"DISABLE_VK_LAYER_feed_vk": "1"
}
}
}
+20
View File
@@ -0,0 +1,20 @@
@echo off
rem Launch a Vulkan game with VK_LAYER_feed_vk active, without touching the registry.
rem
rem run-with-feed-layer.bat "E:\path\to\game.exe" [args...]
rem
rem Only needed when dlss5-feed.log says the Vulkan interop entry points are missing.
rem Registry implicit-layer keys are deliberately avoided: other hook software
rem (overlays, capture tools) rewrites them, and a per-launch env var cannot be
rem clobbered or leak into other games.
setlocal
if "%~1"=="" (
echo Usage: run-with-feed-layer.bat "path\to\game.exe" [args...]
exit /b 1
)
set "VK_LAYER_PATH=%~dp0"
set "VK_INSTANCE_LAYERS=VK_LAYER_feed_vk"
echo Launching with VK_LAYER_feed_vk from "%VK_LAYER_PATH%"
start "" %*
endlocal
+77
View File
@@ -0,0 +1,77 @@
$ErrorActionPreference = 'Continue'
$g = 'D:\SteamLibrary\steamapps\common\ForzaHorizon5'
$src = 'H:\javahome\bin'
$bak = 'E:\deepseek\game-backups\ForzaHorizon5'
Write-Host '=== STEP 0: backups (outside the game folder, so the game never sees them) ==='
New-Item -ItemType Directory -Force -Path $bak | Out-Null
foreach ($n in @('nvngx_dlss.dll', 'nvngx_dlssg.dll', 'dxgi.dll', 'ReShade.ini', 'ReShade.log', 'ReShadePreset.ini')) {
if (Test-Path "$g\$n") {
Copy-Item "$g\$n" "$bak\$n.orig" -Force
$h = (Get-FileHash "$g\$n" -Algorithm SHA256).Hash
Write-Host (" {0,-22} {1,12} B {2}" -f $n, (Get-Item "$g\$n").Length, $h)
}
}
Write-Host (" backups -> " + $bak)
Write-Host ''
Write-Host '=== STEP 1: record the before-state of the DLSS runtime ==='
Write-Host (" FH5 nvngx_dlss.dll : {0} {1} (version {2})" -f (Get-Item "$g\nvngx_dlss.dll").Length,
(Get-FileHash "$g\nvngx_dlss.dll" -Algorithm SHA256).Hash.Substring(0,16),
(Get-Item "$g\nvngx_dlss.dll").VersionInfo.FileVersion)
Write-Host (" our nvngx_dlss.dll : {0} {1} (DLSS 5 era)" -f (Get-Item "$src\nvngx_dlss.dll").Length,
(Get-FileHash "$src\nvngx_dlss.dll" -Algorithm SHA256).Hash.Substring(0,16))
Write-Host ''
Write-Host '=== STEP 2: install the neural-rendering stack into the game folder ==='
$plan = @(
@{ n = 'deep-fried-chicken.addon64'; why = 'neural consumer (the add-on ReShade loads)' },
@{ n = 'deep-fried-chicken-nvngx.dll'; why = 'its NGX shim' },
@{ n = 'deep-fried-chicken.cfg'; why = 'its config (arm=1 hook_mode=3 enabled=1)' },
@{ n = 'nvngx_dlssnr.dll'; why = 'DLSS neural-rendering runtime (feature 18)' },
@{ n = 'nvngx_dlss.dll'; why = 'upgrade 3.1.11 -> 310.9.1 (DLSS 5 era SR runtime)' }
)
foreach ($p in $plan) {
if (-not (Test-Path "$src\$($p.n)")) { Write-Host (" MISSING SOURCE: " + $p.n); continue }
Copy-Item "$src\$($p.n)" "$g\$($p.n)" -Force
Write-Host (" {0,-28} {1,12} B <- {2}" -f $p.n, (Get-Item "$g\$($p.n)").Length, $p.why)
}
Write-Host ''
Write-Host '=== STEP 3: deliberately NOT installed, and why ==='
Write-Host ' dlss5-feed.addon64 : NOT copied -- FH5 already makes its own DLSS calls via Streamline.'
Write-Host ' The feeder builds a synthetic contract for games that have none;'
Write-Host ' two DLSS features in one frame is exactly what we do not want.'
Write-Host ' layer-x64 / VK layer: NOT copied -- this is a Direct3D 12 game, not Vulkan.'
Write-Host ' reshade-shaders : NOT copied -- DFC is an add-on, not an .fx effect. No preset needed.'
Write-Host ''
Write-Host '=== STEP 4: verify what ReShade will find in that folder ==='
Add-Type -Namespace P3 -Name N -MemberDefinition @'
[DllImport("kernel32", SetLastError=true, CharSet=CharSet.Unicode, EntryPoint="LoadLibraryExW")]
public static extern IntPtr L(string f, IntPtr h, uint fl);
[DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="GetProcAddress")]
public static extern IntPtr G(IntPtr h, string n);
[DllImport("kernel32", EntryPoint="FreeLibrary")] public static extern bool F(IntPtr h);
'@
foreach ($n in @('dxgi.dll','nvngx_dlss.dll','nvngx_dlssnr.dll','deep-fried-chicken.addon64')) {
$f = "$g\$n"
if (-not (Test-Path $f)) { Write-Host (" {0,-30} MISSING" -f $n); continue }
$h = [P3.N]::L($f, [IntPtr]::Zero, 0x1)
$ex = @()
if ($h -ne [IntPtr]::Zero) {
foreach ($s in @('ReShadeVersion','ReShadeRegisterAddon','NVSDK_NGX_D3D12_Init','NVSDK_NGX_D3D12_Init_Ext','ReShadeAddon')) {
if ([P3.N]::G($h,$s) -ne [IntPtr]::Zero) { $ex += $s }
}
[void][P3.N]::F($h)
}
Write-Host (" {0,-30} {1,12} B exports: {2}" -f $n, (Get-Item $f).Length, $(if ($ex) { $ex -join ', ' } else { '(none probed)' }))
}
Write-Host ''
Write-Host '=== STEP 5: total space used in the game folder by our files ==='
$ours = @('deep-fried-chicken.addon64','deep-fried-chicken-nvngx.dll','deep-fried-chicken.cfg','nvngx_dlssnr.dll','nvngx_dlss.dll')
$sum = 0
foreach ($n in $ours) { if (Test-Path "$g\$n") { $sum += (Get-Item "$g\$n").Length } }
Write-Host (" {0:N0} MB" -f ($sum / 1MB))
+110
View File
@@ -0,0 +1,110 @@
$ErrorActionPreference = 'Continue'
$nte = 'D:\Neverness To Everness\Client\WindowsNoEditor\HT\Binaries\Win64'
$src = 'H:\javahome\bin'
$reshade = 'E:\deepseek\MinecraftDLSS\tools\games' # not used; kept for clarity
Write-Host '=== STEP 0: 目标目录是我上次还原干净的,先确认没有任何游戏自己的文件会被覆盖 ==='
$willAdd = @('d3d12.dll','ReShade.ini','ReShadePreset.ini',
'deep-fried-chicken.addon64','deep-fried-chicken-nvngx.dll','deep-fried-chicken.cfg',
'nvngx_dlssnr.dll')
$clash = @()
foreach ($n in $willAdd) { if (Test-Path "$nte\$n") { $clash += $n } }
if ($clash.Count -eq 0) {
Write-Host ' 无冲突:我们只新增文件,不动游戏自己的任何文件'
} else {
Write-Host (' 注意,这些已存在,会被覆盖: ' + ($clash -join ', '))
}
Write-Host (' HTGame.exe 在位: ' + (Test-Path "$nte\HTGame.exe"))
Write-Host (' 目录里已有 dxgi.dll / d3d12.dll: dxgi=' + (Test-Path "$nte\dxgi.dll") + ' d3d12=' + (Test-Path "$nte\d3d12.dll"))
Write-Host ''
Write-Host '=== STEP 1: 代理 DLL —— 命名为 d3d12.dll(ReShade 官方支持的代理名)==='
# 用经过验证的 add-on 构建:导出 ReShadeRegisterAddon + ReShadeUnregisterAddon,
# 且已在 Minecraft 上实测成功加载过 add-on。
$addonBuild = "$src\opengl32.dll"
Copy-Item $addonBuild "$nte\d3d12.dll" -Force
$h = (Get-FileHash $addonBuild -Algorithm SHA256).Hash
Write-Host (" d3d12.dll <- " + $addonBuild)
Write-Host (" 大小 {0,12:N0} B 版本 {1} SHA256 {2}" -f (Get-Item "$nte\d3d12.dll").Length, (Get-Item "$nte\d3d12.dll").VersionInfo.FileVersion, $h)
Write-Host ''
Write-Host '=== STEP 2: ReShade 配置(中文界面 + 深度设置)==='
$ini = @"
[DEPTH]
DepthCopyBeforeClears=1
UseAspectRatioHeuristics=1
[GENERAL]
NoDebugInfo=1
NoEffectCache=0
PerformanceMode=0
[INPUT]
GamepadNavigation=0
KeyOverlay=36,0,0,0
[OVERLAY]
Language=zh-CN
ShowFPS=2
TutorialProgress=4
"@
[IO.File]::WriteAllText("$nte\ReShade.ini", $ini, (New-Object Text.UTF8Encoding $false))
[IO.File]::WriteAllText("$nte\ReShadePreset.ini", '', (New-Object Text.UTF8Encoding $false))
Write-Host ' ReShade.ini 写好(KeyOverlay=Home,界面中文)'
Get-Content "$nte\ReShade.ini" | ForEach-Object { Write-Host (" " + $_) }
Write-Host ''
Write-Host '=== STEP 3: 神经渲染组件 ==='
$plan = @(
@{ n='deep-fried-chicken.addon64'; why='神经消费者(Deep Fried Chicken 2.0.0)' },
@{ n='deep-fried-chicken-nvngx.dll'; why='它的 NGX 垫片' },
@{ n='deep-fried-chicken.cfg'; why='配置:arm=1 hook_mode=3(AUTO) enabled=1' },
@{ n='nvngx_dlssnr.dll'; why='DLSS 5 神经渲染运行时(feature 18),v310.8.0.0' }
)
foreach ($p in $plan) {
if (-not (Test-Path "$src\$($p.n)")) { Write-Host (" 缺源文件: " + $p.n); continue }
Copy-Item "$src\$($p.n)" "$nte\$($p.n)" -Force
Write-Host (" {0,-30} {1,12:N0} B {2}" -f $p.n, (Get-Item "$nte\$($p.n)").Length, $p.why)
}
Write-Host ''
Write-Host '=== STEP 4: 故意没有装的东西,以及理由 ==='
Write-Host ' nvngx_dlss.dll : 不动 —— 异环自带 310.5,和我们的 310.8 同代,碰它只会多一个变量'
Write-Host ' nvngx_dlssg.dll : 不动 —— 同理(自带 310.5.2)'
Write-Host ' dlss5-feed.addon64 : 不装 —— 异环自己做 DLSS 调用,feeder 是给没有 DLSS 的游戏造契约的'
Write-Host ' layer-x64 / VK layer: 不装 —— 这是 Direct3D 12 游戏,不是 Vulkan'
Write-Host ' reshade-shaders : 不装 —— DFC 是 add-on 不是 .fx,这条路径不需要着色器'
Write-Host ''
Write-Host '=== STEP 5: 校验(哈希 + 导出符号)==='
foreach ($p in @(
@($src.'ToString'(),'') )) { }
$check = @('d3d12.dll','deep-fried-chicken.addon64','nvngx_dlssnr.dll')
Add-Type -Namespace P9 -Name N -MemberDefinition @'
[DllImport("kernel32", SetLastError=true, CharSet=CharSet.Unicode, EntryPoint="LoadLibraryExW")]
public static extern IntPtr L(string f, IntPtr h, uint fl);
[DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="GetProcAddress")]
public static extern IntPtr G(IntPtr h, string n);
[DllImport("kernel32", EntryPoint="FreeLibrary")] public static extern bool F(IntPtr h);
'@
foreach ($n in $check) {
$f = "$nte\$n"
$hh = [P9.N]::L($f, [IntPtr]::Zero, 0x1)
$ex = @()
if ($hh -ne [IntPtr]::Zero) {
foreach ($s in @('ReShadeVersion','ReShadeRegisterAddon','ReShadeUnregisterAddon','AddonInit','AddonUninit','NVSDK_NGX_D3D12_Init_Ext')) {
if ([P9.N]::G($hh,$s) -ne [IntPtr]::Zero) { $ex += $s }
}
[void][P9.N]::F($hh)
}
Write-Host (" {0,-30} 导出: {1}" -f $n, $(if ($ex) { $ex -join ', ' } else { '(未探测到)' }))
}
Write-Host ''
Write-Host '=== 装完的清单 ==='
Get-ChildItem $nte -File | Where-Object { $_.Name -match '^(d3d12\.dll|ReShade|deep-fried|nvngx_dlssnr)' } |
Sort-Object Name | ForEach-Object { Write-Host (" {0,-32} {1,12:N0} B" -f $_.Name, $_.Length) }
$sum = 0
foreach ($n in $willAdd) { if (Test-Path "$nte\$n") { $sum += (Get-Item "$nte\$n").Length } }
Write-Host (" 合计 {0:N0} MB" -f ($sum / 1MB))
+43
View File
@@ -0,0 +1,43 @@
# Reverts everything this session added to Forza Horizon 5 and Neverness to Everness.
# Nothing of either game's own files is deleted except files we created.
# Run: powershell -ExecutionPolicy Bypass -File .\revert-games.ps1
$ErrorActionPreference = 'Continue'
$fh5 = 'D:\SteamLibrary\steamapps\common\ForzaHorizon5'
$nte = 'D:\Neverness To Everness\Client\WindowsNoEditor\HT\Binaries\Win64'
$bak = 'E:\deepseek\game-backups'
Write-Host '=== Forza Horizon 5 ==='
# Remove what we added
foreach ($n in @('deep-fried-chicken.addon64','deep-fried-chicken-nvngx.dll','deep-fried-chicken.cfg',
'nvngx_dlssnr.dll','deep-fried-chicken.log','ReShade.log')) {
if (Test-Path "$fh5\$n") { Remove-Item "$fh5\$n" -Force; Write-Host (" removed " + $n) }
}
# Restore the runtime we replaced and the config/log the installer had written
foreach ($n in @('nvngx_dlss.dll','ReShade.ini','ReShadePreset.ini','ReShade.log')) {
if (Test-Path "$bak\ForzaHorizon5\$n.orig") {
Copy-Item "$bak\ForzaHorizon5\$n.orig" "$fh5\$n" -Force
Write-Host (" restored " + $n)
}
}
# dxgi.dll was there before us (ReShade 6.8.0.2158). Restore the byte-identical original.
if (Test-Path "$bak\ForzaHorizon5\dxgi.dll.orig") {
Copy-Item "$bak\ForzaHorizon5\dxgi.dll.orig" "$fh5\dxgi.dll" -Force
Write-Host ' restored dxgi.dll'
}
Write-Host ' -> back to stock (ReShade proxy kept, since it was already installed before this session)'
Write-Host ''
Write-Host '=== Neverness to Everness ==='
foreach ($n in @('dxgi.dll','ReShade.ini','ReShade.log','ReShadePreset.ini',
'deep-fried-chicken.addon64','deep-fried-chicken-nvngx.dll','deep-fried-chicken.cfg',
'deep-fried-chicken.log','nvngx_dlssnr.dll')) {
if (Test-Path "$nte\$n") { Remove-Item "$nte\$n" -Force; Write-Host (" removed " + $n) }
}
Write-Host ' -> back to stock (we never overwrote any of the game'"'"'s own files there)'
Write-Host ''
Write-Host '=== verify ==='
Write-Host (' FH5 nvngx_dlss.dll : ' + (Get-Item "$fh5\nvngx_dlss.dll").VersionInfo.FileVersion)
Write-Host (' FH5 leftovers : ' + (@(Get-ChildItem $fh5 -File | Where-Object { $_.Name -match 'deep-fried|nvngx_dlssnr' }).Count) + ' file(s)')
Write-Host (' NTE leftovers : ' + (@(Get-ChildItem $nte -File | Where-Object { $_.Name -match 'deep-fried|nvngx_dlssnr|^dxgi\.dll$|^ReShade' }).Count) + ' file(s)')
+51
View File
@@ -0,0 +1,51 @@
$ErrorActionPreference = 'Continue'
$nte = 'D:\Neverness To Everness\Client\WindowsNoEditor\HT\Binaries\Win64'
Write-Host '=== what this session put into the NTE client folder ==='
$ours = @('dxgi.dll','D3D12.dll','ReShade.ini','ReShade.log','ReShadePreset.ini',
'deep-fried-chicken.addon64','deep-fried-chicken-nvngx.dll','deep-fried-chicken.cfg',
'deep-fried-chicken.log','nvngx_dlssnr.dll')
foreach ($n in $ours) {
$p = Join-Path $nte $n
if (Test-Path $p) {
Write-Host (" PRESENT {0,-30} {1,12} B {2}" -f $n, (Get-Item $p).Length, (Get-Item $p).LastWriteTime.ToString('MM-dd HH:mm'))
}
}
Write-Host ' (anything not listed above is the game''s own and is left untouched)'
Write-Host ''
Write-Host '=== revert: remove every file we added to NTE ==='
$removed = 0
foreach ($n in $ours) {
$p = Join-Path $nte $n
if (Test-Path $p) {
Remove-Item $p -Force -ErrorAction SilentlyContinue
if (-not (Test-Path $p)) { Write-Host (" removed " + $n); $removed++ }
else { Write-Host (" COULD NOT REMOVE " + $n) }
}
}
Write-Host (" {0} file(s) removed" -f $removed)
Write-Host ''
Write-Host '=== verify NTE is back to stock ==='
$left = Get-ChildItem $nte -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match 'deep-fried|nvngx_dlssnr|^dxgi\.dll$|^D3D12\.dll$|^ReShade' }
if ($left) { $left | ForEach-Object { Write-Host (" LEFTOVER: " + $_.Name) } }
else { Write-Host ' clean - nothing of ours remains in that folder' }
Write-Host ''
Write-Host '=== is the game''s own DLSS plugin payload untouched? ==='
Get-ChildItem 'D:\Neverness To Everness\Client\WindowsNoEditor\Engine\Plugins\Runtime\Nvidia' -Recurse -File -Filter '*.dll' -ErrorAction SilentlyContinue |
ForEach-Object { Write-Host (" {0,12} v{1,-14} {2}" -f $_.Length, $_.VersionInfo.FileVersion, $_.Name) }
Write-Host ''
Write-Host '=== evidence of the anti-cheat being active ==='
Get-Service 'AntiCheatExpert Service' -ErrorAction SilentlyContinue | ForEach-Object { Write-Host (" service: {0} / {1}" -f $_.Status, $_.StartType) }
Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -match 'ACE|AntiCheat|HTGame|NTELauncher' } |
ForEach-Object { Write-Host (" running: {0} (PID {1})" -f $_.ProcessName, $_.Id) }
Write-Host ' --- crash artifacts in the client folder (last 3 days) ---'
Get-ChildItem 'D:\Neverness To Everness' -Recurse -Depth 3 -File -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -in @('.dmp','.log','.txt') -and $_.LastWriteTime -gt (Get-Date).AddDays(-3) -and $_.Length -gt 0 } |
Sort-Object LastWriteTime -Descending | Select-Object -First 12 |
ForEach-Object { Write-Host (" {0} {1,10} B {2}" -f $_.LastWriteTime.ToString('MM-dd HH:mm'), $_.Length, $_.FullName.Replace('D:\Neverness To Everness\','')) }
+36
View File
@@ -0,0 +1,36 @@
$ErrorActionPreference = 'Continue'
# Watches the Ananta client folder for ReShade / DFC logs and archives every new version
# immediately. Written because last time the only two logs that could explain a crash were
# deleted before anyone read them.
$nte = 'D:\SteamLibrary\steamapps\common\ForzaHorizon5'
$arc = 'E:\deepseek\game-backups\ForzaHorizon5\logs-live'
New-Item -ItemType Directory -Force -Path $arc | Out-Null
$seen = @{}
$deadline = (Get-Date).AddHours(3)
Write-Output ("watcher started " + (Get-Date -Format 'HH:mm:ss') + " watching: " + $nte)
Write-Output ("archive: " + $arc)
while ((Get-Date) -lt $deadline) {
foreach ($n in @('ReShade.log', 'deep-fried-chicken.log', 'ReShade.ini')) {
$p = Join-Path $nte $n
if (-not (Test-Path $p)) { continue }
$fi = Get-Item $p
$key = $n + '|' + $fi.LastWriteTimeUtc.Ticks + '|' + $fi.Length
if ($seen.ContainsKey($key)) { continue }
$seen[$key] = $true
$stamp = (Get-Date).ToString('HHmmss')
$dst = Join-Path $arc ($stamp + '__' + $n)
try {
Copy-Item $p $dst -Force -ErrorAction Stop
Write-Output ("ARCHIVED {0} {1,10} B -> {2}" -f (Get-Date -Format 'HH:mm:ss'), $fi.Length, (Split-Path $dst -Leaf))
} catch {
Write-Output ("COPY FAILED " + $n + " : " + $_.Exception.Message)
}
}
Start-Sleep -Seconds 3
}
Write-Output "watcher window ended (3 hours)"
+36
View File
@@ -0,0 +1,36 @@
$ErrorActionPreference = 'Continue'
# Watches the Ananta client folder for ReShade / DFC logs and archives every new version
# immediately. Written because last time the only two logs that could explain a crash were
# deleted before anyone read them.
$nte = 'D:\Neverness To Everness\Client\WindowsNoEditor\HT\Binaries\Win64'
$arc = 'E:\deepseek\game-backups\NevernessToEverness\logs-live'
New-Item -ItemType Directory -Force -Path $arc | Out-Null
$seen = @{}
$deadline = (Get-Date).AddHours(3)
Write-Output ("watcher started " + (Get-Date -Format 'HH:mm:ss') + " watching: " + $nte)
Write-Output ("archive: " + $arc)
while ((Get-Date) -lt $deadline) {
foreach ($n in @('ReShade.log', 'deep-fried-chicken.log', 'ReShade.ini')) {
$p = Join-Path $nte $n
if (-not (Test-Path $p)) { continue }
$fi = Get-Item $p
$key = $n + '|' + $fi.LastWriteTimeUtc.Ticks + '|' + $fi.Length
if ($seen.ContainsKey($key)) { continue }
$seen[$key] = $true
$stamp = (Get-Date).ToString('HHmmss')
$dst = Join-Path $arc ($stamp + '__' + $n)
try {
Copy-Item $p $dst -Force -ErrorAction Stop
Write-Output ("ARCHIVED {0} {1,10} B -> {2}" -f (Get-Date -Format 'HH:mm:ss'), $fi.Length, (Split-Path $dst -Leaf))
} catch {
Write-Output ("COPY FAILED " + $n + " : " + $_.Exception.Message)
}
}
Start-Sleep -Seconds 3
}
Write-Output "watcher window ended (3 hours)"