DirectCompute使用GPU滤波

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设置

工具->选项,设置包含文件路径

DirectCompute使用GPU滤波_第1张图片

设置库文件路径


第三步,开始编程。

为了实用和可重用,做成一个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,pBufferInm_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一样,我们根本就不用关心程序或者说某个线程在哪个核上运行!”。

你可能感兴趣的:(DirectCompute使用GPU滤波)