美文网首页
Reshade代码学习

Reshade代码学习

作者: 赵海洋 | 来源:发表于2023-02-07 13:36 被阅读0次

    项目介绍

    Reshaper是一个能够注入到游戏进程,并且在游戏运行时在游戏画面上显示一个界面。并主要提供了画面的增强、附加、调整参考等功能(通过Hook 各种3D函数并且加载脚本修改画面)。

    ReShade API 允许您与加载 ReShade 的应用程序的资源和渲染命令进行交互。它抽象出 ReShade 支持的各种图形 API (Direct3D 9/10/11/12、OpenGL 和 Vulkan)之间的差异,以便编写适用于各种应用程序的附加组件,无论它们使用何种图形 API。

    Reshpaer还带了一个Reshade FX工具用来定义和支持一种脚本格式,并能将这种格式转换为各3D引擎使用的HLSL(DirectX), GLSL(OpenGL) or SPIR-V(行业标准中间语言)等语言。

    摘要

    主要代码文件介绍

    File Description
    dll_log.cpp 日志功能
    dll_main.cpp 代码主入口
    dll_resources.cpp 加载dll内嵌资源 (e.g. built-in shaders)
    effect_lexer.cpp Lexical analyzer for C-like languages 类C语言的词法解析器
    effect_parser.cpp ReShade FX语言解析
    effect_preprocessor.cpp C-like preprocessor implementation 类C语言预处理器
    hook.cpp MinHook封装代码
    hook_manager.cpp 提供DLL Hook的接口,dllmain里调用 Automatic hook installation based on DLL exports
    input.cpp Hook窗口消息队列函数以及鼠标键盘状态拦截
    runtime.cpp 核心ReShade运行时,包括特效管理
    runtime_gui.cpp 覆盖渲染和所有与用户界面相关的内容

    overlay

    引用 imgui.h 和 reshade.hpp 后,在dll入口函数中使用reshade::register_addon、reshade::register_overlay 注册即可。然后在功能函数中使用imgui来绘制想要展示的内容。

    D3D版本问题

    3.0之后不再支持DirectX8,可以使用第三方的d3d8.dll将游戏改为D3D9的引擎,然后再使用Reshaper。

    Reshade运行模式

    常见模式是在安装时将Reshade(64).dll重命名为dxgi、d3d9.dll、opengl32.dll等,放到目标进程同目录,并且生成Reshade.ini。

    目标进程在运行时加载改名后的Reshade(64).dll,后续文档简称为Reshade.dll。加载dll中一些功能步骤见下面具体分析。这里简要说明:加载后hook一系列dll(系统和3d引擎),然后在设备初使化时加入自己的功能,在绘制函数使用imgui修改画面,并提供对addon的加载(各addon里也可以添加自己的功能)。

    接口

    • reshade_api_format.hpp 定义了常用颜色格式,以及格式转换相关
    • reshade_api_resource.hpp 会包含reshade_api_format,定义resource_view等,类似于IDirect3DResource9等接口的功能
    • reshade_api_pipeline.hpp 会包含reshade_api_resource,定义sharder\pipeline等相关宏定义
    • reshade_api_device.hpp 包含reshade_api_pipeline,接口device、command、swapchain等,抽象了各3d SDK共用的一些概念
    • reshade_api.hpp 会包含reshade_api_resource,定义effect_runtime,用来控制显示内容
    • reshade_events.hpp 包含reshade_api,定义插件响应的事件(addon_event)和回调函数格式。
    • reshade_overlay.hpp 提供一个导出的imgui的api封装,供addon里使用。addon在register_addon后初使化这个api封装,实际imgui的函数指针指向Reshade的dll内部。

    effect_runtime

    effect_runtime是Reshade的重要接口,提供了一些重要接口,也会出现在各种回调和功能函数中,有点类似全局app对象。

    重要接口列表:

    • render_effects(command_list *cmd_list, resource_view rtv, resource_view rtv_srgb = { 0 })
    • capture_screenshot get_screenshot_width_and_height
    • is_key|mouse_* 判断当前按键和鼠标状态
      (截屏、获取宽高等)
    • *_uniform_variable_* effect变量表读写和枚举相关函数
    • *_texture_variable_* texture变量表读写和枚举相关函数
    • find|get*_technique_* technique读写和枚举相关函数
    • find|get*_preprocessor_* preprocessor相关函数

    配置文件

    代码ini_file.cpp文件中在reshade::global_config函数里,使用全局变量g_target_executable_path(dll加载时初使化)和ReShade.ini拼接起来做为ini配置文件路径。所以配置文件保存在每一个被hook的进程同目录中。

    在addon.cpp中可以使用config_get_value等(内部实际调用Reshade.dll的导出函数ReShadeSetConfigValue和ReShadeSetConfigValue)用来负责设置配置,参数除了ini的section、key、value外就只有一个reshade::api::effect_runtime *runtime

    dll_main流程

    1. 加载配置文件
    2. 通过自己的文件名来判断当前是什么3d引擎,分别判断前缀:d3d\dxgi\opengl32\dinput等。
    3. 如果自己的文件名无指定引擎并且也不是uwp软件,则加载配置文件(不存在时失败)
    4. 判断基本目录
      1. 如果配置文件中INSTALL下有BasePath的定义,则使用它(可以带环境变量)。
      2. 如果环境变量中定义有RESHADE_BASE_PATH_OVERRIDE,则使用它。
      3. 如果文件名无指定引擎则默认使用可执行程序所在目录,否则使用本dll所在目录。
    5. 配置里没有INSTALL-EnableLogging或者为true时开启日志功能。
    6. 枚举该进程加载的所有模块,看看是否有导出函数为ReShadeVersion的,如果有则退出流程。
    7. 配置里INSTALL-DumpExceptions为true时,添加崩溃报告生成功能。
    8. Hook user.dll
    9. 根据上面判断的3D引擎,hook不同的dll
      1. 非opengl情况下,注册所有版本的系统目录下的d3d[N].dlldxgi.dll的Hook。
      2. 非opengl情况下,win7下hook非系统目录下的d3d12.dll,而非win7下,hook系统目录下的d3d12.dll
      3. 非d3d情况下,下Hook系统目录下的 opengl32.dll
      4. 注册所有名为vrclient[_x64].dll的hook。
      5. 注册系统目录下的dinput.dll,如果当前dll名为dinput8,则hook系统目录下的dinput8.dll

    hook相关

    Reshade.dll本身导出了不少与系统同名函数,每个同名函数都是为了Hook其它系统dll或3D引擎dll中的同名函数。

    hook_manager.cpp中定义了3种hook方法:

    enum class hook_method
    {
        export_hook,
        function_hook, // 函数级Hook,通过MHook库插入汇编跳转代码
        vtable_hook // 类虚表替换
    };
    
    • 公共函数 reshade::hooks::register_module
      • 判断dll是否已经加载,如果未加载则加入到延迟hook列表中,如果已经加载则使用function_hook类型来调*用 install_internal将该dll中的部分函数重定向到Reshade.dll中。
      • 如果该dll名称和Reshape改名后同名,则将路径置给全局变量s_export_hook_path,然后延迟Hook。
    • enumerate_module_exports 用于加载一个dll句柄对应的所有导出函数信息。
    • install_internal 枚举被hook的dll的所有导出函数,找到和Reshade.dll中名称相同的函数然后hook它到Reshade.dll中。
    • export_hook类型指被Reshade.dll改名后占用的dll,比如原始的dxgi.dll文件。
    • reshade::hooks::ensure_export_module_loaded中加载并hooks_export_hook_path全局变量指向的dll。会在第一次触发自己导出的3D引擎同名函数后,在转调reshade::hooks::call时触发。
    • reshade::hooks::call获取Hooked函数对应的原跳板函数,用于真实调用(内部会确保加载原板块)

    ui绘制相关

    void reshade::runtime::draw_gui()中负责主要的ui绘制工作。

    在各插件中,可以注册reshade::register_overlay("Test", &draw_debug_overlay); 来显示一个新窗口的绘制函数,使用reshade::register_overlay(nullptr, &draw_settings_overlay);注册一个绘制生成设置项的函数。在函数内直接使用ImGui相关函数绘制即可。

    常用事件

    • init_device device对象初使化时回调,reshade::register_event<reshade::addon_event::init_device>(&on_init_device),会将device对象传递给回调函数。
    • destroy_device device对象释放前回调。

    effect

    在settings里设置shader目录,点击reload可以加载effect列表。在reshade::runtime::load_effects()中枚举所有effect,主要是找到所有的fx文件。然后开启多个线程调用reshade::runtime::load_effect函数挨个加载。

    reshade::runtime::load_effect加载每一个fx文件,读取源码并和编译,将全部effect放到runtime的_effects成员里。

    显示逻辑:在界面点击选中effect后,在下一次触发绘制时触发runtime::on_present();,会调用到reshade::runtime::update_effects(),然后会触发reshade::runtime::create_effect来创建effect。

    主要内容:

    • 创建textures
    • 编译常量
    • 创建 query pool
    • 创建 pipeline layout
    • 创建 global constant buffer
    • Initialize techniques and passes

    然后runtime::on_present();继续调用runtime::render_effects绘制特效。

    PS: 有缘再继续更新

    相关文章

      网友评论

          本文标题:Reshade代码学习

          本文链接:https://www.haomeiwen.com/subject/bgwzcdtx.html