DirectX12(D3D12)基础教程(二十三) ——DirectShaderCompiler 头文件接口 ID3DInclude 的应用

目录

  • 1、前言
  • 2、ID3DInclude 回调接口介绍
  • 3、基本使用方法
  • 4、示例代码中的实现

1、前言

  本章教程较短小,但内容十分重要,是后续更灵活使用 Shader 编程的重要基础之一。也就是对 Shader 代码进行头文件分离复用设计进行全面支持。或者直白的说,本章内容重点就是让各位掌握以编程的方式在代码中支持 Shader 头文件的方法,方便在设计 Shader 编辑器之类工具时,可以让所有的 Shader 组织的更有层次。本章实例代码依然基于示例 GRSD3D12Sample/25-IBL-MultiInstance-Sphere

2、ID3DInclude 回调接口介绍

  这个接口是个很有意思的接口,名字看上去是很 COM 化的,但其实跟 COM 没啥关系,只是一个定义了两个纯虚接口函数的类而已,其详细定义摘录如下:

typedef interface ID3DInclude ID3DInclude;
#undef INTERFACE
#define INTERFACE ID3DInclude
DECLARE_INTERFACE(ID3DInclude)
{STDMETHOD(Open)(THIS_ D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) PURE;STDMETHOD(Close)(THIS_ LPCVOID pData) PURE;
};
typedef ID3DInclude* LPD3DINCLUDE;

  ID3DInclude 是从Direct3D 10及更高版本中引入的回调接口,它用于处理着色器编译过程中的文件包含操作,即 “#include” 宏命令。在着色器代码中,我们经常需要包含其他文件,例如头文件,来共享常用的函数或变量定义。ID3DInclude 接口允许应用程序为着色器编译器提供自定义的文件包含逻辑。

  ID3DInclude 接口定义在 d3dcompiler.h 头文件中,并且通常与 D3DCompileD3DCompileFromFile 函数一起使用,这些函数是用于编译着色器的主要函数。

  下面是 ID3DInclude 接口中定义的主要方法:

  • Open:当编译器遇到 #include 指令时,它会调用此方法。应用程序应该实现此方法以打开指定的文件,并返回一个指向文件内容的指针。

  • Close:在编译器完成处理一个包含的文件后,它会调用此方法。应用程序应该实现此方法以关闭文件并释放任何关联的资源。

      需要注意的是,仅包含这两个函数的版本是用于D3D12配套版本的着色器编译器的。

3、基本使用方法

  为了使用自定义的包含逻辑,你需要实现一个类,该类继承自 ID3DInclude 接口,并实现上述所有方法。然后,在调用 D3DCompileD3DCompileFromFile 时,你可以将你的实现作为参数传递。

  下面是一个简单的示例,展示了如何实现 ID3DInclude 接口:

#include <d3dcompiler.h>
class MyInclude : public ID3DInclude
{
public:virtual HRESULT STDMETHODCALLTYPE Open(D3D_INCLUDE_TYPE IncludeType, const char* pFileName, const void* pParentData, const void** ppData, UINT* pBytes) override{// 在这里实现你的文件打开逻辑// ...return S_OK;}virtual HRESULT STDMETHODCALLTYPE Close(const void* pData) override{// 在这里实现你的文件关闭逻辑// ...return S_OK;}
};// 使用自定义的包含逻辑编译着色器
ID3DBlob* compiledShader = nullptr;
HRESULT hr = D3DCompileFromFile(shaderFilePath, nullptr, nullptr, "VSMain", "vs_5_0", 0, 0, &compiledShader, nullptr, &myIncludeInstance);

  在上面的示例中,MyInclude 类实现了 ID3DInclude 接口,并在构造函数中接受一个搜索路径参数。你可以根据需要在 Open 方法中实现自定义的文件搜索和打开逻辑。然后,在调用 D3DCompileFromFile 时,我们将 myIncludeInstanceMyInclude 类的一个实例)作为参数传递,以便使用自定义的包含逻辑。

4、示例代码中的实现

在 GRSD3D12Sample/25-IBL-MultiInstance-Sphere 及系列示例中,具体实现这个接口如下:

