6.2 HLSL使用流程
6.2.1编写HLSL和编译程序
使用过程从在HLSL中编写着色器程序开始。
通常,这是通过创建一个标准ASCII文本文件来完成的,该文件包含一个或多个着色器程序的代码。这可以通过任何标准文本编辑器或开发环境来完成。
一旦程序的代码编写完成,就会由D3D着色器编译器进行编译。通过D3D编译器DLL导出的D3D编译器函数或使用独立的fxc.exe工具通过命令行访问编译器。还有D3DX辅助函数(D3DXCompileFromFile\D3DXCompileFromResource),可以简化直接从文件或嵌入资源进行编译的过程。
通常情况下,编译器除了接受代码本身之外,还接受配置参数。这些参数指示入口函数、用于编译的着色器配置文件、预处理器宏定义列表、预处理器要包括的附加文件列表以及编译标志列表(包括优化和调试选项)。
如果编译失败或产生警告,D3DCompile和D3DX函数会将警告或错误消息存储在嵌入ID3D10Blob对象的字符串中。与C++编译一样,这些消息包括一条信息性消息和一个行号,因此可以很容易地找到并更正错误或警告。如果使用fxc.exe,消息将简单地输出到命令行。
对于应用程序的调试构建,通常需要在启用D3D10_shader_debug(fxc中的/Zi开关)标志的情况下编译着色器程序。这导致编译器将调试信息嵌入到字节码流中,字节码流可用于将指令和常量映射回HLSL源代码中相应的行。这允许对着色器程序进行源代码级调试,这比直接调试程序集要方便得多。请注意,也可能希望通过使用D3D10_SHADER_SKIP_optimizations(fxc中的/Od开关)来禁用优化,因为这将防止指令重新排序和折叠,从而更容易调试程序。
着色器程序可以使用PIX进行调试,PIX是DirectX SDK附带的一种调试和分析实用程序。
6.2.1字节码
一旦编译成功完成,编译器就会输出一种称为字节码的中间格式。这是一个不透明的二进制流,包含着色器的所有程序集指令,以及嵌入的反射和调试元数据。如果程序员想检查生成的汇编指令进行验证,可以通过使用D3D编译器DLL导出的D3DDisassemble函数来获得。命令行工具fxc.exe还将在编译完成时将程序集输出到命令行,或者可以将其配置为将程序集列表输出到文件。此外,PIX可用于检查D3D11着色器对象并查看程序集代码。有关着色器部件的详细信息,请参阅DirectX SDK文档中的HLSL参考部分。
6.2.3运行时着色器对象
一旦为着色器程序生成了字节码,就可以用于创建D3D11着色器对象。
ID3D11Device接口提供了六种创建着色器对象的方法,其中一种用于管道的每个可编程阶段。这些方法是CreateVertexShader、CreateHullShader、CreateDomainShader、reateGeometryShader、Create PixelShader和CreateComputeShader。其中每一个都接受一个字节码流,并生成一个ID3Dll*Shader接口,该接口表示可以绑定到管道的运行时着色器对象。
有关编译顶点着色器和创建运行时着色器对象的简单示例,请参见清单6.1。
ID3D10Blob* compiledShader;
ID3D10Blob* errorMessages;
//编译顶点着色器程序源文件
HRESULT hr = D3DXllCompileFromFile(filePath, NULL, NULL, "VSMain","vs_5_0", 0, 0, NULL, &compiledShader,&errorMessages, NULL );
if ( FAILED( hr ) )
{
if ( errorMessages )
{
const char* msg = (char*)( errorMessages->GetBufferPointer() );
Log::6et().Write( msg );
}
else
{
Log::Get().Write( "D3DXllCompileFromFile failed" );
}
}
else
{
ID3DllVertexShader* vertexShader = NULL;
//创建顶点着色器
device->CreateVertexShader(compiledShader->GetBufferPointer(),
compiledShader->GetBufferSize(),
NULL,
&vertexShader );
}
6.2.4绑定到管道
色器对象通过调用ID3D11DeviceContext上可用的各种SetShader方法绑定到管道。一旦所有必需的着色器对象都绑定到其相应的阶段,就可以发出Draw调用,并且绑定的着色器程序将用于渲染几何体。或者,在计算着色器的情况下,可以发出Dispatch调用来使用计算管道。
绑定顶点着色器到管道
m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0);
如果着色器程序使用着色器资源、采样器或常量缓冲区,则在发出Draw或Dispatch调用之前必须绑定这些资源。这是通过在设备上下文上调用适当的SetShaderResource、SetConstantBuffers和SetSamplers方法来完成的。
未排序的访问视图使用CSSetUnorderedAccessViews绑定到计算着色器阶段,并使用OMSetRenderTargetsAndUnorderedAccess views绑定至像素着色器阶段。