cuda 3D纹理插值

cuda 3D texture Interpolation

我正在尝试使用带有以下代码的纹理内存使用cuda插入3D数组。我已经将输入f [x] [y] [z]绘制为固定的z值,然后对数组x和y进行插值并再次绘制i,它们看上去完全不同。我也在1维(使用不同的代码)中进行了尝试,并且可以正常工作,因此我认为代码中肯定有错误。你能帮我找到它吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#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\
", 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 \
", 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] = cudaAddressModeCl  
tex.addressMode[1] = cudaAddressModeCl
tex.addressMode[2] = cudaAddressModeCl

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


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

1
2
3
4
5
6
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;
    }

关于线性插值的纹理化还有很多可以说的,所以如果您进一步研究它,我建议您对这里介绍的材料有一个扎实的理解。对于具有非归一化坐标的线性滤波,对于在特定方向上具有N个数据点的纹理,插值范围(不包括钳位区域)的尺寸为N-1。通常可以通过在先前链接的材料中仔细应用表格查找方程式来处理这种关系,但是对于您的示例,为使更改次数最少,我们可以免除此更改,而只是在小心如何计算期望的功能值时以及传递到纹理查找的xyz值。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#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)\
", \\
                msg, cudaGetErrorString(__err), \\
                __FILE__, __LINE__); \\
            fprintf(stderr,"*** FAILED - ABORTING\
"); \\
            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] = cudaAddressModeCl
tex.addressMode[1] = cudaAddressModeCl
tex.addressMode[2] = cudaAddressModeCl

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\
", i/(float)I_FACT,j/(float)I_FACT,k/(float)I_FACT, my_result, test_val); return 1;}
  }
printf("success!\
");

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参数将大大增加上述测试代码的运行时间。