/* *-----------------------------------------------------------------------* |                                                                       | |  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) 2017 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. */ // Defines the exported functions for the DLL application. // // recompile commands // (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu // (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu /************* INPUTS *****************************/ /* string 'fp' or 'bp' - forward / backward projection single gpuArray volume or data object struct cfg - contain configuration for astra, created by ASTRA_initialize.m double array vec - contain projection geometry for astra, created by ASTRA_initialize.m (optional) single gpuArray - volume or data object to write the results to */ #include "cuda_runtime.h" #include "device_launch_parameters.h" #include #include #include #include #include #include #include "mex.h" #include "gpu/mxGPUArray.h" #include "util3d.h" #include "dims3d.h" #include "par3d_bp.h" #include "par3d_fp.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray const *prhs[]) { //mexPrintf("Warning: loading development version of ASTRA\n"); //mexPrintf("Ninputs:%i\n", nrhs); if (!((nrhs == 4) || (nrhs == 5) || (nrhs == 8 ) || (nrhs == 11 ) )) mexErrMsgTxt("4,5, 8, or 11 input arguments required"); using namespace astraCUDA3d; char const * const errId = "parallel:gpu:mexGPUExample:InvalidInput"; char const * const errMsg = "Invalid input to MEX file."; /* Throw an error if the input is not a GPU array. */ if (!mxIsGPUArray(prhs[1])) { mexErrMsgIdAndTxt(errId, "The second input must be GPU array"); } /* Load configuration */ SDimensions3D dims; mxArray * tmp; double * val; #define SETVAR(name) do {tmp = mxGetField(prhs[2], 0, ""#name""); if (tmp!=NULL) { val = mxGetPr(tmp); dims.name = (unsigned int)val[0]; }} while (0); SETVAR(iVolX); SETVAR(iVolY); SETVAR(iVolZ); SETVAR(iProjAngles); SETVAR(iProjU); SETVAR(iProjV); SETVAR(iRaysPerDetDim); SETVAR(iRaysPerVoxelDim); #undef SETVAR /* Initialize the MathWorks GPU API. */ mxInitGPU(); /* load confuguration of angles */ double * my_angles = mxGetPr(prhs[3]); int Nangles = (int)mxGetM(prhs[3]); SPar3DProjection* angle = new SPar3DProjection[Nangles]; #define SETVAR(name,i,j) do { angle[i].name = my_angles[i+j*Nangles]; } while (0); for (int i = 0; i < Nangles; i++) { SETVAR(fRayX, i, 0); SETVAR(fRayY, i, 1); SETVAR(fRayZ, i, 2); SETVAR(fDetSX, i, 3); SETVAR(fDetSY, i, 4); SETVAR(fDetSZ, i, 5); SETVAR(fDetUX, i, 6); SETVAR(fDetUY, i, 7); SETVAR(fDetUZ, i, 8); SETVAR(fDetVX, i, 9); SETVAR(fDetVY, i, 10); SETVAR(fDetVZ, i, 11); // mexPrintf("---------------------- \n"); } #undef SETVAR char * task = mxArrayToString(prhs[0]); //mexPrintf("--------- Task %s \n ", task); /* Load input data */ mxGPUArray const * m_data = mxGPUCreateFromMxArray(prhs[1]); if ((mxGPUGetClassID(m_data) != mxSINGLE_CLASS)) { mexErrMsgIdAndTxt(errId, errMsg); } float * p_data = (float *)mxGPUGetDataReadOnly(m_data); DeformField DF; if (nrhs == 8 || nrhs == 11 ) { /* load deformation field */ DF.use_deform = true; DF.use_linear_model = false; // assume contant deformation DF.X0 = mxGPUCreateFromMxArray(prhs[5]); DF.Y0 = mxGPUCreateFromMxArray(prhs[6]); DF.Z0 = mxGPUCreateFromMxArray(prhs[7]); if ((mxGPUGetClassID(DF.X0) != mxSINGLE_CLASS) | (mxGPUGetClassID(DF.Y0) != mxSINGLE_CLASS) | (mxGPUGetClassID(DF.Z0) != mxSINGLE_CLASS)) { mexPrintf("wrong input type: deformation fields has to be single\n"); mexErrMsgIdAndTxt(errId, errMsg); } if (nrhs == 11 ) { DF.use_linear_model = true; // assume linear deformation DF.X1 = mxGPUCreateFromMxArray(prhs[8]); DF.Y1 = mxGPUCreateFromMxArray(prhs[9]); DF.Z1 = mxGPUCreateFromMxArray(prhs[10]); if ((mxGPUGetClassID(DF.X1) != mxSINGLE_CLASS) | (mxGPUGetClassID(DF.Y1) != mxSINGLE_CLASS) | (mxGPUGetClassID(DF.Z1) != mxSINGLE_CLASS)) { mexPrintf("wrong input type: deformation fields has to be single\n"); mexErrMsgIdAndTxt(errId, errMsg); } } } else DF.use_deform = false; if (strcmp(task, "fp")==0) { //mexPrintf(" forward projection \n "); /* make volume array (no copying) */ cudaPitchedPtr volData; volData.ptr = p_data; volData.pitch = dims.iVolX * sizeof(float); volData.xsize = dims.iVolX; volData.ysize = dims.iVolY; mxGPUArray * m_projData; if(nrhs >= 5 && !mxIsEmpty(prhs[4]) ) { /**** copy of the array is the slow operation and also GPU memory is limited *****/ // m_projData = mxGPUCopyFromMxArray(prhs[4]); /* Use ugly trick to write directly to the provided GPU array ... => Now it is writting directly into the input field !!! DANGEROUS */ m_projData = const_cast(mxGPUCreateFromMxArray(prhs[4])); if ((mxGPUGetClassID(m_projData) != mxSINGLE_CLASS)) { mexPrintf("m_projData\n"); mexErrMsgIdAndTxt(errId, errMsg); } const mwSize * projSize = mxGPUGetDimensions(m_projData); if (dims.iProjU != projSize[0] || dims.iProjV != projSize[1] || dims.iProjAngles != projSize[2]) mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array"); //mexPrintf("Writting directly to the input array\n\n"); } else { /* allocate projection field */ int const Ndim = 3; mwSize projSize[3]; projSize[0] = (mwSize)dims.iProjU; projSize[1] = (mwSize)dims.iProjV; projSize[2] = (mwSize)dims.iProjAngles; m_projData = mxGPUCreateGPUArray(Ndim, projSize, mxSINGLE_CLASS, mxREAL, MX_GPU_INITIALIZE_VALUES); } /* make cudaPitchedPtr for projection field */ cudaPitchedPtr projData; projData.ptr = (float *)mxGPUGetData(m_projData); projData.pitch = dims.iProjU * sizeof(float); projData.xsize = dims.iProjU; projData.ysize = dims.iProjV; //mexPrintf("astraCUDA3d::Par3DFP \n ") ; astraCUDA3d::Par3DFP(volData, projData, dims, angle, 1.0f, DF); checkLastError("After Projector"); /* Wrap the result up as a MATLAB gpuArray for return. */ if (nlhs > 0) plhs[0] = mxGPUCreateMxArrayOnGPU(m_projData); mxGPUDestroyGPUArray(m_projData); mxGPUDestroyGPUArray(m_data); } else if (strcmp(task, "bp")==0) { //mexPrintf(" backward projection \n "); /* make projection field (no copying) */ cudaPitchedPtr projData; projData.ptr = p_data; projData.pitch = dims.iProjU * sizeof(float); projData.xsize = dims.iProjU; projData.ysize = dims.iProjAngles; mxGPUArray* m_volData; if(nrhs >= 5 && !mxIsEmpty(prhs[4]) ) { /**** copy of the array is the slow operation and also GPU memory is limited *****/ // m_volData = mxGPUCopyFromMxArray(prhs[4]); /* Use ugly trick to write directly to the provided GPU array ... => Now it is writting directly into the input field !!! DANGEROUS */ m_volData = const_cast(mxGPUCreateFromMxArray(prhs[4])); if ((mxGPUGetClassID(m_volData) != mxSINGLE_CLASS)) { mexPrintf("m_volData\n"); mexErrMsgIdAndTxt(errId, errMsg); } mwSize volSize[3]; const mwSize * volSize0 = mxGPUGetDimensions(m_volData); if (mxGPUGetNumberOfDimensions(m_volData)==3) { volSize[0]=volSize0[0]; volSize[1]=volSize0[1]; volSize[2]=volSize0[2]; } else { volSize[0]=volSize0[0]; volSize[1]=volSize0[1]; volSize[2]=1; } if (dims.iVolX != volSize[0] || dims.iVolY != volSize[1] || dims.iVolZ != volSize[2]) mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array"); } else { /* allocate volume data */ int const Ndim = 3; mwSize volSize[3]; volSize[0] = (mwSize)dims.iVolX; volSize[1] = (mwSize)dims.iVolY; volSize[2] = (mwSize)dims.iVolZ; m_volData = mxGPUCreateGPUArray(Ndim, volSize, mxSINGLE_CLASS, mxREAL, MX_GPU_INITIALIZE_VALUES); } /* make volume array pointer*/ cudaPitchedPtr volData; volData.ptr = (float *)mxGPUGetData(m_volData); volData.pitch = dims.iVolX * sizeof(float); volData.xsize = dims.iVolX; volData.ysize = dims.iVolY; astraCUDA3d::Par3DBP(volData, projData, dims, angle, 1.0f, DF); checkLastError("After Projector"); /* Wrap the result up as a MATLAB gpuArray for return. */ if (nlhs > 0) plhs[0] = mxGPUCreateMxArrayOnGPU(m_volData); mxGPUDestroyGPUArray(m_volData); mxGPUDestroyGPUArray(m_data); } else mexPrintf("No such option"); if (DF.use_deform) { //mexPrintf("Deleted DF"); mxGPUDestroyGPUArray(DF.X0); mxGPUDestroyGPUArray(DF.Y0); mxGPUDestroyGPUArray(DF.Z0); if (DF.use_linear_model) { mxGPUDestroyGPUArray(DF.X1); mxGPUDestroyGPUArray(DF.Y1); mxGPUDestroyGPUArray(DF.Z1); } } }