initial commit

This commit is contained in:
2026-08-07 15:56:42 +09:00
commit 91ad25aca9
1012 changed files with 159314 additions and 0 deletions
+315
View File
@@ -0,0 +1,315 @@
// 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");
}