cuda 3D纹理插值

时间:2014-08-31 10:25:29

标签: cuda textures interpolation

我正在尝试用cuda插入一个3D数组,使用纹理存储器和下面的代码。我已将输入f [x] [y] [z]绘制为固定的z值,然后我为x和y插入我的数组并再次绘制i,它们看起来完全不同。我也在一维(使用不同的代码)尝试了这个,并且它工作,所以我假设我的代码中一定有错误。你能帮我找到吗?

#include <cuda_runtime.h>
#include <cuda.h>
#include <iostream>
#include <fstream>

typedef float myType;

texture<myType, 3> tex;
cudaArray *d_volumeArray = 0;

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
    if (code != cudaSuccess) 
    {
        fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
        if (abort) { getchar(); exit(code); }
    }
}

__global__ void getInterpolatedFunctionValue(double x, double y, double z){
//http://stackoverflow.com/questions/10643790/texture-memory-tex2d-basics
    printf("%f \n", tex3D(tex, x+0.5f, y+0.5f, z+0.5f));
}

using namespace std;

int main(){

int nx=100, ny=100, nz=10;
myType f[nx][ny][nz];
for(int i=0; i<nx; i++)
  for(int j=0; j<ny; j++)
    for(int k=0; k<nz; k++){ 
      f[i][j][k] = sin(i/10.0)*cos(j/10.0)+k;
    }

const cudaExtent extend = make_cudaExtent(nx, ny, nz);
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<myType>();
gpuErrchk(cudaMalloc3DArray(&d_volumeArray, &channelDesc, extend));

cudaMemcpy3DParms copyParams = {0};
copyParams.srcPtr   = make_cudaPitchedPtr((void*)f, extend.width*sizeof(myType), extend.width, extend.height);
copyParams.dstArray = d_volumeArray;
copyParams.extent   = extend;
copyParams.kind     = cudaMemcpyHostToDevice;
gpuErrchk(cudaMemcpy3D(&copyParams));

tex.normalized = false;                      
tex.filterMode = cudaFilterModeLinear;      
tex.addressMode[0] = cudaAddressModeClamp;   
tex.addressMode[1] = cudaAddressModeClamp;
tex.addressMode[2] = cudaAddressModeClamp;

gpuErrchk(cudaBindTextureToArray(tex, d_volumeArray, channelDesc));

for(int i=0; i<nx*2; i++){
  for(int j=0; j<ny*2; j++){
    getInterpolatedFunctionValue <<<1, 1>>> (float(i)/2, float(j)/2, 3.0);
    gpuErrchk(cudaPeekAtLastError());
    gpuErrchk(cudaDeviceSynchronize());
  }
}

gpuErrchk(cudaUnbindTexture(tex));
gpuErrchk(cudaFreeArray(d_volumeArray));

return 0;
}

更新: @Robert Crovella:在我看来,如果你绘制输出并将插值与原始插值进行比较,你可以更好地看到我的问题。我将在下面添加它们。整数除法没有计划,我修复它,但这不是我的问题的原因

@JackOLantern:我知道这篇文章和你的代码有我的版本的模板。但在我看来,它并没有像我预期的那样有效。

由于我没有足够的声誉在这里上传图像,我将链接这两个图像。数字1显示了修复z值的输入值的图表,以及我的代码完成的插值图2。原始数据的范围为[2,4],而插值的范围为[-2,10],结构完全不同。我希望这有助于更好地理解我的问题。

1

enter image description here

2

enter image description here

1 个答案:

答案 0 :(得分:2)

主要问题似乎是您的基础纹理存储索引顺序已反转。 x维度是快速变化的矩阵维度(在这种情况下为第3个下标)和warp中快速变化的线程维度(尽管与此示例无关)。在您的代码中,我认为以下内容总结了必要的更改:

myType f[nz][ny][nx];
for(int i=0; i<nx; i++)
  for(int j=0; j<ny; j++)
    for(int k=0; k<nz; k++){
      f[k][j][i] = sin(i/10.0f)*cos(j/10.0f)+k;
    }

