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 这里某牛软…

C语言-结构体基本概念

在C语言中&#xff0c;结构体提供了一种将多种不同数据类型组装起来形成自定义变量类型的方式&#xff0c;使得我们可以更好地表示现实生活中的复杂对象。比如&#xff0c;一个学生可以有学号&#xff08;整型&#xff09;、姓名&#xff08;字符串&#xff09;、分数&#xff…

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连接上…

Java Spring 的 ApplicationContext - 核心接口

ApplicationContext 是 Spring 框架中一个核心的接口&#xff0c;它代表了 Spring IoC 容器的配置&#xff0c;用于实例化、配置和组装应用中的对象。通过 ApplicationContext&#xff0c;我们可以方便地获取 Spring 容器中管理的 Bean&#xff0c;同时它还提供了一些额外的功能…

spring提高并发能力

小型系统一般都是在controller直接处理接口逻辑&#xff0c;这样占用的是tomcat的工作线程 &#xff08;1&#xff09;这个线程是有数的&#xff0c;不能无限大 &#xff08;2&#xff09;这个线程同时负责接收处理请求&#xff0c;如果被占满&#xff0c;这无法接收新来的请…

2024深圳杯东三省数学建模ABC选题建议详细思路分析

2024东三省/深圳杯ABC题详细思路分析和选题建议 ABC三题难度分析 A<C<B 我们将同时做ABC中的两题&#xff0c;今天可以给全部的代码&#xff0c;明天凌晨给全部的文章 B难点&#xff1a;工件要怎么处理 pdf文件的处理 C难点&#xff1a;你需要自行找到不同编译结果的主…

基于FPGA轻松玩转AI

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

Python写个二维码

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

word删除单页的页眉

在Microsoft Word中&#xff0c;如果您想删除单页的页眉而不是整个文档的页眉&#xff0c;您需要确保该页位于一个独立的节中&#xff0c;或者调整其页眉设置以使其不同于其他页面。以下是如何删除单页页眉的步骤&#xff1a; 打开Word文档&#xff1a;首先&#xff0c;打开包含…

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…

使用 NVM 管理 Node.js 版本

在软件开发中&#xff0c;管理项目所依赖的运行环境版本是一项挑战&#xff0c;尤其是在使用 Node.js 这样频繁更新的平台时。Node Version Manager&#xff08;NVM&#xff09;是一种流行的工具&#xff0c;它允许开发者在同一台机器上安装和使用多个 Node.js 版本。本文将介绍…

基于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

代码随想录训练营26day-贪心算法4

一、860 柠檬水找零 题目解析&#xff1a;注意一开始是没有零钱&#xff0c;也只会取5 10 20三类数字&#xff0c;因此从这3类数字出发&#xff0c;去判断。 1 如果是5元&#xff0c;那么直接收&#xff0c;five&#xff1b; 2 如果是10元&#xff0c;那么需要five--&#x…

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

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

微信小程序实现蓝牙连接通讯

由于最近的项目在做小程序蓝牙通讯这块的内容&#xff0c;所以将实现的过程在这简单的记录下。 1、首先要初始化蓝牙-检查蓝牙适配器是否打开 async initBluetooth(): Promise<void> {return new Promise((resolve: any, reject: any) > {const _this thisif (Bluet…