我正在尝试用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(©Params));
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
2
答案 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。这种关系通常通过在先前链接的材料中仔细应用表查找方程来处理,但是对于您的示例,为了进行最小数量的更改,我们可以省去这一点,并且只需要小心我们如何计算预期的功能值以及传递给纹理查找的x
,y
和z
值。
以下是主要修改为存储顺序的示例。由于我不想绘制数据,我选择修改代码以注入验证检查。
#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(©Params);
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
参数将大大增加上述测试代码的运行时间。