紋理對象是CUDA針對紋理參考缺點而提出的升級版,其作用和紋理參考完全一致,但是使用方法更加靈活。與紋理參考相比,CUDA對其進行各方面的升級,一方面是可以再代碼中申請和銷燬,另一方面則可以作爲設備函數的參數進行傳入;可以滿足一些特殊的需求。
一個紋理對象是用cudaCreateTextureObject()產生的。cudaCreateTextureObject()有4個參數,常用的前三個是
- cudaTextureObject_t *texObj:需要生產的紋理對象;
- cudaResourceDesc *resDesc:資源描述符,用來獲取述紋理數據;
- cudaTextureDesc *texDesc:紋理描述符,用來描述紋理參數;
- 其中cudaTextureDesc定義如下:
struct cudaTextureDesc
{
enum cudaTextureAddressMode addressMode[3];
enum cudaTextureFilterMode filterMode;
enum cudaTextureReadMode readMode;
int sRGB;
int normalizedCoords;
unsigned int maxAnisotropy;
enum cudaTextureFilterMode mipmapFilterMode;
float mipmapLevelBias;
float minMipmapLevelClamp;
float maxMipmapLevelClamp;
};
使用紋理對象主要包括紋理對象創建、紋理訪問和紋理對象銷燬。
- 紋理對象創建
紋理對象創建之前首先要分別對紋理資源和紋理對象屬性進行確定,分別對應cudaResoruceDesc和cudaTextureDesc;然後即可利用cudaCreateTextureObject來創建紋理對象。
- 紋理訪問
紋理對象的紋理訪問也和紋理參考一樣,也是使用tex1D或tex2D等函數進行操作。
- 紋理對象銷燬
紋理對象的銷燬直接使用cudaDestroyTextureObject即可。值得注意的是應該先銷燬對象,然後再釋放對應的設備內存。
- 一個使用紋理對象的簡單例子:
// 簡單紋理變換函數
__global__ void transformKernel(float* output,cudaTextureObject_t texObj,
int width, int height, float theta){
// 計算紋理座標
unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;
float u = x / (float)width;
float v = y / (float)height;
// 座標轉換
u -= 0.5f;
v -= 0.5f;
float tu = u * cosf(theta) - v * sinf(theta) + 0.5f;
float tv = v * cosf(theta) + u * sinf(theta) + 0.5f;
// 從紋理中讀取並寫入全局存儲
output[y * width + x] = tex2D<float>(texObj, tu, tv);
}
int main(){
// 定義CUDA array
cudaChannelFormatDesc channelDesc =
cudaCreateChannelDesc(32, 0, 0, 0,
cudaChannelFormatKindFloat);
cudaArray* cuArray;
cudaMallocArray(&cuArray, &channelDesc, width, height);
// 拷貝數據到CUDA array
cudaMemcpyToArray(cuArray, 0, 0, h_data, size,
cudaMemcpyHostToDevice);
// 定義資源描述符
struct cudaResourceDesc resDesc;
memset(&resDesc, 0, sizeof(resDesc));
resDesc.resType = cudaResourceTypeArray;
resDesc.res.array.array = cuArray;
// 定義紋理對象參數
struct cudaTextureDesc texDesc;
memset(&texDesc, 0, sizeof(texDesc));
texDesc.addressMode[0] = cudaAddressModeWrap;
texDesc.addressMode[1] = cudaAddressModeWrap;
texDesc.filterMode = cudaFilterModeLinear;
texDesc.readMode = cudaReadModeElementType;
texDesc.normalizedCoords = 1;
// 生產紋理對象
cudaTextureObject_t texObj = 0;
cudaCreateTextureObject(&texObj, &resDesc, &texDesc, NULL);
// 分配用於保持結果的內存
float* output;
cudaMalloc(&output, width * height * sizeof(float));
// 調用Kernel
dim3 dimBlock(16, 16);
dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x,
(height + dimBlock.y - 1) / dimBlock.y);
transformKernel<<<dimGrid, dimBlock>>>(output, texObj, width, height, angle);
// 銷燬紋理對象
cudaDestroyTextureObject(texObj);
// 釋放內存
cudaFreeArray(cuArray);
}
在使用紋理對象時還有一些特殊注意。通常而言,使用紋理對象的目的就是使用多個紋理對象,因此會申請一個cudaTextureObject_t 數組。注意在使用時必須將其拷貝到device端才能正常使用。