紋理存儲器是GPU常用的優化方法。
舉個慄
參考《GPGPU編程技術——從GLSL、CUDA到OpenCL》代碼6-2(離散卷積程序)。因爲CUDA版本的不同,所以本人對原書的代碼進行修改,修改後的代碼如下。
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
using namespace std;
// Block 的尺寸爲[BLOCK_X * BLOCK_Y]
#define BLOCK_X 16u
#define BLOCK_Y 16u
// 定義一個四維向量相加的宏
#define VectorAdd(a, b) a.x += b.x; a.y += b.y; a.z += b.z; a.w += b.w;
// 定義一個四維向量與標量除法的宏
#define VectorScalarDev(a, b, c) a.x = b.x / c; a.y = b.y / c; a.z = b.z / c; a.w = b.w / c;
// 紋理相關全局變量
texture<float4, 2, cudaReadModeElementType> refTex;
cudaArray* cuArray;
__global__ void convolution(int nWidth, int nHeight, int nRadius, float4* pfResult)
{
// 計算線程號
const int idxX = blockIdx.x * blockDim.x + threadIdx.x;
const int idxY = blockIdx.y * blockDim.y + threadIdx.y;
// 將線程號映射到全局存儲器
const int idxResult = idxY * nHeight + idxX;
// 近鄰元素的數量
int nTotal = 0;
// 近鄰元素之和
float4 f4Sum = { 0.0f, 0.0f, 0.0f, 0.0f };
// 計算結果:輸出四元向量
float4 f4Result = { 0.0f, 0.0f, 0.0f, 0.0f };
float4 f4Temp = { 0.0f, 0.0f, 0.0f, 0.0f };
// 近鄰元素求和
for (int ii = idxX - nRadius; ii < idxX + nRadius; ii++)
{
for (int jj = idxY - nRadius; jj <= idxY + nRadius; jj++)
{
if (ii >= 0 && jj >= 0 && ii < nWidth && jj < nHeight)
{
f4Temp = tex2D(refTex, ii, jj);
VectorAdd(f4Sum, f4Temp);
nTotal++;
}
}
}
VectorScalarDev(f4Result, f4Sum, (float)nTotal);
*(pfResult + idxResult) = f4Result;
}
int main()
{
unsigned unWidth = 1024u;
unsigned unHeight = 1024u;
unsigned unSizeData = unWidth * unHeight;
unsigned unRadius = 2u;
// 準備輸入數據
unsigned unData = 0;
float4* pf4Sampler;
cudaMallocHost((void **)&pf4Sampler, unSizeData * sizeof(float4));
for (unsigned i = 0; i < unSizeData; i++)
{
pf4Sampler[i].x = (float)(unData++);
pf4Sampler[i].y = (float)(unData++);
pf4Sampler[i].z = (float)(unData++);
pf4Sampler[i].w = (float)(unData++);
}
// 準備紋理
cudaChannelFormatDesc cuDesc = cudaCreateChannelDesc<float4>();
cudaMallocArray(&cuArray, &cuDesc, unWidth, unHeight);
cudaMemcpyToArray(cuArray, 0, 0, pf4Sampler, unSizeData * sizeof(float4), cudaMemcpyHostToDevice);
// 分配全局存儲器
float4* pfResult;
cudaMalloc((void **)&pfResult, unSizeData * sizeof(float4));
// 配置線程並調用內核
dim3 block(BLOCK_X, BLOCK_Y);
dim3 grid(ceil((float)unWidth / BLOCK_X), ceil((float)unHeight / BLOCK_Y));
convolution <<< grid, block >>>(unWidth, unHeight, unRadius, pfResult);
// 結果
cudaMemcpy(pf4Sampler, pfResult, unSizeData * sizeof(float4), cudaMemcpyDeviceToHost);
cudaUnbindTexture(refTex);
cudaFreeHost(pf4Sampler);
cudaFreeArray(cuArray);
cudaFree(pfResult);
return 0;
}
砸開喫
一般使用紋理存儲器處理圖形(樹上是這麼說的),至於有什麼好處,樹上說了一大堆。天書啊( ╯□╰ )看不懂(* ̄︶ ̄)y。不過好歹有個栗子可以砸開喫。
- 聲明紋理參照系 refTex以及CUDA數組cuArray
- 準備輸入數據 pf4Sampler。
- (cudaArray類型本身並非模板,因此在使用cudaMallocArray() 分配存儲器空間時調用) cudaChannelFormatDesc() 設置數組的數據類型。cudaMemcpyToArray() 初始化CUDA數組。
- 紋理綁定cudaBindTextureToArray()。
- 紋理拾取tex2D。
- 數據操作。
- 解除綁定。
- 釋放CUDA數組。
經過8道工序就個享受紋理的美味了。
在CUDA的Samples中也提供了一個關於紋理的例子simpleTexture.cu。
參考:
《GPGPU編程技術——從GLSL、CUDA到OpenCL》♥♥♥♥♥
《數字圖像處理高級應用——基於MATLAB與CUDA的實現》♥♥♥
《基於CUDA的並行程序設計》♥♥♥
《CUDA專家手冊》♥♥♥♥♥
《高性能CUDA應用設計與開發》♥♥♥♥