DirectCompute使用GPU滤波
前面已经写了第一个OpenCL的程序《OpenCL使用GPU滤波》,了解了GPU的编程。对于使用异构计算,除了OpenCL,还有微软的DirectCompute,因此,结合前文,写一个使用DirectCompute的使用GPU滤波的程序。第一个DirectCompute程序。
话不多说,和《OpenCL使用GPU滤波》一样,步骤都差不多。
第一步,下载DX11,使用下面的地址或者搜索“DXSDK_JUN10”http://www.filestube.com/8yGinHs9h2WV6SJLdOtn45/DXSDK-Jun10.html
下载完后安装。安装就不介绍了。
第二步, VS2008设置
工具->选项,设置包含文件路径
设置库文件路径
第三步,开始编程。
为了实用和可重用,做成一个C++类。这个类必须包含3个函数,InitDirectCompute初始化, Uninit释放, Filter_GPU滤波,Filter_GPU可能会反复调用。
首先是初始化DirectCompute。初始化很复杂,不过基本是照着做就行了。
//初始化DirectCompute 。iSrDataLen数据个数,iFilterLen滤波阶数
BOOL CDX_Filter::InitDirectCompute(INint iSrcDataLen,IN int iFilterLen)
{//初始化时需要建缓冲,因此需要知道缓冲长度
if(iSrcDataLen<=0 || iFilterLen <=0)
{
return FALSE;
}
if(m_bInitDirectCompute)
{
return FALSE;
}
m_iFilterLen[0] = iFilterLen;
m_iSrcBufferLen =iSrcDataLen;
m_pSrcBuffer = newfloat[iFilterLen+ iSrcDataLen];
#if defined(DEBUG)||defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
#endif
m_pDevice = NULL;
m_pContext = NULL;
HRESULT hr = S_OK;
//Create Device
UINT uCreationFlags= D3D11_CREATE_DEVICE_SINGLETHREADED;
#if defined(DEBUG) || defined(_DEBUG)
uCreationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_FEATURE_LEVEL flOut;
static const D3D_FEATURE_LEVEL flvl[]= { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0};
typedef HRESULT (WINAPI * LPD3D11CREATEDEVICE)(IDXGIAdapter*, D3D_DRIVER_TYPE,HMODULE, UINT32,CONST D3D_FEATURE_LEVEL*,UINT, UINT32,ID3D11Device**, D3D_FEATURE_LEVEL*,ID3D11DeviceContext** );
static LPD3D11CREATEDEVICE s_DynamicD3D11CreateDevice= NULL;
if ( s_DynamicD3D11CreateDevice== NULL )
{//动态加载D3D11.dll
HMODULE hModD3D11= LoadLibrary( "d3d11.dll");
s_DynamicD3D11CreateDevice = ( LPD3D11CREATEDEVICE )GetProcAddress(hModD3D11, "D3D11CreateDevice");
}
s_DynamicD3D11CreateDevice( NULL, D3D_DRIVER_TYPE_HARDWARE,NULL, uCreationFlags,flvl, sizeof(flvl) / sizeof(D3D_FEATURE_LEVEL),
D3D11_SDK_VERSION, &m_pDevice,&flOut, &m_pContext);
//Create ComputeShader
DWORD dwShaderFlags= D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined(_DEBUG )
// Set the D3DCOMPILE_DEBUG flag to embed debug informationin the shaders.
// Setting this flag improves the shader debuggingexperience, but still allows
// the shaders to be optimized and to run exactly the waythey will run in
// the release configuration of this program.
dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif
const D3D_SHADER_MACROdefines[] =
{
"USE_STRUCTURED_BUFFERS", "1",
NULL, NULL
};
// We generally prefer to use the higher CS shader profilewhen possible as CS 5.0 is better performance on 11-class hardware
LPCSTR pProfile= "cs_4_0"; //此处我的机器使用CS 5.0编译通不过,我装的是DX11,可能是显卡有点老了,Geforce9600
如果改成cs_5_0能编译通过最好。
ID3DBlob* pErrorBlob= NULL;
ID3DBlob* pBlob= NULL;
//从文件创建shader,Filter.hlsl文件名,Filter_GPU_Single Shader中的函数
hr = D3DX11CompileFromFile("Filter.hlsl", defines, NULL, "Filter_GPU_Single", pProfile,
dwShaderFlags, NULL,NULL, &pBlob,&pErrorBlob, NULL);
if ( FAILED(hr) )
{//Shader是在程序运行时编译的。如果编译通不过,在此处输出编译错误
if ( pErrorBlob )
OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer());
SAFE_RELEASE( pErrorBlob);
SAFE_RELEASE( pBlob);
return FALSE;
}
hr = m_pDevice->CreateComputeShader( pBlob->GetBufferPointer(), pBlob->GetBufferSize(), NULL,&m_pCS );
SAFE_RELEASE( pErrorBlob);
SAFE_RELEASE( pBlob);
开始建立所需要的缓冲。
//为CPU中的数组创建GPU中相应Buffer
if(FAILED(CreateStructureBuffer(m_pDevice,sizeof(float),iSrcDataLen + iFilterLen,(void*)m_pSrcBuffer,&m_pBuffer0)))
{//源数据缓冲
TRACE("创建buffer0失败\n");
return FALSE;
}
if(FAILED(CreateStructureBuffer(m_pDevice,sizeof(float),iFilterLen,(void*)m_pFilterBuffer,&m_pBuffer1)))
{//滤波系数缓冲
TRACE("创建buffer1失败\n");
return FALSE;
}
if(FAILED(CreateStructureBuffer(m_pDevice,sizeof(int),1,(void*)m_iFilterLen,&m_pBuffer2)))
{//滤波长度缓冲
TRACE("创建buffer2失败\n");
return FALSE;
}
if(FAILED(CreateStructureBuffer(m_pDevice,sizeof(float),iSrcDataLen,NULL,&m_pBufferResult)))
{//输出缓冲
TRACE("创建bufferOut失败\n");
return FALSE;
}
//为buffer创建相应的resource view,以便access buffer。
if(FAILED(CreateBufferSRV(m_pDevice,m_pBuffer0,&m_pBuf0SRV)))
{
TRACE("创建buffer0 SRV 失败\n");
return FALSE;
}
if(FAILED(CreateBufferSRV(m_pDevice,m_pBuffer1,&m_pBuf1SRV)))
{
TRACE("创建buffer1 SRV 失败\n");
return FALSE;
}
if(FAILED(CreateBufferSRV(m_pDevice,m_pBuffer2,&m_pBuf2SRV)))
{
TRACE("创建buffer2 SRV 失败\n");
return FALSE;
}
if(FAILED(CreateBufferUAV(m_pDevice,m_pBufferResult,&m_pBufResultUAV)))
{
TRACE("创建bufferOut Unordered Access View失败\n");
return FALSE;
}
初始化完毕
m_bInitDirectCompute = TRUE;
return TRUE;
}
析构就不用说了。
下面开始写滤波函数
//用GPU滤波,pBufferIn需要滤波的数据,pBuferOut滤波后的数据
BOOL CDX_Filter::Filter_GPU(float *pBufferIn,float *pBuferOut)
{
if(!m_bInitFilter|| !m_bInitDirectCompute)
{
return FALSE;
}
memcpy(m_pSrcBuffer,m_pDataSave + 1,(m_iFilterLen[0]-1) * sizeof(float));//把上一次的数据尾的数据拷到源数据缓冲头
memcpy(m_pSrcBuffer+ m_iFilterLen[0] -1,pBufferIn,m_iSrcBufferLen * sizeof(float));//拷贝源数据
memcpy(m_pDataSave,pBufferIn + m_iSrcBufferLen - m_iFilterLen[0],m_iFilterLen[0]* sizeof(float));//把本次的数据尾的数据暂存,以便下次使用
m_pContext->UpdateSubresource(m_pBuffer0,0,NULL,m_pSrcBuffer,0,0); // 更新缓冲的数据
ID3D11ShaderResourceView* shaderResourceViews[3]={m_pBuf0SRV,m_pBuf1SRV,m_pBuf2SRV};
m_pContext->CSSetShader(m_pCS,NULL,0); //设置Shader
m_pContext->CSSetShaderResources(0,3,shaderResourceViews); //设置缓冲数据
m_pContext->CSSetUnorderedAccessViews(0,1,&m_pBufResultUAV,NULL);//设置UAV
m_pContext->Dispatch(m_iSrcBufferLen,1,1);//执行(m_iSrcBufferLen最大不能超过65535)
//清空Shader和各个ShaderView以及以及一些Constant Buffer
m_pContext->CSSetShader(NULL,NULL,0);
ID3D11UnorderedAccessView* ppUAViewNULL[1]={NULL};
m_pContext->CSSetUnorderedAccessViews(0,1,ppUAViewNULL,NULL);
ID3D11ShaderResourceView* ppSRVNULL[2]={NULL,NULL};
m_pContext->CSSetShaderResources(0,2,ppSRVNULL);
ID3D11Buffer* ppCBNULL[1]={NULL};
m_pContext->CSSetConstantBuffers(0,1,ppCBNULL);
//获得结果数据
ID3D11Buffer* debugBuf=NULL;
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc,sizeof(desc));
m_pBufferResult->GetDesc(&desc);
desc.CPUAccessFlags=D3D11_CPU_ACCESS_READ;
desc.Usage=D3D11_USAGE_STAGING;
desc.BindFlags=0;
desc.MiscFlags=0;
if(SUCCEEDED(m_pDevice->CreateBuffer(&desc,NULL,&debugBuf)))
{
m_pContext->CopyResource(debugBuf,m_pBufferResult);
}
D3D11_MAPPED_SUBRESOURCE MappedResource;
m_pContext->Map(debugBuf,0,D3D11_MAP_READ,0,&MappedResource);
memcpy(pBuferOut,MappedResource.pData,m_iSrcBufferLen * sizeof(float));
m_pContext->Unmap(debugBuf,0);
SAFE_RELEASE(debugBuf);
return TRUE;
}
Shader文件中的函数
StructuredBuffer<float> Buffer0 : register(t0);
StructuredBuffer<float> Buffer1 : register(t1);
StructuredBuffer<int> Buffer2 : register(t2);
RWStructuredBuffer<float> BufferOut : register(u0);
[numthreads(1, 1, 1)]
void Filter_GPU_Single(uint3 DTid : SV_DispatchThreadID )
{
int i=0;
float fSum = 0.0;
for(i=0;i< Buffer2[0];i++)
{
fSum += Buffer0[DTid.x + i] *Buffer1[i];
}
BufferOut[DTid.x] = fSum;
}
为了验证用GPU计算的结果是否正确,还需要写CPU计算的程序,以便验证其正确性。CPU计算此处就不贴代码了,完整代码请到http://download.csdn.net/detail/iddialog/4683767下载。
以上程序在win7 、DX11和 VC++ 2008 + SP1 编译通过。能够正常运行。运行结果GPU和CPU运算结果是一致的。由于每次计算后,m_pDataSave的内容发生变化,输出缓冲的前面一段数据可能会不一样。如果要测试GPU和CPU两种方式的结果是否一样,需要每次在滤波前把m_pDataSave的内容设成一样。
如果VS2008没有SP1,需要修改stdafx.h文件
删除下面这行
#include <afxcontrolbars.h> // 功能区和控件条的MFC支持
添加
#ifdef CWinAppEx
#undef CWinAppEx
#endif
#define CWinAppEx CWinApp
结尾:
由于第一次写DirectCompute程序,错误在所难免。只是对前文《OpenCL使用GPU滤波》的另一个实现,多通道滤波函数就没有写了。学习DirectCompute编程而已。还是要提一下C++AMP,这个是比较新的技术,集成在VS2012中,我还没有安装VS2012。等有空了安装了,再来写C++AMP的程序。还是那句话“什么时候我们的程序由CPU和GPU自动调节运行就好了,对用户和程序员均不透明,就像双核或者多核CPU一样,我们根本就不用关心程序或者说某个线程在哪个核上运行!”。