DirectX 11 计算着色器






4.58/5 (9投票s)
通过计算着色器 (GPGPU) 进行高性能计算。
- 下载 OpenCL_matrix.zip - 2.4 KB
- 下载 matrixmath_cs.zip - 8.4 KB
- 下载 amp_matrixmultiply.zip - 8.4 KB
- 下载 AMP_matrixmultiply.zip - 8.3 KB
- 下载 MatrixMath_CS.zip - 8.4 KB
- 下载 computeshader.zip - 24.5 KB
引言
本文介绍通过 DirectX11 计算着色器实现 GPGPU。
GPGPU(在图形处理单元上进行通用计算)涉及使用图形处理单元执行重复计算,利用 GPU 上可用的庞大处理元素阵列。
本文将演示在 GPU 上执行的非常简单的三角计算。
附加的代码展示了经典的 GPGPU 用法,通过生成 nrow*nrow 个 GPU 线程来对 方形矩阵进行求平方(乘法)运算。选择此示例是因为输出元素可以独立计算。
背景
GPGPU 已经存在一年多,NVIDIA 引入了 CUDA,AMD 引入了接近金属和 AMD stream,以及许多其他爱好者尝试使用 DirectX9 像素着色器来实现 GPGPU。
使用代码
附加代码使用 VS2010 Beta 1 编译,并使用 DirectX SDK (2009 年 8 月) 的库在 Windows 7 RC 上运行。此代码无法在 Windows XP 上运行,因为 DirectX11 不适用于 Windows XP。源代码的某些部分摘自 DirectX SDK 2009 年 8 月的示例,并进行了调整以适应程序。
代码的起点是 Start(void*)
。程序分为以下几个子部分
创建设备(最简单的一部分)
使用 D3D_DRIVER_TYPE_REFERENCE
进行仿真,并使用 D3D_DRIVER_TYPE_HARDWARE
在 GPU 上运行代码(您需要硬件支持才能做到这一点)。
D3D11CreateDevice( NULL,D3D_DRIVER_TYPE_REFERENCE/*D3D_DRIVER_TYPE_HARDWARE*/,
NULL, D3D11_CREATE_DEVICE_SINGLETHREADED|D3D11_CREATE_DEVICE_DEBUG,
NULL, 0,D3D11_SDK_VERSION, &pDeviceOut, &flOut, &pContextOut );
加载 GPU
困难的部分在于程序员必须将缓冲区加载到 GPU 进行处理。附加的源代码将对此提供更多的说明
//for input buffer
HRESULT CreateStructuredBufferOnGPU( ID3D11Device* pDevice,
UINT uElementSize, UINT uCount, VOID* pInitData,
ID3D11Buffer** ppBufOut )
{
*ppBufOut = NULL;
D3D11_BUFFER_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
desc.ByteWidth = uElementSize * uCount;
desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
desc.StructureByteStride = uElementSize;
if ( pInitData )
{
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = pInitData;
return pDevice->CreateBuffer( &desc, &InitData, ppBufOut );
}
else
return pDevice->CreateBuffer( &desc, NULL, ppBufOut );
}
//for input buffer
HRESULT CreateBufferSRV( ID3D11Device* pDevice, ID3D11Buffer* pBuffer,
ID3D11ShaderResourceView** ppSRVOut )
{
D3D11_BUFFER_DESC descBuf;
ZeroMemory( &descBuf, sizeof(descBuf) );
pBuffer->GetDesc( &descBuf );
D3D11_SHADER_RESOURCE_VIEW_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
desc.BufferEx.FirstElement = 0;
if ( descBuf.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS )
{
// This is a Raw Buffer
desc.Format = DXGI_FORMAT_R32_TYPELESS;
desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
desc.BufferEx.NumElements = descBuf.ByteWidth / 4;
}
else
if ( descBuf.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED )
{
// This is a Structured Buffer
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.BufferEx.NumElements =
descBuf.ByteWidth / descBuf.StructureByteStride;
}
else
{
return E_INVALIDARG;
}
return pDevice->CreateShaderResourceView( pBuffer, &desc, ppSRVOut );
}
//for output buffer
HRESULT CreateBufferUAV( ID3D11Device* pDevice, ID3D11Buffer* pBuffer,
ID3D11UnorderedAccessView** ppUAVOut )
{
D3D11_BUFFER_DESC descBuf;
ZeroMemory( &descBuf, sizeof(descBuf) );
pBuffer->GetDesc( &descBuf );
D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
desc.Buffer.FirstElement = 0;
if ( descBuf.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS )
{
// This is a Raw Buffer
desc.Format = DXGI_FORMAT_R32_TYPELESS;
// Format must be DXGI_FORMAT_R32_TYPELESS,
// when creating Raw Unordered Access View
desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
desc.Buffer.NumElements = descBuf.ByteWidth / 4;
}
else
if ( descBuf.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED )
{
// This is a Structured Buffer
desc.Format = DXGI_FORMAT_UNKNOWN;
// Format must be must be DXGI_FORMAT_UNKNOWN,
// when creating a View of a Structured Buffer
desc.Buffer.NumElements =
descBuf.ByteWidth / descBuf.StructureByteStride;
}
else
{
return E_INVALIDARG;
}
return pDevice->CreateUnorderedAccessView( pBuffer, &desc, ppUAVOut );
}
Run
此命令将数据分发到 GPU 可用的处理元素,其性能直接与硬件和驱动程序支持相关(这适用于使用 D3D_DRIVER_TYPE_HARDWARE
创建的设备)。
pd3dImmediateContext->Dispatch( X, Y, Z );
读取输出缓冲区
以前,使用 DirectX9 时,这部分是最痛苦的部分,但使用 DirectX 11 计算着色器,这变得容易多了。
首先,创建一个带有 CPU 访问标志设置为 D3D11_CPU_ACCESS_READ
的临时读取缓冲区。然后,复制缓冲区,并将其映射到指针,如下所示
pd3dImmediateContext->CopyResource( debugbuf, pBuffer );
BufType *p;
pContextOut->Map( debugbuf, 0, D3D11_MAP_READ, 0, &MappedResource );
p = (BufType*)MappedResource.pData; //p will hold the output buffer
关注点
使用计算着色器,我们可以实现涉及流体的基于物理的模拟(可能是我下一个项目)。
我也使用 Vulkan 实现了计算着色器:https://bitbucket.org/asif_bahrainwala/matrix-multiply/src/master/