关于使用线性插值进行纹理化可以说的话还有很多,所以如果你进一步深入研究,我建议对所提出的材料有充分的了解here。对于具有非标准化坐标的线性滤波,对于在特定方向上具有N个数据点的纹理,插值范围(不包括钳位区域)将具有维度N-1。这种关系通常通过在先前链接的材料中仔细应用表查找方程来处理,但是对于您的示例,为了进行最小数量的更改,我们可以省去这一点,并且只需要小心我们如何计算预期的功能值以及传递给纹理查找的xyz值。

以下是主要修改为存储顺序的示例。由于我不想绘制数据,我选择修改代码以注入验证检查。

#include <iostream>
#include <fstream>
#define NX 100
#define NY 100
#define NZ 10
#define TOL 0.003f
#define I_FACT 2
typedef float myType;

texture<myType, 3> tex;
cudaArray *d_volumeArray = 0;

__global__ void getInterpolatedFunctionValue(myType x, myType y, myType z, myType *result){
    *result = tex3D(tex, x+0.5f, y+0.5f, z+0.5f);
}

#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

using namespace std;

int main(){

int nx=NX, ny=NY, nz=NZ;
myType f[nz][ny][nx];

for(int ix=0; ix<nx; ix++)
  for(int iy=0; iy<ny; iy++)
    for(int iz=0; iz<nz; iz++){
      f[iz][iy][ix] = sin(ix/(float)10)*cos(iy/(float)10)+iz;
    }

const cudaExtent extent = make_cudaExtent(nx, ny, nz);
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<myType>();
cudaMalloc3DArray(&d_volumeArray, &channelDesc, extent);
cudaCheckErrors("cudaMalloc3D error");

cudaMemcpy3DParms copyParams = {0};
copyParams.srcPtr   = make_cudaPitchedPtr((void*)f, extent.width*sizeof(myType), extent.width, extent.height);
copyParams.dstArray = d_volumeArray;
copyParams.extent   = extent;
copyParams.kind     = cudaMemcpyHostToDevice;
cudaMemcpy3D(&copyParams);
cudaCheckErrors("cudaMemcpy3D fail");

tex.normalized = false;
tex.filterMode = cudaFilterModeLinear;
tex.addressMode[0] = cudaAddressModeClamp;
tex.addressMode[1] = cudaAddressModeClamp;
tex.addressMode[2] = cudaAddressModeClamp;

cudaBindTextureToArray(tex, d_volumeArray, channelDesc);
cudaCheckErrors("bind fail");
myType my_result;
myType *d_result, *h_result = &my_result;
cudaMalloc(&d_result, sizeof(myType));


for(int i=0; i<(nx-1)*I_FACT; i++)
  for(int j=0; j<(ny-1)*I_FACT; j++)
    for (int k = 0; k <(nz-1)*I_FACT; k++){
    myType test_val = sin(i/(float)(10*I_FACT))*cos(j/(float)(10*I_FACT)) + k/(float)(I_FACT);
    getInterpolatedFunctionValue <<<1, 1>>> (i/(float)I_FACT, j/(float)I_FACT, k/(float)I_FACT, d_result);
    cudaDeviceSynchronize();
    cudaCheckErrors("kernel fail");
    cudaMemcpy(h_result, d_result, sizeof(myType), cudaMemcpyDeviceToHost);
    cudaCheckErrors("cudaMemcpy fail");
    if (fabs(my_result - test_val) > TOL) {printf("mismatch at x:%f, y:%f, z:%f, was:%f, should be: %f\n", i/(float)I_FACT,j/(float)I_FACT,k/(float)I_FACT, my_result, test_val); return 1;}
  }
printf("success!\n");

cudaUnbindTexture(tex);
cudaCheckErrors("unbind fail");
cudaFreeArray(d_volumeArray);
cudaCheckErrors("free fail");

return 0;
}

此代码似乎对我来说正常运行,使用CUDA 6.5在K40c上大约需要30秒。将来,如果您将验证检查构建到您的求助请求中,而不是期望其他人绘制您的数据以确定其有效性,那么将会很有帮助。这使得其他人可以轻松地帮助您,并且还明确声明您期望的结果的性质。

上面代码中内置的容差可能不正确,涵盖了很多种情况。纹理硬件具有以8位分数精度存储的系数(参考前一链接),并且在3D情况下,您将这些系数中的3个相乘。因此,最多容差可能需要大约是纹理中存储数据的最大值的0.005倍,但我还没有进行仔细的容差分析。

增加I_FACT参数将大大增加上述测试代码的运行时间。