#pragma once
#include <SDKDDKVer.h>
#include <tchar.h>
#define WIN32_LEAN_AND_MEAN // 从 Windows 头中排除极少使用的资料
#include <windows.h>
#include <strsafe.h>#include <atlconv.h>
#include <atlcoll.h>
#include <atlstr.h>
#include <d3dcompiler.h>#include "GRS_Mem.h"#define GRS_FILE_IS_EXIST(f) (INVALID_FILE_ATTRIBUTES != ::GetFileAttributes(f))#ifndef GRS_SAFE_CLOSEFILE
//安全关闭一个文件句柄
#define GRS_SAFE_CLOSEFILE(h) if(INVALID_HANDLE_VALUE != (h)){::CloseHandle(h);(h)=INVALID_HANDLE_VALUE;}
#endif // !GRS_SAFE_CLOSEFILE// 强制D3DCompiler搜索标准的Include目录,通常这也没啥用,只是保留记得有这么个设置即可
#ifndef D3D_COMPILE_STANDARD_FILE_INCLUDE
#define D3D_COMPILE_STANDARD_FILE_INCLUDE ((ID3DInclude*)(UINT_PTR)1)
#endiftypedef CAtlArray<CAtlString> CStringList;class CGRSD3DCompilerInclude final : public ID3DInclude
{
protected:CStringList m_DirList;
public:CGRSD3DCompilerInclude(LPCTSTR pszDir){AddDir(pszDir);}virtual ~CGRSD3DCompilerInclude(){}
public:VOID AddDir(LPCTSTR pszDir){CString strDir(pszDir);if ( ( strDir.GetLength() > 0 ) ){ //非空路径if (_T('\\') == strDir[strDir.GetLength() - 1]){strDir.SetAt(strDir.GetLength() - 1, _T('\0'));}for ( size_t i = 0; i < m_DirList.GetCount(); i++ ){if ( m_DirList[i] == strDir ){// 已经存在不用再添加了return;}}m_DirList.Add(strDir);}}
public:STDMETHOD(Open)(THIS_ D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) override{HANDLE hFile = INVALID_HANDLE_VALUE;VOID* pFileData = nullptr;*ppData = nullptr;HRESULT _hrRet = S_OK;try{ CString strFileName(pFileName);TCHAR pszFullFileName[MAX_PATH] = {};for (size_t i = 0; i < m_DirList.GetCount(); i++){HRESULT hr = ::StringCchPrintf(pszFullFileName, MAX_PATH, _T("%s\\%s"), m_DirList[i].GetBuffer(), strFileName.GetBuffer() );if ( FAILED(hr) ){AtlThrow(hr);}if ( !GRS_FILE_IS_EXIST(pszFullFileName) ){// 记录下这个错误,直到最后真找不到那么就作为最后的错误码返回_hrRet = HRESULT_FROM_WIN32(::GetLastError());continue;}hFile = ::CreateFile(pszFullFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);if ( INVALID_HANDLE_VALUE == hFile ){AtlThrowLastWin32();}DWORD dwFileSize = ::GetFileSize(hFile, NULL);//Include文件一般不会超过4G大小,所以......if (INVALID_FILE_SIZE == dwFileSize){AtlThrowLastWin32();}VOID* pFileData = GRS_CALLOC(dwFileSize);if (nullptr == pFileData){AtlThrowLastWin32();}if (!::ReadFile(hFile, pFileData, dwFileSize, (LPDWORD)pBytes, NULL)){AtlThrowLastWin32();}GRS_SAFE_CLOSEFILE(hFile);*ppData = pFileData;_hrRet = S_OK;break;				}}catch (CAtlException& e){GRS_SAFE_CLOSEFILE(hFile);GRS_SAFE_FREE(pFileData);_hrRet = e.m_hr;}GRS_SAFE_CLOSEFILE(hFile);return _hrRet;}STDMETHOD(Close)(THIS_ LPCVOID pData) override{GRS_FREE((VOID*)pData);return S_OK;}
};

  上面代码中,只是加入了一个预编译文件可能路径的支持,即用一个字符串对象的链表来存储所有潜在路径,在需要打开头文件时,就顺序搜索这个字符串列表,直到找到指定的头文件为止。

  在实际使用时,就是在程序开头处,将所有的包含路径都加入到该对象中,然后编译时直接使用即可。

// 0-2、得到当前的工作目录,方便我们使用相对路径来访问各种资源文件
{if (0 == ::GetModuleFileName(nullptr, g_pszAppPath, MAX_PATH)){GRS_THROW_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));}WCHAR* lastSlash = _tcsrchr(g_pszAppPath, _T('\\'));if (lastSlash){//删除Exe文件名*(lastSlash) = _T('\0');}lastSlash = _tcsrchr(g_pszAppPath, _T('\\'));if (lastSlash){//删除x64路径*(lastSlash) = _T('\0');}lastSlash = _tcsrchr(g_pszAppPath, _T('\\'));if (lastSlash){//删除Debug 或 Release路径*(lastSlash + 1) = _T('\0');}// Shader 路径::StringCchPrintf(g_pszShaderPath, MAX_PATH, _T("%s25-IBL-MultiInstance-Sphere\\Shader"), g_pszAppPath);// 资源路径::StringCchPrintf(g_pszAssetsPath, MAX_PATH, _T("%sAssets"), g_pszAppPath);
}// 0-3、准备编程Shader时处理包含文件的类及路径        
TCHAR pszPublicShaderPath[MAX_PATH] = {};
::StringCchPrintf(pszPublicShaderPath, MAX_PATH, _T("%sShader"), g_pszAppPath);
CGRSD3DCompilerInclude grsD3DCompilerInclude(pszPublicShaderPath);
grsD3DCompilerInclude.AddDir(g_pszShaderPath);

需要编译shader时:

ComPtr<ID3DBlob> pIVSCode;
ComPtr<ID3DBlob> pIGSCode;
ComPtr<ID3DBlob> pIPSCode;
ComPtr<ID3DBlob> pIErrorMsg;::StringCchPrintf(pszShaderFileName, MAX_PATH, _T("%s\\GRS_1Times_GS_HDR_2_CubeMap_VS_GS.hlsl"), g_pszShaderPath);HRESULT hr = D3DCompileFromFile(pszShaderFileName, nullptr, &grsD3DCompilerInclude, "VSMain", "vs_5_0", nCompileFlags, 0, &pIVSCode, &pIErrorMsg);if (FAILED(hr))
{ATLTRACE("编译 Vertex Shader:\"%s\" 发生错误:%s\n", T2A(pszShaderFileName), pIErrorMsg ? pIErrorMsg->GetBufferPointer() : "无法读取文件!");GRS_THROW_IF_FAILED(hr);
}
pIErrorMsg.Reset();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/827914.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第68天:APP攻防-XposedFridaHook证书校验反代理代理转发

目录 思维导图 案例一&#xff1a;某牛防抓包-xposed&frida&r0capture 如何检测是否启动了反代理 xp框架 方案二&#xff1a;某社交防抓包-Proxifier&frida&r0capture 思维导图 案例一&#xff1a;某牛防抓包-xposed&frida&r0capture 这里某牛软…

Python | Leetcode Python题解之第46题全排列

题目&#xff1a; 题解&#xff1a; class Solution:def permute(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""def backtrack(first 0):# 所有数都填完了if first n: res.append(nums[:])for i in range(first, n):# 动…

WebSocket的原理、作用、API、常见注解和生命周期的简单介绍,附带SpringBoot示例

文章目录 原理作用客户端 API服务端 API生命周期常见注解SpringBoot示例 WebSocket是一种 通信协议 &#xff0c;它在 客户端和服务器之间建立了一个双向通信的网络连接 。WebSocket是一种基于TCP连接上进行 全双工通信 的 协议 。 WebSocket允许客户端和服务器在 单个TCP连接上…

基于FPGA轻松玩转AI

启动人工智能应用从来没有像现在这样容易&#xff01;受益于像Xilinx Zynq UltraScale MPSoC 这样的FPGA&#xff0c;AI现在也可以离线使用或在边缘部署、使用.可用于开发和部署用于实时推理的机器学习应用&#xff0c;因此将AI集成到应用中变得轻而易举。图像检测或分类、模式…

Python写个二维码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、进入官网下载二、下载一下三.输入代码 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、进入官网下载 官网 pip insta…

vue3推荐算法

Vue 3 推荐算法主要指的是在 Vue 3 框架中实现的或者适用于 Vue 3 的算法库或组件库。Vue 3 由于其优秀的设计和性能&#xff0c;被广泛应用于构建各种类型的应用程序&#xff0c;包括需要复杂算法支持的项目。以下是一些在 Vue 3 中可能会用到的推荐算法资源&#xff1a; Vue-…

田忌赛马【洛谷P1650】

P1650 田忌赛马 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<iostream> #include <algorithm> #include<cstdio> #include <map> using namespace std; const int N1e5100; int n; map<int,int>a,b;//映射&#xff0c;速度->数量…

新网站上线需要注意什么?

质量保证&#xff1a;确保网站的所有功能和页面都经过了充分的测试&#xff0c;并且在各种不同的浏览器和设备上都能够正常运行。检查所有链接、表单和交互式元素&#xff0c;确保它们都能够按照预期工作。优化性能&#xff1a;确保网站加载速度快&#xff0c;响应迅速。优化图…

Python-VBA函数之旅-isinstance函数

目录 一、isinstance函数的常见应用场景&#xff1a; 二、isinstance函数使用注意事项&#xff1a; 三、如何用好isinstance函数&#xff1f; 1、isinstance函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff…

基于spring boot学生综合测评系统

基于spring boot学生综合测评系统设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件…

typedef 定义函数指针

typdef int(*FUNC_TYPE)(int,int) FUNC_TYPE p NULL; 定义了一个函数指针 函数指针作为函数的参数的用法demon

黄金行情下跌有投资机会吗?

尽管黄金价格的波动常常引起投资者的高度关注&#xff0c;但行情的下跌未必只是警讯&#xff0c;亦可能蕴藏着某些难得的投资机会。总之&#xff0c;答案是肯定的——在黄金行情下跌时&#xff0c;依旧有适宜的投资机会&#xff0c;只是这需要投资者具备相应的应对知识和策略。…

美森快船和以星快船有什么区别?美线海运都有哪些快船?

在繁忙的国际海运市场中&#xff0c;快船服务以其高效、快捷的特点受到广大货主的青睐。其中&#xff0c;美森快船和以星快船作为知名的海运服务提供商&#xff0c;凭借着卓越的服务品质&#xff0c;在航运界树立了良好的口碑。那么&#xff0c;美森快船和以星快船究竟有何不同…

利用ollama和open-webui本地部署通义千问Qwen1.5-7B-Chat模型

目录 1 安装ollama 2 安装open-webui 2.1 镜像下载 3 配置ollama的模型转换工具环境 3.1 下载ollama源码 3.2 下载ollama子模块 3.3 创建ollama虚拟环境 3.4 安装依赖 3.5 编译量化工具 7 创建ollama模型 8 运行模型 参考文献&#xff1a; 1 安装ollama curl -fsSL …

2-2 任务:闰年判断

本次课&#xff0c;我们讨论了闰年的判断方法、关系运算符与关系表达式、逻辑运算符与逻辑表达式&#xff0c;以及流程控制结构中的选择结构。 闰年判断 闰年是为了使日历年与地球绕太阳公转的时间保持一致而设定的&#xff0c;具有366天。闰年的判断规则如下&#xff1a; 普…

16V/2A高集成功率MOS同步降压转换器SOT23-6封装

概述&#xff1a; PCD8020 是一款内部集成两个功率 MOS 的高效率 2A 同步整流降压转换器。 该器件提供PWM 与 PFM 两种控制模式&#xff0c; 能够在很宽的负载范围内实现高效率。PCD8020 采用小巧的 SOT23-6 封装&#xff0c; 外围器件少&#xff0c; 从而实现小尺寸的系统电源…

【算法】约瑟夫环

文章目录 题目一1.数组模拟1.1出圈顺序递归求出圈顺序 1.2最后出圈人 2.环形链表【DEMO】3.递推求最后出圈人3.13.2 题目二1.数组模拟2.递推求最后出圈人2.12.2 题目一 1-n编号 s开始1-m报数 报到m出圈 求出圈顺序or最后人 1.数组模拟 1.1出圈顺序 递归求出圈顺序 // AC输入…

python 如何判断两个字典是否相等

Python 字典的 cmp() 函数用于比较两个字典元素。 语法 cmp()方法语法&#xff1a; cmp(dict1, dict2)参数 dict1 -- 比较的字典。 dict2 -- 比较的字典。 返回值 如果两个字典的元素相同返回0&#xff0c;如果字典dict1大于字典dict2返回1&#xff0c;如果字典dict1小于…

为AI电脑生态注入强悍动力,安耐美PlatiGemini 1200W高性能电源

在DIY攒机的过程中&#xff0c;电源是非常重要的一环&#xff0c;现在高性能的硬件功耗往往很高&#xff0c;因此一款优秀的电源整个系统稳定运行的基石。最近&#xff0c;我发现一款由安耐美&#xff08;Enermax&#xff09;推出的PlatiGemini 1200W电源&#xff0c;它不仅满足…

ShardingSphere-JDBC快速入门

ShardingSphere-JDBC读写分离快速入门 一、ShardingSphere-JDBC 读写分离1.创建springboot程序1.1 添加依赖1.2 java代码1.3 配置 2.测试 二、ShardingSphere-JDBC垂直分片1.创建springboot程序1.1 导入依赖1.2 java代码1.3 配置 2.测试 三、ShardingSphere-JDBC水平分片1.创建…