[CUDA]紋理對象 Texture Object

紋理對象是CUDA針對紋理參考缺點而提出的升級版,其作用和紋理參考完全一致,但是使用方法更加靈活。與紋理參考相比,CUDA對其進行各方面的升級,一方面是可以再代碼中申請和銷燬,另一方面則可以作爲設備函數的參數進行傳入;可以滿足一些特殊的需求。

一個紋理對象是用cudaCreateTextureObject()產生的。cudaCreateTextureObject()有4個參數,常用的前三個是

  1. cudaTextureObject_t *texObj:需要生產的紋理對象;
  2. cudaResourceDesc *resDesc:資源描述符,用來獲取述紋理數據;
  3. 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端才能正常使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章