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

本文主要是介绍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();

这篇关于DirectX12(D3D12)基础教程(二十三) ——DirectShaderCompiler 头文件接口 ID3DInclude 的应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/931975

相关文章

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

spring中的ImportSelector接口示例详解

《spring中的ImportSelector接口示例详解》Spring的ImportSelector接口用于动态选择配置类,实现条件化和模块化配置,关键方法selectImports根据注解信息返回... 目录一、核心作用二、关键方法三、扩展功能四、使用示例五、工作原理六、应用场景七、自定义实现Impor

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

MybatisPlus service接口功能介绍

《MybatisPlusservice接口功能介绍》:本文主要介绍MybatisPlusservice接口功能介绍,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录Service接口基本用法进阶用法总结:Lambda方法Service接口基本用法MyBATisP

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

Java中的Closeable接口及常见问题

《Java中的Closeable接口及常见问题》Closeable是Java中的一个标记接口,用于表示可以被关闭的对象,它定义了一个标准的方法来释放对象占用的系统资源,下面给大家介绍Java中的Clo... 目录1. Closeable接口概述2. 主要用途3. 实现类4. 使用方法5. 实现自定义Clos

Python Flask 库及应用场景

《PythonFlask库及应用场景》Flask是Python生态中​轻量级且高度灵活的Web开发框架,基于WerkzeugWSGI工具库和Jinja2模板引擎构建,下面给大家介绍PythonFl... 目录一、Flask 库简介二、核心组件与架构三、常用函数与核心操作 ​1. 基础应用搭建​2. 路由与参

Spring Boot中的YML配置列表及应用小结

《SpringBoot中的YML配置列表及应用小结》在SpringBoot中使用YAML进行列表的配置不仅简洁明了,还能提高代码的可读性和可维护性,:本文主要介绍SpringBoot中的YML配... 目录YAML列表的基础语法在Spring Boot中的应用从YAML读取列表列表中的复杂对象其他注意事项总

java对接第三方接口的三种实现方式

《java对接第三方接口的三种实现方式》:本文主要介绍java对接第三方接口的三种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录HttpURLConnection调用方法CloseableHttpClient调用RestTemplate调用总结在日常工作