- 图形学
- 游戏开发
【免费下载链接】OptiScaler
OptiScaler bridges upscaling/frame gen across GPUs. Supports DLSS2+/XeSS/FSR2+ inputs, replaces native upscalers, enables FSR-FG/XeFG on non-FG titles. Supports Nukem mod for DLSSG-to-FSR3 FG.
本篇技术指南聚焦 OptiScaler 开源项目对贡献者提出的预编译头(Precompiled Header,PCH)使用规范。OptiScaler 是一个跨 GPU 的上采样/帧生成桥接层(支持 DLSS2+/XeSS/FSR2+ 输入、替换原生上采样器、为非帧生成游戏启用 FSR-FG/XeFG 等功能),代码量横跨 DirectX 11/12、Vulkan 与大量第三方 SDK,构建链庞大。阅读本文后,你将理解该项目如何通过规范的 PCH 用法将构建速度提升 2 倍以上,掌握.cpp/.h文件的正确 include 顺序、pch.h的内容边界,以及如何在贡献代码时避免触发全量重编译。
为什么 OptiScaler 如此依赖 PCH
OptiScaler 的源码体量巨大:OptiScaler/目录下有近 200 个源文件,覆盖 hooks/、upscalers/、framegen/、shaders/ 等数十个模块,并且大量文件同时依赖 Windows SDK、Vulkan、DirectX 12、NVNGX、spdlog、FidelityFX 等重型头文件。
如果每个.cpp文件都各自重新解析这些头文件,编译器将重复处理数百万行代码,构建时间会急剧膨胀。项目因此启用了 MSVC 的预编译头机制:把稳定、少变的系统/第三方头文件一次性编译成.pch缓存,后续每个源文件直接复用。
从 CONTRIBUTING.md 的说明可以确认:正确使用 PCH 能让 OptiScaler 的构建速度快于标准构建 2 倍以上;反之,一旦在头文件中包含pch.h、或把pch.h当作通用工具桶,就会破坏编译器缓存预编译状态的能力,被迫进行不必要的全量重编译,导致整体构建退化。
四条 PCH 硬性规范(贡献者必读)
所有向 OptiScaler 提交代码的贡献者都必须遵守以下要求(原文见 CONTRIBUTING.md):
.cpp源文件:每个源文件必须把#include "pch.h"作为第一个非注释行。这是让编译器命中 PCH 缓存的前提,任何在它之前插入其他头文件或宏定义都会使该文件的 PCH 失效。.h头文件:严禁在头文件内部#include "pch.h"。头文件可能被多个.cpp间接包含,一旦其中出现 PCH include,会导致重复/错误的预编译状态引用,破坏整个缓存链。- 工具代码边界:不要把通用工具函数、宏或全局声明塞进
pch.h。这类代码应放入SysUtils.h,或新建一个职责明确的功能头文件。 - 依赖准入:只把大型、稳定的第三方或系统头文件(例如 Windows/SDK 头文件)加入
pch.h。频繁变动、自己项目内部的头文件不应进入 PCH。
规范在源码中的落实
规范的第一条在仓库中得到了一致执行。搜索结果(#include "pch.h"计数)显示,从 dllmain.cpp 到 D3D12_Hooks.cpp、FSR2Feature_Dx12.cpp、Shader_Common.cpp、Nvngx_Nukems.cpp 等120 多个.cpp文件的第一行都是#include "pch.h",无一例外:
// dllmain.cpp 开头 #include "pch.h" #include "dllmain.h" // ...而头文件侧则严格遵循规范第二条——搜索整个OptiScaler/目录,pch.h从不出现在任何.h文件中。
走进pch.h:内容边界的真实写照
OptiScaler/pch.h的完整内容很好地示范了规范第三条与第四条的落地方式(文件顶部注释即写明了三条铁律):
#pragma once // pch.h: This is a precompiled header. // - DO NOT include this file in other header files (.h/.hpp). // - This file must be the FIRST include in every implementation file (.cpp). // - Only include stable, rarely-changed system/third-party headers here // (e.g., Windows.h, Vulkan, DX12) to improve build speeds. #include "SysUtils.h" #include "Config.h"注意一个容易误解的细节:pch.h内部确实#include "SysUtils.h"和"Config.h"——它们并非违反"只放稳定第三方头"的规则,而是因为SysUtils.h本身就是一堆稳定系统头文件的聚合层(见下文),Config.h是项目构建时的基础配置头;这两个头文件自身不再引入大量变动内容。这恰恰说明了第四条的意图:PCH 应容纳"几乎不变"的内容,而不是随业务迭代频繁改动的模块头。
pch.cpp:PCH 的“创建者”角色
OptiScaler/pch.cpp是整个 PCH 机制的起点,它只有一行:
#include "pch.h"在 MSVC 工程中,只有一个编译单元被配置为PrecompiledHeader = Create(即生成.pch缓存文件),其他所有.cpp都被配置为Use(复用该缓存)。在 OptiScaler.vcxproj 中可以看到pch.cpp在 Debug / ReleaseDebug / Release 三个配置下均为Create:
<ClCompile Include="pch.cpp"> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseDebug|x64'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> </ClCompile>而其余所有源文件(以 Debug|x64 为例)均使用:
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>同时工程开启了MultiProcessorCompilation(多处理器并行编译)与stdcpplatest语言标准。可以推断:PCH + 多核并行编译的组合,是 OptiScaler 维持数百文件、多 SDK 依赖下可接受迭代速度的关键工程手段。
为什么工具代码要放进SysUtils.h而不是pch.h
规范第三条并非洁癖,而是有实际编译性能代价的:pch.h的任何改动都会让整个项目触发全量重建。如果开发者把随手写的工具宏、调试辅助函数塞进pch.h,那么每次调整这些工具代码,所有.cpp文件都会被强制重编译——这正是原文档警告的"全局工具桶破坏缓存"。
观察 SysUtils.h,这个被pch.h引用的头文件才是真正的"稳定系统头聚合层":
#pragma once #define WIN32_LEAN_AND_MEAN #define NOMINMAX #define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #include <Windows.h> #include <string> #include <stdint.h> #include <libloaderapi.h> #include <ranges> #include <winternl.h> #include <d3dkmthk.h> #define NV_WINDOWS #define NVSDK_NGX #define NGX_ENABLE_DEPRECATED_GET_PARAMETERS #define NGX_ENABLE_DEPRECATED_SHUTDOWN #include <nvsdk_ngx.h> #include <nvsdk_ngx_defs.h> #define SPDLOG_USE_STD_FORMAT #define SPDLOG_WCHAR_FILENAMES #define SPDLOG_WCHAR_TO_UTF8_SUPPORT #include "spdlog/spdlog.h"它集中了Windows.h、nvsdk_ngx.h(DLSS SDK)、spdlog等重型且少变的头文件,以及一系列项目级编译开关(如ENABLE_DEBUG_LAYER_DX12、VULKAN_DEBUG_LAYER、LOG_ASYNC等,默认注释关闭)。
而真正会经常变动的通用工具则放在 SysUtils.h 的后半部分——例如LOG_TRACE/LOG_DEBUG/LOG_INFO/LOG_WARN/LOG_ERROR等日志宏、SAFE_RELEASE/SAFE_CLOSE_HANDLE等资源释放宏、wstring_to_string/string_to_wstring/to_lower_in_place等字符串转换函数。由于 OptiScaler 同时面向 Windows 原生与 Linux 桥接场景(如 setup_linux.sh 所示),这些宽窄字符串转换工具是各模块的公共依赖。
这里存在一个值得说明的工程权衡:SysUtils.h本身也被pch.h包含,因此它里面的内容同样"进入"了 PCH。但因为它同时被其他头文件直接引用(属于项目基础设施),且改动频率极低,所以这种安排符合第四条"稳定优先"的原则;而新增业务工具函数的正确位置是另建功能头文件(如 Util.h、MathUtils.h),不要往pch.h里堆。
常见错误自查清单
贡献者提交代码前,可对照以下清单自检:
| 检查项 | 正确做法 | 错误示例(会导致构建退化) |
|---|---|---|
.cpp首行 | #include "pch.h"必须是第一个非注释行 | 先写#include <Windows.h>或业务头再包含pch.h |
.h文件 | 绝不包含pch.h | 在某.h里写#include "pch.h" |
| 工具代码归属 | 放入 SysUtils.h 或新建功能头 | 把新宏/工具函数写进pch.h |
| 新增大型依赖 | 评估是否稳定、是否被多数文件需要 | 把频繁改动的内部模块头塞进pch.h |
额外提醒:由于 OptiScaler 同时维护 Debug / ReleaseDebug / Release / Win32 / x64 多套配置(见 OptiScaler.vcxproj 中的ItemDefinitionGroup),且工程通过DelayLoadDLLs延迟加载vulkan-1.dll、d3d12.dll等运行时库,提交时无需、也不应在这些生成配置上手工改动 PCH 相关节点。
小结
PCH 是 OptiScaler 控制庞大构建链的核心工程手段:pch.cpp负责Create缓存,120+ 个.cpp文件统一Use并强制首行包含pch.h,头文件永不触碰pch.h,通用工具与稳定系统头各归其位。贡献者只要守住这四条规则,就能让整个项目的构建保持比标准构建快 2 倍以上的速度,避免因个人代码风格触发全量重编译。构建相关的工程配置细节可继续查阅 OptiScaler.vcxproj 与 OptiScaler.vcxproj.filters,环境搭建步骤见 setup_windows.bat 与 setup_linux.sh。
- 图形学
- 游戏开发
【免费下载链接】OptiScaler
OptiScaler bridges upscaling/frame gen across GPUs. Supports DLSS2+/XeSS/FSR2+ inputs, replaces native upscalers, enables FSR-FG/XeFG on non-FG titles. Supports Nukem mod for DLSSG-to-FSR3 FG.
相关推荐
Hazel Engine编译优化:PCH预编译头与增量编译配置
Hazel Engine编译优化:PCH预编译头与增量编译配置 编译效率痛点与解决方案 你是否还在忍受Hazel Engine动辄数分钟的编译等待?当项目规模扩
游戏开发图形学3倍提速!Termux-packages构建优化:编译缓存与预编译头实践指南
3倍提速!Termux packages构建优化:编译缓存与预编译头实践指南 为什么构建速度如此重要? 在Android终端环境Termux中开发时,你是否遇到
开发工具包管理器构建工具CMake预编译头使用详解:PCH配置与编译速度提升技巧
CMake预编译头使用详解:PCH配置与编译速度提升技巧 你是否还在忍受C++项目漫长的编译等待?是否希望在不改变代码逻辑的情况下大幅提升构建效率?本文将详细介
构建工具开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考