mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-17 22:49:08 +09:00
316 lines
11 KiB
Plaintext
316 lines
11 KiB
Plaintext
|
||
// interp3_gpu.m - fast texture based GPU based interpolation method for 3D deformation
|
||
//
|
||
// RECOMPILE: mexcuda -output interp3_gpu_mex interp3_gpu_ker.cu interp3_gpu_mex.cpp
|
||
//
|
||
// %*-----------------------------------------------------------------------*
|
||
// %| |
|
||
// %| Except where otherwise noted, this work is licensed under a |
|
||
// %| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||
// %| International (CC BY-NC-SA 4.0) license. |
|
||
// %| |
|
||
// %| Copyright (c) 2018 by Paul Scherrer Institute (http://www.psi.ch) |
|
||
// %| |
|
||
// %| Author: CXS group, PSI |
|
||
// %*-----------------------------------------------------------------------*
|
||
// % You may use this code with the following provisions:
|
||
// %
|
||
// % If the code is fully or partially redistributed, or rewritten in another
|
||
// % computing language this notice should be included in the redistribution.
|
||
// %
|
||
// % If this code, or subfunctions or parts of it, is used for research in a
|
||
// % publication or if it is fully or partially rewritten for another
|
||
// % computing language the authors and institution should be acknowledged
|
||
// % in written form in the publication: “Data processing was carried out
|
||
// % using the “cSAXS matlab package” developed by the CXS group,
|
||
// % Paul Scherrer Institut, Switzerland.”
|
||
// % Variations on the latter text can be incorporated upon discussion with
|
||
// % the CXS group if needed to more specifically reflect the use of the package
|
||
// % for the published work.
|
||
// %
|
||
// % A publication that focuses on describing features, or parameters, that
|
||
// % are already existing in the code should be first discussed with the
|
||
// % authors.
|
||
// %
|
||
// % This code and subroutines are part of a continuous development, they
|
||
// % are provided “as they are” without guarantees or liability on part
|
||
// % of PSI or the authors. It is the user responsibility to ensure its
|
||
// % proper use and the correctness of the results.
|
||
|
||
|
||
|
||
|
||
#include <algorithm>
|
||
#include <cuda_runtime_api.h>
|
||
#include "interp3_gpu.hpp"
|
||
#include <cuda.h>
|
||
#include <iostream>
|
||
#include <list>
|
||
|
||
#include "mex.h"
|
||
#include "gpu/mxGPUArray.h"
|
||
|
||
#define MAX(x,y) (x>y?x:y);
|
||
#define MIN(x,y) (x<y?x:y);
|
||
#define ABS(x) (x>0?x:-x);
|
||
#define INF (1023);
|
||
|
||
typedef const unsigned int cuint;
|
||
typedef const int cint;
|
||
|
||
typedef texture<float, 3, cudaReadModeElementType> texture3D;
|
||
|
||
static texture3D ImgTexture, X_tex, Y_tex, Z_tex;
|
||
|
||
// splitting volume on smaller blocks to prevent GPU crashes
|
||
static cuint g_blockX = 256;
|
||
static cuint g_blockY = 256;
|
||
static cuint g_blockZ = 256;
|
||
|
||
|
||
cudaArray* allocateVolumeArray( cuint X, cuint Y, cuint Z)
|
||
{
|
||
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
|
||
cudaArray* cuArray;
|
||
cudaExtent extent;
|
||
extent.width = X;
|
||
extent.height = Y;
|
||
extent.depth = Z;
|
||
cudaError err = cudaMalloc3DArray(&cuArray, &channelDesc, extent);
|
||
if (err != cudaSuccess) {
|
||
mexPrintf ("Failed to allocate %dx%dx%d GPU array\n",X,Y,Z);
|
||
return 0;
|
||
}
|
||
return cuArray;
|
||
}
|
||
|
||
static bool bindVolumeDataTexture(const cudaArray* array, texture3D & Texture, bool normalized)
|
||
{
|
||
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
|
||
Texture.addressMode[0] = cudaAddressModeClamp;
|
||
Texture.addressMode[1] = cudaAddressModeClamp;
|
||
Texture.addressMode[2] = cudaAddressModeClamp;
|
||
Texture.filterMode = cudaFilterModeLinear; //cudaFilterModePoint
|
||
Texture.normalized = normalized;
|
||
|
||
cudaError err = cudaBindTextureToArray(Texture, array, channelDesc);
|
||
checkLastError("cudaBindTextureToArray ");
|
||
return true;
|
||
}
|
||
|
||
bool transferVolumeToArray(const mxGPUArray * m_img, cudaArray *& array)
|
||
{
|
||
|
||
mwSize const * dimensions = mxGPUGetDimensions(m_img);
|
||
mwSize Ndim = mxGPUGetNumberOfDimensions(m_img);
|
||
int M = (int)dimensions[0];
|
||
int N = (int)dimensions[1];
|
||
int O = Ndim > 2 ? (int)dimensions[2] : 1;
|
||
|
||
// get the values into float array
|
||
const float * img =(const float *)mxGPUGetDataReadOnly(m_img);
|
||
|
||
array = allocateVolumeArray(M,N,O);
|
||
if (array == 0)
|
||
return false;
|
||
|
||
if (M * sizeof(float) > 2048) {
|
||
mexPrintf("Volume is too large to be transfered to GPU array");
|
||
return false;
|
||
}
|
||
|
||
/* make volume array (no copying) */
|
||
cudaPitchedPtr volume;
|
||
volume.ptr = (float *)img;
|
||
volume.pitch = M * sizeof(float);
|
||
volume.xsize = M;
|
||
volume.ysize = N;
|
||
|
||
cudaExtent extent;
|
||
extent.width = M;
|
||
extent.height = N;
|
||
extent.depth = O;
|
||
|
||
cudaMemcpy3DParms p;
|
||
cudaPos zp = { 0, 0, 0 };
|
||
p.srcArray = 0;
|
||
p.srcPos = zp;
|
||
p.srcPtr = volume;
|
||
p.dstArray = array;
|
||
p.dstPtr.ptr = 0;
|
||
p.dstPtr.pitch = 0;
|
||
p.dstPtr.xsize = 0;
|
||
p.dstPtr.ysize = 0;
|
||
p.dstPos = zp;
|
||
p.extent = extent;
|
||
p.kind = cudaMemcpyDeviceToDevice;
|
||
|
||
cudaError err = cudaMemcpy3D(&p);
|
||
|
||
if (!checkLastError("transferVolumeToArray cudaMemcpy3D"))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
int checkLastError(char * msg)
|
||
{
|
||
cudaError_t cudaStatus = cudaGetLastError();
|
||
if (cudaStatus != cudaSuccess) {
|
||
char err[512];
|
||
sprintf(err, "interp3 variation failed \n %s: %s. \n", msg, cudaGetErrorString(cudaStatus));
|
||
mexErrMsgTxt(err);
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
bool cudaTextForceKernelsCompletion()
|
||
{
|
||
cudaError_t returnedCudaError = cudaThreadSynchronize();
|
||
if (returnedCudaError != cudaSuccess) {
|
||
fprintf(stderr, "Failed to force completion of cuda kernels: %d: %s. \n ", returnedCudaError, cudaGetErrorString(returnedCudaError));
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* TEXTURE TRILINEAR INTERPOLATION
|
||
**/
|
||
|
||
|
||
__global__ void kernel_interp3(float * p, cuint N, cuint M, cuint O,
|
||
cuint Xstart, cuint Ystart, cuint Zstart) {
|
||
|
||
// Location in a 3D matrix
|
||
mwSize m = Xstart+ blockIdx.x * blockDim.x + threadIdx.x;
|
||
mwSize n = Ystart+ blockIdx.y * blockDim.y + threadIdx.y;
|
||
mwSize o = Zstart+ blockIdx.z * blockDim.z + threadIdx.z;
|
||
|
||
if (m < M & n < N & o < O)
|
||
{
|
||
float xs, ys, zs; // shifted coordinates
|
||
float mn, nn, on; // normalized coordinates
|
||
mn = (float)(m)/M;
|
||
nn = (float)(n)/N;
|
||
on = (float)(o)/O;
|
||
// mn = (m+0.5f);
|
||
// nn = (n+0.5f);
|
||
// on = (o+0.5f);
|
||
// load deformed coordinates
|
||
xs = m+0.5f - tex3D(X_tex,mn, nn, on);
|
||
ys = n+0.5f - tex3D(Y_tex,mn, nn, on);
|
||
zs = o+0.5f - tex3D(Z_tex,mn, nn, on);
|
||
// get trilinear interplation
|
||
bool outsiders = (xs > 0) & (ys > 0) & (zs > 0) &
|
||
(xs < M) & (ys < N) & (zs < O);
|
||
float p_val = (outsiders ? tex3D(ImgTexture,xs,ys,zs) : 0);
|
||
// write the interpolation to the output
|
||
p[(n)*N+(m)+(o)*M*N] = p_val;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Host function called by MEX gateway.
|
||
*/
|
||
|
||
|
||
|
||
|
||
void interp3_init( float * p, const mxGPUArray * p0, const mxGPUArray *m_X, const mxGPUArray *m_Y, const mxGPUArray *m_Z, cuint M, cuint N, cuint O)
|
||
{
|
||
if (M*N*O*4 > 1024e6) {
|
||
mexPrintf("Image size exceeded 1024MB, textures in interp3 will fail\n");
|
||
return;
|
||
}
|
||
|
||
/* move image to the texture array */
|
||
cudaArray* cuArray, *cuArrayX, *cuArrayY, *cuArrayZ;
|
||
checkLastError("after allocateVolumeArray");
|
||
transferVolumeToArray(p0, cuArray);
|
||
checkLastError("after transferVolumeToArray\n \n ");
|
||
bindVolumeDataTexture(cuArray, ImgTexture, false);
|
||
|
||
/* move X deformation to the texture array */
|
||
checkLastError("after allocateVolumeArray");
|
||
transferVolumeToArray(m_X, cuArrayX);
|
||
checkLastError("after transferVolumeToArray\n \n ");
|
||
bindVolumeDataTexture(cuArrayX, X_tex, true);
|
||
|
||
/* move Y deformation to the texture array */
|
||
checkLastError("after allocateVolumeArray");
|
||
transferVolumeToArray(m_Y, cuArrayY);
|
||
checkLastError("after transferVolumeToArray\n \n ");
|
||
bindVolumeDataTexture(cuArrayY, Y_tex, true);
|
||
|
||
/* move Z deformation to the texture array */
|
||
checkLastError("after allocateVolumeArray");
|
||
transferVolumeToArray(m_Z, cuArrayZ);
|
||
checkLastError("after transferVolumeToArray\n \n ");
|
||
bindVolumeDataTexture(cuArrayZ, Z_tex, true);
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// *************** 3-dim case ***************
|
||
// Choose a reasonably sized number of threads in each dimension for the block.
|
||
int const threadsPerBlockEachDim = 10; // MAX THREAD is 1024 ~ 10*10*10 for 3D
|
||
dim3 const dimThread(threadsPerBlockEachDim, threadsPerBlockEachDim, threadsPerBlockEachDim);
|
||
//mexPrintf("Thread %i %i %i \n ", dimThread.x, dimThread.y, dimThread.z);
|
||
// Compute the thread block and grid sizes based on the board dimensions.
|
||
int const blocksPerGrid_M = (g_blockX + threadsPerBlockEachDim - 1) / threadsPerBlockEachDim;
|
||
int const blocksPerGrid_N = (g_blockY + threadsPerBlockEachDim - 1) / threadsPerBlockEachDim;
|
||
int const blocksPerGrid_O = (g_blockZ + threadsPerBlockEachDim - 1) / threadsPerBlockEachDim;
|
||
dim3 dimBlock(blocksPerGrid_M, blocksPerGrid_N, blocksPerGrid_O);
|
||
//mexPrintf("Block %i %i %i \n ", blocksPerGrid_M, blocksPerGrid_N, blocksPerGrid_O);
|
||
|
||
std::list<cudaStream_t> streams;
|
||
|
||
for ( int blockXstart=0; blockXstart < M; blockXstart += g_blockX)
|
||
for ( int blockYstart=0; blockYstart < N; blockYstart += g_blockY)
|
||
for ( int blockZstart=0; blockZstart < O; blockZstart += g_blockZ)
|
||
{
|
||
cudaStream_t stream;
|
||
cudaStreamCreate(&stream);
|
||
streams.push_back(stream);
|
||
kernel_interp3<<<dimBlock, dimThread, 0, stream>>>
|
||
(p,M,N,O, blockXstart,blockYstart,blockZstart);
|
||
}
|
||
|
||
checkLastError("after kernel");
|
||
cudaThreadSynchronize();
|
||
for (std::list<cudaStream_t>::iterator iter = streams.begin(); iter != streams.end(); ++iter)
|
||
cudaStreamDestroy(*iter);
|
||
|
||
streams.clear();
|
||
|
||
|
||
|
||
// clear memory , unbind textures
|
||
|
||
cudaTextForceKernelsCompletion();
|
||
|
||
cudaFreeArray(cuArray);
|
||
cudaFreeArray(cuArrayX);
|
||
cudaFreeArray(cuArrayY);
|
||
cudaFreeArray(cuArrayZ);
|
||
|
||
checkLastError("after cudaFreeArray");
|
||
cudaUnbindTexture(ImgTexture);
|
||
cudaUnbindTexture(X_tex);
|
||
cudaUnbindTexture(Y_tex);
|
||
cudaUnbindTexture(Z_tex);
|
||
checkLastError("cudaUnbindTexture");
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|