Apply consistent code formatting across the repo. Add clang-format and pre-commit hooks.

This commit is contained in:
Rob Armstrong
2025-03-27 10:30:07 -07:00
parent 2cd58fbc9a
commit ceab6e8bcc
782 changed files with 107230 additions and 106548 deletions

View File

@@ -31,16 +31,20 @@
* See supplied whitepaper for more explanations.
*/
#include <helper_functions.h> // helper functions for string parsing
#include <helper_cuda.h> // helper functions CUDA error checking and initialization
#include <helper_cuda.h> // helper functions CUDA error checking and initialization
#include <helper_functions.h> // helper functions for string parsing
////////////////////////////////////////////////////////////////////////////////
// Process an array of optN options on CPU
////////////////////////////////////////////////////////////////////////////////
extern "C" void BlackScholesCPU(float *h_CallResult, float *h_PutResult,
float *h_StockPrice, float *h_OptionStrike,
float *h_OptionYears, float Riskfree,
float Volatility, int optN);
extern "C" void BlackScholesCPU(float *h_CallResult,
float *h_PutResult,
float *h_StockPrice,
float *h_OptionStrike,
float *h_OptionYears,
float Riskfree,
float Volatility,
int optN);
////////////////////////////////////////////////////////////////////////////////
// Process an array of OptN options on GPU
@@ -51,193 +55,192 @@ extern "C" void BlackScholesCPU(float *h_CallResult, float *h_PutResult,
// Helper function, returning uniformly distributed
// random float in [low, high] range
////////////////////////////////////////////////////////////////////////////////
float RandFloat(float low, float high) {
float t = (float)rand() / (float)RAND_MAX;
return (1.0f - t) * low + t * high;
float RandFloat(float low, float high)
{
float t = (float)rand() / (float)RAND_MAX;
return (1.0f - t) * low + t * high;
}
////////////////////////////////////////////////////////////////////////////////
// Data configuration
////////////////////////////////////////////////////////////////////////////////
const int OPT_N = 4000000;
const int OPT_N = 4000000;
const int NUM_ITERATIONS = 512;
const int OPT_SZ = OPT_N * sizeof(float);
const float RISKFREE = 0.02f;
const int OPT_SZ = OPT_N * sizeof(float);
const float RISKFREE = 0.02f;
const float VOLATILITY = 0.30f;
#define DIV_UP(a, b) (((a) + (b)-1) / (b))
#define DIV_UP(a, b) (((a) + (b) - 1) / (b))
////////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
// Start logs
printf("[%s] - Starting...\n", argv[0]);
int main(int argc, char **argv)
{
// Start logs
printf("[%s] - Starting...\n", argv[0]);
//'h_' prefix - CPU (host) memory space
float
// Results calculated by CPU for reference
*h_CallResultCPU,
*h_PutResultCPU,
// CPU copy of GPU results
*h_CallResultGPU, *h_PutResultGPU,
// CPU instance of input data
*h_StockPrice, *h_OptionStrike, *h_OptionYears;
//'h_' prefix - CPU (host) memory space
float
// Results calculated by CPU for reference
*h_CallResultCPU,
*h_PutResultCPU,
// CPU copy of GPU results
*h_CallResultGPU, *h_PutResultGPU,
// CPU instance of input data
*h_StockPrice, *h_OptionStrike, *h_OptionYears;
//'d_' prefix - GPU (device) memory space
float
// Results calculated by GPU
*d_CallResult,
*d_PutResult,
// GPU instance of input data
*d_StockPrice, *d_OptionStrike, *d_OptionYears;
//'d_' prefix - GPU (device) memory space
float
// Results calculated by GPU
*d_CallResult,
*d_PutResult,
// GPU instance of input data
*d_StockPrice, *d_OptionStrike, *d_OptionYears;
double delta, ref, sum_delta, sum_ref, max_delta, L1norm, gpuTime;
double delta, ref, sum_delta, sum_ref, max_delta, L1norm, gpuTime;
StopWatchInterface *hTimer = NULL;
int i;
StopWatchInterface *hTimer = NULL;
int i;
findCudaDevice(argc, (const char **)argv);
findCudaDevice(argc, (const char **)argv);
sdkCreateTimer(&hTimer);
sdkCreateTimer(&hTimer);
printf("Initializing data...\n");
printf("...allocating CPU memory for options.\n");
h_CallResultCPU = (float *)malloc(OPT_SZ);
h_PutResultCPU = (float *)malloc(OPT_SZ);
h_CallResultGPU = (float *)malloc(OPT_SZ);
h_PutResultGPU = (float *)malloc(OPT_SZ);
h_StockPrice = (float *)malloc(OPT_SZ);
h_OptionStrike = (float *)malloc(OPT_SZ);
h_OptionYears = (float *)malloc(OPT_SZ);
printf("Initializing data...\n");
printf("...allocating CPU memory for options.\n");
h_CallResultCPU = (float *)malloc(OPT_SZ);
h_PutResultCPU = (float *)malloc(OPT_SZ);
h_CallResultGPU = (float *)malloc(OPT_SZ);
h_PutResultGPU = (float *)malloc(OPT_SZ);
h_StockPrice = (float *)malloc(OPT_SZ);
h_OptionStrike = (float *)malloc(OPT_SZ);
h_OptionYears = (float *)malloc(OPT_SZ);
printf("...allocating GPU memory for options.\n");
checkCudaErrors(cudaMalloc((void **)&d_CallResult, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_PutResult, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_StockPrice, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_OptionStrike, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_OptionYears, OPT_SZ));
printf("...allocating GPU memory for options.\n");
checkCudaErrors(cudaMalloc((void **)&d_CallResult, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_PutResult, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_StockPrice, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_OptionStrike, OPT_SZ));
checkCudaErrors(cudaMalloc((void **)&d_OptionYears, OPT_SZ));
printf("...generating input data in CPU mem.\n");
srand(5347);
printf("...generating input data in CPU mem.\n");
srand(5347);
// Generate options set
for (i = 0; i < OPT_N; i++) {
h_CallResultCPU[i] = 0.0f;
h_PutResultCPU[i] = -1.0f;
h_StockPrice[i] = RandFloat(5.0f, 30.0f);
h_OptionStrike[i] = RandFloat(1.0f, 100.0f);
h_OptionYears[i] = RandFloat(0.25f, 10.0f);
}
printf("...copying input data to GPU mem.\n");
// Copy options data to GPU memory for further processing
checkCudaErrors(
cudaMemcpy(d_StockPrice, h_StockPrice, OPT_SZ, cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_OptionStrike, h_OptionStrike, OPT_SZ,
cudaMemcpyHostToDevice));
checkCudaErrors(
cudaMemcpy(d_OptionYears, h_OptionYears, OPT_SZ, cudaMemcpyHostToDevice));
printf("Data init done.\n\n");
printf("Executing Black-Scholes GPU kernel (%i iterations)...\n",
NUM_ITERATIONS);
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
for (i = 0; i < NUM_ITERATIONS; i++) {
BlackScholesGPU<<<DIV_UP((OPT_N / 2), 128), 128 /*480, 128*/>>>(
(float2 *)d_CallResult, (float2 *)d_PutResult, (float2 *)d_StockPrice,
(float2 *)d_OptionStrike, (float2 *)d_OptionYears, RISKFREE, VOLATILITY,
OPT_N);
getLastCudaError("BlackScholesGPU() execution failed\n");
}
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;
// Both call and put is calculated
printf("Options count : %i \n", 2 * OPT_N);
printf("BlackScholesGPU() time : %f msec\n", gpuTime);
printf("Effective memory bandwidth: %f GB/s\n",
((double)(5 * OPT_N * sizeof(float)) * 1E-9) / (gpuTime * 1E-3));
printf("Gigaoptions per second : %f \n\n",
((double)(2 * OPT_N) * 1E-9) / (gpuTime * 1E-3));
printf(
"BlackScholes, Throughput = %.4f GOptions/s, Time = %.5f s, Size = %u "
"options, NumDevsUsed = %u, Workgroup = %u\n",
(((double)(2.0 * OPT_N) * 1.0E-9) / (gpuTime * 1.0E-3)), gpuTime * 1e-3,
(2 * OPT_N), 1, 128);
printf("\nReading back GPU results...\n");
// Read back GPU results to compare them to CPU results
checkCudaErrors(cudaMemcpy(h_CallResultGPU, d_CallResult, OPT_SZ,
cudaMemcpyDeviceToHost));
checkCudaErrors(
cudaMemcpy(h_PutResultGPU, d_PutResult, OPT_SZ, cudaMemcpyDeviceToHost));
printf("Checking the results...\n");
printf("...running CPU calculations.\n\n");
// Calculate options values on CPU
BlackScholesCPU(h_CallResultCPU, h_PutResultCPU, h_StockPrice, h_OptionStrike,
h_OptionYears, RISKFREE, VOLATILITY, OPT_N);
printf("Comparing the results...\n");
// Calculate max absolute difference and L1 distance
// between CPU and GPU results
sum_delta = 0;
sum_ref = 0;
max_delta = 0;
for (i = 0; i < OPT_N; i++) {
ref = h_CallResultCPU[i];
delta = fabs(h_CallResultCPU[i] - h_CallResultGPU[i]);
if (delta > max_delta) {
max_delta = delta;
// Generate options set
for (i = 0; i < OPT_N; i++) {
h_CallResultCPU[i] = 0.0f;
h_PutResultCPU[i] = -1.0f;
h_StockPrice[i] = RandFloat(5.0f, 30.0f);
h_OptionStrike[i] = RandFloat(1.0f, 100.0f);
h_OptionYears[i] = RandFloat(0.25f, 10.0f);
}
sum_delta += delta;
sum_ref += fabs(ref);
}
printf("...copying input data to GPU mem.\n");
// Copy options data to GPU memory for further processing
checkCudaErrors(cudaMemcpy(d_StockPrice, h_StockPrice, OPT_SZ, cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_OptionStrike, h_OptionStrike, OPT_SZ, cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_OptionYears, h_OptionYears, OPT_SZ, cudaMemcpyHostToDevice));
printf("Data init done.\n\n");
L1norm = sum_delta / sum_ref;
printf("L1 norm: %E\n", L1norm);
printf("Max absolute error: %E\n\n", max_delta);
printf("Executing Black-Scholes GPU kernel (%i iterations)...\n", NUM_ITERATIONS);
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
printf("Shutting down...\n");
printf("...releasing GPU memory.\n");
checkCudaErrors(cudaFree(d_OptionYears));
checkCudaErrors(cudaFree(d_OptionStrike));
checkCudaErrors(cudaFree(d_StockPrice));
checkCudaErrors(cudaFree(d_PutResult));
checkCudaErrors(cudaFree(d_CallResult));
for (i = 0; i < NUM_ITERATIONS; i++) {
BlackScholesGPU<<<DIV_UP((OPT_N / 2), 128), 128 /*480, 128*/>>>((float2 *)d_CallResult,
(float2 *)d_PutResult,
(float2 *)d_StockPrice,
(float2 *)d_OptionStrike,
(float2 *)d_OptionYears,
RISKFREE,
VOLATILITY,
OPT_N);
getLastCudaError("BlackScholesGPU() execution failed\n");
}
printf("...releasing CPU memory.\n");
free(h_OptionYears);
free(h_OptionStrike);
free(h_StockPrice);
free(h_PutResultGPU);
free(h_CallResultGPU);
free(h_PutResultCPU);
free(h_CallResultCPU);
sdkDeleteTimer(&hTimer);
printf("Shutdown done.\n");
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;
printf("\n[BlackScholes] - Test Summary\n");
// Both call and put is calculated
printf("Options count : %i \n", 2 * OPT_N);
printf("BlackScholesGPU() time : %f msec\n", gpuTime);
printf("Effective memory bandwidth: %f GB/s\n", ((double)(5 * OPT_N * sizeof(float)) * 1E-9) / (gpuTime * 1E-3));
printf("Gigaoptions per second : %f \n\n", ((double)(2 * OPT_N) * 1E-9) / (gpuTime * 1E-3));
if (L1norm > 1e-6) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
printf("BlackScholes, Throughput = %.4f GOptions/s, Time = %.5f s, Size = %u "
"options, NumDevsUsed = %u, Workgroup = %u\n",
(((double)(2.0 * OPT_N) * 1.0E-9) / (gpuTime * 1.0E-3)),
gpuTime * 1e-3,
(2 * OPT_N),
1,
128);
printf(
"\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
printf("Test passed\n");
exit(EXIT_SUCCESS);
printf("\nReading back GPU results...\n");
// Read back GPU results to compare them to CPU results
checkCudaErrors(cudaMemcpy(h_CallResultGPU, d_CallResult, OPT_SZ, cudaMemcpyDeviceToHost));
checkCudaErrors(cudaMemcpy(h_PutResultGPU, d_PutResult, OPT_SZ, cudaMemcpyDeviceToHost));
printf("Checking the results...\n");
printf("...running CPU calculations.\n\n");
// Calculate options values on CPU
BlackScholesCPU(
h_CallResultCPU, h_PutResultCPU, h_StockPrice, h_OptionStrike, h_OptionYears, RISKFREE, VOLATILITY, OPT_N);
printf("Comparing the results...\n");
// Calculate max absolute difference and L1 distance
// between CPU and GPU results
sum_delta = 0;
sum_ref = 0;
max_delta = 0;
for (i = 0; i < OPT_N; i++) {
ref = h_CallResultCPU[i];
delta = fabs(h_CallResultCPU[i] - h_CallResultGPU[i]);
if (delta > max_delta) {
max_delta = delta;
}
sum_delta += delta;
sum_ref += fabs(ref);
}
L1norm = sum_delta / sum_ref;
printf("L1 norm: %E\n", L1norm);
printf("Max absolute error: %E\n\n", max_delta);
printf("Shutting down...\n");
printf("...releasing GPU memory.\n");
checkCudaErrors(cudaFree(d_OptionYears));
checkCudaErrors(cudaFree(d_OptionStrike));
checkCudaErrors(cudaFree(d_StockPrice));
checkCudaErrors(cudaFree(d_PutResult));
checkCudaErrors(cudaFree(d_CallResult));
printf("...releasing CPU memory.\n");
free(h_OptionYears);
free(h_OptionStrike);
free(h_StockPrice);
free(h_PutResultGPU);
free(h_CallResultGPU);
free(h_PutResultCPU);
free(h_CallResultCPU);
sdkDeleteTimer(&hTimer);
printf("Shutdown done.\n");
printf("\n[BlackScholes] - Test Summary\n");
if (L1norm > 1e-6) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
printf("\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
printf("Test passed\n");
exit(EXIT_SUCCESS);
}

View File

@@ -30,57 +30,69 @@
////////////////////////////////////////////////////////////////////////////////
// Polynomial approximation of cumulative normal distribution function
////////////////////////////////////////////////////////////////////////////////
static double CND(double d) {
const double A1 = 0.31938153;
const double A2 = -0.356563782;
const double A3 = 1.781477937;
const double A4 = -1.821255978;
const double A5 = 1.330274429;
const double RSQRT2PI = 0.39894228040143267793994605993438;
static double CND(double d)
{
const double A1 = 0.31938153;
const double A2 = -0.356563782;
const double A3 = 1.781477937;
const double A4 = -1.821255978;
const double A5 = 1.330274429;
const double RSQRT2PI = 0.39894228040143267793994605993438;
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double cnd = RSQRT2PI * exp(-0.5 * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
double cnd = RSQRT2PI * exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (d > 0) cnd = 1.0 - cnd;
if (d > 0)
cnd = 1.0 - cnd;
return cnd;
return cnd;
}
////////////////////////////////////////////////////////////////////////////////
// Black-Scholes formula for both call and put
////////////////////////////////////////////////////////////////////////////////
static void BlackScholesBodyCPU(float &callResult, float &putResult,
float Sf, // Stock price
float Xf, // Option strike
float Tf, // Option years
float Rf, // Riskless rate
float Vf // Volatility rate
) {
double S = Sf, X = Xf, T = Tf, R = Rf, V = Vf;
static void BlackScholesBodyCPU(float &callResult,
float &putResult,
float Sf, // Stock price
float Xf, // Option strike
float Tf, // Option years
float Rf, // Riskless rate
float Vf // Volatility rate
)
{
double S = Sf, X = Xf, T = Tf, R = Rf, V = Vf;
double sqrtT = sqrt(T);
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
double sqrtT = sqrt(T);
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
// Calculate Call and Put simultaneously
double expRT = exp(-R * T);
callResult = (float)(S * CNDD1 - X * expRT * CNDD2);
putResult = (float)(X * expRT * (1.0 - CNDD2) - S * (1.0 - CNDD1));
// Calculate Call and Put simultaneously
double expRT = exp(-R * T);
callResult = (float)(S * CNDD1 - X * expRT * CNDD2);
putResult = (float)(X * expRT * (1.0 - CNDD2) - S * (1.0 - CNDD1));
}
////////////////////////////////////////////////////////////////////////////////
// Process an array of optN options
////////////////////////////////////////////////////////////////////////////////
extern "C" void BlackScholesCPU(float *h_CallResult, float *h_PutResult,
float *h_StockPrice, float *h_OptionStrike,
float *h_OptionYears, float Riskfree,
float Volatility, int optN) {
for (int opt = 0; opt < optN; opt++)
BlackScholesBodyCPU(h_CallResult[opt], h_PutResult[opt], h_StockPrice[opt],
h_OptionStrike[opt], h_OptionYears[opt], Riskfree,
Volatility);
extern "C" void BlackScholesCPU(float *h_CallResult,
float *h_PutResult,
float *h_StockPrice,
float *h_OptionStrike,
float *h_OptionYears,
float Riskfree,
float Volatility,
int optN)
{
for (int opt = 0; opt < optN; opt++)
BlackScholesBodyCPU(h_CallResult[opt],
h_PutResult[opt],
h_StockPrice[opt],
h_OptionStrike[opt],
h_OptionYears[opt],
Riskfree,
Volatility);
}

View File

@@ -28,79 +28,92 @@
////////////////////////////////////////////////////////////////////////////////
// Polynomial approximation of cumulative normal distribution function
////////////////////////////////////////////////////////////////////////////////
__device__ inline float cndGPU(float d) {
const float A1 = 0.31938153f;
const float A2 = -0.356563782f;
const float A3 = 1.781477937f;
const float A4 = -1.821255978f;
const float A5 = 1.330274429f;
const float RSQRT2PI = 0.39894228040143267793994605993438f;
__device__ inline float cndGPU(float d)
{
const float A1 = 0.31938153f;
const float A2 = -0.356563782f;
const float A3 = 1.781477937f;
const float A4 = -1.821255978f;
const float A5 = 1.330274429f;
const float RSQRT2PI = 0.39894228040143267793994605993438f;
float K = __fdividef(1.0f, (1.0f + 0.2316419f * fabsf(d)));
float K = __fdividef(1.0f, (1.0f + 0.2316419f * fabsf(d)));
float cnd = RSQRT2PI * __expf(-0.5f * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
float cnd = RSQRT2PI * __expf(-0.5f * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (d > 0) cnd = 1.0f - cnd;
if (d > 0)
cnd = 1.0f - cnd;
return cnd;
return cnd;
}
////////////////////////////////////////////////////////////////////////////////
// Black-Scholes formula for both call and put
////////////////////////////////////////////////////////////////////////////////
__device__ inline void BlackScholesBodyGPU(float &CallResult, float &PutResult,
float S, // Stock price
float X, // Option strike
float T, // Option years
float R, // Riskless rate
float V // Volatility rate
) {
float sqrtT, expRT;
float d1, d2, CNDD1, CNDD2;
__device__ inline void BlackScholesBodyGPU(float &CallResult,
float &PutResult,
float S, // Stock price
float X, // Option strike
float T, // Option years
float R, // Riskless rate
float V // Volatility rate
)
{
float sqrtT, expRT;
float d1, d2, CNDD1, CNDD2;
sqrtT = __fdividef(1.0F, rsqrtf(T));
d1 = __fdividef(__logf(S / X) + (R + 0.5f * V * V) * T, V * sqrtT);
d2 = d1 - V * sqrtT;
sqrtT = __fdividef(1.0F, rsqrtf(T));
d1 = __fdividef(__logf(S / X) + (R + 0.5f * V * V) * T, V * sqrtT);
d2 = d1 - V * sqrtT;
CNDD1 = cndGPU(d1);
CNDD2 = cndGPU(d2);
CNDD1 = cndGPU(d1);
CNDD2 = cndGPU(d2);
// Calculate Call and Put simultaneously
expRT = __expf(-R * T);
CallResult = S * CNDD1 - X * expRT * CNDD2;
PutResult = X * expRT * (1.0f - CNDD2) - S * (1.0f - CNDD1);
// Calculate Call and Put simultaneously
expRT = __expf(-R * T);
CallResult = S * CNDD1 - X * expRT * CNDD2;
PutResult = X * expRT * (1.0f - CNDD2) - S * (1.0f - CNDD1);
}
////////////////////////////////////////////////////////////////////////////////
// Process an array of optN options on GPU
////////////////////////////////////////////////////////////////////////////////
__launch_bounds__(128) __global__
void BlackScholesGPU(float2 *__restrict d_CallResult,
float2 *__restrict d_PutResult,
float2 *__restrict d_StockPrice,
float2 *__restrict d_OptionStrike,
float2 *__restrict d_OptionYears, float Riskfree,
float Volatility, int optN) {
////Thread index
// const int tid = blockDim.x * blockIdx.x + threadIdx.x;
////Total number of threads in execution grid
// const int THREAD_N = blockDim.x * gridDim.x;
__launch_bounds__(128) __global__ void BlackScholesGPU(float2 *__restrict d_CallResult,
float2 *__restrict d_PutResult,
float2 *__restrict d_StockPrice,
float2 *__restrict d_OptionStrike,
float2 *__restrict d_OptionYears,
float Riskfree,
float Volatility,
int optN)
{
////Thread index
// const int tid = blockDim.x * blockIdx.x + threadIdx.x;
////Total number of threads in execution grid
// const int THREAD_N = blockDim.x * gridDim.x;
const int opt = blockDim.x * blockIdx.x + threadIdx.x;
const int opt = blockDim.x * blockIdx.x + threadIdx.x;
// Calculating 2 options per thread to increase ILP (instruction level
// parallelism)
if (opt < (optN / 2)) {
float callResult1, callResult2;
float putResult1, putResult2;
BlackScholesBodyGPU(callResult1, putResult1, d_StockPrice[opt].x,
d_OptionStrike[opt].x, d_OptionYears[opt].x, Riskfree,
Volatility);
BlackScholesBodyGPU(callResult2, putResult2, d_StockPrice[opt].y,
d_OptionStrike[opt].y, d_OptionYears[opt].y, Riskfree,
Volatility);
d_CallResult[opt] = make_float2(callResult1, callResult2);
d_PutResult[opt] = make_float2(putResult1, putResult2);
}
// Calculating 2 options per thread to increase ILP (instruction level
// parallelism)
if (opt < (optN / 2)) {
float callResult1, callResult2;
float putResult1, putResult2;
BlackScholesBodyGPU(callResult1,
putResult1,
d_StockPrice[opt].x,
d_OptionStrike[opt].x,
d_OptionYears[opt].x,
Riskfree,
Volatility);
BlackScholesBodyGPU(callResult2,
putResult2,
d_StockPrice[opt].y,
d_OptionStrike[opt].y,
d_OptionYears[opt].y,
Riskfree,
Volatility);
d_CallResult[opt] = make_float2(callResult1, callResult2);
d_PutResult[opt] = make_float2(putResult1, putResult2);
}
}

View File

@@ -32,18 +32,21 @@
*/
#include <cuda_runtime.h>
#include <helper_functions.h> // helper functions for string parsing
#include <nvrtc_helper.h>
#include <helper_functions.h> // helper functions for string parsing
////////////////////////////////////////////////////////////////////////////////
// Process an array of optN options on CPU
////////////////////////////////////////////////////////////////////////////////
extern "C" void BlackScholesCPU(float *h_CallResult, float *h_PutResult,
float *h_StockPrice, float *h_OptionStrike,
float *h_OptionYears, float Riskfree,
float Volatility, int optN);
extern "C" void BlackScholesCPU(float *h_CallResult,
float *h_PutResult,
float *h_StockPrice,
float *h_OptionStrike,
float *h_OptionYears,
float Riskfree,
float Volatility,
int optN);
////////////////////////////////////////////////////////////////////////////////
// Process an array of OptN options on GPU
@@ -54,216 +57,225 @@ extern "C" void BlackScholesCPU(float *h_CallResult, float *h_PutResult,
// random float in [low, high] range
////////////////////////////////////////////////////////////////////////////////
float RandFloat(float low, float high) {
float t = (float)rand() / (float)RAND_MAX;
return (1.0f - t) * low + t * high;
float RandFloat(float low, float high)
{
float t = (float)rand() / (float)RAND_MAX;
return (1.0f - t) * low + t * high;
}
////////////////////////////////////////////////////////////////////////////////
// Data configuration
////////////////////////////////////////////////////////////////////////////////
const int OPT_N = 4000000;
const int NUM_ITERATIONS = 512;
const int OPT_SZ = OPT_N * sizeof(float);
const float RISKFREE = 0.02f;
const float VOLATILITY = 0.30f;
const int OPT_N = 4000000;
const int NUM_ITERATIONS = 512;
const int OPT_SZ = OPT_N * sizeof(float);
const float RISKFREE = 0.02f;
const float VOLATILITY = 0.30f;
#define DIV_UP(a, b) (((a) + (b)-1) / (b))
#define DIV_UP(a, b) (((a) + (b) - 1) / (b))
////////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
// Start logs
printf("[%s] - Starting...\n", argv[0]);
int main(int argc, char **argv)
{
// Start logs
printf("[%s] - Starting...\n", argv[0]);
//'h_' prefix - CPU (host) memory space
float
// Results calculated by CPU for reference
*h_CallResultCPU,
*h_PutResultCPU,
// CPU copy of GPU results
*h_CallResultGPU, *h_PutResultGPU,
// CPU instance of input data
*h_StockPrice, *h_OptionStrike, *h_OptionYears;
//'h_' prefix - CPU (host) memory space
float
// Results calculated by CPU for reference
*h_CallResultCPU,
*h_PutResultCPU,
// CPU copy of GPU results
*h_CallResultGPU, *h_PutResultGPU,
// CPU instance of input data
*h_StockPrice, *h_OptionStrike, *h_OptionYears;
//'d_' prefix - GPU (device) memory space
CUdeviceptr
// Results calculated by GPU
d_CallResult,
d_PutResult,
//'d_' prefix - GPU (device) memory space
CUdeviceptr
// Results calculated by GPU
d_CallResult,
d_PutResult,
// GPU instance of input data
d_StockPrice, d_OptionStrike, d_OptionYears;
// GPU instance of input data
d_StockPrice, d_OptionStrike, d_OptionYears;
double delta, ref, sum_delta, sum_ref, max_delta, L1norm, gpuTime;
double delta, ref, sum_delta, sum_ref, max_delta, L1norm, gpuTime;
StopWatchInterface *hTimer = NULL;
int i;
StopWatchInterface *hTimer = NULL;
int i;
sdkCreateTimer(&hTimer);
sdkCreateTimer(&hTimer);
printf("Initializing data...\n");
printf("...allocating CPU memory for options.\n");
printf("Initializing data...\n");
printf("...allocating CPU memory for options.\n");
h_CallResultCPU = (float *)malloc(OPT_SZ);
h_PutResultCPU = (float *)malloc(OPT_SZ);
h_CallResultGPU = (float *)malloc(OPT_SZ);
h_PutResultGPU = (float *)malloc(OPT_SZ);
h_StockPrice = (float *)malloc(OPT_SZ);
h_OptionStrike = (float *)malloc(OPT_SZ);
h_OptionYears = (float *)malloc(OPT_SZ);
h_CallResultCPU = (float *)malloc(OPT_SZ);
h_PutResultCPU = (float *)malloc(OPT_SZ);
h_CallResultGPU = (float *)malloc(OPT_SZ);
h_PutResultGPU = (float *)malloc(OPT_SZ);
h_StockPrice = (float *)malloc(OPT_SZ);
h_OptionStrike = (float *)malloc(OPT_SZ);
h_OptionYears = (float *)malloc(OPT_SZ);
char *cubin, *kernel_file;
size_t cubinSize;
kernel_file = sdkFindFilePath("BlackScholes_kernel.cuh", argv[0]);
char *cubin, *kernel_file;
size_t cubinSize;
kernel_file = sdkFindFilePath("BlackScholes_kernel.cuh", argv[0]);
// Compile the kernel BlackScholes_kernel.
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
CUmodule module = loadCUBIN(cubin, argc, argv);
// Compile the kernel BlackScholes_kernel.
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
CUmodule module = loadCUBIN(cubin, argc, argv);
CUfunction kernel_addr;
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "BlackScholesGPU"));
CUfunction kernel_addr;
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "BlackScholesGPU"));
printf("...allocating GPU memory for options.\n");
checkCudaErrors(cuMemAlloc(&d_CallResult, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_PutResult, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_StockPrice, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_OptionStrike, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_OptionYears, OPT_SZ));
printf("...allocating GPU memory for options.\n");
checkCudaErrors(cuMemAlloc(&d_CallResult, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_PutResult, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_StockPrice, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_OptionStrike, OPT_SZ));
checkCudaErrors(cuMemAlloc(&d_OptionYears, OPT_SZ));
printf("...generating input data in CPU mem.\n");
srand(5347);
printf("...generating input data in CPU mem.\n");
srand(5347);
// Generate options set
for (i = 0; i < OPT_N; i++) {
h_CallResultCPU[i] = 0.0f;
h_PutResultCPU[i] = -1.0f;
h_StockPrice[i] = RandFloat(5.0f, 30.0f);
h_OptionStrike[i] = RandFloat(1.0f, 100.0f);
h_OptionYears[i] = RandFloat(0.25f, 10.0f);
}
printf("...copying input data to GPU mem.\n");
// Copy options data to GPU memory for further processing
checkCudaErrors(cuMemcpyHtoD(d_StockPrice, h_StockPrice, OPT_SZ));
checkCudaErrors(cuMemcpyHtoD(d_OptionStrike, h_OptionStrike, OPT_SZ));
checkCudaErrors(cuMemcpyHtoD(d_OptionYears, h_OptionYears, OPT_SZ));
printf("Data init done.\n\n");
printf("Executing Black-Scholes GPU kernel (%i iterations)...\n",
NUM_ITERATIONS);
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
dim3 cudaBlockSize(128, 1, 1);
dim3 cudaGridSize(DIV_UP(OPT_N / 2, 128), 1, 1);
float risk = RISKFREE;
float volatility = VOLATILITY;
int optval = OPT_N;
void *arr[] = {(void *)&d_CallResult, (void *)&d_PutResult,
(void *)&d_StockPrice, (void *)&d_OptionStrike,
(void *)&d_OptionYears, (void *)&risk,
(void *)&volatility, (void *)&optval};
for (i = 0; i < NUM_ITERATIONS; i++) {
checkCudaErrors(cuLaunchKernel(kernel_addr, cudaGridSize.x, cudaGridSize.y,
cudaGridSize.z, /* grid dim */
cudaBlockSize.x, cudaBlockSize.y,
cudaBlockSize.z, /* block dim */
0, 0, /* shared mem, stream */
&arr[0], /* arguments */
0));
}
checkCudaErrors(cuCtxSynchronize());
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;
// Both call and put is calculated
printf("Options count : %i \n", 2 * OPT_N);
printf("BlackScholesGPU() time : %f msec\n", gpuTime);
printf("Effective memory bandwidth: %f GB/s\n",
((double)(5 * OPT_N * sizeof(float)) * 1E-9) / (gpuTime * 1E-3));
printf("Gigaoptions per second : %f \n\n",
((double)(2 * OPT_N) * 1E-9) / (gpuTime * 1E-3));
printf(
"BlackScholes, Throughput = %.4f GOptions/s, Time = %.5f s, Size = %u "
"options, NumDevsUsed = %u, Workgroup = %u\n",
(((double)(2.0 * OPT_N) * 1.0E-9) / (gpuTime * 1.0E-3)), gpuTime * 1e-3,
(2 * OPT_N), 1, 128);
printf("\nReading back GPU results...\n");
// Read back GPU results to compare them to CPU results
checkCudaErrors(cuMemcpyDtoH(h_CallResultGPU, d_CallResult, OPT_SZ));
checkCudaErrors(cuMemcpyDtoH(h_PutResultGPU, d_PutResult, OPT_SZ));
printf("Checking the results...\n");
printf("...running CPU calculations.\n\n");
// Calculate options values on CPU
BlackScholesCPU(h_CallResultCPU, h_PutResultCPU, h_StockPrice, h_OptionStrike,
h_OptionYears, RISKFREE, VOLATILITY, OPT_N);
printf("Comparing the results...\n");
// Calculate max absolute difference and L1 distance
// between CPU and GPU results
sum_delta = 0;
sum_ref = 0;
max_delta = 0;
for (i = 0; i < OPT_N; i++) {
ref = h_CallResultCPU[i];
delta = fabs(h_CallResultCPU[i] - h_CallResultGPU[i]);
if (delta > max_delta) {
max_delta = delta;
// Generate options set
for (i = 0; i < OPT_N; i++) {
h_CallResultCPU[i] = 0.0f;
h_PutResultCPU[i] = -1.0f;
h_StockPrice[i] = RandFloat(5.0f, 30.0f);
h_OptionStrike[i] = RandFloat(1.0f, 100.0f);
h_OptionYears[i] = RandFloat(0.25f, 10.0f);
}
sum_delta += delta;
sum_ref += fabs(ref);
}
printf("...copying input data to GPU mem.\n");
// Copy options data to GPU memory for further processing
checkCudaErrors(cuMemcpyHtoD(d_StockPrice, h_StockPrice, OPT_SZ));
checkCudaErrors(cuMemcpyHtoD(d_OptionStrike, h_OptionStrike, OPT_SZ));
checkCudaErrors(cuMemcpyHtoD(d_OptionYears, h_OptionYears, OPT_SZ));
L1norm = sum_delta / sum_ref;
printf("L1 norm: %E\n", L1norm);
printf("Max absolute error: %E\n\n", max_delta);
printf("Data init done.\n\n");
printf("Executing Black-Scholes GPU kernel (%i iterations)...\n", NUM_ITERATIONS);
printf("Shutting down...\n");
printf("...releasing GPU memory.\n");
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
checkCudaErrors(cuMemFree(d_OptionYears));
checkCudaErrors(cuMemFree(d_OptionStrike));
checkCudaErrors(cuMemFree(d_StockPrice));
checkCudaErrors(cuMemFree(d_PutResult));
checkCudaErrors(cuMemFree(d_CallResult));
dim3 cudaBlockSize(128, 1, 1);
dim3 cudaGridSize(DIV_UP(OPT_N / 2, 128), 1, 1);
printf("...releasing CPU memory.\n");
float risk = RISKFREE;
float volatility = VOLATILITY;
int optval = OPT_N;
free(h_OptionYears);
free(h_OptionStrike);
free(h_StockPrice);
free(h_PutResultGPU);
free(h_CallResultGPU);
free(h_PutResultCPU);
free(h_CallResultCPU);
void *arr[] = {(void *)&d_CallResult,
(void *)&d_PutResult,
(void *)&d_StockPrice,
(void *)&d_OptionStrike,
(void *)&d_OptionYears,
(void *)&risk,
(void *)&volatility,
(void *)&optval};
sdkDeleteTimer(&hTimer);
printf("Shutdown done.\n");
for (i = 0; i < NUM_ITERATIONS; i++) {
checkCudaErrors(cuLaunchKernel(kernel_addr,
cudaGridSize.x,
cudaGridSize.y,
cudaGridSize.z, /* grid dim */
cudaBlockSize.x,
cudaBlockSize.y,
cudaBlockSize.z, /* block dim */
0,
0, /* shared mem, stream */
&arr[0], /* arguments */
0));
}
printf("\n[%s] - Test Summary\n", argv[0]);
checkCudaErrors(cuCtxSynchronize());
if (L1norm > 1e-6) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;
printf("Test passed\n");
exit(EXIT_SUCCESS);
// Both call and put is calculated
printf("Options count : %i \n", 2 * OPT_N);
printf("BlackScholesGPU() time : %f msec\n", gpuTime);
printf("Effective memory bandwidth: %f GB/s\n", ((double)(5 * OPT_N * sizeof(float)) * 1E-9) / (gpuTime * 1E-3));
printf("Gigaoptions per second : %f \n\n", ((double)(2 * OPT_N) * 1E-9) / (gpuTime * 1E-3));
printf("BlackScholes, Throughput = %.4f GOptions/s, Time = %.5f s, Size = %u "
"options, NumDevsUsed = %u, Workgroup = %u\n",
(((double)(2.0 * OPT_N) * 1.0E-9) / (gpuTime * 1.0E-3)),
gpuTime * 1e-3,
(2 * OPT_N),
1,
128);
printf("\nReading back GPU results...\n");
// Read back GPU results to compare them to CPU results
checkCudaErrors(cuMemcpyDtoH(h_CallResultGPU, d_CallResult, OPT_SZ));
checkCudaErrors(cuMemcpyDtoH(h_PutResultGPU, d_PutResult, OPT_SZ));
printf("Checking the results...\n");
printf("...running CPU calculations.\n\n");
// Calculate options values on CPU
BlackScholesCPU(
h_CallResultCPU, h_PutResultCPU, h_StockPrice, h_OptionStrike, h_OptionYears, RISKFREE, VOLATILITY, OPT_N);
printf("Comparing the results...\n");
// Calculate max absolute difference and L1 distance
// between CPU and GPU results
sum_delta = 0;
sum_ref = 0;
max_delta = 0;
for (i = 0; i < OPT_N; i++) {
ref = h_CallResultCPU[i];
delta = fabs(h_CallResultCPU[i] - h_CallResultGPU[i]);
if (delta > max_delta) {
max_delta = delta;
}
sum_delta += delta;
sum_ref += fabs(ref);
}
L1norm = sum_delta / sum_ref;
printf("L1 norm: %E\n", L1norm);
printf("Max absolute error: %E\n\n", max_delta);
printf("Shutting down...\n");
printf("...releasing GPU memory.\n");
checkCudaErrors(cuMemFree(d_OptionYears));
checkCudaErrors(cuMemFree(d_OptionStrike));
checkCudaErrors(cuMemFree(d_StockPrice));
checkCudaErrors(cuMemFree(d_PutResult));
checkCudaErrors(cuMemFree(d_CallResult));
printf("...releasing CPU memory.\n");
free(h_OptionYears);
free(h_OptionStrike);
free(h_StockPrice);
free(h_PutResultGPU);
free(h_CallResultGPU);
free(h_PutResultCPU);
free(h_CallResultCPU);
sdkDeleteTimer(&hTimer);
printf("Shutdown done.\n");
printf("\n[%s] - Test Summary\n", argv[0]);
if (L1norm > 1e-6) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
printf("Test passed\n");
exit(EXIT_SUCCESS);
}

View File

@@ -31,58 +31,70 @@
// Polynomial approximation of cumulative normal distribution function
///////////////////////////////////////////////////////////////////////////////
static double CND(double d) {
const double A1 = 0.31938153;
const double A2 = -0.356563782;
const double A3 = 1.781477937;
const double A4 = -1.821255978;
const double A5 = 1.330274429;
const double RSQRT2PI = 0.39894228040143267793994605993438;
static double CND(double d)
{
const double A1 = 0.31938153;
const double A2 = -0.356563782;
const double A3 = 1.781477937;
const double A4 = -1.821255978;
const double A5 = 1.330274429;
const double RSQRT2PI = 0.39894228040143267793994605993438;
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double cnd = RSQRT2PI * exp(-0.5 * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
double cnd = RSQRT2PI * exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (d > 0) cnd = 1.0 - cnd;
if (d > 0)
cnd = 1.0 - cnd;
return cnd;
return cnd;
}
///////////////////////////////////////////////////////////////////////////////
// Black-Scholes formula for both call and put
///////////////////////////////////////////////////////////////////////////////
static void BlackScholesBodyCPU(float &callResult, float &putResult,
float Sf, // Stock price
float Xf, // Option strike
float Tf, // Option years
float Rf, // Riskless rate
float Vf // Volatility rate
) {
double S = Sf, X = Xf, T = Tf, R = Rf, V = Vf;
double sqrtT = sqrt(T);
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
static void BlackScholesBodyCPU(float &callResult,
float &putResult,
float Sf, // Stock price
float Xf, // Option strike
float Tf, // Option years
float Rf, // Riskless rate
float Vf // Volatility rate
)
{
double S = Sf, X = Xf, T = Tf, R = Rf, V = Vf;
double sqrtT = sqrt(T);
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
// Calculate Call and Put simultaneously
double expRT = exp(-R * T);
// Calculate Call and Put simultaneously
double expRT = exp(-R * T);
callResult = (float)(S * CNDD1 - X * expRT * CNDD2);
putResult = (float)(X * expRT * (1.0 - CNDD2) - S * (1.0 - CNDD1));
callResult = (float)(S * CNDD1 - X * expRT * CNDD2);
putResult = (float)(X * expRT * (1.0 - CNDD2) - S * (1.0 - CNDD1));
}
////////////////////////////////////////////////////////////////////////////////
// Process an array of optN options
////////////////////////////////////////////////////////////////////////////////
extern "C" void BlackScholesCPU(float *h_CallResult, float *h_PutResult,
float *h_StockPrice, float *h_OptionStrike,
float *h_OptionYears, float Riskfree,
float Volatility, int optN) {
for (int opt = 0; opt < optN; opt++)
BlackScholesBodyCPU(h_CallResult[opt], h_PutResult[opt], h_StockPrice[opt],
h_OptionStrike[opt], h_OptionYears[opt], Riskfree,
Volatility);
extern "C" void BlackScholesCPU(float *h_CallResult,
float *h_PutResult,
float *h_StockPrice,
float *h_OptionStrike,
float *h_OptionYears,
float Riskfree,
float Volatility,
int optN)
{
for (int opt = 0; opt < optN; opt++)
BlackScholesBodyCPU(h_CallResult[opt],
h_PutResult[opt],
h_StockPrice[opt],
h_OptionStrike[opt],
h_OptionYears[opt],
Riskfree,
Volatility);
}

View File

@@ -29,75 +29,88 @@
// Polynomial approximation of cumulative normal distribution function
///////////////////////////////////////////////////////////////////////////////
__device__ inline float cndGPU(float d) {
const float A1 = 0.31938153f;
const float A2 = -0.356563782f;
const float A3 = 1.781477937f;
const float A4 = -1.821255978f;
const float A5 = 1.330274429f;
const float RSQRT2PI = 0.39894228040143267793994605993438f;
__device__ inline float cndGPU(float d)
{
const float A1 = 0.31938153f;
const float A2 = -0.356563782f;
const float A3 = 1.781477937f;
const float A4 = -1.821255978f;
const float A5 = 1.330274429f;
const float RSQRT2PI = 0.39894228040143267793994605993438f;
float K = __fdividef(1.0f, (1.0f + 0.2316419f * fabsf(d)));
float K = __fdividef(1.0f, (1.0f + 0.2316419f * fabsf(d)));
float cnd = RSQRT2PI * __expf(-0.5f * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
float cnd = RSQRT2PI * __expf(-0.5f * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (d > 0) cnd = 1.0f - cnd;
if (d > 0)
cnd = 1.0f - cnd;
return cnd;
return cnd;
}
///////////////////////////////////////////////////////////////////////////////
// Black-Scholes formula for both call and put
///////////////////////////////////////////////////////////////////////////////
__device__ inline void BlackScholesBodyGPU(float &CallResult, float &PutResult,
float S, // Stock price
float X, // Option strike
float T, // Option years
float R, // Riskless rate
float V // Volatility rate
) {
float sqrtT, expRT;
float d1, d2, CNDD1, CNDD2;
__device__ inline void BlackScholesBodyGPU(float &CallResult,
float &PutResult,
float S, // Stock price
float X, // Option strike
float T, // Option years
float R, // Riskless rate
float V // Volatility rate
)
{
float sqrtT, expRT;
float d1, d2, CNDD1, CNDD2;
sqrtT = __fdividef(1.0F, rsqrtf(T));
d1 = __fdividef(__logf(S / X) + (R + 0.5f * V * V) * T, V * sqrtT);
d2 = d1 - V * sqrtT;
sqrtT = __fdividef(1.0F, rsqrtf(T));
d1 = __fdividef(__logf(S / X) + (R + 0.5f * V * V) * T, V * sqrtT);
d2 = d1 - V * sqrtT;
CNDD1 = cndGPU(d1);
CNDD2 = cndGPU(d2);
CNDD1 = cndGPU(d1);
CNDD2 = cndGPU(d2);
// Calculate Call and Put simultaneously
expRT = __expf(-R * T);
CallResult = S * CNDD1 - X * expRT * CNDD2;
PutResult = X * expRT * (1.0f - CNDD2) - S * (1.0f - CNDD1);
// Calculate Call and Put simultaneously
expRT = __expf(-R * T);
CallResult = S * CNDD1 - X * expRT * CNDD2;
PutResult = X * expRT * (1.0f - CNDD2) - S * (1.0f - CNDD1);
}
////////////////////////////////////////////////////////////////////////////////
// Process an array of optN options on GPU
////////////////////////////////////////////////////////////////////////////////
extern "C" __launch_bounds__(128) __global__
void BlackScholesGPU(float2 *__restrict d_CallResult,
float2 *__restrict d_PutResult,
float2 *__restrict d_StockPrice,
float2 *__restrict d_OptionStrike,
float2 *__restrict d_OptionYears, float Riskfree,
float Volatility, int optN) {
////Thread index
const int opt = blockDim.x * blockIdx.x + threadIdx.x;
extern "C" __launch_bounds__(128) __global__ void BlackScholesGPU(float2 *__restrict d_CallResult,
float2 *__restrict d_PutResult,
float2 *__restrict d_StockPrice,
float2 *__restrict d_OptionStrike,
float2 *__restrict d_OptionYears,
float Riskfree,
float Volatility,
int optN)
{
////Thread index
const int opt = blockDim.x * blockIdx.x + threadIdx.x;
// Calculating 2 options per thread to increase ILP (instruction level
// parallelism)
if (opt < (optN / 2)) {
float callResult1, callResult2;
float putResult1, putResult2;
BlackScholesBodyGPU(callResult1, putResult1, d_StockPrice[opt].x,
d_OptionStrike[opt].x, d_OptionYears[opt].x, Riskfree,
Volatility);
BlackScholesBodyGPU(callResult2, putResult2, d_StockPrice[opt].y,
d_OptionStrike[opt].y, d_OptionYears[opt].y, Riskfree,
Volatility);
d_CallResult[opt] = make_float2(callResult1, callResult2);
d_PutResult[opt] = make_float2(putResult1, putResult2);
}
// Calculating 2 options per thread to increase ILP (instruction level
// parallelism)
if (opt < (optN / 2)) {
float callResult1, callResult2;
float putResult1, putResult2;
BlackScholesBodyGPU(callResult1,
putResult1,
d_StockPrice[opt].x,
d_OptionStrike[opt].x,
d_OptionYears[opt].x,
Riskfree,
Volatility);
BlackScholesBodyGPU(callResult2,
putResult2,
d_StockPrice[opt].y,
d_OptionStrike[opt].y,
d_OptionYears[opt].y,
Riskfree,
Volatility);
d_CallResult[opt] = make_float2(callResult1, callResult2);
d_PutResult[opt] = make_float2(putResult1, putResult2);
}
}

View File

@@ -34,21 +34,21 @@
// primary display in this case.
#define k_dim_min 96
#define k_dim_max 376
#define k_dim_qa 248
#define k_dim_qa 248
// Note that the radius is defined here as exactly 4 since the
// kernel code uses a constant. If you want a different radius
// you must change the kernel accordingly.
#define k_radius_min 4
#define k_radius_max 4
#define k_radius_min 4
#define k_radius_max 4
#define k_radius_default 4
// The values are set to give reasonable runtimes, they can
// be changed but note that running a very large number of
// timesteps can take a very long time and you should avoid
// running on your primary display in this case.
#define k_timesteps_min 1
#define k_timesteps_max 10
#define k_timesteps_min 1
#define k_timesteps_max 10
#define k_timesteps_default 5
#endif

View File

@@ -29,23 +29,28 @@
#define _FDTD3DGPU_H_
#include <cstddef>
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || \
defined(_WIN64) && defined(_MSC_VER)
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) && defined(_MSC_VER)
typedef unsigned __int64 memsize_t;
#else
#include <stdint.h>
typedef uint64_t memsize_t;
#endif
#define k_blockDimX 32
#define k_blockDimX 32
#define k_blockDimMaxY 16
#define k_blockSizeMin 128
#define k_blockSizeMax (k_blockDimX * k_blockDimMaxY)
bool getTargetDeviceGlobalMemSize(memsize_t *result, const int argc,
const char **argv);
bool fdtdGPU(float *output, const float *input, const float *coeff,
const int dimx, const int dimy, const int dimz, const int radius,
const int timesteps, const int argc, const char **argv);
bool getTargetDeviceGlobalMemSize(memsize_t *result, const int argc, const char **argv);
bool fdtdGPU(float *output,
const float *input,
const float *coeff,
const int dimx,
const int dimy,
const int dimz,
const int radius,
const int timesteps,
const int argc,
const char **argv);
#endif

View File

@@ -25,9 +25,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "FDTD3dGPU.h"
#include <cooperative_groups.h>
#include "FDTD3dGPU.h"
namespace cg = cooperative_groups;
// Note: If you change the RADIUS, you should also change the unrolling below
@@ -35,116 +36,125 @@ namespace cg = cooperative_groups;
__constant__ float stencil[RADIUS + 1];
__global__ void FiniteDifferencesKernel(float *output, const float *input,
const int dimx, const int dimy,
const int dimz) {
bool validr = true;
bool validw = true;
const int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
const int gtidy = blockIdx.y * blockDim.y + threadIdx.y;
const int ltidx = threadIdx.x;
const int ltidy = threadIdx.y;
const int workx = blockDim.x;
const int worky = blockDim.y;
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__shared__ float tile[k_blockDimMaxY + 2 * RADIUS][k_blockDimX + 2 * RADIUS];
__global__ void
FiniteDifferencesKernel(float *output, const float *input, const int dimx, const int dimy, const int dimz)
{
bool validr = true;
bool validw = true;
const int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
const int gtidy = blockIdx.y * blockDim.y + threadIdx.y;
const int ltidx = threadIdx.x;
const int ltidy = threadIdx.y;
const int workx = blockDim.x;
const int worky = blockDim.y;
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__shared__ float tile[k_blockDimMaxY + 2 * RADIUS][k_blockDimX + 2 * RADIUS];
const int stride_y = dimx + 2 * RADIUS;
const int stride_z = stride_y * (dimy + 2 * RADIUS);
const int stride_y = dimx + 2 * RADIUS;
const int stride_z = stride_y * (dimy + 2 * RADIUS);
int inputIndex = 0;
int outputIndex = 0;
int inputIndex = 0;
int outputIndex = 0;
// Advance inputIndex to start of inner volume
inputIndex += RADIUS * stride_y + RADIUS;
// Advance inputIndex to start of inner volume
inputIndex += RADIUS * stride_y + RADIUS;
// Advance inputIndex to target element
inputIndex += gtidy * stride_y + gtidx;
// Advance inputIndex to target element
inputIndex += gtidy * stride_y + gtidx;
float infront[RADIUS];
float behind[RADIUS];
float current;
float infront[RADIUS];
float behind[RADIUS];
float current;
const int tx = ltidx + RADIUS;
const int ty = ltidy + RADIUS;
const int tx = ltidx + RADIUS;
const int ty = ltidy + RADIUS;
// Check in bounds
if ((gtidx >= dimx + RADIUS) || (gtidy >= dimy + RADIUS)) validr = false;
// Check in bounds
if ((gtidx >= dimx + RADIUS) || (gtidy >= dimy + RADIUS))
validr = false;
if ((gtidx >= dimx) || (gtidy >= dimy)) validw = false;
if ((gtidx >= dimx) || (gtidy >= dimy))
validw = false;
// Preload the "infront" and "behind" data
for (int i = RADIUS - 2; i >= 0; i--) {
if (validr) behind[i] = input[inputIndex];
// Preload the "infront" and "behind" data
for (int i = RADIUS - 2; i >= 0; i--) {
if (validr)
behind[i] = input[inputIndex];
inputIndex += stride_z;
}
if (validr)
current = input[inputIndex];
outputIndex = inputIndex;
inputIndex += stride_z;
}
if (validr) current = input[inputIndex];
for (int i = 0; i < RADIUS; i++) {
if (validr)
infront[i] = input[inputIndex];
outputIndex = inputIndex;
inputIndex += stride_z;
for (int i = 0; i < RADIUS; i++) {
if (validr) infront[i] = input[inputIndex];
inputIndex += stride_z;
}
inputIndex += stride_z;
}
// Step through the xy-planes
#pragma unroll 9
for (int iz = 0; iz < dimz; iz++) {
// Advance the slice (move the thread-front)
for (int i = RADIUS - 1; i > 0; i--) behind[i] = behind[i - 1];
for (int iz = 0; iz < dimz; iz++) {
// Advance the slice (move the thread-front)
for (int i = RADIUS - 1; i > 0; i--)
behind[i] = behind[i - 1];
behind[0] = current;
current = infront[0];
behind[0] = current;
current = infront[0];
#pragma unroll 4
for (int i = 0; i < RADIUS - 1; i++) infront[i] = infront[i + 1];
for (int i = 0; i < RADIUS - 1; i++)
infront[i] = infront[i + 1];
if (validr) infront[RADIUS - 1] = input[inputIndex];
if (validr)
infront[RADIUS - 1] = input[inputIndex];
inputIndex += stride_z;
outputIndex += stride_z;
cg::sync(cta);
inputIndex += stride_z;
outputIndex += stride_z;
cg::sync(cta);
// Note that for the work items on the boundary of the problem, the
// supplied index when reading the halo (below) may wrap to the
// previous/next row or even the previous/next xy-plane. This is
// acceptable since a) we disable the output write for these work
// items and b) there is at least one xy-plane before/after the
// current plane, so the access will be within bounds.
// Note that for the work items on the boundary of the problem, the
// supplied index when reading the halo (below) may wrap to the
// previous/next row or even the previous/next xy-plane. This is
// acceptable since a) we disable the output write for these work
// items and b) there is at least one xy-plane before/after the
// current plane, so the access will be within bounds.
// Update the data slice in the local tile
// Halo above & below
if (ltidy < RADIUS) {
tile[ltidy][tx] = input[outputIndex - RADIUS * stride_y];
tile[ltidy + worky + RADIUS][tx] = input[outputIndex + worky * stride_y];
}
// Update the data slice in the local tile
// Halo above & below
if (ltidy < RADIUS) {
tile[ltidy][tx] = input[outputIndex - RADIUS * stride_y];
tile[ltidy + worky + RADIUS][tx] = input[outputIndex + worky * stride_y];
}
// Halo left & right
if (ltidx < RADIUS) {
tile[ty][ltidx] = input[outputIndex - RADIUS];
tile[ty][ltidx + workx + RADIUS] = input[outputIndex + workx];
}
// Halo left & right
if (ltidx < RADIUS) {
tile[ty][ltidx] = input[outputIndex - RADIUS];
tile[ty][ltidx + workx + RADIUS] = input[outputIndex + workx];
}
tile[ty][tx] = current;
cg::sync(cta);
tile[ty][tx] = current;
cg::sync(cta);
// Compute the output value
float value = stencil[0] * current;
// Compute the output value
float value = stencil[0] * current;
#pragma unroll 4
for (int i = 1; i <= RADIUS; i++) {
value +=
stencil[i] * (infront[i - 1] + behind[i - 1] + tile[ty - i][tx] +
tile[ty + i][tx] + tile[ty][tx - i] + tile[ty][tx + i]);
}
for (int i = 1; i <= RADIUS; i++) {
value += stencil[i]
* (infront[i - 1] + behind[i - 1] + tile[ty - i][tx] + tile[ty + i][tx] + tile[ty][tx - i]
+ tile[ty][tx + i]);
}
// Store the output value
if (validw) output[outputIndex] = value;
}
// Store the output value
if (validw)
output[outputIndex] = value;
}
}

View File

@@ -28,17 +28,32 @@
#ifndef _FDTD3DREFERENCE_H_
#define _FDTD3DREFERENCE_H_
void generateRandomData(float *data, const int dimx, const int dimy,
const int dimz, const float lowerBound,
void generateRandomData(float *data,
const int dimx,
const int dimy,
const int dimz,
const float lowerBound,
const float upperBound);
void generatePatternData(float *data, const int dimx, const int dimy,
const int dimz, const float lowerBound,
void generatePatternData(float *data,
const int dimx,
const int dimy,
const int dimz,
const float lowerBound,
const float upperBound);
bool fdtdReference(float *output, const float *input, const float *coeff,
const int dimx, const int dimy, const int dimz,
const int radius, const int timesteps);
bool compareData(const float *output, const float *reference, const int dimx,
const int dimy, const int dimz, const int radius,
const float tolerance = 0.0001f);
bool fdtdReference(float *output,
const float *input,
const float *coeff,
const int dimx,
const int dimy,
const int dimz,
const int radius,
const int timesteps);
bool compareData(const float *output,
const float *reference,
const int dimx,
const int dimy,
const int dimz,
const int radius,
const float tolerance = 0.0001f);
#endif

View File

@@ -27,16 +27,14 @@
#include "FDTD3d.h"
#include <iostream>
#include <iomanip>
#include "FDTD3dReference.h"
#include "FDTD3dGPU.h"
#include <helper_functions.h>
#include <math.h>
#include <assert.h>
#include <helper_functions.h>
#include <iomanip>
#include <iostream>
#include <math.h>
#include "FDTD3dGPU.h"
#include "FDTD3dReference.h"
#ifndef CLAMP
#define CLAMP(a, min, max) (MIN(max, MAX(a, min)))
@@ -49,187 +47,186 @@
bool runTest(int argc, const char **argv);
void showHelp(const int argc, const char **argv);
int main(int argc, char **argv) {
bool bTestResult = false;
// Start the log
printf("%s Starting...\n\n", argv[0]);
int main(int argc, char **argv)
{
bool bTestResult = false;
// Start the log
printf("%s Starting...\n\n", argv[0]);
// Check help flag
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printf("Displaying help on console\n");
showHelp(argc, (const char **)argv);
bTestResult = true;
} else {
// Execute
bTestResult = runTest(argc, (const char **)argv);
}
// Check help flag
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printf("Displaying help on console\n");
showHelp(argc, (const char **)argv);
bTestResult = true;
}
else {
// Execute
bTestResult = runTest(argc, (const char **)argv);
}
// Finish
exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);
// Finish
exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);
}
void showHelp(const int argc, const char **argv) {
if (argc > 0) std::cout << std::endl << argv[0] << std::endl;
void showHelp(const int argc, const char **argv)
{
if (argc > 0)
std::cout << std::endl << argv[0] << std::endl;
std::cout << std::endl << "Syntax:" << std::endl;
std::cout << std::left;
std::cout << " " << std::setw(20) << "--device=<device>"
<< "Specify device to use for execution" << std::endl;
std::cout << " " << std::setw(20) << "--dimx=<N>"
<< "Specify number of elements in x direction (excluding halo)"
<< std::endl;
std::cout << " " << std::setw(20) << "--dimy=<N>"
<< "Specify number of elements in y direction (excluding halo)"
<< std::endl;
std::cout << " " << std::setw(20) << "--dimz=<N>"
<< "Specify number of elements in z direction (excluding halo)"
<< std::endl;
std::cout << " " << std::setw(20) << "--radius=<N>"
<< "Specify radius of stencil" << std::endl;
std::cout << " " << std::setw(20) << "--timesteps=<N>"
<< "Specify number of timesteps" << std::endl;
std::cout << " " << std::setw(20) << "--block-size=<N>"
<< "Specify number of threads per block" << std::endl;
std::cout << std::endl;
std::cout << " " << std::setw(20) << "--noprompt"
<< "Skip prompt before exit" << std::endl;
std::cout << std::endl;
std::cout << std::endl << "Syntax:" << std::endl;
std::cout << std::left;
std::cout << " " << std::setw(20) << "--device=<device>"
<< "Specify device to use for execution" << std::endl;
std::cout << " " << std::setw(20) << "--dimx=<N>"
<< "Specify number of elements in x direction (excluding halo)" << std::endl;
std::cout << " " << std::setw(20) << "--dimy=<N>"
<< "Specify number of elements in y direction (excluding halo)" << std::endl;
std::cout << " " << std::setw(20) << "--dimz=<N>"
<< "Specify number of elements in z direction (excluding halo)" << std::endl;
std::cout << " " << std::setw(20) << "--radius=<N>"
<< "Specify radius of stencil" << std::endl;
std::cout << " " << std::setw(20) << "--timesteps=<N>"
<< "Specify number of timesteps" << std::endl;
std::cout << " " << std::setw(20) << "--block-size=<N>"
<< "Specify number of threads per block" << std::endl;
std::cout << std::endl;
std::cout << " " << std::setw(20) << "--noprompt"
<< "Skip prompt before exit" << std::endl;
std::cout << std::endl;
}
bool runTest(int argc, const char **argv) {
float *host_output;
float *device_output;
float *input;
float *coeff;
bool runTest(int argc, const char **argv)
{
float *host_output;
float *device_output;
float *input;
float *coeff;
int defaultDim;
int dimx;
int dimy;
int dimz;
int outerDimx;
int outerDimy;
int outerDimz;
int radius;
int timesteps;
size_t volumeSize;
memsize_t memsize;
int defaultDim;
int dimx;
int dimy;
int dimz;
int outerDimx;
int outerDimy;
int outerDimz;
int radius;
int timesteps;
size_t volumeSize;
memsize_t memsize;
const float lowerBound = 0.0f;
const float upperBound = 1.0f;
const float lowerBound = 0.0f;
const float upperBound = 1.0f;
// Determine default dimensions
printf("Set-up, based upon target device GMEM size...\n");
// Get the memory size of the target device
printf(" getTargetDeviceGlobalMemSize\n");
getTargetDeviceGlobalMemSize(&memsize, argc, argv);
// Determine default dimensions
printf("Set-up, based upon target device GMEM size...\n");
// Get the memory size of the target device
printf(" getTargetDeviceGlobalMemSize\n");
getTargetDeviceGlobalMemSize(&memsize, argc, argv);
// We can never use all the memory so to keep things simple we aim to
// use around half the total memory
memsize /= 2;
// We can never use all the memory so to keep things simple we aim to
// use around half the total memory
memsize /= 2;
// Most of our memory use is taken up by the input and output buffers -
// two buffers of equal size - and for simplicity the volume is a cube:
// dim = floor( (N/2)^(1/3) )
defaultDim = (int)floor(pow((memsize / (2.0 * sizeof(float))), 1.0 / 3.0));
// Most of our memory use is taken up by the input and output buffers -
// two buffers of equal size - and for simplicity the volume is a cube:
// dim = floor( (N/2)^(1/3) )
defaultDim = (int)floor(pow((memsize / (2.0 * sizeof(float))), 1.0 / 3.0));
// By default, make the volume edge size an integer multiple of 128B to
// improve performance by coalescing memory accesses, in a real
// application it would make sense to pad the lines accordingly
int roundTarget = 128 / sizeof(float);
defaultDim = defaultDim / roundTarget * roundTarget;
defaultDim -= k_radius_default * 2;
// By default, make the volume edge size an integer multiple of 128B to
// improve performance by coalescing memory accesses, in a real
// application it would make sense to pad the lines accordingly
int roundTarget = 128 / sizeof(float);
defaultDim = defaultDim / roundTarget * roundTarget;
defaultDim -= k_radius_default * 2;
// Check dimension is valid
if (defaultDim < k_dim_min) {
printf(
"insufficient device memory (maximum volume on device is %d, must be "
"between %d and %d).\n",
defaultDim, k_dim_min, k_dim_max);
exit(EXIT_FAILURE);
} else if (defaultDim > k_dim_max) {
defaultDim = k_dim_max;
}
// Check dimension is valid
if (defaultDim < k_dim_min) {
printf("insufficient device memory (maximum volume on device is %d, must be "
"between %d and %d).\n",
defaultDim,
k_dim_min,
k_dim_max);
exit(EXIT_FAILURE);
}
else if (defaultDim > k_dim_max) {
defaultDim = k_dim_max;
}
// For QA testing, override default volume size
if (checkCmdLineFlag(argc, argv, "qatest")) {
defaultDim = MIN(defaultDim, k_dim_qa);
}
// For QA testing, override default volume size
if (checkCmdLineFlag(argc, argv, "qatest")) {
defaultDim = MIN(defaultDim, k_dim_qa);
}
// set default dim
dimx = defaultDim;
dimy = defaultDim;
dimz = defaultDim;
radius = k_radius_default;
timesteps = k_timesteps_default;
// set default dim
dimx = defaultDim;
dimy = defaultDim;
dimz = defaultDim;
radius = k_radius_default;
timesteps = k_timesteps_default;
// Parse command line arguments
if (checkCmdLineFlag(argc, argv, "dimx")) {
dimx =
CLAMP(getCmdLineArgumentInt(argc, argv, "dimx"), k_dim_min, k_dim_max);
}
// Parse command line arguments
if (checkCmdLineFlag(argc, argv, "dimx")) {
dimx = CLAMP(getCmdLineArgumentInt(argc, argv, "dimx"), k_dim_min, k_dim_max);
}
if (checkCmdLineFlag(argc, argv, "dimy")) {
dimy =
CLAMP(getCmdLineArgumentInt(argc, argv, "dimy"), k_dim_min, k_dim_max);
}
if (checkCmdLineFlag(argc, argv, "dimy")) {
dimy = CLAMP(getCmdLineArgumentInt(argc, argv, "dimy"), k_dim_min, k_dim_max);
}
if (checkCmdLineFlag(argc, argv, "dimz")) {
dimz =
CLAMP(getCmdLineArgumentInt(argc, argv, "dimz"), k_dim_min, k_dim_max);
}
if (checkCmdLineFlag(argc, argv, "dimz")) {
dimz = CLAMP(getCmdLineArgumentInt(argc, argv, "dimz"), k_dim_min, k_dim_max);
}
if (checkCmdLineFlag(argc, argv, "radius")) {
radius = CLAMP(getCmdLineArgumentInt(argc, argv, "radius"), k_radius_min,
k_radius_max);
}
if (checkCmdLineFlag(argc, argv, "radius")) {
radius = CLAMP(getCmdLineArgumentInt(argc, argv, "radius"), k_radius_min, k_radius_max);
}
if (checkCmdLineFlag(argc, argv, "timesteps")) {
timesteps = CLAMP(getCmdLineArgumentInt(argc, argv, "timesteps"),
k_timesteps_min, k_timesteps_max);
}
if (checkCmdLineFlag(argc, argv, "timesteps")) {
timesteps = CLAMP(getCmdLineArgumentInt(argc, argv, "timesteps"), k_timesteps_min, k_timesteps_max);
}
// Determine volume size
outerDimx = dimx + 2 * radius;
outerDimy = dimy + 2 * radius;
outerDimz = dimz + 2 * radius;
volumeSize = outerDimx * outerDimy * outerDimz;
// Determine volume size
outerDimx = dimx + 2 * radius;
outerDimy = dimy + 2 * radius;
outerDimz = dimz + 2 * radius;
volumeSize = outerDimx * outerDimy * outerDimz;
// Allocate memory
host_output = (float *)calloc(volumeSize, sizeof(float));
input = (float *)malloc(volumeSize * sizeof(float));
coeff = (float *)malloc((radius + 1) * sizeof(float));
// Allocate memory
host_output = (float *)calloc(volumeSize, sizeof(float));
input = (float *)malloc(volumeSize * sizeof(float));
coeff = (float *)malloc((radius + 1) * sizeof(float));
// Create coefficients
for (int i = 0; i <= radius; i++) {
coeff[i] = 0.1f;
}
// Create coefficients
for (int i = 0; i <= radius; i++) {
coeff[i] = 0.1f;
}
// Generate data
printf(" generateRandomData\n\n");
generateRandomData(input, outerDimx, outerDimy, outerDimz, lowerBound,
upperBound);
printf(
"FDTD on %d x %d x %d volume with symmetric filter radius %d for %d "
"timesteps...\n\n",
dimx, dimy, dimz, radius, timesteps);
// Generate data
printf(" generateRandomData\n\n");
generateRandomData(input, outerDimx, outerDimy, outerDimz, lowerBound, upperBound);
printf("FDTD on %d x %d x %d volume with symmetric filter radius %d for %d "
"timesteps...\n\n",
dimx,
dimy,
dimz,
radius,
timesteps);
// Execute on the host
printf("fdtdReference...\n");
fdtdReference(host_output, input, coeff, dimx, dimy, dimz, radius, timesteps);
printf("fdtdReference complete\n");
// Execute on the host
printf("fdtdReference...\n");
fdtdReference(host_output, input, coeff, dimx, dimy, dimz, radius, timesteps);
printf("fdtdReference complete\n");
// Allocate memory
device_output = (float *)calloc(volumeSize, sizeof(float));
// Allocate memory
device_output = (float *)calloc(volumeSize, sizeof(float));
// Execute on the device
printf("fdtdGPU...\n");
fdtdGPU(device_output, input, coeff, dimx, dimy, dimz, radius, timesteps,
argc, argv);
printf("fdtdGPU complete\n");
// Execute on the device
printf("fdtdGPU...\n");
fdtdGPU(device_output, input, coeff, dimx, dimy, dimz, radius, timesteps, argc, argv);
printf("fdtdGPU complete\n");
// Compare the results
float tolerance = 0.0001f;
printf("\nCompareData (tolerance %f)...\n", tolerance);
return compareData(device_output, host_output, dimx, dimy, dimz, radius,
tolerance);
// Compare the results
float tolerance = 0.0001f;
printf("\nCompareData (tolerance %f)...\n", tolerance);
return compareData(device_output, host_output, dimx, dimy, dimz, radius, tolerance);
}

View File

@@ -25,239 +25,236 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "FDTD3dGPU.h"
#include <iostream>
#include <algorithm>
#include <helper_functions.h>
#include <helper_cuda.h>
#include <helper_functions.h>
#include <iostream>
#include "FDTD3dGPU.h"
#include "FDTD3dGPUKernel.cuh"
bool getTargetDeviceGlobalMemSize(memsize_t *result, const int argc,
const char **argv) {
int deviceCount = 0;
int targetDevice = 0;
size_t memsize = 0;
bool getTargetDeviceGlobalMemSize(memsize_t *result, const int argc, const char **argv)
{
int deviceCount = 0;
int targetDevice = 0;
size_t memsize = 0;
// Get the number of CUDA enabled GPU devices
printf(" cudaGetDeviceCount\n");
checkCudaErrors(cudaGetDeviceCount(&deviceCount));
// Get the number of CUDA enabled GPU devices
printf(" cudaGetDeviceCount\n");
checkCudaErrors(cudaGetDeviceCount(&deviceCount));
// Select target device (device 0 by default)
targetDevice = findCudaDevice(argc, (const char **)argv);
// Select target device (device 0 by default)
targetDevice = findCudaDevice(argc, (const char **)argv);
// Query target device for maximum memory allocation
printf(" cudaGetDeviceProperties\n");
struct cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, targetDevice));
// Query target device for maximum memory allocation
printf(" cudaGetDeviceProperties\n");
struct cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, targetDevice));
memsize = deviceProp.totalGlobalMem;
memsize = deviceProp.totalGlobalMem;
// Save the result
*result = (memsize_t)memsize;
return true;
// Save the result
*result = (memsize_t)memsize;
return true;
}
bool fdtdGPU(float *output, const float *input, const float *coeff,
const int dimx, const int dimy, const int dimz, const int radius,
const int timesteps, const int argc, const char **argv) {
const int outerDimx = dimx + 2 * radius;
const int outerDimy = dimy + 2 * radius;
const int outerDimz = dimz + 2 * radius;
const size_t volumeSize = outerDimx * outerDimy * outerDimz;
int deviceCount = 0;
int targetDevice = 0;
float *bufferOut = 0;
float *bufferIn = 0;
dim3 dimBlock;
dim3 dimGrid;
bool fdtdGPU(float *output,
const float *input,
const float *coeff,
const int dimx,
const int dimy,
const int dimz,
const int radius,
const int timesteps,
const int argc,
const char **argv)
{
const int outerDimx = dimx + 2 * radius;
const int outerDimy = dimy + 2 * radius;
const int outerDimz = dimz + 2 * radius;
const size_t volumeSize = outerDimx * outerDimy * outerDimz;
int deviceCount = 0;
int targetDevice = 0;
float *bufferOut = 0;
float *bufferIn = 0;
dim3 dimBlock;
dim3 dimGrid;
// Ensure that the inner data starts on a 128B boundary
const int padding = (128 / sizeof(float)) - radius;
const size_t paddedVolumeSize = volumeSize + padding;
// Ensure that the inner data starts on a 128B boundary
const int padding = (128 / sizeof(float)) - radius;
const size_t paddedVolumeSize = volumeSize + padding;
#ifdef GPU_PROFILING
cudaEvent_t profileStart = 0;
cudaEvent_t profileEnd = 0;
const int profileTimesteps = timesteps - 1;
cudaEvent_t profileStart = 0;
cudaEvent_t profileEnd = 0;
const int profileTimesteps = timesteps - 1;
if (profileTimesteps < 1) {
printf(
" cannot profile with fewer than two timesteps (timesteps=%d), "
"profiling is disabled.\n",
timesteps);
}
if (profileTimesteps < 1) {
printf(" cannot profile with fewer than two timesteps (timesteps=%d), "
"profiling is disabled.\n",
timesteps);
}
#endif
// Check the radius is valid
if (radius != RADIUS) {
printf("radius is invalid, must be %d - see kernel for details.\n", RADIUS);
exit(EXIT_FAILURE);
}
// Check the radius is valid
if (radius != RADIUS) {
printf("radius is invalid, must be %d - see kernel for details.\n", RADIUS);
exit(EXIT_FAILURE);
}
// Get the number of CUDA enabled GPU devices
checkCudaErrors(cudaGetDeviceCount(&deviceCount));
// Get the number of CUDA enabled GPU devices
checkCudaErrors(cudaGetDeviceCount(&deviceCount));
// Select target device (device 0 by default)
targetDevice = findCudaDevice(argc, (const char **)argv);
// Select target device (device 0 by default)
targetDevice = findCudaDevice(argc, (const char **)argv);
checkCudaErrors(cudaSetDevice(targetDevice));
checkCudaErrors(cudaSetDevice(targetDevice));
// Allocate memory buffers
checkCudaErrors(
cudaMalloc((void **)&bufferOut, paddedVolumeSize * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&bufferIn, paddedVolumeSize * sizeof(float)));
// Allocate memory buffers
checkCudaErrors(cudaMalloc((void **)&bufferOut, paddedVolumeSize * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&bufferIn, paddedVolumeSize * sizeof(float)));
// Check for a command-line specified block size
int userBlockSize;
// Check for a command-line specified block size
int userBlockSize;
if (checkCmdLineFlag(argc, (const char **)argv, "block-size")) {
userBlockSize = getCmdLineArgumentInt(argc, argv, "block-size");
// Constrain to a multiple of k_blockDimX
userBlockSize = (userBlockSize / k_blockDimX * k_blockDimX);
if (checkCmdLineFlag(argc, (const char **)argv, "block-size")) {
userBlockSize = getCmdLineArgumentInt(argc, argv, "block-size");
// Constrain to a multiple of k_blockDimX
userBlockSize = (userBlockSize / k_blockDimX * k_blockDimX);
// Constrain within allowed bounds
userBlockSize = MIN(MAX(userBlockSize, k_blockSizeMin), k_blockSizeMax);
} else {
userBlockSize = k_blockSizeMax;
}
// Constrain within allowed bounds
userBlockSize = MIN(MAX(userBlockSize, k_blockSizeMin), k_blockSizeMax);
}
else {
userBlockSize = k_blockSizeMax;
}
// Check the device limit on the number of threads
struct cudaFuncAttributes funcAttrib;
checkCudaErrors(cudaFuncGetAttributes(&funcAttrib, FiniteDifferencesKernel));
// Check the device limit on the number of threads
struct cudaFuncAttributes funcAttrib;
checkCudaErrors(cudaFuncGetAttributes(&funcAttrib, FiniteDifferencesKernel));
userBlockSize = MIN(userBlockSize, funcAttrib.maxThreadsPerBlock);
userBlockSize = MIN(userBlockSize, funcAttrib.maxThreadsPerBlock);
// Set the block size
dimBlock.x = k_blockDimX;
// Visual Studio 2005 does not like std::min
// dimBlock.y = std::min<size_t>(userBlockSize / k_blockDimX,
// (size_t)k_blockDimMaxY);
dimBlock.y = ((userBlockSize / k_blockDimX) < (size_t)k_blockDimMaxY)
? (userBlockSize / k_blockDimX)
: (size_t)k_blockDimMaxY;
dimGrid.x = (unsigned int)ceil((float)dimx / dimBlock.x);
dimGrid.y = (unsigned int)ceil((float)dimy / dimBlock.y);
printf(" set block size to %dx%d\n", dimBlock.x, dimBlock.y);
printf(" set grid size to %dx%d\n", dimGrid.x, dimGrid.y);
// Set the block size
dimBlock.x = k_blockDimX;
// Visual Studio 2005 does not like std::min
// dimBlock.y = std::min<size_t>(userBlockSize / k_blockDimX,
// (size_t)k_blockDimMaxY);
dimBlock.y = ((userBlockSize / k_blockDimX) < (size_t)k_blockDimMaxY) ? (userBlockSize / k_blockDimX)
: (size_t)k_blockDimMaxY;
dimGrid.x = (unsigned int)ceil((float)dimx / dimBlock.x);
dimGrid.y = (unsigned int)ceil((float)dimy / dimBlock.y);
printf(" set block size to %dx%d\n", dimBlock.x, dimBlock.y);
printf(" set grid size to %dx%d\n", dimGrid.x, dimGrid.y);
// Check the block size is valid
if (dimBlock.x < RADIUS || dimBlock.y < RADIUS) {
printf("invalid block size, x (%d) and y (%d) must be >= radius (%d).\n",
dimBlock.x, dimBlock.y, RADIUS);
exit(EXIT_FAILURE);
}
// Check the block size is valid
if (dimBlock.x < RADIUS || dimBlock.y < RADIUS) {
printf("invalid block size, x (%d) and y (%d) must be >= radius (%d).\n", dimBlock.x, dimBlock.y, RADIUS);
exit(EXIT_FAILURE);
}
// Copy the input to the device input buffer
checkCudaErrors(cudaMemcpy(bufferIn + padding, input,
volumeSize * sizeof(float),
cudaMemcpyHostToDevice));
// Copy the input to the device input buffer
checkCudaErrors(cudaMemcpy(bufferIn + padding, input, volumeSize * sizeof(float), cudaMemcpyHostToDevice));
// Copy the input to the device output buffer (actually only need the halo)
checkCudaErrors(cudaMemcpy(bufferOut + padding, input,
volumeSize * sizeof(float),
cudaMemcpyHostToDevice));
// Copy the input to the device output buffer (actually only need the halo)
checkCudaErrors(cudaMemcpy(bufferOut + padding, input, volumeSize * sizeof(float), cudaMemcpyHostToDevice));
// Copy the coefficients to the device coefficient buffer
checkCudaErrors(
cudaMemcpyToSymbol(stencil, (void *)coeff, (radius + 1) * sizeof(float)));
// Copy the coefficients to the device coefficient buffer
checkCudaErrors(cudaMemcpyToSymbol(stencil, (void *)coeff, (radius + 1) * sizeof(float)));
#ifdef GPU_PROFILING
// Create the events
checkCudaErrors(cudaEventCreate(&profileStart));
checkCudaErrors(cudaEventCreate(&profileEnd));
// Create the events
checkCudaErrors(cudaEventCreate(&profileStart));
checkCudaErrors(cudaEventCreate(&profileEnd));
#endif
// Execute the FDTD
float *bufferSrc = bufferIn + padding;
float *bufferDst = bufferOut + padding;
printf(" GPU FDTD loop\n");
// Execute the FDTD
float *bufferSrc = bufferIn + padding;
float *bufferDst = bufferOut + padding;
printf(" GPU FDTD loop\n");
#ifdef GPU_PROFILING
// Enqueue start event
checkCudaErrors(cudaEventRecord(profileStart, 0));
// Enqueue start event
checkCudaErrors(cudaEventRecord(profileStart, 0));
#endif
for (int it = 0; it < timesteps; it++) {
printf("\tt = %d ", it);
for (int it = 0; it < timesteps; it++) {
printf("\tt = %d ", it);
// Launch the kernel
printf("launch kernel\n");
FiniteDifferencesKernel<<<dimGrid, dimBlock>>>(bufferDst, bufferSrc, dimx,
dimy, dimz);
// Launch the kernel
printf("launch kernel\n");
FiniteDifferencesKernel<<<dimGrid, dimBlock>>>(bufferDst, bufferSrc, dimx, dimy, dimz);
// Toggle the buffers
// Visual Studio 2005 does not like std::swap
// std::swap<float *>(bufferSrc, bufferDst);
float *tmp = bufferDst;
bufferDst = bufferSrc;
bufferSrc = tmp;
}
// Toggle the buffers
// Visual Studio 2005 does not like std::swap
// std::swap<float *>(bufferSrc, bufferDst);
float *tmp = bufferDst;
bufferDst = bufferSrc;
bufferSrc = tmp;
}
printf("\n");
printf("\n");
#ifdef GPU_PROFILING
// Enqueue end event
checkCudaErrors(cudaEventRecord(profileEnd, 0));
// Enqueue end event
checkCudaErrors(cudaEventRecord(profileEnd, 0));
#endif
// Wait for the kernel to complete
checkCudaErrors(cudaDeviceSynchronize());
// Wait for the kernel to complete
checkCudaErrors(cudaDeviceSynchronize());
// Read the result back, result is in bufferSrc (after final toggle)
checkCudaErrors(cudaMemcpy(output, bufferSrc, volumeSize * sizeof(float),
cudaMemcpyDeviceToHost));
// Read the result back, result is in bufferSrc (after final toggle)
checkCudaErrors(cudaMemcpy(output, bufferSrc, volumeSize * sizeof(float), cudaMemcpyDeviceToHost));
// Report time
#ifdef GPU_PROFILING
float elapsedTimeMS = 0;
float elapsedTimeMS = 0;
if (profileTimesteps > 0) {
checkCudaErrors(
cudaEventElapsedTime(&elapsedTimeMS, profileStart, profileEnd));
}
if (profileTimesteps > 0) {
checkCudaErrors(cudaEventElapsedTime(&elapsedTimeMS, profileStart, profileEnd));
}
if (profileTimesteps > 0) {
// Convert milliseconds to seconds
double elapsedTime = elapsedTimeMS * 1.0e-3;
double avgElapsedTime = elapsedTime / (double)profileTimesteps;
// Determine number of computations per timestep
size_t pointsComputed = dimx * dimy * dimz;
// Determine throughput
double throughputM = 1.0e-6 * (double)pointsComputed / avgElapsedTime;
printf(
"FDTD3d, Throughput = %.4f MPoints/s, Time = %.5f s, Size = %u Points, "
"NumDevsUsed = %u, Blocksize = %u\n",
throughputM, avgElapsedTime, pointsComputed, 1,
dimBlock.x * dimBlock.y);
}
if (profileTimesteps > 0) {
// Convert milliseconds to seconds
double elapsedTime = elapsedTimeMS * 1.0e-3;
double avgElapsedTime = elapsedTime / (double)profileTimesteps;
// Determine number of computations per timestep
size_t pointsComputed = dimx * dimy * dimz;
// Determine throughput
double throughputM = 1.0e-6 * (double)pointsComputed / avgElapsedTime;
printf("FDTD3d, Throughput = %.4f MPoints/s, Time = %.5f s, Size = %u Points, "
"NumDevsUsed = %u, Blocksize = %u\n",
throughputM,
avgElapsedTime,
pointsComputed,
1,
dimBlock.x * dimBlock.y);
}
#endif
// Cleanup
if (bufferIn) {
checkCudaErrors(cudaFree(bufferIn));
}
// Cleanup
if (bufferIn) {
checkCudaErrors(cudaFree(bufferIn));
}
if (bufferOut) {
checkCudaErrors(cudaFree(bufferOut));
}
if (bufferOut) {
checkCudaErrors(cudaFree(bufferOut));
}
#ifdef GPU_PROFILING
if (profileStart) {
checkCudaErrors(cudaEventDestroy(profileStart));
}
if (profileStart) {
checkCudaErrors(cudaEventDestroy(profileStart));
}
if (profileEnd) {
checkCudaErrors(cudaEventDestroy(profileEnd));
}
if (profileEnd) {
checkCudaErrors(cudaEventDestroy(profileEnd));
}
#endif
return true;
return true;
}

View File

@@ -27,152 +27,165 @@
#include "FDTD3dReference.h"
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <stdio.h>
void generateRandomData(float *data, const int dimx, const int dimy,
const int dimz, const float lowerBound,
const float upperBound) {
srand(0);
void generateRandomData(float *data,
const int dimx,
const int dimy,
const int dimz,
const float lowerBound,
const float upperBound)
{
srand(0);
for (int iz = 0; iz < dimz; iz++) {
for (int iy = 0; iy < dimy; iy++) {
for (int ix = 0; ix < dimx; ix++) {
*data = (float)(lowerBound +
((float)rand() / (float)RAND_MAX) *
(upperBound - lowerBound));
++data;
}
}
}
}
void generatePatternData(float *data, const int dimx, const int dimy,
const int dimz, const float lowerBound,
const float upperBound) {
for (int iz = 0; iz < dimz; iz++) {
for (int iy = 0; iy < dimy; iy++) {
for (int ix = 0; ix < dimx; ix++) {
*data = (float)(lowerBound +
((float)iz / (float)dimz) * (upperBound - lowerBound));
++data;
}
}
}
}
bool fdtdReference(float *output, const float *input, const float *coeff,
const int dimx, const int dimy, const int dimz,
const int radius, const int timesteps) {
const int outerDimx = dimx + 2 * radius;
const int outerDimy = dimy + 2 * radius;
const int outerDimz = dimz + 2 * radius;
const size_t volumeSize = outerDimx * outerDimy * outerDimz;
const int stride_y = outerDimx;
const int stride_z = stride_y * outerDimy;
float *intermediate = 0;
const float *bufsrc = 0;
float *bufdst = 0;
float *bufdstnext = 0;
// Allocate temporary buffer
printf(" calloc intermediate\n");
intermediate = (float *)calloc(volumeSize, sizeof(float));
// Decide which buffer to use first (result should end up in output)
if ((timesteps % 2) == 0) {
bufsrc = input;
bufdst = intermediate;
bufdstnext = output;
} else {
bufsrc = input;
bufdst = output;
bufdstnext = intermediate;
}
// Run the FDTD (naive method)
printf(" Host FDTD loop\n");
for (int it = 0; it < timesteps; it++) {
printf("\tt = %d\n", it);
const float *src = bufsrc;
float *dst = bufdst;
for (int iz = -radius; iz < dimz + radius; iz++) {
for (int iy = -radius; iy < dimy + radius; iy++) {
for (int ix = -radius; ix < dimx + radius; ix++) {
if (ix >= 0 && ix < dimx && iy >= 0 && iy < dimy && iz >= 0 &&
iz < dimz) {
float value = (*src) * coeff[0];
for (int ir = 1; ir <= radius; ir++) {
value += coeff[ir] * (*(src + ir) + *(src - ir)); // horizontal
value += coeff[ir] * (*(src + ir * stride_y) +
*(src - ir * stride_y)); // vertical
value +=
coeff[ir] * (*(src + ir * stride_z) +
*(src - ir * stride_z)); // in front & behind
for (int iz = 0; iz < dimz; iz++) {
for (int iy = 0; iy < dimy; iy++) {
for (int ix = 0; ix < dimx; ix++) {
*data = (float)(lowerBound + ((float)rand() / (float)RAND_MAX) * (upperBound - lowerBound));
++data;
}
*dst = value;
} else {
*dst = *src;
}
++dst;
++src;
}
}
}
// Rotate buffers
float *tmp = bufdst;
bufdst = bufdstnext;
bufdstnext = tmp;
bufsrc = (const float *)tmp;
}
printf("\n");
if (intermediate) free(intermediate);
return true;
}
bool compareData(const float *output, const float *reference, const int dimx,
const int dimy, const int dimz, const int radius,
const float tolerance) {
for (int iz = -radius; iz < dimz + radius; iz++) {
for (int iy = -radius; iy < dimy + radius; iy++) {
for (int ix = -radius; ix < dimx + radius; ix++) {
if (ix >= 0 && ix < dimx && iy >= 0 && iy < dimy && iz >= 0 &&
iz < dimz) {
// Determine the absolute difference
float difference = fabs(*reference - *output);
float error;
void generatePatternData(float *data,
const int dimx,
const int dimy,
const int dimz,
const float lowerBound,
const float upperBound)
{
for (int iz = 0; iz < dimz; iz++) {
for (int iy = 0; iy < dimy; iy++) {
for (int ix = 0; ix < dimx; ix++) {
*data = (float)(lowerBound + ((float)iz / (float)dimz) * (upperBound - lowerBound));
++data;
}
}
}
}
// Determine the relative error
if (*reference != 0)
error = difference / *reference;
else
error = difference;
bool fdtdReference(float *output,
const float *input,
const float *coeff,
const int dimx,
const int dimy,
const int dimz,
const int radius,
const int timesteps)
{
const int outerDimx = dimx + 2 * radius;
const int outerDimy = dimy + 2 * radius;
const int outerDimz = dimz + 2 * radius;
const size_t volumeSize = outerDimx * outerDimy * outerDimz;
const int stride_y = outerDimx;
const int stride_z = stride_y * outerDimy;
float *intermediate = 0;
const float *bufsrc = 0;
float *bufdst = 0;
float *bufdstnext = 0;
// Check the error is within the tolerance
if (error > tolerance) {
printf("Data error at point (%d,%d,%d)\t%f instead of %f\n", ix, iy,
iz, *output, *reference);
return false;
}
// Allocate temporary buffer
printf(" calloc intermediate\n");
intermediate = (float *)calloc(volumeSize, sizeof(float));
// Decide which buffer to use first (result should end up in output)
if ((timesteps % 2) == 0) {
bufsrc = input;
bufdst = intermediate;
bufdstnext = output;
}
else {
bufsrc = input;
bufdst = output;
bufdstnext = intermediate;
}
// Run the FDTD (naive method)
printf(" Host FDTD loop\n");
for (int it = 0; it < timesteps; it++) {
printf("\tt = %d\n", it);
const float *src = bufsrc;
float *dst = bufdst;
for (int iz = -radius; iz < dimz + radius; iz++) {
for (int iy = -radius; iy < dimy + radius; iy++) {
for (int ix = -radius; ix < dimx + radius; ix++) {
if (ix >= 0 && ix < dimx && iy >= 0 && iy < dimy && iz >= 0 && iz < dimz) {
float value = (*src) * coeff[0];
for (int ir = 1; ir <= radius; ir++) {
value += coeff[ir] * (*(src + ir) + *(src - ir)); // horizontal
value += coeff[ir] * (*(src + ir * stride_y) + *(src - ir * stride_y)); // vertical
value += coeff[ir] * (*(src + ir * stride_z) + *(src - ir * stride_z)); // in front & behind
}
*dst = value;
}
else {
*dst = *src;
}
++dst;
++src;
}
}
}
++output;
++reference;
}
// Rotate buffers
float *tmp = bufdst;
bufdst = bufdstnext;
bufdstnext = tmp;
bufsrc = (const float *)tmp;
}
}
return true;
printf("\n");
if (intermediate)
free(intermediate);
return true;
}
bool compareData(const float *output,
const float *reference,
const int dimx,
const int dimy,
const int dimz,
const int radius,
const float tolerance)
{
for (int iz = -radius; iz < dimz + radius; iz++) {
for (int iy = -radius; iy < dimy + radius; iy++) {
for (int ix = -radius; ix < dimx + radius; ix++) {
if (ix >= 0 && ix < dimx && iy >= 0 && iy < dimy && iz >= 0 && iz < dimz) {
// Determine the absolute difference
float difference = fabs(*reference - *output);
float error;
// Determine the relative error
if (*reference != 0)
error = difference / *reference;
else
error = difference;
// Check the error is within the tolerance
if (error > tolerance) {
printf("Data error at point (%d,%d,%d)\t%f instead of %f\n", ix, iy, iz, *output, *reference);
return false;
}
}
++output;
++reference;
}
}
}
return true;
}

View File

@@ -36,13 +36,14 @@
/// \param[in] count vector size
/// \param[out] sum result
///////////////////////////////////////////////////////////////////////////////
__global__ void AddKernel(const float *op1, const float *op2, int count,
float *sum) {
const int pos = threadIdx.x + blockIdx.x * blockDim.x;
__global__ void AddKernel(const float *op1, const float *op2, int count, float *sum)
{
const int pos = threadIdx.x + blockIdx.x * blockDim.x;
if (pos >= count) return;
if (pos >= count)
return;
sum[pos] = op1[pos] + op2[pos];
sum[pos] = op1[pos] + op2[pos];
}
///////////////////////////////////////////////////////////////////////////////
@@ -52,9 +53,10 @@ __global__ void AddKernel(const float *op1, const float *op2, int count,
/// \param[in] count vector size
/// \param[out] sum result
///////////////////////////////////////////////////////////////////////////////
static void Add(const float *op1, const float *op2, int count, float *sum) {
dim3 threads(256);
dim3 blocks(iDivUp(count, threads.x));
static void Add(const float *op1, const float *op2, int count, float *sum)
{
dim3 threads(256);
dim3 blocks(iDivUp(count, threads.x));
AddKernel<<<blocks, threads>>>(op1, op2, count, sum);
AddKernel<<<blocks, threads>>>(op1, op2, count, sum);
}

View File

@@ -36,13 +36,12 @@
// Common includes
///////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <memory.h>
#include <math.h>
#include <helper_cuda.h>
#include <math.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
///////////////////////////////////////////////////////////////////////////////
// Common constants
@@ -54,23 +53,24 @@ const int StrideAlignment = 32;
///////////////////////////////////////////////////////////////////////////////
// Align up n to the nearest multiple of m
inline int iAlignUp(int n, int m = StrideAlignment) {
int mod = n % m;
inline int iAlignUp(int n, int m = StrideAlignment)
{
int mod = n % m;
if (mod)
return n + m - mod;
else
return n;
if (mod)
return n + m - mod;
else
return n;
}
// round up n/m
inline int iDivUp(int n, int m) { return (n + m - 1) / m; }
// swap two values
template <typename T>
inline void Swap(T &a, T &b) {
T t = a;
a = b;
b = t;
template <typename T> inline void Swap(T &a, T &b)
{
T t = a;
a = b;
b = t;
}
#endif

View File

@@ -38,56 +38,62 @@
/// \param[out] Iy y derivative
/// \param[out] Iz temporal derivative
///////////////////////////////////////////////////////////////////////////////
__global__ void ComputeDerivativesKernel(int width, int height, int stride,
float *Ix, float *Iy, float *Iz,
__global__ void ComputeDerivativesKernel(int width,
int height,
int stride,
float *Ix,
float *Iy,
float *Iz,
cudaTextureObject_t texSource,
cudaTextureObject_t texTarget) {
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
cudaTextureObject_t texTarget)
{
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
const int pos = ix + iy * stride;
const int pos = ix + iy * stride;
if (ix >= width || iy >= height) return;
if (ix >= width || iy >= height)
return;
float dx = 1.0f / (float)width;
float dy = 1.0f / (float)height;
float dx = 1.0f / (float)width;
float dy = 1.0f / (float)height;
float x = ((float)ix + 0.5f) * dx;
float y = ((float)iy + 0.5f) * dy;
float x = ((float)ix + 0.5f) * dx;
float y = ((float)iy + 0.5f) * dy;
float t0, t1;
// x derivative
t0 = tex2D<float>(texSource, x - 2.0f * dx, y);
t0 -= tex2D<float>(texSource, x - 1.0f * dx, y) * 8.0f;
t0 += tex2D<float>(texSource, x + 1.0f * dx, y) * 8.0f;
t0 -= tex2D<float>(texSource, x + 2.0f * dx, y);
t0 /= 12.0f;
float t0, t1;
// x derivative
t0 = tex2D<float>(texSource, x - 2.0f * dx, y);
t0 -= tex2D<float>(texSource, x - 1.0f * dx, y) * 8.0f;
t0 += tex2D<float>(texSource, x + 1.0f * dx, y) * 8.0f;
t0 -= tex2D<float>(texSource, x + 2.0f * dx, y);
t0 /= 12.0f;
t1 = tex2D<float>(texTarget, x - 2.0f * dx, y);
t1 -= tex2D<float>(texTarget, x - 1.0f * dx, y) * 8.0f;
t1 += tex2D<float>(texTarget, x + 1.0f * dx, y) * 8.0f;
t1 -= tex2D<float>(texTarget, x + 2.0f * dx, y);
t1 /= 12.0f;
t1 = tex2D<float>(texTarget, x - 2.0f * dx, y);
t1 -= tex2D<float>(texTarget, x - 1.0f * dx, y) * 8.0f;
t1 += tex2D<float>(texTarget, x + 1.0f * dx, y) * 8.0f;
t1 -= tex2D<float>(texTarget, x + 2.0f * dx, y);
t1 /= 12.0f;
Ix[pos] = (t0 + t1) * 0.5f;
Ix[pos] = (t0 + t1) * 0.5f;
// t derivative
Iz[pos] = tex2D<float>(texTarget, x, y) - tex2D<float>(texSource, x, y);
// t derivative
Iz[pos] = tex2D<float>(texTarget, x, y) - tex2D<float>(texSource, x, y);
// y derivative
t0 = tex2D<float>(texSource, x, y - 2.0f * dy);
t0 -= tex2D<float>(texSource, x, y - 1.0f * dy) * 8.0f;
t0 += tex2D<float>(texSource, x, y + 1.0f * dy) * 8.0f;
t0 -= tex2D<float>(texSource, x, y + 2.0f * dy);
t0 /= 12.0f;
// y derivative
t0 = tex2D<float>(texSource, x, y - 2.0f * dy);
t0 -= tex2D<float>(texSource, x, y - 1.0f * dy) * 8.0f;
t0 += tex2D<float>(texSource, x, y + 1.0f * dy) * 8.0f;
t0 -= tex2D<float>(texSource, x, y + 2.0f * dy);
t0 /= 12.0f;
t1 = tex2D<float>(texTarget, x, y - 2.0f * dy);
t1 -= tex2D<float>(texTarget, x, y - 1.0f * dy) * 8.0f;
t1 += tex2D<float>(texTarget, x, y + 1.0f * dy) * 8.0f;
t1 -= tex2D<float>(texTarget, x, y + 2.0f * dy);
t1 /= 12.0f;
t1 = tex2D<float>(texTarget, x, y - 2.0f * dy);
t1 -= tex2D<float>(texTarget, x, y - 1.0f * dy) * 8.0f;
t1 += tex2D<float>(texTarget, x, y + 1.0f * dy) * 8.0f;
t1 -= tex2D<float>(texTarget, x, y + 2.0f * dy);
t1 /= 12.0f;
Iy[pos] = (t0 + t1) * 0.5f;
Iy[pos] = (t0 + t1) * 0.5f;
}
///////////////////////////////////////////////////////////////////////////////
@@ -102,43 +108,40 @@ __global__ void ComputeDerivativesKernel(int width, int height, int stride,
/// \param[out] Iy y derivative
/// \param[out] Iz temporal derivative
///////////////////////////////////////////////////////////////////////////////
static void ComputeDerivatives(const float *I0, const float *I1, int w, int h,
int s, float *Ix, float *Iy, float *Iz) {
dim3 threads(32, 6);
dim3 blocks(iDivUp(w, threads.x), iDivUp(h, threads.y));
static void ComputeDerivatives(const float *I0, const float *I1, int w, int h, int s, float *Ix, float *Iy, float *Iz)
{
dim3 threads(32, 6);
dim3 blocks(iDivUp(w, threads.x), iDivUp(h, threads.y));
cudaTextureObject_t texSource, texTarget;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texSource, texTarget;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)I0;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = w;
texRes.res.pitch2D.height = h;
texRes.res.pitch2D.pitchInBytes = s * sizeof(float);
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)I0;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = w;
texRes.res.pitch2D.height = h;
texRes.res.pitch2D.pitchInBytes = s * sizeof(float);
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texSource, &texRes, &texDescr, NULL));
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)I1;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = w;
texRes.res.pitch2D.height = h;
texRes.res.pitch2D.pitchInBytes = s * sizeof(float);
checkCudaErrors(
cudaCreateTextureObject(&texTarget, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texSource, &texRes, &texDescr, NULL));
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)I1;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = w;
texRes.res.pitch2D.height = h;
texRes.res.pitch2D.pitchInBytes = s * sizeof(float);
checkCudaErrors(cudaCreateTextureObject(&texTarget, &texRes, &texDescr, NULL));
ComputeDerivativesKernel<<<blocks, threads>>>(w, h, s, Ix, Iy, Iz, texSource,
texTarget);
ComputeDerivativesKernel<<<blocks, threads>>>(w, h, s, Ix, Iy, Iz, texSource, texTarget);
}

View File

@@ -36,25 +36,24 @@
/// \param[in] stride image stride
/// \param[out] out result
///////////////////////////////////////////////////////////////////////////////
__global__ void DownscaleKernel(int width, int height, int stride, float *out,
cudaTextureObject_t texFine) {
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
__global__ void DownscaleKernel(int width, int height, int stride, float *out, cudaTextureObject_t texFine)
{
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
if (ix >= width || iy >= height) {
return;
}
if (ix >= width || iy >= height) {
return;
}
float dx = 1.0f / (float)width;
float dy = 1.0f / (float)height;
float dx = 1.0f / (float)width;
float dy = 1.0f / (float)height;
float x = ((float)ix + 0.5f) * dx;
float y = ((float)iy + 0.5f) * dy;
float x = ((float)ix + 0.5f) * dx;
float y = ((float)iy + 0.5f) * dy;
out[ix + iy * stride] = 0.25f * (tex2D<float>(texFine, x - dx * 0.25f, y) +
tex2D<float>(texFine, x + dx * 0.25f, y) +
tex2D<float>(texFine, x, y - dy * 0.25f) +
tex2D<float>(texFine, x, y + dy * 0.25f));
out[ix + iy * stride] = 0.25f
* (tex2D<float>(texFine, x - dx * 0.25f, y) + tex2D<float>(texFine, x + dx * 0.25f, y)
+ tex2D<float>(texFine, x, y - dy * 0.25f) + tex2D<float>(texFine, x, y + dy * 0.25f));
}
///////////////////////////////////////////////////////////////////////////////
@@ -66,33 +65,33 @@ __global__ void DownscaleKernel(int width, int height, int stride, float *out,
/// \param[in] stride image stride
/// \param[out] out result
///////////////////////////////////////////////////////////////////////////////
static void Downscale(const float *src, int width, int height, int stride,
int newWidth, int newHeight, int newStride, float *out) {
dim3 threads(32, 8);
dim3 blocks(iDivUp(newWidth, threads.x), iDivUp(newHeight, threads.y));
static void
Downscale(const float *src, int width, int height, int stride, int newWidth, int newHeight, int newStride, float *out)
{
dim3 threads(32, 8);
dim3 blocks(iDivUp(newWidth, threads.x), iDivUp(newHeight, threads.y));
cudaTextureObject_t texFine;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texFine;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)src;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = stride * sizeof(float);
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)src;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = stride * sizeof(float);
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(cudaCreateTextureObject(&texFine, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texFine, &texRes, &texDescr, NULL));
DownscaleKernel<<<blocks, threads>>>(newWidth, newHeight, newStride, out,
texFine);
DownscaleKernel<<<blocks, threads>>>(newWidth, newHeight, newStride, out, texFine);
}

View File

@@ -28,12 +28,12 @@
#include "common.h"
// include kernels
#include "addKernel.cuh"
#include "derivativesKernel.cuh"
#include "downscaleKernel.cuh"
#include "solverKernel.cuh"
#include "upscaleKernel.cuh"
#include "warpingKernel.cuh"
#include "derivativesKernel.cuh"
#include "solverKernel.cuh"
#include "addKernel.cuh"
///////////////////////////////////////////////////////////////////////////////
/// \brief method logic
@@ -51,166 +51,203 @@
/// \param[out] u horizontal displacement
/// \param[out] v vertical displacement
///////////////////////////////////////////////////////////////////////////////
void ComputeFlowCUDA(const float *I0, const float *I1, int width, int height,
int stride, float alpha, int nLevels, int nWarpIters,
int nSolverIters, float *u, float *v) {
printf("Computing optical flow on GPU...\n");
void ComputeFlowCUDA(const float *I0,
const float *I1,
int width,
int height,
int stride,
float alpha,
int nLevels,
int nWarpIters,
int nSolverIters,
float *u,
float *v)
{
printf("Computing optical flow on GPU...\n");
// pI0 and pI1 will hold device pointers
const float **pI0 = new const float *[nLevels];
const float **pI1 = new const float *[nLevels];
// pI0 and pI1 will hold device pointers
const float **pI0 = new const float *[nLevels];
const float **pI1 = new const float *[nLevels];
int *pW = new int[nLevels];
int *pH = new int[nLevels];
int *pS = new int[nLevels];
int *pW = new int[nLevels];
int *pH = new int[nLevels];
int *pS = new int[nLevels];
// device memory pointers
float *d_tmp;
float *d_du0;
float *d_dv0;
float *d_du1;
float *d_dv1;
// device memory pointers
float *d_tmp;
float *d_du0;
float *d_dv0;
float *d_du1;
float *d_dv1;
float *d_Ix;
float *d_Iy;
float *d_Iz;
float *d_Ix;
float *d_Iy;
float *d_Iz;
float *d_u;
float *d_v;
float *d_nu;
float *d_nv;
float *d_u;
float *d_v;
float *d_nu;
float *d_nv;
const int dataSize = stride * height * sizeof(float);
const int dataSize = stride * height * sizeof(float);
checkCudaErrors(cudaMalloc(&d_tmp, dataSize));
checkCudaErrors(cudaMalloc(&d_du0, dataSize));
checkCudaErrors(cudaMalloc(&d_dv0, dataSize));
checkCudaErrors(cudaMalloc(&d_du1, dataSize));
checkCudaErrors(cudaMalloc(&d_dv1, dataSize));
checkCudaErrors(cudaMalloc(&d_tmp, dataSize));
checkCudaErrors(cudaMalloc(&d_du0, dataSize));
checkCudaErrors(cudaMalloc(&d_dv0, dataSize));
checkCudaErrors(cudaMalloc(&d_du1, dataSize));
checkCudaErrors(cudaMalloc(&d_dv1, dataSize));
checkCudaErrors(cudaMalloc(&d_Ix, dataSize));
checkCudaErrors(cudaMalloc(&d_Iy, dataSize));
checkCudaErrors(cudaMalloc(&d_Iz, dataSize));
checkCudaErrors(cudaMalloc(&d_Ix, dataSize));
checkCudaErrors(cudaMalloc(&d_Iy, dataSize));
checkCudaErrors(cudaMalloc(&d_Iz, dataSize));
checkCudaErrors(cudaMalloc(&d_u, dataSize));
checkCudaErrors(cudaMalloc(&d_v, dataSize));
checkCudaErrors(cudaMalloc(&d_nu, dataSize));
checkCudaErrors(cudaMalloc(&d_nv, dataSize));
checkCudaErrors(cudaMalloc(&d_u, dataSize));
checkCudaErrors(cudaMalloc(&d_v, dataSize));
checkCudaErrors(cudaMalloc(&d_nu, dataSize));
checkCudaErrors(cudaMalloc(&d_nv, dataSize));
// prepare pyramid
// prepare pyramid
int currentLevel = nLevels - 1;
// allocate GPU memory for input images
checkCudaErrors(cudaMalloc(pI0 + currentLevel, dataSize));
checkCudaErrors(cudaMalloc(pI1 + currentLevel, dataSize));
int currentLevel = nLevels - 1;
// allocate GPU memory for input images
checkCudaErrors(cudaMalloc(pI0 + currentLevel, dataSize));
checkCudaErrors(cudaMalloc(pI1 + currentLevel, dataSize));
checkCudaErrors(cudaMemcpy((void *)pI0[currentLevel], I0, dataSize,
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy((void *)pI1[currentLevel], I1, dataSize,
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy((void *)pI0[currentLevel], I0, dataSize, cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy((void *)pI1[currentLevel], I1, dataSize, cudaMemcpyHostToDevice));
pW[currentLevel] = width;
pH[currentLevel] = height;
pS[currentLevel] = stride;
pW[currentLevel] = width;
pH[currentLevel] = height;
pS[currentLevel] = stride;
for (; currentLevel > 0; --currentLevel) {
int nw = pW[currentLevel] / 2;
int nh = pH[currentLevel] / 2;
int ns = iAlignUp(nw);
for (; currentLevel > 0; --currentLevel) {
int nw = pW[currentLevel] / 2;
int nh = pH[currentLevel] / 2;
int ns = iAlignUp(nw);
checkCudaErrors(
cudaMalloc(pI0 + currentLevel - 1, ns * nh * sizeof(float)));
checkCudaErrors(
cudaMalloc(pI1 + currentLevel - 1, ns * nh * sizeof(float)));
checkCudaErrors(cudaMalloc(pI0 + currentLevel - 1, ns * nh * sizeof(float)));
checkCudaErrors(cudaMalloc(pI1 + currentLevel - 1, ns * nh * sizeof(float)));
Downscale(pI0[currentLevel], pW[currentLevel], pH[currentLevel],
pS[currentLevel], nw, nh, ns, (float *)pI0[currentLevel - 1]);
Downscale(pI0[currentLevel],
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
nw,
nh,
ns,
(float *)pI0[currentLevel - 1]);
Downscale(pI1[currentLevel], pW[currentLevel], pH[currentLevel],
pS[currentLevel], nw, nh, ns, (float *)pI1[currentLevel - 1]);
Downscale(pI1[currentLevel],
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
nw,
nh,
ns,
(float *)pI1[currentLevel - 1]);
pW[currentLevel - 1] = nw;
pH[currentLevel - 1] = nh;
pS[currentLevel - 1] = ns;
}
checkCudaErrors(cudaMemset(d_u, 0, stride * height * sizeof(float)));
checkCudaErrors(cudaMemset(d_v, 0, stride * height * sizeof(float)));
// compute flow
for (; currentLevel < nLevels; ++currentLevel) {
for (int warpIter = 0; warpIter < nWarpIters; ++warpIter) {
checkCudaErrors(cudaMemset(d_du0, 0, dataSize));
checkCudaErrors(cudaMemset(d_dv0, 0, dataSize));
checkCudaErrors(cudaMemset(d_du1, 0, dataSize));
checkCudaErrors(cudaMemset(d_dv1, 0, dataSize));
// on current level we compute optical flow
// between frame 0 and warped frame 1
WarpImage(pI1[currentLevel], pW[currentLevel], pH[currentLevel],
pS[currentLevel], d_u, d_v, d_tmp);
ComputeDerivatives(pI0[currentLevel], d_tmp, pW[currentLevel],
pH[currentLevel], pS[currentLevel], d_Ix, d_Iy, d_Iz);
for (int iter = 0; iter < nSolverIters; ++iter) {
SolveForUpdate(d_du0, d_dv0, d_Ix, d_Iy, d_Iz, pW[currentLevel],
pH[currentLevel], pS[currentLevel], alpha, d_du1, d_dv1);
Swap(d_du0, d_du1);
Swap(d_dv0, d_dv1);
}
// update u, v
Add(d_u, d_du0, pH[currentLevel] * pS[currentLevel], d_u);
Add(d_v, d_dv0, pH[currentLevel] * pS[currentLevel], d_v);
pW[currentLevel - 1] = nw;
pH[currentLevel - 1] = nh;
pS[currentLevel - 1] = ns;
}
if (currentLevel != nLevels - 1) {
// prolongate solution
float scaleX = (float)pW[currentLevel + 1] / (float)pW[currentLevel];
checkCudaErrors(cudaMemset(d_u, 0, stride * height * sizeof(float)));
checkCudaErrors(cudaMemset(d_v, 0, stride * height * sizeof(float)));
Upscale(d_u, pW[currentLevel], pH[currentLevel], pS[currentLevel],
pW[currentLevel + 1], pH[currentLevel + 1], pS[currentLevel + 1],
scaleX, d_nu);
// compute flow
for (; currentLevel < nLevels; ++currentLevel) {
for (int warpIter = 0; warpIter < nWarpIters; ++warpIter) {
checkCudaErrors(cudaMemset(d_du0, 0, dataSize));
checkCudaErrors(cudaMemset(d_dv0, 0, dataSize));
float scaleY = (float)pH[currentLevel + 1] / (float)pH[currentLevel];
checkCudaErrors(cudaMemset(d_du1, 0, dataSize));
checkCudaErrors(cudaMemset(d_dv1, 0, dataSize));
Upscale(d_v, pW[currentLevel], pH[currentLevel], pS[currentLevel],
pW[currentLevel + 1], pH[currentLevel + 1], pS[currentLevel + 1],
scaleY, d_nv);
// on current level we compute optical flow
// between frame 0 and warped frame 1
WarpImage(pI1[currentLevel], pW[currentLevel], pH[currentLevel], pS[currentLevel], d_u, d_v, d_tmp);
Swap(d_u, d_nu);
Swap(d_v, d_nv);
ComputeDerivatives(
pI0[currentLevel], d_tmp, pW[currentLevel], pH[currentLevel], pS[currentLevel], d_Ix, d_Iy, d_Iz);
for (int iter = 0; iter < nSolverIters; ++iter) {
SolveForUpdate(d_du0,
d_dv0,
d_Ix,
d_Iy,
d_Iz,
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
alpha,
d_du1,
d_dv1);
Swap(d_du0, d_du1);
Swap(d_dv0, d_dv1);
}
// update u, v
Add(d_u, d_du0, pH[currentLevel] * pS[currentLevel], d_u);
Add(d_v, d_dv0, pH[currentLevel] * pS[currentLevel], d_v);
}
if (currentLevel != nLevels - 1) {
// prolongate solution
float scaleX = (float)pW[currentLevel + 1] / (float)pW[currentLevel];
Upscale(d_u,
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
pW[currentLevel + 1],
pH[currentLevel + 1],
pS[currentLevel + 1],
scaleX,
d_nu);
float scaleY = (float)pH[currentLevel + 1] / (float)pH[currentLevel];
Upscale(d_v,
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
pW[currentLevel + 1],
pH[currentLevel + 1],
pS[currentLevel + 1],
scaleY,
d_nv);
Swap(d_u, d_nu);
Swap(d_v, d_nv);
}
}
}
checkCudaErrors(cudaMemcpy(u, d_u, dataSize, cudaMemcpyDeviceToHost));
checkCudaErrors(cudaMemcpy(v, d_v, dataSize, cudaMemcpyDeviceToHost));
checkCudaErrors(cudaMemcpy(u, d_u, dataSize, cudaMemcpyDeviceToHost));
checkCudaErrors(cudaMemcpy(v, d_v, dataSize, cudaMemcpyDeviceToHost));
// cleanup
for (int i = 0; i < nLevels; ++i) {
checkCudaErrors(cudaFree((void *)pI0[i]));
checkCudaErrors(cudaFree((void *)pI1[i]));
}
// cleanup
for (int i = 0; i < nLevels; ++i) {
checkCudaErrors(cudaFree((void *)pI0[i]));
checkCudaErrors(cudaFree((void *)pI1[i]));
}
delete[] pI0;
delete[] pI1;
delete[] pW;
delete[] pH;
delete[] pS;
delete[] pI0;
delete[] pI1;
delete[] pW;
delete[] pH;
delete[] pS;
checkCudaErrors(cudaFree(d_tmp));
checkCudaErrors(cudaFree(d_du0));
checkCudaErrors(cudaFree(d_dv0));
checkCudaErrors(cudaFree(d_du1));
checkCudaErrors(cudaFree(d_dv1));
checkCudaErrors(cudaFree(d_Ix));
checkCudaErrors(cudaFree(d_Iy));
checkCudaErrors(cudaFree(d_Iz));
checkCudaErrors(cudaFree(d_nu));
checkCudaErrors(cudaFree(d_nv));
checkCudaErrors(cudaFree(d_u));
checkCudaErrors(cudaFree(d_v));
checkCudaErrors(cudaFree(d_tmp));
checkCudaErrors(cudaFree(d_du0));
checkCudaErrors(cudaFree(d_dv0));
checkCudaErrors(cudaFree(d_du1));
checkCudaErrors(cudaFree(d_dv1));
checkCudaErrors(cudaFree(d_Ix));
checkCudaErrors(cudaFree(d_Iy));
checkCudaErrors(cudaFree(d_Iz));
checkCudaErrors(cudaFree(d_nu));
checkCudaErrors(cudaFree(d_nv));
checkCudaErrors(cudaFree(d_u));
checkCudaErrors(cudaFree(d_v));
}

View File

@@ -28,16 +28,15 @@
#ifndef FLOW_CUDA_H
#define FLOW_CUDA_H
void ComputeFlowCUDA(
const float *I0, // source frame
const float *I1, // tracked frame
int width, // frame width
int height, // frame height
int stride, // row access stride
float alpha, // smoothness coefficient
int nLevels, // number of levels in pyramid
int nWarpIters, // number of warping iterations per pyramid level
int nSolverIters, // number of solver iterations (for linear system)
float *u, // output horizontal flow
float *v); // output vertical flow
void ComputeFlowCUDA(const float *I0, // source frame
const float *I1, // tracked frame
int width, // frame width
int height, // frame height
int stride, // row access stride
float alpha, // smoothness coefficient
int nLevels, // number of levels in pyramid
int nWarpIters, // number of warping iterations per pyramid level
int nSolverIters, // number of solver iterations (for linear system)
float *u, // output horizontal flow
float *v); // output vertical flow
#endif

View File

@@ -25,9 +25,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "common.h"
#include "flowGold.h"
#include "common.h"
///////////////////////////////////////////////////////////////////////////////
/// \brief host texture fetch
///
@@ -41,42 +42,49 @@
/// \param[in] y y coord of the point to fetch value at
/// \return fetched value
///////////////////////////////////////////////////////////////////////////////
inline float Tex2D(const float *t, int w, int h, int s, float x, float y) {
// integer parts in floating point format
float intPartX, intPartY;
inline float Tex2D(const float *t, int w, int h, int s, float x, float y)
{
// integer parts in floating point format
float intPartX, intPartY;
// get fractional parts of coordinates
float dx = fabsf(modff(x, &intPartX));
float dy = fabsf(modff(y, &intPartY));
// get fractional parts of coordinates
float dx = fabsf(modff(x, &intPartX));
float dy = fabsf(modff(y, &intPartY));
// assume pixels are squares
// one of the corners
int ix0 = (int)intPartX;
int iy0 = (int)intPartY;
// assume pixels are squares
// one of the corners
int ix0 = (int)intPartX;
int iy0 = (int)intPartY;
// mirror out-of-range position
if (ix0 < 0) ix0 = abs(ix0 + 1);
// mirror out-of-range position
if (ix0 < 0)
ix0 = abs(ix0 + 1);
if (iy0 < 0) iy0 = abs(iy0 + 1);
if (iy0 < 0)
iy0 = abs(iy0 + 1);
if (ix0 >= w) ix0 = w * 2 - ix0 - 1;
if (ix0 >= w)
ix0 = w * 2 - ix0 - 1;
if (iy0 >= h) iy0 = h * 2 - iy0 - 1;
if (iy0 >= h)
iy0 = h * 2 - iy0 - 1;
// corner which is opposite to (ix0, iy0)
int ix1 = ix0 + 1;
int iy1 = iy0 + 1;
// corner which is opposite to (ix0, iy0)
int ix1 = ix0 + 1;
int iy1 = iy0 + 1;
if (ix1 >= w) ix1 = w * 2 - ix1 - 1;
if (ix1 >= w)
ix1 = w * 2 - ix1 - 1;
if (iy1 >= h) iy1 = h * 2 - iy1 - 1;
if (iy1 >= h)
iy1 = h * 2 - iy1 - 1;
float res = t[ix0 + iy0 * s] * (1.0f - dx) * (1.0f - dy);
res += t[ix1 + iy0 * s] * dx * (1.0f - dy);
res += t[ix0 + iy1 * s] * (1.0f - dx) * dy;
res += t[ix1 + iy1 * s] * dx * dy;
float res = t[ix0 + iy0 * s] * (1.0f - dx) * (1.0f - dy);
res += t[ix1 + iy0 * s] * dx * (1.0f - dy);
res += t[ix0 + iy1 * s] * (1.0f - dx) * dy;
res += t[ix1 + iy1 * s] * dx * dy;
return res;
return res;
}
///////////////////////////////////////////////////////////////////////////////
@@ -92,16 +100,21 @@ inline float Tex2D(const float *t, int w, int h, int s, float x, float y) {
/// \param[in] y y coord of the point to fetch value at
/// \return fetched value
///////////////////////////////////////////////////////////////////////////////
inline float Tex2Di(const float *src, int w, int h, int s, int x, int y) {
if (x < 0) x = abs(x + 1);
inline float Tex2Di(const float *src, int w, int h, int s, int x, int y)
{
if (x < 0)
x = abs(x + 1);
if (y < 0) y = abs(y + 1);
if (y < 0)
y = abs(y + 1);
if (x >= w) x = w * 2 - x - 1;
if (x >= w)
x = w * 2 - x - 1;
if (y >= h) y = h * 2 - y - 1;
if (y >= h)
y = h * 2 - y - 1;
return src[x + y * s];
return src[x + y * s];
}
///////////////////////////////////////////////////////////////////////////////
@@ -115,23 +128,24 @@ inline float Tex2Di(const float *src, int w, int h, int s, int x, int y) {
/// \param[in] newStride image new stride
/// \param[out] out downscaled image data
///////////////////////////////////////////////////////////////////////////////
static void Downscale(const float *src, int width, int height, int stride,
int newWidth, int newHeight, int newStride, float *out) {
for (int i = 0; i < newHeight; ++i) {
for (int j = 0; j < newWidth; ++j) {
const int srcX = j * 2;
const int srcY = i * 2;
// average 4 neighbouring pixels
float sum;
sum = Tex2Di(src, width, height, stride, srcX + 0, srcY + 0);
sum += Tex2Di(src, width, height, stride, srcX + 0, srcY + 1);
sum += Tex2Di(src, width, height, stride, srcX + 1, srcY + 0);
sum += Tex2Di(src, width, height, stride, srcX + 1, srcY + 1);
// normalize
sum *= 0.25f;
out[j + i * newStride] = sum;
static void
Downscale(const float *src, int width, int height, int stride, int newWidth, int newHeight, int newStride, float *out)
{
for (int i = 0; i < newHeight; ++i) {
for (int j = 0; j < newWidth; ++j) {
const int srcX = j * 2;
const int srcY = i * 2;
// average 4 neighbouring pixels
float sum;
sum = Tex2Di(src, width, height, stride, srcX + 0, srcY + 0);
sum += Tex2Di(src, width, height, stride, srcX + 0, srcY + 1);
sum += Tex2Di(src, width, height, stride, srcX + 1, srcY + 0);
sum += Tex2Di(src, width, height, stride, srcX + 1, srcY + 1);
// normalize
sum *= 0.25f;
out[j + i * newStride] = sum;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -146,18 +160,25 @@ static void Downscale(const float *src, int width, int height, int stride,
/// \param[in] scale value scale factor (multiplier)
/// \param[out] out upscaled field component
///////////////////////////////////////////////////////////////////////////////
static void Upscale(const float *src, int width, int height, int stride,
int newWidth, int newHeight, int newStride, float scale,
float *out) {
for (int i = 0; i < newHeight; ++i) {
for (int j = 0; j < newWidth; ++j) {
// position within smaller image
float x = ((float)j - 0.5f) * 0.5f;
float y = ((float)i - 0.5f) * 0.5f;
static void Upscale(const float *src,
int width,
int height,
int stride,
int newWidth,
int newHeight,
int newStride,
float scale,
float *out)
{
for (int i = 0; i < newHeight; ++i) {
for (int j = 0; j < newWidth; ++j) {
// position within smaller image
float x = ((float)j - 0.5f) * 0.5f;
float y = ((float)i - 0.5f) * 0.5f;
out[j + i * newStride] = Tex2D(src, width, height, stride, x, y) * scale;
out[j + i * newStride] = Tex2D(src, width, height, stride, x, y) * scale;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -176,18 +197,18 @@ static void Upscale(const float *src, int width, int height, int stride,
/// \param[in] v vertical displacement
/// \param[out] out warped image
///////////////////////////////////////////////////////////////////////////////
static void WarpImage(const float *src, int w, int h, int s, const float *u,
const float *v, float *out) {
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
// warped coords
float x = (float)j + u[pos];
float y = (float)i + v[pos];
static void WarpImage(const float *src, int w, int h, int s, const float *u, const float *v, float *out)
{
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
// warped coords
float x = (float)j + u[pos];
float y = (float)i + v[pos];
out[pos] = Tex2D(src, w, h, s, x, y);
out[pos] = Tex2D(src, w, h, s, x, y);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -201,48 +222,48 @@ static void WarpImage(const float *src, int w, int h, int s, const float *u,
/// \param[out] Iy y derivative
/// \param[out] Iz temporal derivative
///////////////////////////////////////////////////////////////////////////////
static void ComputeDerivatives(const float *I0, const float *I1, int w, int h,
int s, float *Ix, float *Iy, float *Iz) {
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
float t0, t1;
// derivative filter is (1, -8, 0, 8, -1)/12
// x derivative
t0 = Tex2Di(I0, w, h, s, j - 2, i);
t0 -= Tex2Di(I0, w, h, s, j - 1, i) * 8.0f;
t0 += Tex2Di(I0, w, h, s, j + 1, i) * 8.0f;
t0 -= Tex2Di(I0, w, h, s, j + 2, i);
t0 /= 12.0f;
static void ComputeDerivatives(const float *I0, const float *I1, int w, int h, int s, float *Ix, float *Iy, float *Iz)
{
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
float t0, t1;
// derivative filter is (1, -8, 0, 8, -1)/12
// x derivative
t0 = Tex2Di(I0, w, h, s, j - 2, i);
t0 -= Tex2Di(I0, w, h, s, j - 1, i) * 8.0f;
t0 += Tex2Di(I0, w, h, s, j + 1, i) * 8.0f;
t0 -= Tex2Di(I0, w, h, s, j + 2, i);
t0 /= 12.0f;
t1 = Tex2Di(I1, w, h, s, j - 2, i);
t1 -= Tex2Di(I1, w, h, s, j - 1, i) * 8.0f;
t1 += Tex2Di(I1, w, h, s, j + 1, i) * 8.0f;
t1 -= Tex2Di(I1, w, h, s, j + 2, i);
t1 /= 12.0f;
t1 = Tex2Di(I1, w, h, s, j - 2, i);
t1 -= Tex2Di(I1, w, h, s, j - 1, i) * 8.0f;
t1 += Tex2Di(I1, w, h, s, j + 1, i) * 8.0f;
t1 -= Tex2Di(I1, w, h, s, j + 2, i);
t1 /= 12.0f;
// spatial derivatives are averaged
Ix[pos] = (t0 + t1) * 0.5f;
// spatial derivatives are averaged
Ix[pos] = (t0 + t1) * 0.5f;
// t derivative
Iz[pos] = I1[pos] - I0[pos];
// t derivative
Iz[pos] = I1[pos] - I0[pos];
// y derivative
t0 = Tex2Di(I0, w, h, s, j, i - 2);
t0 -= Tex2Di(I0, w, h, s, j, i - 1) * 8.0f;
t0 += Tex2Di(I0, w, h, s, j, i + 1) * 8.0f;
t0 -= Tex2Di(I0, w, h, s, j, i + 2);
t0 /= 12.0f;
// y derivative
t0 = Tex2Di(I0, w, h, s, j, i - 2);
t0 -= Tex2Di(I0, w, h, s, j, i - 1) * 8.0f;
t0 += Tex2Di(I0, w, h, s, j, i + 1) * 8.0f;
t0 -= Tex2Di(I0, w, h, s, j, i + 2);
t0 /= 12.0f;
t1 = Tex2Di(I1, w, h, s, j, i - 2);
t1 -= Tex2Di(I1, w, h, s, j, i - 1) * 8.0f;
t1 += Tex2Di(I1, w, h, s, j, i + 1) * 8.0f;
t1 -= Tex2Di(I1, w, h, s, j, i + 2);
t1 /= 12.0f;
t1 = Tex2Di(I1, w, h, s, j, i - 2);
t1 -= Tex2Di(I1, w, h, s, j, i - 1) * 8.0f;
t1 += Tex2Di(I1, w, h, s, j, i + 1) * 8.0f;
t1 -= Tex2Di(I1, w, h, s, j, i + 2);
t1 /= 12.0f;
Iy[pos] = (t0 + t1) * 0.5f;
Iy[pos] = (t0 + t1) * 0.5f;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -261,45 +282,53 @@ static void ComputeDerivatives(const float *I0, const float *I1, int w, int h,
/// \param[out] du1 new horizontal displacement approximation
/// \param[out] dv1 new vertical displacement approximation
///////////////////////////////////////////////////////////////////////////////
static void SolveForUpdate(const float *du0, const float *dv0, const float *Ix,
const float *Iy, const float *Iz, int w, int h,
int s, float alpha, float *du1, float *dv1) {
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
int left, right, up, down;
static void SolveForUpdate(const float *du0,
const float *dv0,
const float *Ix,
const float *Iy,
const float *Iz,
int w,
int h,
int s,
float alpha,
float *du1,
float *dv1)
{
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
int left, right, up, down;
// handle borders
if (j != 0)
left = pos - 1;
else
left = pos;
// handle borders
if (j != 0)
left = pos - 1;
else
left = pos;
if (j != w - 1)
right = pos + 1;
else
right = pos;
if (j != w - 1)
right = pos + 1;
else
right = pos;
if (i != 0)
down = pos - s;
else
down = pos;
if (i != 0)
down = pos - s;
else
down = pos;
if (i != h - 1)
up = pos + s;
else
up = pos;
if (i != h - 1)
up = pos + s;
else
up = pos;
float sumU = (du0[left] + du0[right] + du0[up] + du0[down]) * 0.25f;
float sumV = (dv0[left] + dv0[right] + dv0[up] + dv0[down]) * 0.25f;
float sumU = (du0[left] + du0[right] + du0[up] + du0[down]) * 0.25f;
float sumV = (dv0[left] + dv0[right] + dv0[up] + dv0[down]) * 0.25f;
float frac = (Ix[pos] * sumU + Iy[pos] * sumV + Iz[pos]) /
(Ix[pos] * Ix[pos] + Iy[pos] * Iy[pos] + alpha);
float frac = (Ix[pos] * sumU + Iy[pos] * sumV + Iz[pos]) / (Ix[pos] * Ix[pos] + Iy[pos] * Iy[pos] + alpha);
du1[pos] = sumU - Ix[pos] * frac;
dv1[pos] = sumV - Iy[pos] * frac;
du1[pos] = sumU - Ix[pos] * frac;
dv1[pos] = sumV - Iy[pos] * frac;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -318,145 +347,177 @@ static void SolveForUpdate(const float *du0, const float *dv0, const float *Ix,
/// \param[out] u horizontal displacement
/// \param[out] v vertical displacement
///////////////////////////////////////////////////////////////////////////////
void ComputeFlowGold(const float *I0, const float *I1, int width, int height,
int stride, float alpha, int nLevels, int nWarpIters,
int nSolverIters, float *u, float *v) {
printf("Computing optical flow on CPU...\n");
void ComputeFlowGold(const float *I0,
const float *I1,
int width,
int height,
int stride,
float alpha,
int nLevels,
int nWarpIters,
int nSolverIters,
float *u,
float *v)
{
printf("Computing optical flow on CPU...\n");
float *u0 = u;
float *v0 = v;
float *u0 = u;
float *v0 = v;
const float **pI0 = new const float *[nLevels];
const float **pI1 = new const float *[nLevels];
const float **pI0 = new const float *[nLevels];
const float **pI1 = new const float *[nLevels];
int *pW = new int[nLevels];
int *pH = new int[nLevels];
int *pS = new int[nLevels];
int *pW = new int[nLevels];
int *pH = new int[nLevels];
int *pS = new int[nLevels];
const int pixelCountAligned = height * stride;
const int pixelCountAligned = height * stride;
float *tmp = new float[pixelCountAligned];
float *du0 = new float[pixelCountAligned];
float *dv0 = new float[pixelCountAligned];
float *du1 = new float[pixelCountAligned];
float *dv1 = new float[pixelCountAligned];
float *Ix = new float[pixelCountAligned];
float *Iy = new float[pixelCountAligned];
float *Iz = new float[pixelCountAligned];
float *nu = new float[pixelCountAligned];
float *nv = new float[pixelCountAligned];
float *tmp = new float[pixelCountAligned];
float *du0 = new float[pixelCountAligned];
float *dv0 = new float[pixelCountAligned];
float *du1 = new float[pixelCountAligned];
float *dv1 = new float[pixelCountAligned];
float *Ix = new float[pixelCountAligned];
float *Iy = new float[pixelCountAligned];
float *Iz = new float[pixelCountAligned];
float *nu = new float[pixelCountAligned];
float *nv = new float[pixelCountAligned];
// prepare pyramid
int currentLevel = nLevels - 1;
pI0[currentLevel] = I0;
pI1[currentLevel] = I1;
// prepare pyramid
int currentLevel = nLevels - 1;
pI0[currentLevel] = I0;
pI1[currentLevel] = I1;
pW[currentLevel] = width;
pH[currentLevel] = height;
pS[currentLevel] = stride;
pW[currentLevel] = width;
pH[currentLevel] = height;
pS[currentLevel] = stride;
for (; currentLevel > 0; --currentLevel) {
int nw = pW[currentLevel] / 2;
int nh = pH[currentLevel] / 2;
int ns = iAlignUp(nw);
pI0[currentLevel - 1] = new float[ns * nh];
pI1[currentLevel - 1] = new float[ns * nh];
for (; currentLevel > 0; --currentLevel) {
int nw = pW[currentLevel] / 2;
int nh = pH[currentLevel] / 2;
int ns = iAlignUp(nw);
pI0[currentLevel - 1] = new float[ns * nh];
pI1[currentLevel - 1] = new float[ns * nh];
Downscale(pI0[currentLevel], pW[currentLevel], pH[currentLevel],
pS[currentLevel], nw, nh, ns, (float *)pI0[currentLevel - 1]);
Downscale(pI0[currentLevel],
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
nw,
nh,
ns,
(float *)pI0[currentLevel - 1]);
Downscale(pI1[currentLevel], pW[currentLevel], pH[currentLevel],
pS[currentLevel], nw, nh, ns, (float *)pI1[currentLevel - 1]);
Downscale(pI1[currentLevel],
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
nw,
nh,
ns,
(float *)pI1[currentLevel - 1]);
pW[currentLevel - 1] = nw;
pH[currentLevel - 1] = nh;
pS[currentLevel - 1] = ns;
}
// initial approximation
memset(u, 0, stride * height * sizeof(float));
memset(v, 0, stride * height * sizeof(float));
// compute flow
for (; currentLevel < nLevels; ++currentLevel) {
for (int warpIter = 0; warpIter < nWarpIters; ++warpIter) {
memset(du0, 0, pixelCountAligned * sizeof(float));
memset(dv0, 0, pixelCountAligned * sizeof(float));
memset(du1, 0, pixelCountAligned * sizeof(float));
memset(dv1, 0, pixelCountAligned * sizeof(float));
WarpImage(pI1[currentLevel], pW[currentLevel], pH[currentLevel],
pS[currentLevel], u, v, tmp);
// on current level we compute optical flow
// between frame 0 and warped frame 1
ComputeDerivatives(pI0[currentLevel], tmp, pW[currentLevel],
pH[currentLevel], pS[currentLevel], Ix, Iy, Iz);
for (int iter = 0; iter < nSolverIters; ++iter) {
SolveForUpdate(du0, dv0, Ix, Iy, Iz, pW[currentLevel], pH[currentLevel],
pS[currentLevel], alpha, du1, dv1);
Swap(du0, du1);
Swap(dv0, dv1);
}
// update u, v
for (int i = 0; i < pH[currentLevel] * pS[currentLevel]; ++i) {
u[i] += du0[i];
v[i] += dv0[i];
}
} // end for (int warpIter = 0; warpIter < nWarpIters; ++warpIter)
if (currentLevel != nLevels - 1) {
// prolongate solution
float scaleX = (float)pW[currentLevel + 1] / (float)pW[currentLevel];
Upscale(u, pW[currentLevel], pH[currentLevel], pS[currentLevel],
pW[currentLevel + 1], pH[currentLevel + 1], pS[currentLevel + 1],
scaleX, nu);
float scaleY = (float)pH[currentLevel + 1] / (float)pH[currentLevel];
Upscale(v, pW[currentLevel], pH[currentLevel], pS[currentLevel],
pW[currentLevel + 1], pH[currentLevel + 1], pS[currentLevel + 1],
scaleY, nv);
Swap(u, nu);
Swap(v, nv);
pW[currentLevel - 1] = nw;
pH[currentLevel - 1] = nh;
pS[currentLevel - 1] = ns;
}
} // end for (; currentLevel < nLevels; ++currentLevel)
if (u != u0) {
// solution is not in the specified array
// copy
memcpy(u0, u, pixelCountAligned * sizeof(float));
memcpy(v0, v, pixelCountAligned * sizeof(float));
Swap(u, nu);
Swap(v, nv);
}
// initial approximation
memset(u, 0, stride * height * sizeof(float));
memset(v, 0, stride * height * sizeof(float));
// cleanup
// last level is not being freed here
// because it refers to input images
for (int i = 0; i < nLevels - 1; ++i) {
delete[] pI0[i];
delete[] pI1[i];
}
// compute flow
for (; currentLevel < nLevels; ++currentLevel) {
for (int warpIter = 0; warpIter < nWarpIters; ++warpIter) {
memset(du0, 0, pixelCountAligned * sizeof(float));
memset(dv0, 0, pixelCountAligned * sizeof(float));
delete[] pI0;
delete[] pI1;
delete[] pW;
delete[] pH;
delete[] pS;
delete[] tmp;
delete[] du0;
delete[] dv0;
delete[] du1;
delete[] dv1;
delete[] Ix;
delete[] Iy;
delete[] Iz;
delete[] nu;
delete[] nv;
memset(du1, 0, pixelCountAligned * sizeof(float));
memset(dv1, 0, pixelCountAligned * sizeof(float));
WarpImage(pI1[currentLevel], pW[currentLevel], pH[currentLevel], pS[currentLevel], u, v, tmp);
// on current level we compute optical flow
// between frame 0 and warped frame 1
ComputeDerivatives(
pI0[currentLevel], tmp, pW[currentLevel], pH[currentLevel], pS[currentLevel], Ix, Iy, Iz);
for (int iter = 0; iter < nSolverIters; ++iter) {
SolveForUpdate(
du0, dv0, Ix, Iy, Iz, pW[currentLevel], pH[currentLevel], pS[currentLevel], alpha, du1, dv1);
Swap(du0, du1);
Swap(dv0, dv1);
}
// update u, v
for (int i = 0; i < pH[currentLevel] * pS[currentLevel]; ++i) {
u[i] += du0[i];
v[i] += dv0[i];
}
} // end for (int warpIter = 0; warpIter < nWarpIters; ++warpIter)
if (currentLevel != nLevels - 1) {
// prolongate solution
float scaleX = (float)pW[currentLevel + 1] / (float)pW[currentLevel];
Upscale(u,
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
pW[currentLevel + 1],
pH[currentLevel + 1],
pS[currentLevel + 1],
scaleX,
nu);
float scaleY = (float)pH[currentLevel + 1] / (float)pH[currentLevel];
Upscale(v,
pW[currentLevel],
pH[currentLevel],
pS[currentLevel],
pW[currentLevel + 1],
pH[currentLevel + 1],
pS[currentLevel + 1],
scaleY,
nv);
Swap(u, nu);
Swap(v, nv);
}
} // end for (; currentLevel < nLevels; ++currentLevel)
if (u != u0) {
// solution is not in the specified array
// copy
memcpy(u0, u, pixelCountAligned * sizeof(float));
memcpy(v0, v, pixelCountAligned * sizeof(float));
Swap(u, nu);
Swap(v, nv);
}
// cleanup
// last level is not being freed here
// because it refers to input images
for (int i = 0; i < nLevels - 1; ++i) {
delete[] pI0[i];
delete[] pI1[i];
}
delete[] pI0;
delete[] pI1;
delete[] pW;
delete[] pH;
delete[] pS;
delete[] tmp;
delete[] du0;
delete[] dv0;
delete[] du1;
delete[] dv1;
delete[] Ix;
delete[] Iy;
delete[] Iz;
delete[] nu;
delete[] nv;
}

View File

@@ -28,17 +28,16 @@
#ifndef FLOW_GOLD_H
#define FLOW_GOLD_H
void ComputeFlowGold(
const float *I0, // source frame
const float *I1, // tracked frame
int width, // frame width
int height, // frame height
int stride, // row access stride
float alpha, // smoothness coefficient
int nLevels, // number of levels in pyramid
int nWarpIters, // number of warping iterations per pyramid level
int nIters, // number of solver iterations (for linear system)
float *u, // output horizontal flow
float *v); // output vertical flow
void ComputeFlowGold(const float *I0, // source frame
const float *I1, // tracked frame
int width, // frame width
int height, // frame height
int stride, // row access stride
float alpha, // smoothness coefficient
int nLevels, // number of levels in pyramid
int nWarpIters, // number of warping iterations per pyramid level
int nIters, // number of solver iterations (for linear system)
float *u, // output horizontal flow
float *v); // output vertical flow
#endif

View File

@@ -31,12 +31,11 @@ const static char *const sSDKsample = "HSOpticalFlow";
const float THRESHOLD = 0.05f;
#include <cuda_runtime.h>
#include <helper_functions.h>
#include "common.h"
#include "flowGold.h"
#include "flowCUDA.h"
#include <helper_functions.h>
#include "flowGold.h"
///////////////////////////////////////////////////////////////////////////////
/// \brief save optical flow in format described on vision.middlebury.edu/flow
@@ -47,30 +46,30 @@ const float THRESHOLD = 0.05f;
/// \param[in] u horizontal displacement
/// \param[in] v vertical displacement
///////////////////////////////////////////////////////////////////////////////
void WriteFloFile(const char *name, int w, int h, int s, const float *u,
const float *v) {
FILE *stream;
stream = fopen(name, "wb");
void WriteFloFile(const char *name, int w, int h, int s, const float *u, const float *v)
{
FILE *stream;
stream = fopen(name, "wb");
if (stream == 0) {
printf("Could not save flow to \"%s\"\n", name);
return;
}
float data = 202021.25f;
fwrite(&data, sizeof(float), 1, stream);
fwrite(&w, sizeof(w), 1, stream);
fwrite(&h, sizeof(h), 1, stream);
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
fwrite(u + pos, sizeof(float), 1, stream);
fwrite(v + pos, sizeof(float), 1, stream);
if (stream == 0) {
printf("Could not save flow to \"%s\"\n", name);
return;
}
}
fclose(stream);
float data = 202021.25f;
fwrite(&data, sizeof(float), 1, stream);
fwrite(&w, sizeof(w), 1, stream);
fwrite(&h, sizeof(h), 1, stream);
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
const int pos = j + i * s;
fwrite(u + pos, sizeof(float), 1, stream);
fwrite(v + pos, sizeof(float), 1, stream);
}
}
fclose(stream);
}
///////////////////////////////////////////////////////////////////////////////
@@ -85,41 +84,41 @@ void WriteFloFile(const char *name, int w, int h, int s, const float *u,
/// \param[in] exePath executable file path
/// \return true if image is successfully loaded or false otherwise
///////////////////////////////////////////////////////////////////////////////
bool LoadImageAsFP32(float *&img_data, int &img_w, int &img_h, int &img_s,
const char *name, const char *exePath) {
printf("Loading \"%s\" ...\n", name);
char *name_ = sdkFindFilePath(name, exePath);
bool LoadImageAsFP32(float *&img_data, int &img_w, int &img_h, int &img_s, const char *name, const char *exePath)
{
printf("Loading \"%s\" ...\n", name);
char *name_ = sdkFindFilePath(name, exePath);
if (!name_) {
printf("File not found\n");
return false;
}
unsigned char *data = 0;
unsigned int w = 0, h = 0;
bool result = sdkLoadPPM4ub(name_, &data, &w, &h);
if (result == false) {
printf("Invalid file format\n");
return false;
}
img_w = w;
img_h = h;
img_s = iAlignUp(img_w);
img_data = new float[img_s * h];
// source is 4 channel image
const int widthStep = 4 * img_w;
for (int i = 0; i < img_h; ++i) {
for (int j = 0; j < img_w; ++j) {
img_data[j + i * img_s] = ((float)data[j * 4 + i * widthStep]) / 255.0f;
if (!name_) {
printf("File not found\n");
return false;
}
}
return true;
unsigned char *data = 0;
unsigned int w = 0, h = 0;
bool result = sdkLoadPPM4ub(name_, &data, &w, &h);
if (result == false) {
printf("Invalid file format\n");
return false;
}
img_w = w;
img_h = h;
img_s = iAlignUp(img_w);
img_data = new float[img_s * h];
// source is 4 channel image
const int widthStep = 4 * img_w;
for (int i = 0; i < img_h; ++i) {
for (int j = 0; j < img_w; ++j) {
img_data[j + i * img_s] = ((float)data[j * 4 + i * widthStep]) / 255.0f;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
@@ -133,105 +132,108 @@ bool LoadImageAsFP32(float *&img_data, int &img_w, int &img_h, int &img_s,
/// \param[in] h_v vertical displacement
/// \return true if discrepancy is lower than a given threshold
///////////////////////////////////////////////////////////////////////////////
bool CompareWithGold(int width, int height, int stride, const float *h_uGold,
const float *h_vGold, const float *h_u, const float *h_v) {
float error = 0.0f;
bool CompareWithGold(int width,
int height,
int stride,
const float *h_uGold,
const float *h_vGold,
const float *h_u,
const float *h_v)
{
float error = 0.0f;
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
const int pos = j + i * stride;
error += fabsf(h_u[pos] - h_uGold[pos]) + fabsf(h_v[pos] - h_vGold[pos]);
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
const int pos = j + i * stride;
error += fabsf(h_u[pos] - h_uGold[pos]) + fabsf(h_v[pos] - h_vGold[pos]);
}
}
}
error /= (float)(width * height);
error /= (float)(width * height);
printf("L1 error : %.6f\n", error);
printf("L1 error : %.6f\n", error);
return (error < THRESHOLD);
return (error < THRESHOLD);
}
///////////////////////////////////////////////////////////////////////////////
/// application entry point
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
// welcome message
printf("%s Starting...\n\n", sSDKsample);
int main(int argc, char **argv)
{
// welcome message
printf("%s Starting...\n\n", sSDKsample);
// pick GPU
findCudaDevice(argc, (const char **)argv);
// pick GPU
findCudaDevice(argc, (const char **)argv);
// find images
const char *const sourceFrameName = "frame10.ppm";
const char *const targetFrameName = "frame11.ppm";
// find images
const char *const sourceFrameName = "frame10.ppm";
const char *const targetFrameName = "frame11.ppm";
// image dimensions
int width;
int height;
// row access stride
int stride;
// image dimensions
int width;
int height;
// row access stride
int stride;
// flow is computed from source image to target image
float *h_source; // source image, host memory
float *h_target; // target image, host memory
// flow is computed from source image to target image
float *h_source; // source image, host memory
float *h_target; // target image, host memory
// load image from file
if (!LoadImageAsFP32(h_source, width, height, stride, sourceFrameName,
argv[0])) {
exit(EXIT_FAILURE);
}
// load image from file
if (!LoadImageAsFP32(h_source, width, height, stride, sourceFrameName, argv[0])) {
exit(EXIT_FAILURE);
}
if (!LoadImageAsFP32(h_target, width, height, stride, targetFrameName,
argv[0])) {
exit(EXIT_FAILURE);
}
if (!LoadImageAsFP32(h_target, width, height, stride, targetFrameName, argv[0])) {
exit(EXIT_FAILURE);
}
// allocate host memory for CPU results
float *h_uGold = new float[stride * height];
float *h_vGold = new float[stride * height];
// allocate host memory for CPU results
float *h_uGold = new float[stride * height];
float *h_vGold = new float[stride * height];
// allocate host memory for GPU results
float *h_u = new float[stride * height];
float *h_v = new float[stride * height];
// allocate host memory for GPU results
float *h_u = new float[stride * height];
float *h_v = new float[stride * height];
// smoothness
// if image brightness is not within [0,1]
// this paramter should be scaled appropriately
const float alpha = 0.2f;
// smoothness
// if image brightness is not within [0,1]
// this paramter should be scaled appropriately
const float alpha = 0.2f;
// number of pyramid levels
const int nLevels = 5;
// number of pyramid levels
const int nLevels = 5;
// number of solver iterations on each level
const int nSolverIters = 500;
// number of solver iterations on each level
const int nSolverIters = 500;
// number of warping iterations
const int nWarpIters = 3;
// number of warping iterations
const int nWarpIters = 3;
ComputeFlowGold(h_source, h_target, width, height, stride, alpha, nLevels,
nWarpIters, nSolverIters, h_uGold, h_vGold);
ComputeFlowGold(
h_source, h_target, width, height, stride, alpha, nLevels, nWarpIters, nSolverIters, h_uGold, h_vGold);
ComputeFlowCUDA(h_source, h_target, width, height, stride, alpha, nLevels,
nWarpIters, nSolverIters, h_u, h_v);
ComputeFlowCUDA(h_source, h_target, width, height, stride, alpha, nLevels, nWarpIters, nSolverIters, h_u, h_v);
// compare results (L1 norm)
bool status =
CompareWithGold(width, height, stride, h_uGold, h_vGold, h_u, h_v);
// compare results (L1 norm)
bool status = CompareWithGold(width, height, stride, h_uGold, h_vGold, h_u, h_v);
WriteFloFile("FlowGPU.flo", width, height, stride, h_u, h_v);
WriteFloFile("FlowGPU.flo", width, height, stride, h_u, h_v);
WriteFloFile("FlowCPU.flo", width, height, stride, h_uGold, h_vGold);
WriteFloFile("FlowCPU.flo", width, height, stride, h_uGold, h_vGold);
// free resources
delete[] h_uGold;
delete[] h_vGold;
// free resources
delete[] h_uGold;
delete[] h_vGold;
delete[] h_u;
delete[] h_v;
delete[] h_u;
delete[] h_v;
delete[] h_source;
delete[] h_target;
delete[] h_source;
delete[] h_target;
// report self-test status
exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
// report self-test status
exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
}

View File

@@ -25,9 +25,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "common.h"
#include <cooperative_groups.h>
#include "common.h"
namespace cg = cooperative_groups;
///////////////////////////////////////////////////////////////////////////////
@@ -48,108 +49,117 @@ namespace cg = cooperative_groups;
/// \param[out] dv1 new vertical displacement approximation
///////////////////////////////////////////////////////////////////////////////
template <int bx, int by>
__global__ void JacobiIteration(const float *du0, const float *dv0,
const float *Ix, const float *Iy,
const float *Iz, int w, int h, int s,
float alpha, float *du1, float *dv1) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__global__ void JacobiIteration(const float *du0,
const float *dv0,
const float *Ix,
const float *Iy,
const float *Iz,
int w,
int h,
int s,
float alpha,
float *du1,
float *dv1)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
volatile __shared__ float du[(bx + 2) * (by + 2)];
volatile __shared__ float dv[(bx + 2) * (by + 2)];
volatile __shared__ float du[(bx + 2) * (by + 2)];
volatile __shared__ float dv[(bx + 2) * (by + 2)];
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
// position within global memory array
const int pos = min(ix, w - 1) + min(iy, h - 1) * s;
// position within global memory array
const int pos = min(ix, w - 1) + min(iy, h - 1) * s;
// position within shared memory array
const int shMemPos = threadIdx.x + 1 + (threadIdx.y + 1) * (bx + 2);
// position within shared memory array
const int shMemPos = threadIdx.x + 1 + (threadIdx.y + 1) * (bx + 2);
// Load data to shared memory.
// load tile being processed
du[shMemPos] = du0[pos];
dv[shMemPos] = dv0[pos];
// Load data to shared memory.
// load tile being processed
du[shMemPos] = du0[pos];
dv[shMemPos] = dv0[pos];
// load necessary neighbouring elements
// We clamp out-of-range coordinates.
// It is equivalent to mirroring
// because we access data only one step away from borders.
if (threadIdx.y == 0) {
// beginning of the tile
const int bsx = blockIdx.x * blockDim.x;
const int bsy = blockIdx.y * blockDim.y;
// element position within matrix
int x, y;
// element position within linear array
// gm - global memory
// sm - shared memory
int gmPos, smPos;
// load necessary neighbouring elements
// We clamp out-of-range coordinates.
// It is equivalent to mirroring
// because we access data only one step away from borders.
if (threadIdx.y == 0) {
// beginning of the tile
const int bsx = blockIdx.x * blockDim.x;
const int bsy = blockIdx.y * blockDim.y;
// element position within matrix
int x, y;
// element position within linear array
// gm - global memory
// sm - shared memory
int gmPos, smPos;
x = min(bsx + threadIdx.x, w - 1);
// row just below the tile
y = max(bsy - 1, 0);
gmPos = y * s + x;
smPos = threadIdx.x + 1;
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
x = min(bsx + threadIdx.x, w - 1);
// row just below the tile
y = max(bsy - 1, 0);
gmPos = y * s + x;
smPos = threadIdx.x + 1;
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
// row above the tile
y = min(bsy + by, h - 1);
smPos += (by + 1) * (bx + 2);
gmPos = y * s + x;
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
} else if (threadIdx.y == 1) {
// beginning of the tile
const int bsx = blockIdx.x * blockDim.x;
const int bsy = blockIdx.y * blockDim.y;
// element position within matrix
int x, y;
// element position within linear array
// gm - global memory
// sm - shared memory
int gmPos, smPos;
y = min(bsy + threadIdx.x, h - 1);
// column to the left
x = max(bsx - 1, 0);
smPos = bx + 2 + threadIdx.x * (bx + 2);
gmPos = x + y * s;
// check if we are within tile
if (threadIdx.x < by) {
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
// column to the right
x = min(bsx + bx, w - 1);
gmPos = y * s + x;
smPos += bx + 1;
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
// row above the tile
y = min(bsy + by, h - 1);
smPos += (by + 1) * (bx + 2);
gmPos = y * s + x;
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
}
}
else if (threadIdx.y == 1) {
// beginning of the tile
const int bsx = blockIdx.x * blockDim.x;
const int bsy = blockIdx.y * blockDim.y;
// element position within matrix
int x, y;
// element position within linear array
// gm - global memory
// sm - shared memory
int gmPos, smPos;
cg::sync(cta);
y = min(bsy + threadIdx.x, h - 1);
// column to the left
x = max(bsx - 1, 0);
smPos = bx + 2 + threadIdx.x * (bx + 2);
gmPos = x + y * s;
if (ix >= w || iy >= h) return;
// check if we are within tile
if (threadIdx.x < by) {
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
// column to the right
x = min(bsx + bx, w - 1);
gmPos = y * s + x;
smPos += bx + 1;
du[smPos] = du0[gmPos];
dv[smPos] = dv0[gmPos];
}
}
// now all necessary data are loaded to shared memory
int left, right, up, down;
left = shMemPos - 1;
right = shMemPos + 1;
up = shMemPos + bx + 2;
down = shMemPos - bx - 2;
cg::sync(cta);
float sumU = (du[left] + du[right] + du[up] + du[down]) * 0.25f;
float sumV = (dv[left] + dv[right] + dv[up] + dv[down]) * 0.25f;
if (ix >= w || iy >= h)
return;
float frac = (Ix[pos] * sumU + Iy[pos] * sumV + Iz[pos]) /
(Ix[pos] * Ix[pos] + Iy[pos] * Iy[pos] + alpha);
// now all necessary data are loaded to shared memory
int left, right, up, down;
left = shMemPos - 1;
right = shMemPos + 1;
up = shMemPos + bx + 2;
down = shMemPos - bx - 2;
du1[pos] = sumU - Ix[pos] * frac;
dv1[pos] = sumV - Iy[pos] * frac;
float sumU = (du[left] + du[right] + du[up] + du[down]) * 0.25f;
float sumV = (dv[left] + dv[right] + dv[up] + dv[down]) * 0.25f;
float frac = (Ix[pos] * sumU + Iy[pos] * sumV + Iz[pos]) / (Ix[pos] * Ix[pos] + Iy[pos] * Iy[pos] + alpha);
du1[pos] = sumU - Ix[pos] * frac;
dv1[pos] = sumV - Iy[pos] * frac;
}
///////////////////////////////////////////////////////////////////////////////
@@ -168,14 +178,22 @@ __global__ void JacobiIteration(const float *du0, const float *dv0,
/// \param[out] du1 new horizontal displacement approximation
/// \param[out] dv1 new vertical displacement approximation
///////////////////////////////////////////////////////////////////////////////
static void SolveForUpdate(const float *du0, const float *dv0, const float *Ix,
const float *Iy, const float *Iz, int w, int h,
int s, float alpha, float *du1, float *dv1) {
// CTA size
dim3 threads(32, 6);
// grid size
dim3 blocks(iDivUp(w, threads.x), iDivUp(h, threads.y));
static void SolveForUpdate(const float *du0,
const float *dv0,
const float *Ix,
const float *Iy,
const float *Iz,
int w,
int h,
int s,
float alpha,
float *du1,
float *dv1)
{
// CTA size
dim3 threads(32, 6);
// grid size
dim3 blocks(iDivUp(w, threads.x), iDivUp(h, threads.y));
JacobiIteration<32, 6><<<blocks, threads>>>(du0, dv0, Ix, Iy, Iz, w, h, s,
alpha, du1, dv1);
JacobiIteration<32, 6><<<blocks, threads>>>(du0, dv0, Ix, Iy, Iz, w, h, s, alpha, du1, dv1);
}

View File

@@ -35,19 +35,20 @@
/// \param[in] scale scale factor (multiplier)
/// \param[out] out result
///////////////////////////////////////////////////////////////////////////////
__global__ void UpscaleKernel(int width, int height, int stride, float scale,
float *out, cudaTextureObject_t texCoarse) {
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
__global__ void UpscaleKernel(int width, int height, int stride, float scale, float *out, cudaTextureObject_t texCoarse)
{
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
if (ix >= width || iy >= height) return;
if (ix >= width || iy >= height)
return;
float x = ((float)ix + 0.5f) / (float)width;
float y = ((float)iy + 0.5f) / (float)height;
float x = ((float)ix + 0.5f) / (float)width;
float y = ((float)iy + 0.5f) / (float)height;
// exploit hardware interpolation
// and scale interpolated vector to match next pyramid level resolution
out[ix + iy * stride] = tex2D<float>(texCoarse, x, y) * scale;
// exploit hardware interpolation
// and scale interpolated vector to match next pyramid level resolution
out[ix + iy * stride] = tex2D<float>(texCoarse, x, y) * scale;
}
///////////////////////////////////////////////////////////////////////////////
@@ -62,35 +63,40 @@ __global__ void UpscaleKernel(int width, int height, int stride, float scale,
/// \param[in] scale value scale factor (multiplier)
/// \param[out] out upscaled field component
///////////////////////////////////////////////////////////////////////////////
static void Upscale(const float *src, int width, int height, int stride,
int newWidth, int newHeight, int newStride, float scale,
float *out) {
dim3 threads(32, 8);
dim3 blocks(iDivUp(newWidth, threads.x), iDivUp(newHeight, threads.y));
static void Upscale(const float *src,
int width,
int height,
int stride,
int newWidth,
int newHeight,
int newStride,
float scale,
float *out)
{
dim3 threads(32, 8);
dim3 blocks(iDivUp(newWidth, threads.x), iDivUp(newHeight, threads.y));
cudaTextureObject_t texCoarse;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texCoarse;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)src;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = stride * sizeof(float);
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)src;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = stride * sizeof(float);
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texCoarse, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texCoarse, &texRes, &texDescr, NULL));
UpscaleKernel<<<blocks, threads>>>(newWidth, newHeight, newStride, scale, out,
texCoarse);
UpscaleKernel<<<blocks, threads>>>(newWidth, newHeight, newStride, scale, out, texCoarse);
}

View File

@@ -36,20 +36,26 @@
/// \param[in] v vertical displacement
/// \param[out] out result
///////////////////////////////////////////////////////////////////////////////
__global__ void WarpingKernel(int width, int height, int stride, const float *u,
const float *v, float *out,
cudaTextureObject_t texToWarp) {
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
__global__ void WarpingKernel(int width,
int height,
int stride,
const float *u,
const float *v,
float *out,
cudaTextureObject_t texToWarp)
{
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
const int pos = ix + iy * stride;
const int pos = ix + iy * stride;
if (ix >= width || iy >= height) return;
if (ix >= width || iy >= height)
return;
float x = ((float)ix + u[pos] + 0.5f) / (float)width;
float y = ((float)iy + v[pos] + 0.5f) / (float)height;
float x = ((float)ix + u[pos] + 0.5f) / (float)width;
float y = ((float)iy + v[pos] + 0.5f) / (float)height;
out[pos] = tex2D<float>(texToWarp, x, y);
out[pos] = tex2D<float>(texToWarp, x, y);
}
///////////////////////////////////////////////////////////////////////////////
@@ -68,33 +74,32 @@ __global__ void WarpingKernel(int width, int height, int stride, const float *u,
/// \param[in] v vertical displacement
/// \param[out] out warped image
///////////////////////////////////////////////////////////////////////////////
static void WarpImage(const float *src, int w, int h, int s, const float *u,
const float *v, float *out) {
dim3 threads(32, 6);
dim3 blocks(iDivUp(w, threads.x), iDivUp(h, threads.y));
static void WarpImage(const float *src, int w, int h, int s, const float *u, const float *v, float *out)
{
dim3 threads(32, 6);
dim3 blocks(iDivUp(w, threads.x), iDivUp(h, threads.y));
cudaTextureObject_t texToWarp;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texToWarp;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)src;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = w;
texRes.res.pitch2D.height = h;
texRes.res.pitch2D.pitchInBytes = s * sizeof(float);
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = (void *)src;
texRes.res.pitch2D.desc = cudaCreateChannelDesc<float>();
texRes.res.pitch2D.width = w;
texRes.res.pitch2D.height = h;
texRes.res.pitch2D.pitchInBytes = s * sizeof(float);
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeMirror;
texDescr.addressMode[1] = cudaAddressModeMirror;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texToWarp, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texToWarp, &texRes, &texDescr, NULL));
WarpingKernel<<<blocks, threads>>>(w, h, s, u, v, out, texToWarp);
WarpingKernel<<<blocks, threads>>>(w, h, s, u, v, out, texToWarp);
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,369 +26,504 @@
*/
#include <stdio.h>
#include "helper_cuda.h"
#include "Mandelbrot_kernel.h"
#include "Mandelbrot_kernel.cuh"
#include "Mandelbrot_kernel.h"
#include "helper_cuda.h"
// The Mandelbrot CUDA GPU thread function
template <class T>
__global__ void Mandelbrot0(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const T xOff, const T yOff,
const T xJP, const T yJP, const T scale,
const uchar4 colors, const int frame,
const int animationFrame, const int gridWidth,
const int numBlocks, const bool isJ) {
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks;
blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
__global__ void Mandelbrot0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const T xOff,
const T yOff,
const T xJP,
const T yJP,
const T scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int gridWidth,
const int numBlocks,
const bool isJ)
{
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
if ((ix < imageW) && (iy < imageH)) {
// Calculate the location
const T xPos = (T)ix * scale + xOff;
const T yPos = (T)iy * scale + yOff;
if ((ix < imageW) && (iy < imageH)) {
// Calculate the location
const T xPos = (T)ix * scale + xOff;
const T yPos = (T)iy * scale + yOff;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrot<T>(xPos, yPos, xJP, yJP, crunch, isJ);
// int m = blockIdx.x; // uncomment to see scheduling
// order
m = m > 0 ? crunch - m : 0;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrot<T>(xPos, yPos, xJP, yJP, crunch, isJ);
// int m = blockIdx.x; // uncomment to see scheduling
// order
m = m > 0 ? crunch - m : 0;
// Convert the Mandelbrot index into a color
uchar4 color;
// Convert the Mandelbrot index into a color
uchar4 color;
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
} else {
color.x = 0;
color.y = 0;
color.z = 0;
}
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
}
else {
color.x = 0;
color.y = 0;
color.z = 0;
}
// Output the pixel
int pixel = imageW * iy + ix;
// Output the pixel
int pixel = imageW * iy + ix;
if (frame == 0) {
color.w = 0;
dst[pixel] = color;
} else {
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;
dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;
dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;
}
if (frame == 0) {
color.w = 0;
dst[pixel] = color;
}
else {
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;
dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;
dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;
}
}
}
}
} // Mandelbrot0
} // Mandelbrot0
// The Mandelbrot CUDA GPU thread function (double single version)
__global__ void MandelbrotDS0(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const float xOff0,
const float xOff1, const float yOff0,
const float yOff1, const float xJP,
const float yJP, const float scale,
const uchar4 colors, const int frame,
const int animationFrame, const int gridWidth,
const int numBlocks, const bool isJ) {
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks;
blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
__global__ void MandelbrotDS0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const float xOff0,
const float xOff1,
const float yOff0,
const float yOff1,
const float xJP,
const float yJP,
const float scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int gridWidth,
const int numBlocks,
const bool isJ)
{
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
if ((ix < imageW) && (iy < imageH)) {
// Calculate the location
float xPos0 = (float)ix * scale;
float xPos1 = 0.0f;
float yPos0 = (float)iy * scale;
float yPos1 = 0.0f;
dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);
dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);
if ((ix < imageW) && (iy < imageH)) {
// Calculate the location
float xPos0 = (float)ix * scale;
float xPos1 = 0.0f;
float yPos0 = (float)iy * scale;
float yPos1 = 0.0f;
dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);
dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);
// Calculate the Mandelbrot index for the current location
int m =
CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);
m = m > 0 ? crunch - m : 0;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);
m = m > 0 ? crunch - m : 0;
// Convert the Mandelbrot index into a color
uchar4 color;
// Convert the Mandelbrot index into a color
uchar4 color;
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
} else {
color.x = 0;
color.y = 0;
color.z = 0;
}
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
}
else {
color.x = 0;
color.y = 0;
color.z = 0;
}
// Output the pixel
int pixel = imageW * iy + ix;
// Output the pixel
int pixel = imageW * iy + ix;
if (frame == 0) {
color.w = 0;
dst[pixel] = color;
} else {
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;
dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;
dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;
}
if (frame == 0) {
color.w = 0;
dst[pixel] = color;
}
else {
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;
dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;
dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;
}
}
}
}
} // MandelbrotDS0
} // MandelbrotDS0
// The Mandelbrot secondary AA pass CUDA GPU thread function
template <class T>
__global__ void Mandelbrot1(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const T xOff, const T yOff,
const T xJP, const T yJP, const T scale,
const uchar4 colors, const int frame,
const int animationFrame, const int gridWidth,
const int numBlocks, const bool isJ) {
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks;
blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
__global__ void Mandelbrot1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const T xOff,
const T yOff,
const T xJP,
const T yJP,
const T scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int gridWidth,
const int numBlocks,
const bool isJ)
{
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
if ((ix < imageW) && (iy < imageH)) {
// Get the current pixel color
int pixel = imageW * iy + ix;
uchar4 pixelColor = dst[pixel];
int count = 0;
if ((ix < imageW) && (iy < imageH)) {
// Get the current pixel color
int pixel = imageW * iy + ix;
uchar4 pixelColor = dst[pixel];
int count = 0;
// Search for pixels out of tolerance surrounding the current pixel
if (ix > 0) {
count += CheckColors(pixelColor, dst[pixel - 1]);
}
// Search for pixels out of tolerance surrounding the current pixel
if (ix > 0) {
count += CheckColors(pixelColor, dst[pixel - 1]);
}
if (ix + 1 < imageW) {
count += CheckColors(pixelColor, dst[pixel + 1]);
}
if (ix + 1 < imageW) {
count += CheckColors(pixelColor, dst[pixel + 1]);
}
if (iy > 0) {
count += CheckColors(pixelColor, dst[pixel - imageW]);
}
if (iy > 0) {
count += CheckColors(pixelColor, dst[pixel - imageW]);
}
if (iy + 1 < imageH) {
count += CheckColors(pixelColor, dst[pixel + imageW]);
}
if (iy + 1 < imageH) {
count += CheckColors(pixelColor, dst[pixel + imageW]);
}
if (count) {
// Calculate the location
const T xPos = (T)ix * scale + xOff;
const T yPos = (T)iy * scale + yOff;
if (count) {
// Calculate the location
const T xPos = (T)ix * scale + xOff;
const T yPos = (T)iy * scale + yOff;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrot(xPos, yPos, xJP, yJP, crunch, isJ);
m = m > 0 ? crunch - m : 0;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrot(xPos, yPos, xJP, yJP, crunch, isJ);
m = m > 0 ? crunch - m : 0;
// Convert the Mandelbrot index into a color
uchar4 color;
// Convert the Mandelbrot index into a color
uchar4 color;
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
} else {
color.x = 0;
color.y = 0;
color.z = 0;
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
}
else {
color.x = 0;
color.y = 0;
color.z = 0;
}
// Output the pixel
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;
dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;
dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;
}
}
// Output the pixel
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;
dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;
dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;
}
}
}
} // Mandelbrot1
} // Mandelbrot1
// The Mandelbrot secondary AA pass CUDA GPU thread function (double single
// version)
__global__ void MandelbrotDS1(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const float xOff0,
const float xOff1, const float yOff0,
const float yOff1, const float xJP,
const float yJP, const float scale,
const uchar4 colors, const int frame,
const int animationFrame, const int gridWidth,
const int numBlocks, const bool isJ) {
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks;
blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
__global__ void MandelbrotDS1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const float xOff0,
const float xOff1,
const float yOff0,
const float yOff1,
const float xJP,
const float yJP,
const float scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int gridWidth,
const int numBlocks,
const bool isJ)
{
// loop until all blocks completed
for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {
unsigned int blockX = blockIndex % gridWidth;
unsigned int blockY = blockIndex / gridWidth;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
// process this block
const int ix = blockDim.x * blockX + threadIdx.x;
const int iy = blockDim.y * blockY + threadIdx.y;
if ((ix < imageW) && (iy < imageH)) {
// Get the current pixel color
int pixel = imageW * iy + ix;
uchar4 pixelColor = dst[pixel];
int count = 0;
if ((ix < imageW) && (iy < imageH)) {
// Get the current pixel color
int pixel = imageW * iy + ix;
uchar4 pixelColor = dst[pixel];
int count = 0;
// Search for pixels out of tolerance surrounding the current pixel
if (ix > 0) {
count += CheckColors(pixelColor, dst[pixel - 1]);
}
// Search for pixels out of tolerance surrounding the current pixel
if (ix > 0) {
count += CheckColors(pixelColor, dst[pixel - 1]);
}
if (ix + 1 < imageW) {
count += CheckColors(pixelColor, dst[pixel + 1]);
}
if (ix + 1 < imageW) {
count += CheckColors(pixelColor, dst[pixel + 1]);
}
if (iy > 0) {
count += CheckColors(pixelColor, dst[pixel - imageW]);
}
if (iy > 0) {
count += CheckColors(pixelColor, dst[pixel - imageW]);
}
if (iy + 1 < imageH) {
count += CheckColors(pixelColor, dst[pixel + imageW]);
}
if (iy + 1 < imageH) {
count += CheckColors(pixelColor, dst[pixel + imageW]);
}
if (count) {
// Calculate the location
float xPos0 = (float)ix * scale;
float xPos1 = 0.0f;
float yPos0 = (float)iy * scale;
float yPos1 = 0.0f;
dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);
dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);
if (count) {
// Calculate the location
float xPos0 = (float)ix * scale;
float xPos1 = 0.0f;
float yPos0 = (float)iy * scale;
float yPos1 = 0.0f;
dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);
dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);
// Calculate the Mandelbrot index for the current location
int m =
CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);
m = m > 0 ? crunch - m : 0;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);
m = m > 0 ? crunch - m : 0;
// Convert the Mandelbrot index into a color
uchar4 color;
// Convert the Mandelbrot index into a color
uchar4 color;
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
} else {
color.x = 0;
color.y = 0;
color.z = 0;
if (m) {
m += animationFrame;
color.x = m * colors.x;
color.y = m * colors.y;
color.z = m * colors.z;
}
else {
color.x = 0;
color.y = 0;
color.z = 0;
}
// Output the pixel
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;
dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;
dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;
}
}
// Output the pixel
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;
dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;
dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;
}
}
}
} // MandelbrotDS1
} // MandelbrotDS1
// The host CPU Mandelbrot thread spawner
void RunMandelbrot0(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const double xOff, const double yOff,
const double xjp, const double yjp, const double scale,
const uchar4 colors, const int frame,
const int animationFrame, const int mode, const int numSMs,
const bool isJ, int version) {
dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);
dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));
void RunMandelbrot0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xjp,
const double yjp,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int mode,
const int numSMs,
const bool isJ,
int version)
{
dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);
dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));
int numWorkerBlocks = numSMs;
int numWorkerBlocks = numSMs;
switch (mode) {
switch (mode) {
default:
case 0:
Mandelbrot0<float><<<numWorkerBlocks, threads>>>(
dst, imageW, imageH, crunch, (float)xOff, (float)yOff, (float)xjp,
(float)yjp, (float)scale, colors, frame, animationFrame, grid.x,
grid.x * grid.y, isJ);
break;
Mandelbrot0<float><<<numWorkerBlocks, threads>>>(dst,
imageW,
imageH,
crunch,
(float)xOff,
(float)yOff,
(float)xjp,
(float)yjp,
(float)scale,
colors,
frame,
animationFrame,
grid.x,
grid.x * grid.y,
isJ);
break;
case 1:
float x0, x1, y0, y1;
dsdeq(x0, x1, xOff);
dsdeq(y0, y1, yOff);
MandelbrotDS0<<<numWorkerBlocks, threads>>>(
dst, imageW, imageH, crunch, x0, x1, y0, y1, (float)xjp, (float)yjp,
(float)scale, colors, frame, animationFrame, grid.x, grid.x * grid.y,
isJ);
break;
float x0, x1, y0, y1;
dsdeq(x0, x1, xOff);
dsdeq(y0, y1, yOff);
MandelbrotDS0<<<numWorkerBlocks, threads>>>(dst,
imageW,
imageH,
crunch,
x0,
x1,
y0,
y1,
(float)xjp,
(float)yjp,
(float)scale,
colors,
frame,
animationFrame,
grid.x,
grid.x * grid.y,
isJ);
break;
case 2:
Mandelbrot0<double><<<numWorkerBlocks, threads>>>(
dst, imageW, imageH, crunch, xOff, yOff, xjp, yjp, scale, colors,
frame, animationFrame, grid.x, grid.x * grid.y, isJ);
break;
}
Mandelbrot0<double><<<numWorkerBlocks, threads>>>(dst,
imageW,
imageH,
crunch,
xOff,
yOff,
xjp,
yjp,
scale,
colors,
frame,
animationFrame,
grid.x,
grid.x * grid.y,
isJ);
break;
}
getLastCudaError("Mandelbrot0 kernel execution failed.\n");
} // RunMandelbrot0
getLastCudaError("Mandelbrot0 kernel execution failed.\n");
} // RunMandelbrot0
// The host CPU Mandelbrot thread spawner
void RunMandelbrot1(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const double xOff, const double yOff,
const double xjp, const double yjp, const double scale,
const uchar4 colors, const int frame,
const int animationFrame, const int mode, const int numSMs,
const bool isJ, int version) {
dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);
dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));
void RunMandelbrot1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xjp,
const double yjp,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int mode,
const int numSMs,
const bool isJ,
int version)
{
dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);
dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));
int numWorkerBlocks = numSMs;
int numWorkerBlocks = numSMs;
switch (mode) {
switch (mode) {
default:
case 0:
Mandelbrot1<float><<<numWorkerBlocks, threads>>>(
dst, imageW, imageH, crunch, (float)xOff, (float)yOff, (float)xjp,
(float)yjp, (float)scale, colors, frame, animationFrame, grid.x,
grid.x * grid.y, isJ);
break;
Mandelbrot1<float><<<numWorkerBlocks, threads>>>(dst,
imageW,
imageH,
crunch,
(float)xOff,
(float)yOff,
(float)xjp,
(float)yjp,
(float)scale,
colors,
frame,
animationFrame,
grid.x,
grid.x * grid.y,
isJ);
break;
case 1:
float x0, x1, y0, y1;
dsdeq(x0, x1, xOff);
dsdeq(y0, y1, yOff);
MandelbrotDS1<<<numWorkerBlocks, threads>>>(
dst, imageW, imageH, crunch, x0, x1, y0, y1, (float)xjp, (float)yjp,
(float)scale, colors, frame, animationFrame, grid.x, grid.x * grid.y,
isJ);
break;
float x0, x1, y0, y1;
dsdeq(x0, x1, xOff);
dsdeq(y0, y1, yOff);
MandelbrotDS1<<<numWorkerBlocks, threads>>>(dst,
imageW,
imageH,
crunch,
x0,
x1,
y0,
y1,
(float)xjp,
(float)yjp,
(float)scale,
colors,
frame,
animationFrame,
grid.x,
grid.x * grid.y,
isJ);
break;
case 2:
Mandelbrot1<double><<<numWorkerBlocks, threads>>>(
dst, imageW, imageH, crunch, xOff, yOff, xjp, yjp, scale, colors,
frame, animationFrame, grid.x, grid.x * grid.y, isJ);
break;
}
Mandelbrot1<double><<<numWorkerBlocks, threads>>>(dst,
imageW,
imageH,
crunch,
xOff,
yOff,
xjp,
yjp,
scale,
colors,
frame,
animationFrame,
grid.x,
grid.x * grid.y,
isJ);
break;
}
getLastCudaError("Mandelbrot1 kernel execution failed.\n");
} // RunMandelbrot1
getLastCudaError("Mandelbrot1 kernel execution failed.\n");
} // RunMandelbrot1

View File

@@ -30,253 +30,313 @@
#define ABS(n) ((n) < 0 ? -(n) : (n))
/* dfloat class declaration */
class dfloat {
private:
float val[2];
class dfloat
{
private:
float val[2];
public:
dfloat() { val[0] = val[1] = 0; }
dfloat(float a, float b) {
val[0] = a;
val[1] = b;
}
dfloat(double b);
inline float operator[](unsigned idx) const { return val[idx]; }
public:
dfloat() { val[0] = val[1] = 0; }
dfloat(float a, float b)
{
val[0] = a;
val[1] = b;
}
dfloat(double b);
inline float operator[](unsigned idx) const { return val[idx]; }
};
inline dfloat operator+(const dfloat &dsa, const dfloat &dsb);
inline dfloat operator-(const dfloat &dsa, const dfloat &dsb);
inline dfloat operator*(const dfloat &dsa, const dfloat &dsb);
inline int operator<(const dfloat &a, float b) { return a[0] < b; }
inline int operator<(const dfloat &a, float b) { return a[0] < b; }
// The core Mandelbrot calculation function template
template <class T>
inline int CalcMandelbrot(const T xPos, const T yPos, const T xJParam,
const T yJParam, const int crunch,
const bool isJulia) {
T x, y, xx, yy, xC, yC;
int i = crunch;
inline int
CalcMandelbrot(const T xPos, const T yPos, const T xJParam, const T yJParam, const int crunch, const bool isJulia)
{
T x, y, xx, yy, xC, yC;
int i = crunch;
if (isJulia) {
xC = xJParam;
yC = yJParam;
y = yPos;
x = xPos;
yy = y * y;
xx = x * x;
} else {
xC = xPos;
yC = yPos;
x = y = 0;
xx = yy = 0;
}
if (isJulia) {
xC = xJParam;
yC = yJParam;
y = yPos;
x = xPos;
yy = y * y;
xx = x * x;
}
else {
xC = xPos;
yC = yPos;
x = y = 0;
xx = yy = 0;
}
while (--i && (xx + yy < 4.0f)) {
y = x * y + x * y + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
}
while (--i && (xx + yy < 4.0f)) {
y = x * y + x * y + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
}
return i;
} // CalcMandelbrot
return i;
} // CalcMandelbrot
inline void updatePixel(uchar4 &dst, const uchar4 &color, int frame) {
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst.x = (dst.x * frame + color.x + frame2) / frame1;
dst.y = (dst.y * frame + color.y + frame2) / frame1;
dst.z = (dst.z * frame + color.z + frame2) / frame1;
inline void updatePixel(uchar4 &dst, const uchar4 &color, int frame)
{
int frame1 = frame + 1;
int frame2 = frame1 / 2;
dst.x = (dst.x * frame + color.x + frame2) / frame1;
dst.y = (dst.y * frame + color.y + frame2) / frame1;
dst.z = (dst.z * frame + color.z + frame2) / frame1;
}
inline void setColor(uchar4 &dst, const uchar4 &colors, int &m,
const int animationFrame) {
if (m == 0) {
dst.x = 0;
dst.y = 0;
dst.z = 0;
return;
}
inline void setColor(uchar4 &dst, const uchar4 &colors, int &m, const int animationFrame)
{
if (m == 0) {
dst.x = 0;
dst.y = 0;
dst.z = 0;
return;
}
m += animationFrame;
dst.x = m * colors.x;
dst.y = m * colors.y;
dst.z = m * colors.z;
m += animationFrame;
dst.x = m * colors.x;
dst.y = m * colors.y;
dst.z = m * colors.z;
}
template <class T, class T_>
void runMandelbrotGold0(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const T xOff, const T yOff,
const T xJParam, const T yJParam, const T scale,
const uchar4 colors, const int frame,
const int animationFrame, const bool isJulia) {
for (int iy = 0; iy < imageH; iy++)
for (int ix = 0; ix < imageW; ix++) {
// Calculate the location
const T_ xPos = (T)ix * scale + xOff;
const T_ yPos = (T)iy * scale + yOff;
void runMandelbrotGold0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const T xOff,
const T yOff,
const T xJParam,
const T yJParam,
const T scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia)
{
for (int iy = 0; iy < imageH; iy++)
for (int ix = 0; ix < imageW; ix++) {
// Calculate the location
const T_ xPos = (T)ix * scale + xOff;
const T_ yPos = (T)iy * scale + yOff;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrot<T_>(xPos, yPos, xJParam, yJParam, crunch, isJulia);
m = m > 0 ? crunch - m : 0;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrot<T_>(xPos, yPos, xJParam, yJParam, crunch, isJulia);
m = m > 0 ? crunch - m : 0;
// Convert the Mandelbrot index into a color
uchar4 color;
// Convert the Mandelbrot index into a color
uchar4 color;
setColor(color, colors, m, animationFrame);
setColor(color, colors, m, animationFrame);
// Output the pixel
int pixel = imageW * iy + ix;
// Output the pixel
int pixel = imageW * iy + ix;
if (frame == 0) {
color.w = 0;
dst[pixel] = color;
} else
updatePixel(dst[pixel], color, frame);
}
if (frame == 0) {
color.w = 0;
dst[pixel] = color;
}
else
updatePixel(dst[pixel], color, frame);
}
} // runMandelbrotGold0_
} // runMandelbrotGold0_
// Determine if two pixel colors are within tolerance
inline int CheckColors(const uchar4 &color0, const uchar4 &color1) {
int x = color1.x - color0.x;
int y = color1.y - color0.y;
int z = color1.z - color0.z;
return (ABS(x) > 10) || (ABS(y) > 10) || (ABS(z) > 10);
} // CheckColors
inline int CheckColors(const uchar4 &color0, const uchar4 &color1)
{
int x = color1.x - color0.x;
int y = color1.y - color0.y;
int z = color1.z - color0.z;
return (ABS(x) > 10) || (ABS(y) > 10) || (ABS(z) > 10);
} // CheckColors
template <class T, class T_>
void runMandelbrotGold1(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const T xOff, const T yOff,
const T xJParam, const T yJParam, const T scale,
const uchar4 colors, const int frame,
const int animationFrame, const bool isJulia) {
for (int iy = 0; iy < imageH; iy++)
for (int ix = 0; ix < imageW; ix++) {
// Get the current pixel color
int pixel = imageW * iy + ix;
uchar4 pixelColor = dst[pixel];
int count = 0;
void runMandelbrotGold1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const T xOff,
const T yOff,
const T xJParam,
const T yJParam,
const T scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia)
{
for (int iy = 0; iy < imageH; iy++)
for (int ix = 0; ix < imageW; ix++) {
// Get the current pixel color
int pixel = imageW * iy + ix;
uchar4 pixelColor = dst[pixel];
int count = 0;
// Search for pixels out of tolerance surrounding the current pixel
if (ix > 0) count += CheckColors(pixelColor, dst[pixel - 1]);
// Search for pixels out of tolerance surrounding the current pixel
if (ix > 0)
count += CheckColors(pixelColor, dst[pixel - 1]);
if (ix + 1 < imageW) count += CheckColors(pixelColor, dst[pixel + 1]);
if (ix + 1 < imageW)
count += CheckColors(pixelColor, dst[pixel + 1]);
if (iy > 0) count += CheckColors(pixelColor, dst[pixel - imageW]);
if (iy > 0)
count += CheckColors(pixelColor, dst[pixel - imageW]);
if (iy + 1 < imageH)
count += CheckColors(pixelColor, dst[pixel + imageW]);
if (iy + 1 < imageH)
count += CheckColors(pixelColor, dst[pixel + imageW]);
if (count) {
// Calculate the location
const T_ xPos = (T)ix * scale + xOff;
const T_ yPos = (T)iy * scale + yOff;
if (count) {
// Calculate the location
const T_ xPos = (T)ix * scale + xOff;
const T_ yPos = (T)iy * scale + yOff;
// Calculate the Mandelbrot index for the current location
int m =
CalcMandelbrot<T_>(xPos, yPos, xJParam, yJParam, crunch, isJulia);
m = m > 0 ? crunch - m : 0;
// Calculate the Mandelbrot index for the current location
int m = CalcMandelbrot<T_>(xPos, yPos, xJParam, yJParam, crunch, isJulia);
m = m > 0 ? crunch - m : 0;
// Convert the Mandelbrot index into a color
uchar4 color;
// Convert the Mandelbrot index into a color
uchar4 color;
setColor(color, colors, m, animationFrame);
setColor(color, colors, m, animationFrame);
// Output the pixel
updatePixel(dst[pixel], color, frame);
}
}
} // RunMandelbrotGold1_
// Output the pixel
updatePixel(dst[pixel], color, frame);
}
}
} // RunMandelbrotGold1_
/* Implementation of exported functions */
void RunMandelbrotGold1(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const float xOff, const float yOff,
const float xJParam, const float yJParam,
const float scale, const uchar4 colors, const int frame,
const int animationFrame, const bool isJulia) {
runMandelbrotGold1<float, float>(dst, imageW, imageH, crunch, xOff, yOff,
xJParam, yJParam, scale, colors, frame,
animationFrame, isJulia);
void RunMandelbrotGold1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const float xOff,
const float yOff,
const float xJParam,
const float yJParam,
const float scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia)
{
runMandelbrotGold1<float, float>(
dst, imageW, imageH, crunch, xOff, yOff, xJParam, yJParam, scale, colors, frame, animationFrame, isJulia);
} // RunMandelbrotGold1
} // RunMandelbrotGold1
void RunMandelbrotDSGold1(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const double xOff,
const double yOff, const double xJParam,
const double yJParam, const double scale,
const uchar4 colors, const int frame,
const int animationFrame, const bool isJulia) {
runMandelbrotGold1<double, dfloat>(dst, imageW, imageH, crunch, xOff, yOff,
xJParam, yJParam, scale, colors, frame,
animationFrame, isJulia);
void RunMandelbrotDSGold1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xJParam,
const double yJParam,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia)
{
runMandelbrotGold1<double, dfloat>(
dst, imageW, imageH, crunch, xOff, yOff, xJParam, yJParam, scale, colors, frame, animationFrame, isJulia);
} // RunMandelbrotDSGold1
} // RunMandelbrotDSGold1
void RunMandelbrotGold0(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const float xOff, const float yOff,
const float xJParam, const float yJParam,
const float scale, const uchar4 colors, const int frame,
const int animationFrame, const bool isJulia) {
runMandelbrotGold0<float, float>(dst, imageW, imageH, crunch, xOff, yOff,
xJParam, yJParam, scale, colors, frame,
animationFrame, isJulia);
} // RunMandelbrotGold0
void RunMandelbrotGold0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const float xOff,
const float yOff,
const float xJParam,
const float yJParam,
const float scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia)
{
runMandelbrotGold0<float, float>(
dst, imageW, imageH, crunch, xOff, yOff, xJParam, yJParam, scale, colors, frame, animationFrame, isJulia);
} // RunMandelbrotGold0
void RunMandelbrotDSGold0(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const double xOff,
const double yOff, const double xJParam,
const double yJParam, const double scale,
const uchar4 colors, const int frame,
const int animationFrame, const bool isJulia) {
runMandelbrotGold0<double, dfloat>(dst, imageW, imageH, crunch, xOff, yOff,
xJParam, yJParam, scale, colors, frame,
animationFrame, isJulia);
} // RunMandelbrotDSGold0
void RunMandelbrotDSGold0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xJParam,
const double yJParam,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia)
{
runMandelbrotGold0<double, dfloat>(
dst, imageW, imageH, crunch, xOff, yOff, xJParam, yJParam, scale, colors, frame, animationFrame, isJulia);
} // RunMandelbrotDSGold0
/*dfloat operations implementation */
/* Construct a DS number equal to a double precision floating point number b*/
dfloat::dfloat(double b) {
val[0] = (float)b;
val[1] = (float)(b - val[0]);
dfloat::dfloat(double b)
{
val[0] = (float)b;
val[1] = (float)(b - val[0]);
}
inline dfloat operator+(const dfloat &dsa, const dfloat &dsb) {
// Compute dsa + dsb using Knuth's trick.
float t1 = dsa[0] + dsb[0];
float e = t1 - dsa[0];
float t2 = ((dsb[0] - e) + (dsa[0] - (t1 - e))) + dsa[1] + dsb[1];
inline dfloat operator+(const dfloat &dsa, const dfloat &dsb)
{
// Compute dsa + dsb using Knuth's trick.
float t1 = dsa[0] + dsb[0];
float e = t1 - dsa[0];
float t2 = ((dsb[0] - e) + (dsa[0] - (t1 - e))) + dsa[1] + dsb[1];
// The result is t1 + t2, after normalization.
e = t1 + t2;
return dfloat(e, t2 - (e - t1));
// The result is t1 + t2, after normalization.
e = t1 + t2;
return dfloat(e, t2 - (e - t1));
}
inline dfloat operator-(const dfloat &dsa, const dfloat &dsb) {
// Compute dsa - dsb using Knuth's trick.
float t1 = dsa[0] - dsb[0];
float e = t1 - dsa[0];
float t2 = ((-dsb[0] - e) + (dsa[0] - (t1 - e))) + dsa[1] - dsb[1];
inline dfloat operator-(const dfloat &dsa, const dfloat &dsb)
{
// Compute dsa - dsb using Knuth's trick.
float t1 = dsa[0] - dsb[0];
float e = t1 - dsa[0];
float t2 = ((-dsb[0] - e) + (dsa[0] - (t1 - e))) + dsa[1] - dsb[1];
// The result is t1 + t2, after normalization.
e = t1 + t2;
return dfloat(e, t2 - (e - t1));
// The result is t1 + t2, after normalization.
e = t1 + t2;
return dfloat(e, t2 - (e - t1));
}
inline dfloat operator*(const dfloat &dsa, const dfloat &dsb) {
// This splits dsa(1) and dsb(1) into high-order and low-order words.
float c11 = dsa[0] * dsb[0];
float c21 = dsa[0] * dsb[0] - c11;
inline dfloat operator*(const dfloat &dsa, const dfloat &dsb)
{
// This splits dsa(1) and dsb(1) into high-order and low-order words.
float c11 = dsa[0] * dsb[0];
float c21 = dsa[0] * dsb[0] - c11;
// Compute dsa[0] * dsb[1] + dsa[1] * dsb[0] (only high-order word is needed).
float c2 = dsa[0] * dsb[1] + dsa[1] * dsb[0];
// Compute dsa[0] * dsb[1] + dsa[1] * dsb[0] (only high-order word is needed).
float c2 = dsa[0] * dsb[1] + dsa[1] * dsb[0];
// Compute (c11, c21) + c2 using Knuth's trick, also adding low-order product.
float t1 = c11 + c2;
float e = t1 - c11;
float t2 = ((c2 - e) + (c11 - (t1 - e))) + c21 + dsa[1] * dsb[1];
// Compute (c11, c21) + c2 using Knuth's trick, also adding low-order product.
float t1 = c11 + c2;
float e = t1 - c11;
float t2 = ((c2 - e) + (c11 - (t1 - e))) + c21 + dsa[1] * dsb[1];
// The result is t1 + t2, after normalization.
e = t1 + t2;
return dfloat(e, t2 - (e - t1));
// The result is t1 + t2, after normalization.
e = t1 + t2;
return dfloat(e, t2 - (e - t1));
}

View File

@@ -30,33 +30,57 @@
#include <vector_types.h>
extern "C" void RunMandelbrotGold0(uchar4 *dst, const int imageW,
const int imageH, const int crunch,
const float xOff, const float yOff,
const float xJParam, const float yJParam,
const float scale, const uchar4 colors,
const int frame, const int animationFrame,
const bool isJulia);
extern "C" void RunMandelbrotDSGold0(uchar4 *dst, const int imageW,
const int imageH, const int crunch,
const double xOff, const double yOff,
const double xJParam, const double yJParam,
const double scale, const uchar4 colors,
const int frame, const int animationFrame,
const bool isJulia);
extern "C" void RunMandelbrotGold1(uchar4 *dst, const int imageW,
const int imageH, const int crunch,
const float xOff, const float yOff,
const float xJParam, const float yJParam,
const float scale, const uchar4 colors,
const int frame, const int animationFrame,
const bool isJulia);
extern "C" void RunMandelbrotDSGold1(uchar4 *dst, const int imageW,
const int imageH, const int crunch,
const double xOff, const double yOff,
const double xJParam, const double yJParam,
const double scale, const uchar4 colors,
const int frame, const int animationFrame,
const bool isJulia);
extern "C" void RunMandelbrotGold0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const float xOff,
const float yOff,
const float xJParam,
const float yJParam,
const float scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia);
extern "C" void RunMandelbrotDSGold0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xJParam,
const double yJParam,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia);
extern "C" void RunMandelbrotGold1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const float xOff,
const float yOff,
const float xJParam,
const float yJParam,
const float scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia);
extern "C" void RunMandelbrotDSGold1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xJParam,
const double yJParam,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const bool isJulia);
#endif

View File

@@ -26,8 +26,9 @@
*/
#include <stdio.h>
#include "helper_cuda.h"
#include "Mandelbrot_kernel.h"
#include "helper_cuda.h"
// The dimensions of the thread block
#define BLOCKDIM_X 16
@@ -40,73 +41,75 @@
// This function sets the DS number A equal to the double precision floating
// point number B.
inline void dsdeq(float &a0, float &a1, double b) {
a0 = (float)b;
a1 = (float)(b - a0);
} // dsdcp
inline void dsdeq(float &a0, float &a1, double b)
{
a0 = (float)b;
a1 = (float)(b - a0);
} // dsdcp
// This function sets the DS number A equal to the single precision floating
// point number B.
__device__ inline void dsfeq(float &a0, float &a1, float b) {
a0 = b;
a1 = 0.0f;
} // dsfeq
__device__ inline void dsfeq(float &a0, float &a1, float b)
{
a0 = b;
a1 = 0.0f;
} // dsfeq
// This function computes c = a + b.
__device__ inline void dsadd(float &c0, float &c1, const float a0,
const float a1, const float b0, const float b1) {
// Compute dsa + dsb using Knuth's trick.
float t1 = a0 + b0;
float e = t1 - a0;
float t2 = ((b0 - e) + (a0 - (t1 - e))) + a1 + b1;
__device__ inline void dsadd(float &c0, float &c1, const float a0, const float a1, const float b0, const float b1)
{
// Compute dsa + dsb using Knuth's trick.
float t1 = a0 + b0;
float e = t1 - a0;
float t2 = ((b0 - e) + (a0 - (t1 - e))) + a1 + b1;
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dsadd
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dsadd
// This function computes c = a - b.
__device__ inline void dssub(float &c0, float &c1, const float a0,
const float a1, const float b0, const float b1) {
// Compute dsa - dsb using Knuth's trick.
float t1 = a0 - b0;
float e = t1 - a0;
float t2 = ((-b0 - e) + (a0 - (t1 - e))) + a1 - b1;
__device__ inline void dssub(float &c0, float &c1, const float a0, const float a1, const float b0, const float b1)
{
// Compute dsa - dsb using Knuth's trick.
float t1 = a0 - b0;
float e = t1 - a0;
float t2 = ((-b0 - e) + (a0 - (t1 - e))) + a1 - b1;
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dssub
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dssub
#if 1
// This function multiplies DS numbers A and B to yield the DS product C.
__device__ inline void dsmul(float &c0, float &c1, const float a0,
const float a1, const float b0, const float b1) {
// This splits dsa(1) and dsb(1) into high-order and low-order words.
float cona = a0 * 8193.0f;
float conb = b0 * 8193.0f;
float sa1 = cona - (cona - a0);
float sb1 = conb - (conb - b0);
float sa2 = a0 - sa1;
float sb2 = b0 - sb1;
__device__ inline void dsmul(float &c0, float &c1, const float a0, const float a1, const float b0, const float b1)
{
// This splits dsa(1) and dsb(1) into high-order and low-order words.
float cona = a0 * 8193.0f;
float conb = b0 * 8193.0f;
float sa1 = cona - (cona - a0);
float sb1 = conb - (conb - b0);
float sa2 = a0 - sa1;
float sb2 = b0 - sb1;
// Multilply a0 * b0 using Dekker's method.
float c11 = a0 * b0;
float c21 = (((sa1 * sb1 - c11) + sa1 * sb2) + sa2 * sb1) + sa2 * sb2;
// Multilply a0 * b0 using Dekker's method.
float c11 = a0 * b0;
float c21 = (((sa1 * sb1 - c11) + sa1 * sb2) + sa2 * sb1) + sa2 * sb2;
// Compute a0 * b1 + a1 * b0 (only high-order word is needed).
float c2 = a0 * b1 + a1 * b0;
// Compute a0 * b1 + a1 * b0 (only high-order word is needed).
float c2 = a0 * b1 + a1 * b0;
// Compute (c11, c21) + c2 using Knuth's trick, also adding low-order product.
float t1 = c11 + c2;
float e = t1 - c11;
float t2 = ((c2 - e) + (c11 - (t1 - e))) + c21 + a1 * b1;
// Compute (c11, c21) + c2 using Knuth's trick, also adding low-order product.
float t1 = c11 + c2;
float e = t1 - c11;
float t2 = ((c2 - e) + (c11 - (t1 - e))) + c21 + a1 * b1;
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dsmul
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dsmul
#else
@@ -118,32 +121,32 @@ __device__ inline void dsmul(float &c0, float &c1, const float a0,
*/
// This function multiplies DS numbers A and B to yield the DS product C.
__device__ inline void dsmul(float &c0, float &c1, const float a0,
const float a1, const float b0, const float b1) {
// This splits dsa(1) and dsb(1) into high-order and low-order words.
float cona = a0 * 8193.0f;
float conb = b0 * 8193.0f;
float sa1 = cona - (cona - a0);
float sb1 = conb - (conb - b0);
float sa2 = a0 - sa1;
float sb2 = b0 - sb1;
__device__ inline void dsmul(float &c0, float &c1, const float a0, const float a1, const float b0, const float b1)
{
// This splits dsa(1) and dsb(1) into high-order and low-order words.
float cona = a0 * 8193.0f;
float conb = b0 * 8193.0f;
float sa1 = cona - (cona - a0);
float sb1 = conb - (conb - b0);
float sa2 = a0 - sa1;
float sb2 = b0 - sb1;
// Multilply a0 * b0 using Dekker's method.
float c11 = __fmul_rn(a0, b0);
float c21 = (((sa1 * sb1 - c11) + sa1 * sb2) + sa2 * sb1) + sa2 * sb2;
// Multilply a0 * b0 using Dekker's method.
float c11 = __fmul_rn(a0, b0);
float c21 = (((sa1 * sb1 - c11) + sa1 * sb2) + sa2 * sb1) + sa2 * sb2;
// Compute a0 * b1 + a1 * b0 (only high-order word is needed).
float c2 = __fmul_rn(a0, b1) + __fmul_rn(a1, b0);
// Compute a0 * b1 + a1 * b0 (only high-order word is needed).
float c2 = __fmul_rn(a0, b1) + __fmul_rn(a1, b0);
// Compute (c11, c21) + c2 using Knuth's trick, also adding low-order product.
float t1 = c11 + c2;
float e = t1 - c11;
float t2 = ((c2 - e) + (c11 - (t1 - e))) + c21 + __fmul_rn(a1, b1);
// Compute (c11, c21) + c2 using Knuth's trick, also adding low-order product.
float t1 = c11 + c2;
float e = t1 - c11;
float t2 = ((c2 - e) + (c11 - (t1 - e))) + c21 + __fmul_rn(a1, b1);
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dsmul
// The result is t1 + t2, after normalization.
c0 = e = t1 + t2;
c1 = t2 - (e - t1);
} // dsmul
#endif
@@ -151,306 +154,331 @@ __device__ inline void dsmul(float &c0, float &c1, const float a0,
#if 1
// Unrolled version
template <class T>
__device__ inline int CalcMandelbrot(const T xPos, const T yPos,
const T xJParam, const T yJParam,
const int crunch, const bool isJulia) {
T x, y, xx, yy;
int i = crunch;
__device__ inline int
CalcMandelbrot(const T xPos, const T yPos, const T xJParam, const T yJParam, const int crunch, const bool isJulia)
{
T x, y, xx, yy;
int i = crunch;
T xC, yC;
T xC, yC;
if (isJulia) {
xC = xJParam;
yC = yJParam;
y = yPos;
x = xPos;
yy = y * y;
xx = x * x;
if (isJulia) {
xC = xJParam;
yC = yJParam;
y = yPos;
x = xPos;
yy = y * y;
xx = x * x;
}
else {
xC = xPos;
yC = yPos;
y = 0;
x = 0;
yy = 0;
xx = 0;
}
} else {
xC = xPos;
yC = yPos;
y = 0;
x = 0;
yy = 0;
xx = 0;
}
do {
// Iteration 1
if (xx + yy > T(4.0))
return i - 1;
do {
// Iteration 1
if (xx + yy > T(4.0)) return i - 1;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 2
if (xx + yy > T(4.0))
return i - 2;
// Iteration 2
if (xx + yy > T(4.0)) return i - 2;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 3
if (xx + yy > T(4.0))
return i - 3;
// Iteration 3
if (xx + yy > T(4.0)) return i - 3;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 4
if (xx + yy > T(4.0))
return i - 4;
// Iteration 4
if (xx + yy > T(4.0)) return i - 4;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 5
if (xx + yy > T(4.0))
return i - 5;
// Iteration 5
if (xx + yy > T(4.0)) return i - 5;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 6
if (xx + yy > T(4.0))
return i - 6;
// Iteration 6
if (xx + yy > T(4.0)) return i - 6;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 7
if (xx + yy > T(4.0))
return i - 7;
// Iteration 7
if (xx + yy > T(4.0)) return i - 7;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 8
if (xx + yy > T(4.0))
return i - 8;
// Iteration 8
if (xx + yy > T(4.0)) return i - 8;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 9
if (xx + yy > T(4.0))
return i - 9;
// Iteration 9
if (xx + yy > T(4.0)) return i - 9;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 10
if (xx + yy > T(4.0))
return i - 10;
// Iteration 10
if (xx + yy > T(4.0)) return i - 10;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 11
if (xx + yy > T(4.0))
return i - 11;
// Iteration 11
if (xx + yy > T(4.0)) return i - 11;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 12
if (xx + yy > T(4.0))
return i - 12;
// Iteration 12
if (xx + yy > T(4.0)) return i - 12;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 13
if (xx + yy > T(4.0))
return i - 13;
// Iteration 13
if (xx + yy > T(4.0)) return i - 13;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 14
if (xx + yy > T(4.0))
return i - 14;
// Iteration 14
if (xx + yy > T(4.0)) return i - 14;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 15
if (xx + yy > T(4.0))
return i - 15;
// Iteration 15
if (xx + yy > T(4.0)) return i - 15;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 16
if (xx + yy > T(4.0))
return i - 16;
// Iteration 16
if (xx + yy > T(4.0)) return i - 16;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 17
if (xx + yy > T(4.0))
return i - 17;
// Iteration 17
if (xx + yy > T(4.0)) return i - 17;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 18
if (xx + yy > T(4.0))
return i - 18;
// Iteration 18
if (xx + yy > T(4.0)) return i - 18;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 19
if (xx + yy > T(4.0))
return i - 19;
// Iteration 19
if (xx + yy > T(4.0)) return i - 19;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
// Iteration 20
i -= 20;
// Iteration 20
i -= 20;
if ((i <= 0) || (xx + yy > T(4.0)))
return i;
if ((i <= 0) || (xx + yy > T(4.0))) return i;
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
} while (1);
} // CalcMandelbrot
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
} while (1);
} // CalcMandelbrot
#else
template <class T>
__device__ inline int CalcMandelbrot(const T xPos, const T yPos,
const T xJParam, const T yJParam,
const int crunch, const isJulia) {
T x, y, xx, yy, xC, yC;
__device__ inline int
CalcMandelbrot(const T xPos, const T yPos, const T xJParam, const T yJParam, const int crunch, const isJulia)
{
T x, y, xx, yy, xC, yC;
if (isJulia) {
xC = xJParam;
yC = yJParam;
y = yPos;
x = xPos;
yy = y * y;
xx = x * x;
if (isJulia) {
xC = xJParam;
yC = yJParam;
y = yPos;
x = xPos;
yy = y * y;
xx = x * x;
}
else {
xC = xPos;
yC = yPos;
y = 0;
x = 0;
yy = 0;
xx = 0;
}
} else {
xC = xPos;
yC = yPos;
y = 0;
x = 0;
yy = 0;
xx = 0;
}
int i = crunch;
int i = crunch;
while (--i && (xx + yy < T(4.0))) {
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
}
while (--i && (xx + yy < T(4.0))) {
y = x * y * T(2.0) + yC;
x = xx - yy + xC;
yy = y * y;
xx = x * x;
}
return i; // i > 0 ? crunch - i : 0;
} // CalcMandelbrot
return i; // i > 0 ? crunch - i : 0;
} // CalcMandelbrot
#endif
// The core Mandelbrot calculation function in double-single precision
__device__ inline int CalcMandelbrotDS(const float xPos0, const float xPos1,
const float yPos0, const float yPos1,
const float xJParam, const float yJParam,
const int crunch, const bool isJulia) {
float xx0, xx1;
float yy0, yy1;
float sum0, sum1;
int i = crunch;
__device__ inline int CalcMandelbrotDS(const float xPos0,
const float xPos1,
const float yPos0,
const float yPos1,
const float xJParam,
const float yJParam,
const int crunch,
const bool isJulia)
{
float xx0, xx1;
float yy0, yy1;
float sum0, sum1;
int i = crunch;
float x0, x1, y0, y1;
float xC0, xC1, yC0, yC1;
float x0, x1, y0, y1;
float xC0, xC1, yC0, yC1;
if (isJulia) {
xC0 = xJParam;
xC1 = 0;
yC0 = yJParam;
yC1 = 0;
y0 = yPos0; // y = yPos;
y1 = yPos1;
x0 = xPos0; // x = xPos;
x1 = xPos1;
dsmul(yy0, yy1, y0, y1, y0, y1); // yy = y * y;
dsmul(xx0, xx1, x0, x1, x0, x1); // xx = x * x;
} else {
xC0 = xPos0;
xC1 = xPos1;
yC0 = yPos0;
yC1 = yPos1;
y0 = 0; // y = 0 ;
y1 = 0;
x0 = 0; // x = 0 ;
x1 = 0;
yy0 = 0; // yy = 0 ;
yy1 = 0;
xx0 = 0; // xx = 0 ;
xx1 = 0;
}
if (isJulia) {
xC0 = xJParam;
xC1 = 0;
yC0 = yJParam;
yC1 = 0;
y0 = yPos0; // y = yPos;
y1 = yPos1;
x0 = xPos0; // x = xPos;
x1 = xPos1;
dsmul(yy0, yy1, y0, y1, y0, y1); // yy = y * y;
dsmul(xx0, xx1, x0, x1, x0, x1); // xx = x * x;
}
else {
xC0 = xPos0;
xC1 = xPos1;
yC0 = yPos0;
yC1 = yPos1;
y0 = 0; // y = 0 ;
y1 = 0;
x0 = 0; // x = 0 ;
x1 = 0;
yy0 = 0; // yy = 0 ;
yy1 = 0;
xx0 = 0; // xx = 0 ;
xx1 = 0;
}
dsadd(sum0, sum1, xx0, xx1, yy0, yy1); // sum = xx + yy;
dsadd(sum0, sum1, xx0, xx1, yy0, yy1); // sum = xx + yy;
while (--i && (sum0 + sum1 < 4.0f)) {
dsmul(y0, y1, x0, x1, y0, y1); // y = x * y * 2.0f + yC; // yC is yPos for
// Mandelbrot and it is yJParam for Julia
dsadd(y0, y1, y0, y1, y0, y1);
dsadd(y0, y1, y0, y1, yC0, yC1);
while (--i && (sum0 + sum1 < 4.0f)) {
dsmul(y0, y1, x0, x1, y0, y1); // y = x * y * 2.0f + yC; // yC is yPos for
// Mandelbrot and it is yJParam for Julia
dsadd(y0, y1, y0, y1, y0, y1);
dsadd(y0, y1, y0, y1, yC0, yC1);
dssub(x0, x1, xx0, xx1, yy0, yy1); // x = xx - yy + xC; // xC is xPos for
// Mandelbrot and it is xJParam for
// Julia
dsadd(x0, x1, x0, x1, xC0, xC1);
dssub(x0, x1, xx0, xx1, yy0, yy1); // x = xx - yy + xC; // xC is xPos for
// Mandelbrot and it is xJParam for
// Julia
dsadd(x0, x1, x0, x1, xC0, xC1);
dsmul(yy0, yy1, y0, y1, y0, y1); // yy = y * y;
dsmul(xx0, xx1, x0, x1, x0, x1); // xx = x * x;
dsadd(sum0, sum1, xx0, xx1, yy0, yy1); // sum = xx + yy;
}
dsmul(yy0, yy1, y0, y1, y0, y1); // yy = y * y;
dsmul(xx0, xx1, x0, x1, x0, x1); // xx = x * x;
dsadd(sum0, sum1, xx0, xx1, yy0, yy1); // sum = xx + yy;
}
return i;
} // CalcMandelbrotDS
return i;
} // CalcMandelbrotDS
// Determine if two pixel colors are within tolerance
__device__ inline int CheckColors(const uchar4 &color0, const uchar4 &color1) {
int x = color1.x - color0.x;
int y = color1.y - color0.y;
int z = color1.z - color0.z;
return (ABS(x) > 10) || (ABS(y) > 10) || (ABS(z) > 10);
} // CheckColors
__device__ inline int CheckColors(const uchar4 &color0, const uchar4 &color1)
{
int x = color1.x - color0.x;
int y = color1.y - color0.y;
int z = color1.z - color0.z;
return (ABS(x) > 10) || (ABS(y) > 10) || (ABS(z) > 10);
} // CheckColors
// Increase the grid size by 1 if the image width or height does not divide
// evenly
// by the thread block dimensions
inline int iDivUp(int a, int b) {
return ((a % b) != 0) ? (a / b + 1) : (a / b);
} // iDivUp
inline int iDivUp(int a, int b) { return ((a % b) != 0) ? (a / b + 1) : (a / b); } // iDivUp

View File

@@ -30,21 +30,37 @@
#include <vector_types.h>
extern "C" void RunMandelbrot0(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const double xOff,
const double yOff, const double xjp,
const double yjp, const double scale,
const uchar4 colors, const int frame,
const int animationFrame, const int mode,
const int numSMs, const bool isJ,
int version = 13);
extern "C" void RunMandelbrot1(uchar4 *dst, const int imageW, const int imageH,
const int crunch, const double xOff,
const double yOff, const double xjp,
const double yjp, const double scale,
const uchar4 colors, const int frame,
const int animationFrame, const int mode,
const int numSMs, const bool isJ,
int version = 13);
extern "C" void RunMandelbrot0(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xjp,
const double yjp,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int mode,
const int numSMs,
const bool isJ,
int version = 13);
extern "C" void RunMandelbrot1(uchar4 *dst,
const int imageW,
const int imageH,
const int crunch,
const double xOff,
const double yOff,
const double xjp,
const double yjp,
const double scale,
const uchar4 colors,
const int frame,
const int animationFrame,
const int mode,
const int numSMs,
const bool isJ,
int version = 13);
#endif

View File

@@ -34,4 +34,3 @@ Download and install the [CUDA Toolkit 12.5](https://developer.nvidia.com/cuda-d
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
## References (for more details)

View File

@@ -31,20 +31,20 @@
* See supplied whitepaper for more explanations.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// includes, project
#include <helper_functions.h> // Helper functions (utilities, parsing, timing)
#include <helper_cuda.h> // helper functions (cuda error checking and initialization)
#include <helper_cuda.h> // helper functions (cuda error checking and initialization)
#include <helper_functions.h> // Helper functions (utilities, parsing, timing)
#include <multithreading.h>
#include "MonteCarlo_common.h"
int *pArgc = NULL;
int *pArgc = NULL;
char **pArgv = NULL;
#ifdef WIN32
@@ -54,42 +54,43 @@ char **pArgv = NULL;
////////////////////////////////////////////////////////////////////////////////
// Common functions
////////////////////////////////////////////////////////////////////////////////
float randFloat(float low, float high) {
float t = (float)rand() / (float)RAND_MAX;
return (1.0f - t) * low + t * high;
float randFloat(float low, float high)
{
float t = (float)rand() / (float)RAND_MAX;
return (1.0f - t) * low + t * high;
}
/// Utility function to tweak problem size for small GPUs
int adjustProblemSize(int GPU_N, int default_nOptions) {
int nOptions = default_nOptions;
int adjustProblemSize(int GPU_N, int default_nOptions)
{
int nOptions = default_nOptions;
// select problem size
for (int i = 0; i < GPU_N; i++) {
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));
int cudaCores = _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) *
deviceProp.multiProcessorCount;
// select problem size
for (int i = 0; i < GPU_N; i++) {
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));
int cudaCores = _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount;
if (cudaCores <= 32) {
nOptions = (nOptions < cudaCores / 2 ? nOptions : cudaCores / 2);
if (cudaCores <= 32) {
nOptions = (nOptions < cudaCores / 2 ? nOptions : cudaCores / 2);
}
}
}
return nOptions;
return nOptions;
}
int adjustGridSize(int GPUIndex, int defaultGridSize) {
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, GPUIndex));
int maxGridSize = deviceProp.multiProcessorCount * 40;
return ((defaultGridSize > maxGridSize) ? maxGridSize : defaultGridSize);
int adjustGridSize(int GPUIndex, int defaultGridSize)
{
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, GPUIndex));
int maxGridSize = deviceProp.multiProcessorCount * 40;
return ((defaultGridSize > maxGridSize) ? maxGridSize : defaultGridSize);
}
///////////////////////////////////////////////////////////////////////////////
// CPU reference functions
///////////////////////////////////////////////////////////////////////////////
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData,
float *h_Random, int pathN);
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData, float *h_Random, int pathN);
// Black-Scholes formula for call options
extern "C" void BlackScholesCall(float &CallResult, TOptionData optionData);
@@ -100,98 +101,99 @@ extern "C" void BlackScholesCall(float &CallResult, TOptionData optionData);
// Timer
StopWatchInterface **hTimer = NULL;
static CUT_THREADPROC solverThread(TOptionPlan *plan) {
// Init GPU
checkCudaErrors(cudaSetDevice(plan->device));
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan->device));
// Start the timer
sdkStartTimer(&hTimer[plan->device]);
// Allocate intermediate memory for MC integrator and initialize
// RNG states
initMonteCarloGPU(plan);
// Main computation
MonteCarloGPU(plan);
checkCudaErrors(cudaDeviceSynchronize());
// Stop the timer
sdkStopTimer(&hTimer[plan->device]);
// Shut down this GPU
closeMonteCarloGPU(plan);
cudaStreamSynchronize(0);
printf("solverThread() finished - GPU Device %d: %s\n", plan->device,
deviceProp.name);
CUT_THREADEND;
}
static void multiSolver(TOptionPlan *plan, int nPlans) {
// allocate and initialize an array of stream handles
cudaStream_t *streams = (cudaStream_t *)malloc(nPlans * sizeof(cudaStream_t));
cudaEvent_t *events = (cudaEvent_t *)malloc(nPlans * sizeof(cudaEvent_t));
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
checkCudaErrors(cudaStreamCreate(&(streams[i])));
checkCudaErrors(cudaEventCreate(&(events[i])));
}
// Init Each GPU
// In CUDA 4.0 we can call cudaSetDevice multiple times to target each device
// Set the device desired, then perform initializations on that device
for (int i = 0; i < nPlans; i++) {
// set the target device to perform initialization on
checkCudaErrors(cudaSetDevice(plan[i].device));
static CUT_THREADPROC solverThread(TOptionPlan *plan)
{
// Init GPU
checkCudaErrors(cudaSetDevice(plan->device));
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan[i].device));
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan->device));
// Allocate intermediate memory for MC integrator
// and initialize RNG state
initMonteCarloGPU(&plan[i]);
}
// Start the timer
sdkStartTimer(&hTimer[plan->device]);
// Allocate intermediate memory for MC integrator and initialize
// RNG states
initMonteCarloGPU(plan);
// Main computation
MonteCarloGPU(plan);
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
checkCudaErrors(cudaDeviceSynchronize());
}
// Start the timer
sdkResetTimer(&hTimer[0]);
sdkStartTimer(&hTimer[0]);
// Stop the timer
sdkStopTimer(&hTimer[plan->device]);
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
// Shut down this GPU
closeMonteCarloGPU(plan);
// Main computations
MonteCarloGPU(&plan[i], streams[i]);
cudaStreamSynchronize(0);
checkCudaErrors(cudaEventRecord(events[i], streams[i]));
}
printf("solverThread() finished - GPU Device %d: %s\n", plan->device, deviceProp.name);
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
cudaEventSynchronize(events[i]);
}
CUT_THREADEND;
}
// Stop the timer
sdkStopTimer(&hTimer[0]);
static void multiSolver(TOptionPlan *plan, int nPlans)
{
// allocate and initialize an array of stream handles
cudaStream_t *streams = (cudaStream_t *)malloc(nPlans * sizeof(cudaStream_t));
cudaEvent_t *events = (cudaEvent_t *)malloc(nPlans * sizeof(cudaEvent_t));
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
closeMonteCarloGPU(&plan[i]);
checkCudaErrors(cudaStreamDestroy(streams[i]));
checkCudaErrors(cudaEventDestroy(events[i]));
}
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
checkCudaErrors(cudaStreamCreate(&(streams[i])));
checkCudaErrors(cudaEventCreate(&(events[i])));
}
// Init Each GPU
// In CUDA 4.0 we can call cudaSetDevice multiple times to target each device
// Set the device desired, then perform initializations on that device
for (int i = 0; i < nPlans; i++) {
// set the target device to perform initialization on
checkCudaErrors(cudaSetDevice(plan[i].device));
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, plan[i].device));
// Allocate intermediate memory for MC integrator
// and initialize RNG state
initMonteCarloGPU(&plan[i]);
}
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
checkCudaErrors(cudaDeviceSynchronize());
}
// Start the timer
sdkResetTimer(&hTimer[0]);
sdkStartTimer(&hTimer[0]);
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
// Main computations
MonteCarloGPU(&plan[i], streams[i]);
checkCudaErrors(cudaEventRecord(events[i], streams[i]));
}
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
cudaEventSynchronize(events[i]);
}
// Stop the timer
sdkStopTimer(&hTimer[0]);
for (int i = 0; i < nPlans; i++) {
checkCudaErrors(cudaSetDevice(plan[i].device));
closeMonteCarloGPU(&plan[i]);
checkCudaErrors(cudaStreamDestroy(streams[i]));
checkCudaErrors(cudaEventDestroy(events[i]));
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -203,285 +205,279 @@ static void multiSolver(TOptionPlan *plan, int nPlans) {
#define PRINT_RESULTS
#undef PRINT_RESULTS
void usage() {
printf("--method=[threaded,streamed] --scaling=[strong,weak] [--help]\n");
printf("Method=threaded: 1 CPU thread for each GPU [default]\n");
printf(
" streamed: 1 CPU thread handles all GPUs (requires CUDA 4.0 or "
"newer)\n");
printf("Scaling=strong : constant problem size\n");
printf(
" weak : problem size scales with number of available GPUs "
"[default]\n");
void usage()
{
printf("--method=[threaded,streamed] --scaling=[strong,weak] [--help]\n");
printf("Method=threaded: 1 CPU thread for each GPU [default]\n");
printf(" streamed: 1 CPU thread handles all GPUs (requires CUDA 4.0 or "
"newer)\n");
printf("Scaling=strong : constant problem size\n");
printf(" weak : problem size scales with number of available GPUs "
"[default]\n");
}
int main(int argc, char **argv) {
char *multiMethodChoice = NULL;
char *scalingChoice = NULL;
bool use_threads = true;
bool bqatest = false;
bool strongScaling = false;
int main(int argc, char **argv)
{
char *multiMethodChoice = NULL;
char *scalingChoice = NULL;
bool use_threads = true;
bool bqatest = false;
bool strongScaling = false;
pArgc = &argc;
pArgv = argv;
pArgc = &argc;
pArgv = argv;
printf("%s Starting...\n\n", argv[0]);
printf("%s Starting...\n\n", argv[0]);
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
bqatest = true;
}
getCmdLineArgumentString(argc, (const char **)argv, "method",
&multiMethodChoice);
getCmdLineArgumentString(argc, (const char **)argv, "scaling",
&scalingChoice);
if (checkCmdLineFlag(argc, (const char **)argv, "h") ||
checkCmdLineFlag(argc, (const char **)argv, "help")) {
usage();
exit(EXIT_SUCCESS);
}
if (multiMethodChoice == NULL) {
use_threads = false;
} else {
if (!strcasecmp(multiMethodChoice, "threaded")) {
use_threads = true;
} else {
use_threads = false;
}
}
if (use_threads == false) {
printf("Using single CPU thread for multiple GPUs\n");
}
if (scalingChoice == NULL) {
strongScaling = false;
} else {
if (!strcasecmp(scalingChoice, "strong")) {
strongScaling = true;
} else {
strongScaling = false;
}
}
// GPU number present in the system
int GPU_N;
checkCudaErrors(cudaGetDeviceCount(&GPU_N));
int nOptions = 8 * 1024;
nOptions = adjustProblemSize(GPU_N, nOptions);
// select problem size
int scale = (strongScaling) ? 1 : GPU_N;
int OPT_N = nOptions * scale;
int PATH_N = 262144;
// initialize the timers
hTimer = new StopWatchInterface *[GPU_N];
for (int i = 0; i < GPU_N; i++) {
sdkCreateTimer(&hTimer[i]);
sdkResetTimer(&hTimer[i]);
}
// Input data array
TOptionData *optionData = new TOptionData[OPT_N];
// Final GPU MC results
TOptionValue *callValueGPU = new TOptionValue[OPT_N];
//"Theoretical" call values by Black-Scholes formula
float *callValueBS = new float[OPT_N];
// Solver config
TOptionPlan *optionSolver = new TOptionPlan[GPU_N];
// OS thread ID
CUTThread *threadID = new CUTThread[GPU_N];
int gpuBase, gpuIndex;
int i;
float time;
double delta, ref, sumDelta, sumRef, sumReserve;
printf("MonteCarloMultiGPU\n");
printf("==================\n");
printf("Parallelization method = %s\n",
use_threads ? "threaded" : "streamed");
printf("Problem scaling = %s\n", strongScaling ? "strong" : "weak");
printf("Number of GPUs = %d\n", GPU_N);
printf("Total number of options = %d\n", OPT_N);
printf("Number of paths = %d\n", PATH_N);
printf("main(): generating input data...\n");
srand(123);
for (i = 0; i < OPT_N; i++) {
optionData[i].S = randFloat(5.0f, 50.0f);
optionData[i].X = randFloat(10.0f, 25.0f);
optionData[i].T = randFloat(1.0f, 5.0f);
optionData[i].R = 0.06f;
optionData[i].V = 0.10f;
callValueGPU[i].Expected = -1.0f;
callValueGPU[i].Confidence = -1.0f;
}
printf("main(): starting %i host threads...\n", GPU_N);
// Get option count for each GPU
for (i = 0; i < GPU_N; i++) {
optionSolver[i].optionCount = OPT_N / GPU_N;
}
// Take into account cases with "odd" option counts
for (i = 0; i < (OPT_N % GPU_N); i++) {
optionSolver[i].optionCount++;
}
// Assign GPU option ranges
gpuBase = 0;
for (i = 0; i < GPU_N; i++) {
optionSolver[i].device = i;
optionSolver[i].optionData = optionData + gpuBase;
optionSolver[i].callValue = callValueGPU + gpuBase;
optionSolver[i].pathN = PATH_N;
optionSolver[i].gridSize =
adjustGridSize(optionSolver[i].device, optionSolver[i].optionCount);
gpuBase += optionSolver[i].optionCount;
}
if (use_threads || bqatest) {
// Start CPU thread for each GPU
for (gpuIndex = 0; gpuIndex < GPU_N; gpuIndex++) {
threadID[gpuIndex] = cutStartThread((CUT_THREADROUTINE)solverThread,
&optionSolver[gpuIndex]);
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
bqatest = true;
}
printf("main(): waiting for GPU results...\n");
cutWaitForThreads(threadID, GPU_N);
getCmdLineArgumentString(argc, (const char **)argv, "method", &multiMethodChoice);
getCmdLineArgumentString(argc, (const char **)argv, "scaling", &scalingChoice);
printf("main(): GPU statistics, threaded\n");
for (i = 0; i < GPU_N; i++) {
cudaDeviceProp deviceProp;
checkCudaErrors(
cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
printf("Options : %i\n", optionSolver[i].optionCount);
printf("Simulation paths: %i\n", optionSolver[i].pathN);
time = sdkGetTimerValue(&hTimer[i]);
printf("Total time (ms.): %f\n", time);
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
if (checkCmdLineFlag(argc, (const char **)argv, "h") || checkCmdLineFlag(argc, (const char **)argv, "help")) {
usage();
exit(EXIT_SUCCESS);
}
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
sumDelta = 0;
sumRef = 0;
sumReserve = 0;
if (multiMethodChoice == NULL) {
use_threads = false;
}
else {
if (!strcasecmp(multiMethodChoice, "threaded")) {
use_threads = true;
}
else {
use_threads = false;
}
}
if (use_threads == false) {
printf("Using single CPU thread for multiple GPUs\n");
}
if (scalingChoice == NULL) {
strongScaling = false;
}
else {
if (!strcasecmp(scalingChoice, "strong")) {
strongScaling = true;
}
else {
strongScaling = false;
}
}
// GPU number present in the system
int GPU_N;
checkCudaErrors(cudaGetDeviceCount(&GPU_N));
int nOptions = 8 * 1024;
nOptions = adjustProblemSize(GPU_N, nOptions);
// select problem size
int scale = (strongScaling) ? 1 : GPU_N;
int OPT_N = nOptions * scale;
int PATH_N = 262144;
// initialize the timers
hTimer = new StopWatchInterface *[GPU_N];
for (int i = 0; i < GPU_N; i++) {
sdkCreateTimer(&hTimer[i]);
sdkResetTimer(&hTimer[i]);
}
// Input data array
TOptionData *optionData = new TOptionData[OPT_N];
// Final GPU MC results
TOptionValue *callValueGPU = new TOptionValue[OPT_N];
//"Theoretical" call values by Black-Scholes formula
float *callValueBS = new float[OPT_N];
// Solver config
TOptionPlan *optionSolver = new TOptionPlan[GPU_N];
// OS thread ID
CUTThread *threadID = new CUTThread[GPU_N];
int gpuBase, gpuIndex;
int i;
float time;
double delta, ref, sumDelta, sumRef, sumReserve;
printf("MonteCarloMultiGPU\n");
printf("==================\n");
printf("Parallelization method = %s\n", use_threads ? "threaded" : "streamed");
printf("Problem scaling = %s\n", strongScaling ? "strong" : "weak");
printf("Number of GPUs = %d\n", GPU_N);
printf("Total number of options = %d\n", OPT_N);
printf("Number of paths = %d\n", PATH_N);
printf("main(): generating input data...\n");
srand(123);
for (i = 0; i < OPT_N; i++) {
BlackScholesCall(callValueBS[i], optionData[i]);
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
ref = callValueBS[i];
sumDelta += delta;
sumRef += fabs(ref);
if (delta > 1e-6) {
sumReserve += callValueGPU[i].Confidence / delta;
}
#ifdef PRINT_RESULTS
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
#endif
optionData[i].S = randFloat(5.0f, 50.0f);
optionData[i].X = randFloat(10.0f, 25.0f);
optionData[i].T = randFloat(1.0f, 5.0f);
optionData[i].R = 0.06f;
optionData[i].V = 0.10f;
callValueGPU[i].Expected = -1.0f;
callValueGPU[i].Confidence = -1.0f;
}
sumReserve /= OPT_N;
}
printf("main(): starting %i host threads...\n", GPU_N);
if (!use_threads || bqatest) {
multiSolver(optionSolver, GPU_N);
// Get option count for each GPU
for (i = 0; i < GPU_N; i++) {
optionSolver[i].optionCount = OPT_N / GPU_N;
}
printf("main(): GPU statistics, streamed\n");
// Take into account cases with "odd" option counts
for (i = 0; i < (OPT_N % GPU_N); i++) {
optionSolver[i].optionCount++;
}
// Assign GPU option ranges
gpuBase = 0;
for (i = 0; i < GPU_N; i++) {
cudaDeviceProp deviceProp;
checkCudaErrors(
cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
printf("Options : %i\n", optionSolver[i].optionCount);
printf("Simulation paths: %i\n", optionSolver[i].pathN);
optionSolver[i].device = i;
optionSolver[i].optionData = optionData + gpuBase;
optionSolver[i].callValue = callValueGPU + gpuBase;
optionSolver[i].pathN = PATH_N;
optionSolver[i].gridSize = adjustGridSize(optionSolver[i].device, optionSolver[i].optionCount);
gpuBase += optionSolver[i].optionCount;
}
time = sdkGetTimerValue(&hTimer[0]);
printf("\nTotal time (ms.): %f\n", time);
printf("\tNote: This is elapsed time for all to compute.\n");
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
if (use_threads || bqatest) {
// Start CPU thread for each GPU
for (gpuIndex = 0; gpuIndex < GPU_N; gpuIndex++) {
threadID[gpuIndex] = cutStartThread((CUT_THREADROUTINE)solverThread, &optionSolver[gpuIndex]);
}
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
sumDelta = 0;
sumRef = 0;
sumReserve = 0;
printf("main(): waiting for GPU results...\n");
cutWaitForThreads(threadID, GPU_N);
for (i = 0; i < OPT_N; i++) {
BlackScholesCall(callValueBS[i], optionData[i]);
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
ref = callValueBS[i];
sumDelta += delta;
sumRef += fabs(ref);
printf("main(): GPU statistics, threaded\n");
if (delta > 1e-6) {
sumReserve += callValueGPU[i].Confidence / delta;
}
for (i = 0; i < GPU_N; i++) {
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
printf("Options : %i\n", optionSolver[i].optionCount);
printf("Simulation paths: %i\n", optionSolver[i].pathN);
time = sdkGetTimerValue(&hTimer[i]);
printf("Total time (ms.): %f\n", time);
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
}
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
sumDelta = 0;
sumRef = 0;
sumReserve = 0;
for (i = 0; i < OPT_N; i++) {
BlackScholesCall(callValueBS[i], optionData[i]);
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
ref = callValueBS[i];
sumDelta += delta;
sumRef += fabs(ref);
if (delta > 1e-6) {
sumReserve += callValueGPU[i].Confidence / delta;
}
#ifdef PRINT_RESULTS
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
#endif
}
sumReserve /= OPT_N;
}
sumReserve /= OPT_N;
}
if (!use_threads || bqatest) {
multiSolver(optionSolver, GPU_N);
printf("main(): GPU statistics, streamed\n");
for (i = 0; i < GPU_N; i++) {
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, optionSolver[i].device));
printf("GPU Device #%i: %s\n", optionSolver[i].device, deviceProp.name);
printf("Options : %i\n", optionSolver[i].optionCount);
printf("Simulation paths: %i\n", optionSolver[i].pathN);
}
time = sdkGetTimerValue(&hTimer[0]);
printf("\nTotal time (ms.): %f\n", time);
printf("\tNote: This is elapsed time for all to compute.\n");
printf("Options per sec.: %f\n", OPT_N / (time * 0.001));
printf("main(): comparing Monte Carlo and Black-Scholes results...\n");
sumDelta = 0;
sumRef = 0;
sumReserve = 0;
for (i = 0; i < OPT_N; i++) {
BlackScholesCall(callValueBS[i], optionData[i]);
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
ref = callValueBS[i];
sumDelta += delta;
sumRef += fabs(ref);
if (delta > 1e-6) {
sumReserve += callValueGPU[i].Confidence / delta;
}
#ifdef PRINT_RESULTS
printf("BS: %f; delta: %E\n", callValueBS[i], delta);
#endif
}
sumReserve /= OPT_N;
}
#ifdef DO_CPU
printf("main(): running CPU MonteCarlo...\n");
TOptionValue callValueCPU;
sumDelta = 0;
sumRef = 0;
printf("main(): running CPU MonteCarlo...\n");
TOptionValue callValueCPU;
sumDelta = 0;
sumRef = 0;
for (i = 0; i < OPT_N; i++) {
MonteCarloCPU(callValueCPU, optionData[i], NULL, PATH_N);
delta = fabs(callValueCPU.Expected - callValueGPU[i].Expected);
ref = callValueCPU.Expected;
sumDelta += delta;
sumRef += fabs(ref);
printf("Exp : %f | %f\t", callValueCPU.Expected, callValueGPU[i].Expected);
printf("Conf: %f | %f\n", callValueCPU.Confidence,
callValueGPU[i].Confidence);
}
for (i = 0; i < OPT_N; i++) {
MonteCarloCPU(callValueCPU, optionData[i], NULL, PATH_N);
delta = fabs(callValueCPU.Expected - callValueGPU[i].Expected);
ref = callValueCPU.Expected;
sumDelta += delta;
sumRef += fabs(ref);
printf("Exp : %f | %f\t", callValueCPU.Expected, callValueGPU[i].Expected);
printf("Conf: %f | %f\n", callValueCPU.Confidence, callValueGPU[i].Confidence);
}
printf("L1 norm: %E\n", sumDelta / sumRef);
printf("L1 norm: %E\n", sumDelta / sumRef);
#endif
printf("Shutting down...\n");
printf("Shutting down...\n");
for (int i = 0; i < GPU_N; i++) {
sdkStartTimer(&hTimer[i]);
checkCudaErrors(cudaSetDevice(i));
}
for (int i = 0; i < GPU_N; i++) {
sdkStartTimer(&hTimer[i]);
checkCudaErrors(cudaSetDevice(i));
}
delete[] optionSolver;
delete[] callValueBS;
delete[] callValueGPU;
delete[] optionData;
delete[] threadID;
delete[] hTimer;
delete[] optionSolver;
delete[] callValueBS;
delete[] callValueGPU;
delete[] optionData;
delete[] threadID;
delete[] hTimer;
printf("Test Summary...\n");
printf("L1 norm : %E\n", sumDelta / sumRef);
printf("Average reserve: %f\n", sumReserve);
printf(
"\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
printf(sumReserve > 1.0f ? "Test passed\n" : "Test failed!\n");
exit(sumReserve > 1.0f ? EXIT_SUCCESS : EXIT_FAILURE);
printf("Test Summary...\n");
printf("L1 norm : %E\n", sumDelta / sumRef);
printf("Average reserve: %f\n", sumReserve);
printf("\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
printf(sumReserve > 1.0f ? "Test passed\n" : "Test failed!\n");
exit(sumReserve > 1.0f ? EXIT_SUCCESS : EXIT_FAILURE);
}

View File

@@ -27,68 +27,71 @@
#ifndef MONTECARLO_COMMON_H
#define MONTECARLO_COMMON_H
#include "realtype.h"
#include "curand_kernel.h"
#include "realtype.h"
////////////////////////////////////////////////////////////////////////////////
// Global types
////////////////////////////////////////////////////////////////////////////////
typedef struct {
float S;
float X;
float T;
float R;
float V;
typedef struct
{
float S;
float X;
float T;
float R;
float V;
} TOptionData;
typedef struct
//#ifdef __CUDACC__
//__align__(8)
//#endif
{
float Expected;
float Confidence;
// #ifdef __CUDACC__
//__align__(8)
// #endif
{
float Expected;
float Confidence;
} TOptionValue;
// GPU outputs before CPU postprocessing
typedef struct {
real Expected;
real Confidence;
typedef struct
{
real Expected;
real Confidence;
} __TOptionValue;
typedef struct {
// Device ID for multi-GPU version
int device;
// Option count for this plan
int optionCount;
typedef struct
{
// Device ID for multi-GPU version
int device;
// Option count for this plan
int optionCount;
// Host-side data source and result destination
TOptionData *optionData;
TOptionValue *callValue;
// Host-side data source and result destination
TOptionData *optionData;
TOptionValue *callValue;
// Temporary Host-side pinned memory for async + faster data transfers
__TOptionValue *h_CallValue;
// Temporary Host-side pinned memory for async + faster data transfers
__TOptionValue *h_CallValue;
// Device- and host-side option data
void *d_OptionData;
void *h_OptionData;
// Device- and host-side option data
void *d_OptionData;
void *h_OptionData;
// Device-side option values
void *d_CallValue;
// Device-side option values
void *d_CallValue;
// Intermediate device-side buffers
void *d_Buffer;
// Intermediate device-side buffers
void *d_Buffer;
// random number generator states
curandState *rngStates;
// random number generator states
curandState *rngStates;
// Pseudorandom samples count
int pathN;
// Pseudorandom samples count
int pathN;
// Time stamp
float time;
// Time stamp
float time;
int gridSize;
int gridSize;
} TOptionPlan;
extern "C" void initMonteCarloGPU(TOptionPlan *plan);

View File

@@ -25,13 +25,12 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <curand.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <curand.h>
//#include "curand_kernel.h"
// #include "curand_kernel.h"
#include "helper_cuda.h"
////////////////////////////////////////////////////////////////////////////////
@@ -42,99 +41,101 @@
////////////////////////////////////////////////////////////////////////////////
// Black-Scholes formula for Monte Carlo results validation
////////////////////////////////////////////////////////////////////////////////
#define A1 0.31938153
#define A2 -0.356563782
#define A3 1.781477937
#define A4 -1.821255978
#define A5 1.330274429
#define A1 0.31938153
#define A2 -0.356563782
#define A3 1.781477937
#define A4 -1.821255978
#define A5 1.330274429
#define RSQRT2PI 0.39894228040143267793994605993438
// Polynomial approximation of
// cumulative normal distribution function
double CND(double d) {
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double CND(double d)
{
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double cnd = RSQRT2PI * exp(-0.5 * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
double cnd = RSQRT2PI * exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (d > 0) cnd = 1.0 - cnd;
if (d > 0)
cnd = 1.0 - cnd;
return cnd;
return cnd;
}
// Black-Scholes formula for call value
extern "C" void BlackScholesCall(float &callValue, TOptionData optionData) {
double S = optionData.S;
double X = optionData.X;
double T = optionData.T;
double R = optionData.R;
double V = optionData.V;
extern "C" void BlackScholesCall(float &callValue, TOptionData optionData)
{
double S = optionData.S;
double X = optionData.X;
double T = optionData.T;
double R = optionData.R;
double V = optionData.V;
double sqrtT = sqrt(T);
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
double expRT = exp(-R * T);
double sqrtT = sqrt(T);
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
double expRT = exp(-R * T);
callValue = (float)(S * CNDD1 - X * expRT * CNDD2);
callValue = (float)(S * CNDD1 - X * expRT * CNDD2);
}
////////////////////////////////////////////////////////////////////////////////
// CPU Monte Carlo
////////////////////////////////////////////////////////////////////////////////
static double endCallValue(double S, double X, double r, double MuByT,
double VBySqrtT) {
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
return (callValue > 0) ? callValue : 0;
static double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)
{
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
return (callValue > 0) ? callValue : 0;
}
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData,
float *h_Samples, int pathN) {
const double S = optionData.S;
const double X = optionData.X;
const double T = optionData.T;
const double R = optionData.R;
const double V = optionData.V;
const double MuByT = (R - 0.5 * V * V) * T;
const double VBySqrtT = V * sqrt(T);
extern "C" void MonteCarloCPU(TOptionValue &callValue, TOptionData optionData, float *h_Samples, int pathN)
{
const double S = optionData.S;
const double X = optionData.X;
const double T = optionData.T;
const double R = optionData.R;
const double V = optionData.V;
const double MuByT = (R - 0.5 * V * V) * T;
const double VBySqrtT = V * sqrt(T);
float *samples;
curandGenerator_t gen;
float *samples;
curandGenerator_t gen;
checkCudaErrors(curandCreateGeneratorHost(&gen, CURAND_RNG_PSEUDO_DEFAULT));
unsigned long long seed = 1234ULL;
checkCudaErrors(curandSetPseudoRandomGeneratorSeed(gen, seed));
checkCudaErrors(curandCreateGeneratorHost(&gen, CURAND_RNG_PSEUDO_DEFAULT));
unsigned long long seed = 1234ULL;
checkCudaErrors(curandSetPseudoRandomGeneratorSeed(gen, seed));
if (h_Samples != NULL) {
samples = h_Samples;
} else {
samples = (float *)malloc(pathN * sizeof(float));
checkCudaErrors(curandGenerateNormal(gen, samples, pathN, 0.0, 1.0));
}
if (h_Samples != NULL) {
samples = h_Samples;
}
else {
samples = (float *)malloc(pathN * sizeof(float));
checkCudaErrors(curandGenerateNormal(gen, samples, pathN, 0.0, 1.0));
}
// for(int i=0; i<10; i++) printf("CPU sample = %f\n", samples[i]);
// for(int i=0; i<10; i++) printf("CPU sample = %f\n", samples[i]);
double sum = 0, sum2 = 0;
double sum = 0, sum2 = 0;
for (int pos = 0; pos < pathN; pos++) {
double sample = samples[pos];
double callValue = endCallValue(S, X, sample, MuByT, VBySqrtT);
sum += callValue;
sum2 += callValue * callValue;
}
for (int pos = 0; pos < pathN; pos++) {
double sample = samples[pos];
double callValue = endCallValue(S, X, sample, MuByT, VBySqrtT);
sum += callValue;
sum2 += callValue * callValue;
}
if (h_Samples == NULL) free(samples);
if (h_Samples == NULL)
free(samples);
checkCudaErrors(curandDestroyGenerator(gen));
checkCudaErrors(curandDestroyGenerator(gen));
// Derive average from the total sum and discount by riskfree rate
callValue.Expected = (float)(exp(-R * T) * sum / (double)pathN);
// Standard deviation
double stdDev = sqrt(((double)pathN * sum2 - sum * sum) /
((double)pathN * (double)(pathN - 1)));
// Confidence width; in 95% of all cases theoretical value lies within these
// borders
callValue.Confidence =
(float)(exp(-R * T) * 1.96 * stdDev / sqrt((double)pathN));
// Derive average from the total sum and discount by riskfree rate
callValue.Expected = (float)(exp(-R * T) * sum / (double)pathN);
// Standard deviation
double stdDev = sqrt(((double)pathN * sum2 - sum * sum) / ((double)pathN * (double)(pathN - 1)));
// Confidence width; in 95% of all cases theoretical value lies within these
// borders
callValue.Confidence = (float)(exp(-R * T) * 1.96 * stdDev / sqrt((double)pathN));
}

View File

@@ -28,13 +28,14 @@
////////////////////////////////////////////////////////////////////////////////
// Global types
////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <cooperative_groups.h>
#include <stdio.h>
#include <stdlib.h>
namespace cg = cooperative_groups;
#include <helper_cuda.h>
#include <curand_kernel.h>
#include <helper_cuda.h>
#include "MonteCarlo_common.h"
////////////////////////////////////////////////////////////////////////////////
@@ -49,26 +50,27 @@ namespace cg = cooperative_groups;
#define MAX_OPTIONS (1024 * 1024)
// Preprocessed input option data
typedef struct {
real S;
real X;
real MuByT;
real VBySqrtT;
typedef struct
{
real S;
real X;
real MuByT;
real VBySqrtT;
} __TOptionData;
////////////////////////////////////////////////////////////////////////////////
// Overloaded shortcut payoff functions for different precision modes
////////////////////////////////////////////////////////////////////////////////
__device__ inline float endCallValue(float S, float X, float r, float MuByT,
float VBySqrtT) {
float callValue = S * __expf(MuByT + VBySqrtT * r) - X;
return (callValue > 0.0F) ? callValue : 0.0F;
__device__ inline float endCallValue(float S, float X, float r, float MuByT, float VBySqrtT)
{
float callValue = S * __expf(MuByT + VBySqrtT * r) - X;
return (callValue > 0.0F) ? callValue : 0.0F;
}
__device__ inline double endCallValue(double S, double X, double r,
double MuByT, double VBySqrtT) {
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
return (callValue > 0.0) ? callValue : 0.0;
__device__ inline double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)
{
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
return (callValue > 0.0) ? callValue : 0.0;
}
#define THREAD_N 256
@@ -78,149 +80,145 @@ __device__ inline double endCallValue(double S, double X, double r,
// per option. It is fastest when the number of thread blocks times the work per
// block is high enough to keep the GPU busy.
////////////////////////////////////////////////////////////////////////////////
static __global__ void MonteCarloOneBlockPerOption(
curandState *__restrict rngStates,
const __TOptionData *__restrict d_OptionData,
__TOptionValue *__restrict d_CallValue, int pathN, int optionN) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
static __global__ void MonteCarloOneBlockPerOption(curandState *__restrict rngStates,
const __TOptionData *__restrict d_OptionData,
__TOptionValue *__restrict d_CallValue,
int pathN,
int optionN)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
const int SUM_N = THREAD_N;
__shared__ real s_SumCall[SUM_N];
__shared__ real s_Sum2Call[SUM_N];
const int SUM_N = THREAD_N;
__shared__ real s_SumCall[SUM_N];
__shared__ real s_Sum2Call[SUM_N];
// determine global thread id
int tid = threadIdx.x + blockIdx.x * blockDim.x;
// determine global thread id
int tid = threadIdx.x + blockIdx.x * blockDim.x;
// Copy random number state to local memory for efficiency
curandState localState = rngStates[tid];
for (int optionIndex = blockIdx.x; optionIndex < optionN;
optionIndex += gridDim.x) {
const real S = d_OptionData[optionIndex].S;
const real X = d_OptionData[optionIndex].X;
const real MuByT = d_OptionData[optionIndex].MuByT;
const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;
// Copy random number state to local memory for efficiency
curandState localState = rngStates[tid];
for (int optionIndex = blockIdx.x; optionIndex < optionN; optionIndex += gridDim.x) {
const real S = d_OptionData[optionIndex].S;
const real X = d_OptionData[optionIndex].X;
const real MuByT = d_OptionData[optionIndex].MuByT;
const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;
// Cycle through the entire samples array:
// derive end stock price for each path
// accumulate partial integrals into intermediate shared memory buffer
for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x) {
__TOptionValue sumCall = {0, 0};
// Cycle through the entire samples array:
// derive end stock price for each path
// accumulate partial integrals into intermediate shared memory buffer
for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x) {
__TOptionValue sumCall = {0, 0};
#pragma unroll 8
for (int i = iSum; i < pathN; i += SUM_N) {
real r = curand_normal(&localState);
real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);
sumCall.Expected += callValue;
sumCall.Confidence += callValue * callValue;
}
for (int i = iSum; i < pathN; i += SUM_N) {
real r = curand_normal(&localState);
real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);
sumCall.Expected += callValue;
sumCall.Confidence += callValue * callValue;
}
s_SumCall[iSum] = sumCall.Expected;
s_Sum2Call[iSum] = sumCall.Confidence;
s_SumCall[iSum] = sumCall.Expected;
s_Sum2Call[iSum] = sumCall.Confidence;
}
// Reduce shared memory accumulators
// and write final result to global memory
cg::sync(cta);
sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32, &d_CallValue[optionIndex]);
}
// Reduce shared memory accumulators
// and write final result to global memory
cg::sync(cta);
sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32,
&d_CallValue[optionIndex]);
}
}
static __global__ void rngSetupStates(curandState *rngState, int device_id) {
// determine global thread id
int tid = threadIdx.x + blockIdx.x * blockDim.x;
// Each threadblock gets different seed,
// Threads within a threadblock get different sequence numbers
curand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0,
&rngState[tid]);
static __global__ void rngSetupStates(curandState *rngState, int device_id)
{
// determine global thread id
int tid = threadIdx.x + blockIdx.x * blockDim.x;
// Each threadblock gets different seed,
// Threads within a threadblock get different sequence numbers
curand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0, &rngState[tid]);
}
////////////////////////////////////////////////////////////////////////////////
// Host-side interface to GPU Monte Carlo
////////////////////////////////////////////////////////////////////////////////
extern "C" void initMonteCarloGPU(TOptionPlan *plan) {
checkCudaErrors(cudaMalloc(&plan->d_OptionData,
sizeof(__TOptionData) * (plan->optionCount)));
checkCudaErrors(cudaMalloc(&plan->d_CallValue,
sizeof(__TOptionValue) * (plan->optionCount)));
checkCudaErrors(cudaMallocHost(&plan->h_OptionData,
sizeof(__TOptionData) * (plan->optionCount)));
// Allocate internal device memory
checkCudaErrors(cudaMallocHost(&plan->h_CallValue,
sizeof(__TOptionValue) * (plan->optionCount)));
// Allocate states for pseudo random number generators
checkCudaErrors(cudaMalloc((void **)&plan->rngStates,
plan->gridSize * THREAD_N * sizeof(curandState)));
checkCudaErrors(cudaMemset(plan->rngStates, 0,
plan->gridSize * THREAD_N * sizeof(curandState)));
extern "C" void initMonteCarloGPU(TOptionPlan *plan)
{
checkCudaErrors(cudaMalloc(&plan->d_OptionData, sizeof(__TOptionData) * (plan->optionCount)));
checkCudaErrors(cudaMalloc(&plan->d_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));
checkCudaErrors(cudaMallocHost(&plan->h_OptionData, sizeof(__TOptionData) * (plan->optionCount)));
// Allocate internal device memory
checkCudaErrors(cudaMallocHost(&plan->h_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));
// Allocate states for pseudo random number generators
checkCudaErrors(cudaMalloc((void **)&plan->rngStates, plan->gridSize * THREAD_N * sizeof(curandState)));
checkCudaErrors(cudaMemset(plan->rngStates, 0, plan->gridSize * THREAD_N * sizeof(curandState)));
// place each device pathN random numbers apart on the random number sequence
rngSetupStates<<<plan->gridSize, THREAD_N>>>(plan->rngStates, plan->device);
getLastCudaError("rngSetupStates kernel failed.\n");
// place each device pathN random numbers apart on the random number sequence
rngSetupStates<<<plan->gridSize, THREAD_N>>>(plan->rngStates, plan->device);
getLastCudaError("rngSetupStates kernel failed.\n");
}
// Compute statistics and deallocate internal device memory
extern "C" void closeMonteCarloGPU(TOptionPlan *plan) {
for (int i = 0; i < plan->optionCount; i++) {
const double RT = plan->optionData[i].R * plan->optionData[i].T;
const double sum = plan->h_CallValue[i].Expected;
const double sum2 = plan->h_CallValue[i].Confidence;
const double pathN = plan->pathN;
// Derive average from the total sum and discount by riskfree rate
plan->callValue[i].Expected = (float)(exp(-RT) * sum / pathN);
// Standard deviation
double stdDev = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));
// Confidence width; in 95% of all cases theoretical value lies within these
// borders
plan->callValue[i].Confidence =
(float)(exp(-RT) * 1.96 * stdDev / sqrt(pathN));
}
extern "C" void closeMonteCarloGPU(TOptionPlan *plan)
{
for (int i = 0; i < plan->optionCount; i++) {
const double RT = plan->optionData[i].R * plan->optionData[i].T;
const double sum = plan->h_CallValue[i].Expected;
const double sum2 = plan->h_CallValue[i].Confidence;
const double pathN = plan->pathN;
// Derive average from the total sum and discount by riskfree rate
plan->callValue[i].Expected = (float)(exp(-RT) * sum / pathN);
// Standard deviation
double stdDev = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));
// Confidence width; in 95% of all cases theoretical value lies within these
// borders
plan->callValue[i].Confidence = (float)(exp(-RT) * 1.96 * stdDev / sqrt(pathN));
}
checkCudaErrors(cudaFree(plan->rngStates));
checkCudaErrors(cudaFreeHost(plan->h_CallValue));
checkCudaErrors(cudaFreeHost(plan->h_OptionData));
checkCudaErrors(cudaFree(plan->d_CallValue));
checkCudaErrors(cudaFree(plan->d_OptionData));
checkCudaErrors(cudaFree(plan->rngStates));
checkCudaErrors(cudaFreeHost(plan->h_CallValue));
checkCudaErrors(cudaFreeHost(plan->h_OptionData));
checkCudaErrors(cudaFree(plan->d_CallValue));
checkCudaErrors(cudaFree(plan->d_OptionData));
}
// Main computations
extern "C" void MonteCarloGPU(TOptionPlan *plan, cudaStream_t stream) {
__TOptionValue *h_CallValue = plan->h_CallValue;
extern "C" void MonteCarloGPU(TOptionPlan *plan, cudaStream_t stream)
{
__TOptionValue *h_CallValue = plan->h_CallValue;
if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS) {
printf("MonteCarloGPU(): bad option count.\n");
return;
}
if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS) {
printf("MonteCarloGPU(): bad option count.\n");
return;
}
__TOptionData *h_OptionData = (__TOptionData *)plan->h_OptionData;
__TOptionData *h_OptionData = (__TOptionData *)plan->h_OptionData;
for (int i = 0; i < plan->optionCount; i++) {
const double T = plan->optionData[i].T;
const double R = plan->optionData[i].R;
const double V = plan->optionData[i].V;
const double MuByT = (R - 0.5 * V * V) * T;
const double VBySqrtT = V * sqrt(T);
h_OptionData[i].S = (real)plan->optionData[i].S;
h_OptionData[i].X = (real)plan->optionData[i].X;
h_OptionData[i].MuByT = (real)MuByT;
h_OptionData[i].VBySqrtT = (real)VBySqrtT;
}
for (int i = 0; i < plan->optionCount; i++) {
const double T = plan->optionData[i].T;
const double R = plan->optionData[i].R;
const double V = plan->optionData[i].V;
const double MuByT = (R - 0.5 * V * V) * T;
const double VBySqrtT = V * sqrt(T);
h_OptionData[i].S = (real)plan->optionData[i].S;
h_OptionData[i].X = (real)plan->optionData[i].X;
h_OptionData[i].MuByT = (real)MuByT;
h_OptionData[i].VBySqrtT = (real)VBySqrtT;
}
checkCudaErrors(cudaMemcpyAsync(plan->d_OptionData, h_OptionData,
plan->optionCount * sizeof(__TOptionData),
cudaMemcpyHostToDevice, stream));
checkCudaErrors(cudaMemcpyAsync(
plan->d_OptionData, h_OptionData, plan->optionCount * sizeof(__TOptionData), cudaMemcpyHostToDevice, stream));
MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(
plan->rngStates, (__TOptionData *)(plan->d_OptionData),
(__TOptionValue *)(plan->d_CallValue), plan->pathN, plan->optionCount);
getLastCudaError("MonteCarloOneBlockPerOption() execution failed\n");
MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(plan->rngStates,
(__TOptionData *)(plan->d_OptionData),
(__TOptionValue *)(plan->d_CallValue),
plan->pathN,
plan->optionCount);
getLastCudaError("MonteCarloOneBlockPerOption() execution failed\n");
checkCudaErrors(cudaMemcpyAsync(h_CallValue, plan->d_CallValue,
plan->optionCount * sizeof(__TOptionValue),
cudaMemcpyDeviceToHost, stream));
checkCudaErrors(cudaMemcpyAsync(
h_CallValue, plan->d_CallValue, plan->optionCount * sizeof(__TOptionValue), cudaMemcpyDeviceToHost, stream));
// cudaDeviceSynchronize();
// cudaDeviceSynchronize();
}

View File

@@ -40,40 +40,40 @@ namespace cg = cooperative_groups;
////////////////////////////////////////////////////////////////////////////////
template <class T, int SUM_N, int blockSize>
__device__ void sumReduce(T *sum, T *sum2, cg::thread_block &cta,
cg::thread_block_tile<32> &tile32,
__TOptionValue *d_CallValue) {
const int VEC = 32;
const int tid = cta.thread_rank();
__device__ void
sumReduce(T *sum, T *sum2, cg::thread_block &cta, cg::thread_block_tile<32> &tile32, __TOptionValue *d_CallValue)
{
const int VEC = 32;
const int tid = cta.thread_rank();
T beta = sum[tid];
T beta2 = sum2[tid];
T temp, temp2;
T beta = sum[tid];
T beta2 = sum2[tid];
T temp, temp2;
for (int i = VEC / 2; i > 0; i >>= 1) {
if (tile32.thread_rank() < i) {
temp = sum[tid + i];
temp2 = sum2[tid + i];
beta += temp;
beta2 += temp2;
sum[tid] = beta;
sum2[tid] = beta2;
for (int i = VEC / 2; i > 0; i >>= 1) {
if (tile32.thread_rank() < i) {
temp = sum[tid + i];
temp2 = sum2[tid + i];
beta += temp;
beta2 += temp2;
sum[tid] = beta;
sum2[tid] = beta2;
}
cg::sync(tile32);
}
cg::sync(tile32);
}
cg::sync(cta);
cg::sync(cta);
if (tid == 0) {
beta = 0;
beta2 = 0;
for (int i = 0; i < blockDim.x; i += VEC) {
beta += sum[i];
beta2 += sum2[i];
if (tid == 0) {
beta = 0;
beta2 = 0;
for (int i = 0; i < blockDim.x; i += VEC) {
beta += sum[i];
beta2 += sum2[i];
}
__TOptionValue t = {beta, beta2};
*d_CallValue = t;
}
__TOptionValue t = {beta, beta2};
*d_CallValue = t;
}
cg::sync(cta);
cg::sync(cta);
}
#endif

View File

@@ -29,41 +29,46 @@
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
// Create thread
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data) {
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, data, 0, NULL);
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data)
{
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, data, 0, NULL);
}
// Wait for thread to finish
void cutEndThread(CUTThread thread) {
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
void cutEndThread(CUTThread thread)
{
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
}
// Wait for multiple threads
void cutWaitForThreads(const CUTThread *threads, int num) {
WaitForMultipleObjects(num, threads, true, INFINITE);
void cutWaitForThreads(const CUTThread *threads, int num)
{
WaitForMultipleObjects(num, threads, true, INFINITE);
for (int i = 0; i < num; i++) {
CloseHandle(threads[i]);
}
for (int i = 0; i < num; i++) {
CloseHandle(threads[i]);
}
}
#else
// Create thread
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data) {
pthread_t thread;
pthread_create(&thread, NULL, func, data);
return thread;
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data)
{
pthread_t thread;
pthread_create(&thread, NULL, func, data);
return thread;
}
// Wait for thread to finish
void cutEndThread(CUTThread thread) { pthread_join(thread, NULL); }
// Wait for multiple threads
void cutWaitForThreads(const CUTThread *threads, int num) {
for (int i = 0; i < num; i++) {
cutEndThread(threads[i]);
}
void cutWaitForThreads(const CUTThread *threads, int num)
{
for (int i = 0; i < num; i++) {
cutEndThread(threads[i]);
}
}
#endif

View File

@@ -38,7 +38,7 @@ typedef HANDLE CUTThread;
typedef unsigned(WINAPI *CUT_THREADROUTINE)(void *);
#define CUT_THREADPROC unsigned WINAPI
#define CUT_THREADEND return 0
#define CUT_THREADEND return 0
#else
// POSIX threads.
@@ -52,20 +52,21 @@ typedef void *(*CUT_THREADROUTINE)(void *);
#endif
#ifdef __cplusplus
extern "C" {
extern "C"
{
#endif
// Create thread.
CUTThread cutStartThread(CUT_THREADROUTINE, void *data);
// Create thread.
CUTThread cutStartThread(CUT_THREADROUTINE, void *data);
// Wait for thread to finish.
void cutEndThread(CUTThread thread);
// Wait for thread to finish.
void cutEndThread(CUTThread thread);
// Wait for multiple threads.
void cutWaitForThreads(const CUTThread *threads, int num);
// Wait for multiple threads.
void cutWaitForThreads(const CUTThread *threads, int num);
#ifdef __cplusplus
} // extern "C"
} // extern "C"
#endif
#endif // MULTITHREADING_H
#endif // MULTITHREADING_H

View File

@@ -28,7 +28,7 @@
#ifndef REALTYPE_H
#define REALTYPE_H
//#define DOUBLE_PRECISION
// #define DOUBLE_PRECISION
#ifndef DOUBLE_PRECISION
typedef float real;

View File

@@ -30,38 +30,58 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include "resize_convert.h"
__global__ void resizeBGRplanarBatchKernel(cudaTextureObject_t texSrc,
float *pDst, int nDstPitch, int nDstHeight, int nSrcHeight,
int batch, float scaleX, float scaleY,
int cropX, int cropY, int cropW, int cropH) {
float *pDst,
int nDstPitch,
int nDstHeight,
int nSrcHeight,
int batch,
float scaleX,
float scaleY,
int cropX,
int cropY,
int cropW,
int cropH)
{
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
if (x >= (int)(cropW/scaleX) || y >= (int)(cropH/scaleY))
if (x >= (int)(cropW / scaleX) || y >= (int)(cropH / scaleY))
return;
int frameSize = nDstPitch*nDstHeight;
float *p = NULL;
int frameSize = nDstPitch * nDstHeight;
float *p = NULL;
for (int i = blockIdx.z; i < batch; i += gridDim.z) {
#pragma unroll
for (int channel=0; channel < 3; channel++){
p = pDst + i * 3 * frameSize + y * nDstPitch + x + channel * frameSize;
*p = tex2D<float>(texSrc, x * scaleX + cropX,
((3 * i + channel) * nSrcHeight + y * scaleY + cropY));
#pragma unroll
for (int channel = 0; channel < 3; channel++) {
p = pDst + i * 3 * frameSize + y * nDstPitch + x + channel * frameSize;
*p = tex2D<float>(texSrc, x * scaleX + cropX, ((3 * i + channel) * nSrcHeight + y * scaleY + cropY));
}
}
}
static void resizeBGRplanarBatchCore(
float *dpSrc, int nSrcPitch, int nSrcWidth, int nSrcHeight,
float *dpDst, int nDstPitch, int nDstWidth, int nDstHeight,
int nBatchSize, cudaStream_t stream, bool whSameResizeRatio,
int cropX, int cropY, int cropW, int cropH) {
static void resizeBGRplanarBatchCore(float *dpSrc,
int nSrcPitch,
int nSrcWidth,
int nSrcHeight,
float *dpDst,
int nDstPitch,
int nDstWidth,
int nDstHeight,
int nBatchSize,
cudaStream_t stream,
bool whSameResizeRatio,
int cropX,
int cropY,
int cropW,
int cropH)
{
cudaTextureObject_t texSrc[2];
int nTiles = 1, h, iTile;
int nTiles = 1, h, iTile;
h = nSrcHeight * 3 * nBatchSize;
while ((h + nTiles - 1) / nTiles > 65536)
@@ -70,65 +90,85 @@ static void resizeBGRplanarBatchCore(
if (nTiles > 2)
return;
int batchTile = nBatchSize / nTiles;
int batchTileLast = nBatchSize - batchTile * (nTiles-1);
int batchTile = nBatchSize / nTiles;
int batchTileLast = nBatchSize - batchTile * (nTiles - 1);
for (iTile = 0; iTile < nTiles; ++iTile) {
int bs = (iTile == nTiles - 1) ? batchTileLast : batchTile;
float *dpSrcNew = dpSrc +
iTile * (batchTile * 3 * nSrcHeight * nSrcPitch);
int bs = (iTile == nTiles - 1) ? batchTileLast : batchTile;
float *dpSrcNew = dpSrc + iTile * (batchTile * 3 * nSrcHeight * nSrcPitch);
cudaResourceDesc resDesc = {};
resDesc.resType = cudaResourceTypePitch2D;
resDesc.res.pitch2D.devPtr = dpSrcNew;
resDesc.res.pitch2D.desc = cudaCreateChannelDesc<float>();
resDesc.res.pitch2D.width = nSrcWidth;
resDesc.res.pitch2D.height = bs * 3 * nSrcHeight;
cudaResourceDesc resDesc = {};
resDesc.resType = cudaResourceTypePitch2D;
resDesc.res.pitch2D.devPtr = dpSrcNew;
resDesc.res.pitch2D.desc = cudaCreateChannelDesc<float>();
resDesc.res.pitch2D.width = nSrcWidth;
resDesc.res.pitch2D.height = bs * 3 * nSrcHeight;
resDesc.res.pitch2D.pitchInBytes = nSrcPitch * sizeof(float);
cudaTextureDesc texDesc = {};
texDesc.filterMode = cudaFilterModeLinear;
texDesc.readMode = cudaReadModeElementType;
cudaTextureDesc texDesc = {};
texDesc.filterMode = cudaFilterModeLinear;
texDesc.readMode = cudaReadModeElementType;
checkCudaErrors(cudaCreateTextureObject(&texSrc[iTile], &resDesc, &texDesc, NULL));
float *dpDstNew = dpDst +
iTile * (batchTile * 3 * nDstHeight * nDstPitch);
float *dpDstNew = dpDst + iTile * (batchTile * 3 * nDstHeight * nDstPitch);
if(cropW == 0 || cropH == 0) {
if (cropW == 0 || cropH == 0) {
cropX = 0;
cropY = 0;
cropW = nSrcWidth;
cropH = nSrcHeight;
}
float scaleX = (cropW*1.0f / nDstWidth);
float scaleY = (cropH*1.0f / nDstHeight);
float scaleX = (cropW * 1.0f / nDstWidth);
float scaleY = (cropH * 1.0f / nDstHeight);
if(whSameResizeRatio == true)
if (whSameResizeRatio == true)
scaleX = scaleY = scaleX > scaleY ? scaleX : scaleY;
dim3 block(32, 32, 1);
size_t blockDimZ = bs;
// Restricting blocks in Z-dim till 32 to not launch too many blocks
blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;
dim3 grid((cropW*1.0f/scaleX + block.x - 1) / block.x,
(cropH*1.0f/scaleY + block.y - 1) / block.y, blockDimZ);
resizeBGRplanarBatchKernel<<<grid, block, 0, stream>>>
(texSrc[iTile], dpDstNew, nDstPitch, nDstHeight, nSrcHeight,
bs, scaleX, scaleY, cropX, cropY, cropW, cropH);
dim3 grid((cropW * 1.0f / scaleX + block.x - 1) / block.x,
(cropH * 1.0f / scaleY + block.y - 1) / block.y,
blockDimZ);
resizeBGRplanarBatchKernel<<<grid, block, 0, stream>>>(
texSrc[iTile], dpDstNew, nDstPitch, nDstHeight, nSrcHeight, bs, scaleX, scaleY, cropX, cropY, cropW, cropH);
}
for (iTile = 0; iTile < nTiles; ++iTile)
checkCudaErrors(cudaDestroyTextureObject(texSrc[iTile]));
}
void resizeBGRplanarBatch(
float *dpSrc, int nSrcPitch, int nSrcWidth, int nSrcHeight,
float *dpDst, int nDstPitch, int nDstWidth, int nDstHeight,
int nBatchSize, cudaStream_t stream,
int cropX, int cropY, int cropW, int cropH, bool whSameResizeRatio) {
resizeBGRplanarBatchCore(dpSrc, nSrcPitch, nSrcWidth, nSrcHeight,
dpDst, nDstPitch, nDstWidth, nDstHeight, nBatchSize, stream,
whSameResizeRatio, cropX, cropY, cropW, cropH);
void resizeBGRplanarBatch(float *dpSrc,
int nSrcPitch,
int nSrcWidth,
int nSrcHeight,
float *dpDst,
int nDstPitch,
int nDstWidth,
int nDstHeight,
int nBatchSize,
cudaStream_t stream,
int cropX,
int cropY,
int cropW,
int cropH,
bool whSameResizeRatio)
{
resizeBGRplanarBatchCore(dpSrc,
nSrcPitch,
nSrcWidth,
nSrcHeight,
dpDst,
nDstPitch,
nDstWidth,
nDstHeight,
nBatchSize,
stream,
whSameResizeRatio,
cropX,
cropY,
cropW,
cropH);
}

View File

@@ -29,84 +29,94 @@
#include <cuda.h>
#include <cuda_runtime.h>
#include "resize_convert.h"
__global__ static void resizeNV12BatchKernel(cudaTextureObject_t texSrcLuma,
cudaTextureObject_t texSrcChroma,
uint8_t *pDstNv12, int nSrcWidth,
int nSrcHeight, int nDstPitch,
int nDstWidth, int nDstHeight,
int nBatchSize) {
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
uint8_t *pDstNv12,
int nSrcWidth,
int nSrcHeight,
int nDstPitch,
int nDstWidth,
int nDstHeight,
int nBatchSize)
{
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
int px = x * 2, py = y * 2;
int px = x * 2, py = y * 2;
if ((px + 1) >= nDstWidth || (py + 1) >= nDstHeight) return;
if ((px + 1) >= nDstWidth || (py + 1) >= nDstHeight)
return;
float fxScale = 1.0f * nSrcWidth / nDstWidth;
float fyScale = 1.0f * nSrcHeight / nDstHeight;
float fxScale = 1.0f * nSrcWidth / nDstWidth;
float fyScale = 1.0f * nSrcHeight / nDstHeight;
uint8_t *p = pDstNv12 + px + py * nDstPitch;
int hh = nDstHeight * 3 / 2;
int nByte = nDstPitch * hh;
int px_fxScale = px * fxScale;
int px_fxScale_1 = (px + 1) * fxScale;
int py_fyScale = py * fyScale;
int py_fyScale_1 = (py + 1) * fyScale;
uint8_t *p = pDstNv12 + px + py * nDstPitch;
int hh = nDstHeight * 3 / 2;
int nByte = nDstPitch * hh;
int px_fxScale = px * fxScale;
int px_fxScale_1 = (px + 1) * fxScale;
int py_fyScale = py * fyScale;
int py_fyScale_1 = (py + 1) * fyScale;
for (int i = blockIdx.z; i < nBatchSize; i+=gridDim.z) {
*(uchar2 *)p = make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale),
tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale));
*(uchar2 *)(p + nDstPitch) =
make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale_1),
tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale_1));
*(uchar2 *)(p + (nDstHeight - y) * nDstPitch) = tex2D<uchar2>(
texSrcChroma, x * fxScale, (hh * i + nDstHeight + y) * fyScale);
p += nByte;
py += hh;
}
for (int i = blockIdx.z; i < nBatchSize; i += gridDim.z) {
*(uchar2 *)p = make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale),
tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale));
*(uchar2 *)(p + nDstPitch) = make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale_1),
tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale_1));
*(uchar2 *)(p + (nDstHeight - y) * nDstPitch) =
tex2D<uchar2>(texSrcChroma, x * fxScale, (hh * i + nDstHeight + y) * fyScale);
p += nByte;
py += hh;
}
}
void resizeNV12Batch(uint8_t *dpSrc, int nSrcPitch, int nSrcWidth,
int nSrcHeight, uint8_t *dpDst, int nDstPitch,
int nDstWidth, int nDstHeight, int nBatchSize,
cudaStream_t stream) {
int hhSrc = ceilf(nSrcHeight * 3.0f / 2.0f);
cudaResourceDesc resDesc = {};
resDesc.resType = cudaResourceTypePitch2D;
resDesc.res.pitch2D.devPtr = dpSrc;
resDesc.res.pitch2D.desc = cudaCreateChannelDesc<uint8_t>();
resDesc.res.pitch2D.width = nSrcWidth;
resDesc.res.pitch2D.height = hhSrc * nBatchSize;
resDesc.res.pitch2D.pitchInBytes = nSrcPitch;
void resizeNV12Batch(uint8_t *dpSrc,
int nSrcPitch,
int nSrcWidth,
int nSrcHeight,
uint8_t *dpDst,
int nDstPitch,
int nDstWidth,
int nDstHeight,
int nBatchSize,
cudaStream_t stream)
{
int hhSrc = ceilf(nSrcHeight * 3.0f / 2.0f);
cudaResourceDesc resDesc = {};
resDesc.resType = cudaResourceTypePitch2D;
resDesc.res.pitch2D.devPtr = dpSrc;
resDesc.res.pitch2D.desc = cudaCreateChannelDesc<uint8_t>();
resDesc.res.pitch2D.width = nSrcWidth;
resDesc.res.pitch2D.height = hhSrc * nBatchSize;
resDesc.res.pitch2D.pitchInBytes = nSrcPitch;
cudaTextureDesc texDesc = {};
texDesc.filterMode = cudaFilterModePoint;
texDesc.readMode = cudaReadModeElementType;
cudaTextureDesc texDesc = {};
texDesc.filterMode = cudaFilterModePoint;
texDesc.readMode = cudaReadModeElementType;
cudaTextureObject_t texLuma = 0;
checkCudaErrors(cudaCreateTextureObject(&texLuma, &resDesc, &texDesc, NULL));
cudaTextureObject_t texLuma = 0;
checkCudaErrors(cudaCreateTextureObject(&texLuma, &resDesc, &texDesc, NULL));
resDesc.res.pitch2D.desc = cudaCreateChannelDesc<uchar2>();
resDesc.res.pitch2D.width /= 2;
resDesc.res.pitch2D.desc = cudaCreateChannelDesc<uchar2>();
resDesc.res.pitch2D.width /= 2;
cudaTextureObject_t texChroma = 0;
checkCudaErrors(cudaCreateTextureObject(&texChroma, &resDesc, &texDesc, NULL));
cudaTextureObject_t texChroma = 0;
checkCudaErrors(cudaCreateTextureObject(&texChroma, &resDesc, &texDesc, NULL));
dim3 block(32, 32, 1);
dim3 block(32, 32, 1);
size_t blockDimZ = nBatchSize;
size_t blockDimZ = nBatchSize;
// Restricting blocks in Z-dim till 32 to not launch too many blocks
blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;
// Restricting blocks in Z-dim till 32 to not launch too many blocks
blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;
dim3 grid((nDstWidth / 2 + block.x) / block.x,
(nDstHeight / 2 + block.y) / block.y, blockDimZ);
resizeNV12BatchKernel<<<grid, block, 0, stream>>>(
texLuma, texChroma, dpDst, nSrcWidth, nSrcHeight, nDstPitch, nDstWidth,
nDstHeight, nBatchSize);
dim3 grid((nDstWidth / 2 + block.x) / block.x, (nDstHeight / 2 + block.y) / block.y, blockDimZ);
resizeNV12BatchKernel<<<grid, block, 0, stream>>>(
texLuma, texChroma, dpDst, nSrcWidth, nSrcHeight, nDstPitch, nDstWidth, nDstHeight, nBatchSize);
checkCudaErrors(cudaDestroyTextureObject(texLuma));
checkCudaErrors(cudaDestroyTextureObject(texChroma));
checkCudaErrors(cudaDestroyTextureObject(texLuma));
checkCudaErrors(cudaDestroyTextureObject(texChroma));
}

View File

@@ -36,119 +36,124 @@
#define CONV_THREADS_X 64
#define CONV_THREADS_Y 10
__forceinline__ __device__ static float clampF(float x, float lower,
float upper) {
return x < lower ? lower : (x > upper ? upper : x);
__forceinline__ __device__ static float clampF(float x, float lower, float upper)
{
return x < lower ? lower : (x > upper ? upper : x);
}
__global__ static void nv12ToBGRplanarBatchKernel(const uint8_t *pNv12,
int nNv12Pitch, float *pBgr,
int nRgbPitch, int nWidth,
int nHeight, int nBatchSize) {
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
int nNv12Pitch,
float *pBgr,
int nRgbPitch,
int nWidth,
int nHeight,
int nBatchSize)
{
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
if ((x << 2) + 1 > nWidth || (y << 1) + 1 > nHeight) return;
if ((x << 2) + 1 > nWidth || (y << 1) + 1 > nHeight)
return;
const uint8_t *__restrict__ pSrc = pNv12;
const uint8_t *__restrict__ pSrc = pNv12;
for (int i = blockIdx.z; i < nBatchSize; i += gridDim.z) {
pSrc = pNv12 + i * ((nHeight * nNv12Pitch * 3) >> 1) + (x << 2) +
(y << 1) * nNv12Pitch;
uchar4 luma2x01, luma2x23, uv2;
*(uint32_t *)&luma2x01 = *(uint32_t *)pSrc;
*(uint32_t *)&luma2x23 = *(uint32_t *)(pSrc + nNv12Pitch);
*(uint32_t *)&uv2 = *(uint32_t *)(pSrc + (nHeight - y) * nNv12Pitch);
for (int i = blockIdx.z; i < nBatchSize; i += gridDim.z) {
pSrc = pNv12 + i * ((nHeight * nNv12Pitch * 3) >> 1) + (x << 2) + (y << 1) * nNv12Pitch;
uchar4 luma2x01, luma2x23, uv2;
*(uint32_t *)&luma2x01 = *(uint32_t *)pSrc;
*(uint32_t *)&luma2x23 = *(uint32_t *)(pSrc + nNv12Pitch);
*(uint32_t *)&uv2 = *(uint32_t *)(pSrc + (nHeight - y) * nNv12Pitch);
float *pDstBlock = (pBgr + i * ((nHeight * nRgbPitch * 3) >> 2) +
((blockIdx.x * blockDim.x) << 2) +
((blockIdx.y * blockDim.y) << 1) * (nRgbPitch >> 2));
float *pDstBlock = (pBgr + i * ((nHeight * nRgbPitch * 3) >> 2) + ((blockIdx.x * blockDim.x) << 2)
+ ((blockIdx.y * blockDim.y) << 1) * (nRgbPitch >> 2));
float2 add1;
float2 add2;
float2 add3;
float2 add00, add01, add02, add03;
float2 d, e;
float2 add1;
float2 add2;
float2 add3;
float2 add00, add01, add02, add03;
float2 d, e;
add00.x = 1.1644f * luma2x01.x;
add01.x = 1.1644f * luma2x01.y;
add00.y = 1.1644f * luma2x01.z;
add01.y = 1.1644f * luma2x01.w;
add00.x = 1.1644f * luma2x01.x;
add01.x = 1.1644f * luma2x01.y;
add00.y = 1.1644f * luma2x01.z;
add01.y = 1.1644f * luma2x01.w;
add02.x = 1.1644f * luma2x23.x;
add03.x = 1.1644f * luma2x23.y;
add02.y = 1.1644f * luma2x23.z;
add03.y = 1.1644f * luma2x23.w;
add02.x = 1.1644f * luma2x23.x;
add03.x = 1.1644f * luma2x23.y;
add02.y = 1.1644f * luma2x23.z;
add03.y = 1.1644f * luma2x23.w;
d.x = uv2.x - 128.0f;
e.x = uv2.y - 128.0f;
d.y = uv2.z - 128.0f;
e.y = uv2.w - 128.0f;
d.x = uv2.x - 128.0f;
e.x = uv2.y - 128.0f;
d.y = uv2.z - 128.0f;
e.y = uv2.w - 128.0f;
add1.x = 2.0172f * d.x;
add1.y = 2.0172f * d.y;
add1.x = 2.0172f * d.x;
add1.y = 2.0172f * d.y;
add2.x = (-0.3918f) * d.x + (-0.8130f) * e.x;
add2.y = (-0.3918f) * d.y + (-0.8130f) * e.y;
add2.x = (-0.3918f) * d.x + (-0.8130f) * e.x;
add2.y = (-0.3918f) * d.y + (-0.8130f) * e.y;
add3.x = 1.5960f * e.x;
add3.y = 1.5960f * e.y;
add3.x = 1.5960f * e.x;
add3.y = 1.5960f * e.y;
int rowStride = (threadIdx.y << 1) * (nRgbPitch >> 2);
int nextRowStride = ((threadIdx.y << 1) + 1) * (nRgbPitch >> 2);
// B
*((float4 *)&pDstBlock[rowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add00.x + add1.x, 0.0f, 255.0f),
clampF(add01.x + add1.x, 0.0f, 255.0f),
clampF(add00.y + add1.y, 0.0f, 255.0f),
clampF(add01.y + add1.y, 0.0f, 255.0f));
*((float4 *)&pDstBlock[nextRowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add02.x + add1.x, 0.0f, 255.0f),
clampF(add03.x + add1.x, 0.0f, 255.0f),
clampF(add02.y + add1.y, 0.0f, 255.0f),
clampF(add03.y + add1.y, 0.0f, 255.0f));
int rowStride = (threadIdx.y << 1) * (nRgbPitch >> 2);
int nextRowStride = ((threadIdx.y << 1) + 1) * (nRgbPitch >> 2);
// B
*((float4 *)&pDstBlock[rowStride + (threadIdx.x << 2)]) = make_float4(clampF(add00.x + add1.x, 0.0f, 255.0f),
clampF(add01.x + add1.x, 0.0f, 255.0f),
clampF(add00.y + add1.y, 0.0f, 255.0f),
clampF(add01.y + add1.y, 0.0f, 255.0f));
*((float4 *)&pDstBlock[nextRowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add02.x + add1.x, 0.0f, 255.0f),
clampF(add03.x + add1.x, 0.0f, 255.0f),
clampF(add02.y + add1.y, 0.0f, 255.0f),
clampF(add03.y + add1.y, 0.0f, 255.0f));
int planeStride = nHeight * nRgbPitch >> 2;
// G
*((float4 *)&pDstBlock[planeStride + rowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add00.x + add2.x, 0.0f, 255.0f),
clampF(add01.x + add2.x, 0.0f, 255.0f),
clampF(add00.y + add2.y, 0.0f, 255.0f),
clampF(add01.y + add2.y, 0.0f, 255.0f));
*((float4 *)&pDstBlock[planeStride + nextRowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add02.x + add2.x, 0.0f, 255.0f),
clampF(add03.x + add2.x, 0.0f, 255.0f),
clampF(add02.y + add2.y, 0.0f, 255.0f),
clampF(add03.y + add2.y, 0.0f, 255.0f));
int planeStride = nHeight * nRgbPitch >> 2;
// G
*((float4 *)&pDstBlock[planeStride + rowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add00.x + add2.x, 0.0f, 255.0f),
clampF(add01.x + add2.x, 0.0f, 255.0f),
clampF(add00.y + add2.y, 0.0f, 255.0f),
clampF(add01.y + add2.y, 0.0f, 255.0f));
*((float4 *)&pDstBlock[planeStride + nextRowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add02.x + add2.x, 0.0f, 255.0f),
clampF(add03.x + add2.x, 0.0f, 255.0f),
clampF(add02.y + add2.y, 0.0f, 255.0f),
clampF(add03.y + add2.y, 0.0f, 255.0f));
// R
*((float4
*)&pDstBlock[(planeStride << 1) + rowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add00.x + add3.x, 0.0f, 255.0f),
clampF(add01.x + add3.x, 0.0f, 255.0f),
clampF(add00.y + add3.y, 0.0f, 255.0f),
clampF(add01.y + add3.y, 0.0f, 255.0f));
*((float4 *)&pDstBlock[(planeStride << 1) + nextRowStride +
(threadIdx.x << 2)]) =
make_float4(clampF(add02.x + add3.x, 0.0f, 255.0f),
clampF(add03.x + add3.x, 0.0f, 255.0f),
clampF(add02.y + add3.y, 0.0f, 255.0f),
clampF(add03.y + add3.y, 0.0f, 255.0f));
}
// R
*((float4 *)&pDstBlock[(planeStride << 1) + rowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add00.x + add3.x, 0.0f, 255.0f),
clampF(add01.x + add3.x, 0.0f, 255.0f),
clampF(add00.y + add3.y, 0.0f, 255.0f),
clampF(add01.y + add3.y, 0.0f, 255.0f));
*((float4 *)&pDstBlock[(planeStride << 1) + nextRowStride + (threadIdx.x << 2)]) =
make_float4(clampF(add02.x + add3.x, 0.0f, 255.0f),
clampF(add03.x + add3.x, 0.0f, 255.0f),
clampF(add02.y + add3.y, 0.0f, 255.0f),
clampF(add03.y + add3.y, 0.0f, 255.0f));
}
}
void nv12ToBGRplanarBatch(uint8_t *pNv12, int nNv12Pitch, float *pBgr,
int nRgbPitch, int nWidth, int nHeight,
int nBatchSize, cudaStream_t stream) {
dim3 threads(CONV_THREADS_X, CONV_THREADS_Y);
void nv12ToBGRplanarBatch(uint8_t *pNv12,
int nNv12Pitch,
float *pBgr,
int nRgbPitch,
int nWidth,
int nHeight,
int nBatchSize,
cudaStream_t stream)
{
dim3 threads(CONV_THREADS_X, CONV_THREADS_Y);
size_t blockDimZ = nBatchSize;
size_t blockDimZ = nBatchSize;
// Restricting blocks in Z-dim till 32 to not launch too many blocks
blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;
// Restricting blocks in Z-dim till 32 to not launch too many blocks
blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;
dim3 blocks((nWidth / 4 - 1) / threads.x + 1,
(nHeight / 2 - 1) / threads.y + 1, blockDimZ);
nv12ToBGRplanarBatchKernel<<<blocks, threads, 0, stream>>>(
pNv12, nNv12Pitch, pBgr, nRgbPitch, nWidth, nHeight, nBatchSize);
dim3 blocks((nWidth / 4 - 1) / threads.x + 1, (nHeight / 2 - 1) / threads.y + 1, blockDimZ);
nv12ToBGRplanarBatchKernel<<<blocks, threads, 0, stream>>>(
pNv12, nNv12Pitch, pBgr, nRgbPitch, nWidth, nHeight, nBatchSize);
}

View File

@@ -29,28 +29,45 @@
#ifndef __H_RESIZE_CONVERT__
#define __H_RESIZE_CONVERT__
#include <iostream>
#include <helper_cuda.h>
#include <iostream>
// nv12 resize
extern "C"
void resizeNV12Batch(
uint8_t *dpSrc, int nSrcPitch, int nSrcWidth, int nSrcHeight,
uint8_t *dpDst, int nDstPitch, int nDstWidth, int nDstHeight,
int nBatchSize, cudaStream_t stream = 0);
extern "C" void resizeNV12Batch(uint8_t *dpSrc,
int nSrcPitch,
int nSrcWidth,
int nSrcHeight,
uint8_t *dpDst,
int nDstPitch,
int nDstWidth,
int nDstHeight,
int nBatchSize,
cudaStream_t stream = 0);
// bgr resize
extern "C"
void resizeBGRplanarBatch(
float *dpSrc, int nSrcPitch, int nSrcWidth, int nSrcHeight,
float *dpDst, int nDstPitch, int nDstWidth, int nDstHeight,
int nBatchSize, cudaStream_t stream = 0,
int cropX = 0, int cropY = 0, int cropW = 0, int cropH = 0,
bool whSameResizeRatio = false);
extern "C" void resizeBGRplanarBatch(float *dpSrc,
int nSrcPitch,
int nSrcWidth,
int nSrcHeight,
float *dpDst,
int nDstPitch,
int nDstWidth,
int nDstHeight,
int nBatchSize,
cudaStream_t stream = 0,
int cropX = 0,
int cropY = 0,
int cropW = 0,
int cropH = 0,
bool whSameResizeRatio = false);
//NV12 to bgr planar
extern "C"
void nv12ToBGRplanarBatch(uint8_t *pNv12, int nNv12Pitch,
float *pRgb, int nRgbPitch, int nWidth, int nHeight,
int nBatchSize, cudaStream_t stream=0);
// NV12 to bgr planar
extern "C" void nv12ToBGRplanarBatch(uint8_t *pNv12,
int nNv12Pitch,
float *pRgb,
int nRgbPitch,
int nWidth,
int nHeight,
int nBatchSize,
cudaStream_t stream = 0);
#endif

View File

@@ -51,398 +51,424 @@ Run
*/
#include <cassert>
#include <cuda.h>
#include <cuda_runtime.h>
#include <fstream>
#include <iostream>
#include <math.h>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <fstream>
#include <iostream>
#include <memory>
#include "resize_convert.h"
#include "utils.h"
#define TEST_LOOP 20
typedef struct _nv12_to_bgr24_context_t {
int width;
int height;
int pitch;
typedef struct _nv12_to_bgr24_context_t
{
int width;
int height;
int pitch;
int dst_width;
int dst_height;
int dst_pitch;
int dst_width;
int dst_height;
int dst_pitch;
int batch;
int device; // cuda device ID
int batch;
int device; // cuda device ID
char *input_nv12_file;
char *input_nv12_file;
int ctx_pitch; // the value will be suitable for Texture memroy.
int ctx_heights; // the value will be even.
int ctx_pitch; // the value will be suitable for Texture memroy.
int ctx_heights; // the value will be even.
} nv12_to_bgr24_context;
nv12_to_bgr24_context g_ctx;
static void printHelp(const char *app_name) {
std::cout << "Usage:" << app_name << " [options]\n\n";
std::cout << "OPTIONS:\n";
std::cout << "\t-h,--help\n\n";
std::cout << "\t-input=nv12file nv12 input file\n";
std::cout
<< "\t-width=width input nv12 image width, <1 -- 4096>\n";
std::cout
<< "\t-height=height input nv12 image height, <1 -- 4096>\n";
std::cout
<< "\t-pitch=pitch(optional) input nv12 image pitch, <0 -- 4096>\n";
std::cout
<< "\t-dst_width=width output BGR image width, <1 -- 4096>\n";
std::cout
<< "\t-dst_height=height output BGR image height, <1 -- 4096>\n";
std::cout
<< "\t-dst_pitch=pitch(optional) output BGR image pitch, <0 -- 4096>\n";
std::cout
<< "\t-batch=batch process frames count, <1 -- 4096>\n\n";
std::cout
<< "\t-device=device_num(optional) cuda device number, <0 -- 4096>\n\n";
static void printHelp(const char *app_name)
{
std::cout << "Usage:" << app_name << " [options]\n\n";
std::cout << "OPTIONS:\n";
std::cout << "\t-h,--help\n\n";
std::cout << "\t-input=nv12file nv12 input file\n";
std::cout << "\t-width=width input nv12 image width, <1 -- 4096>\n";
std::cout << "\t-height=height input nv12 image height, <1 -- 4096>\n";
std::cout << "\t-pitch=pitch(optional) input nv12 image pitch, <0 -- 4096>\n";
std::cout << "\t-dst_width=width output BGR image width, <1 -- 4096>\n";
std::cout << "\t-dst_height=height output BGR image height, <1 -- 4096>\n";
std::cout << "\t-dst_pitch=pitch(optional) output BGR image pitch, <0 -- 4096>\n";
std::cout << "\t-batch=batch process frames count, <1 -- 4096>\n\n";
std::cout << "\t-device=device_num(optional) cuda device number, <0 -- 4096>\n\n";
return;
return;
}
int parseCmdLine(int argc, char *argv[]) {
char **argp = (char **)argv;
char *arg = (char *)argv[0];
int parseCmdLine(int argc, char *argv[])
{
char **argp = (char **)argv;
char *arg = (char *)argv[0];
memset(&g_ctx, 0, sizeof(g_ctx));
memset(&g_ctx, 0, sizeof(g_ctx));
if ((arg && (!strcmp(arg, "-h") || !strcmp(arg, "--help")))) {
printHelp(argv[0]);
return -1;
}
if (argc == 1) {
// Run using default arguments
g_ctx.input_nv12_file = sdkFindFilePath("test1920x1080.nv12", argv[0]);
if (g_ctx.input_nv12_file == NULL) {
printf("Cannot find input file test1920x1080.nv12\n Exiting\n");
return EXIT_FAILURE;
}
g_ctx.width = 1920;
g_ctx.height = 1080;
g_ctx.dst_width = 640;
g_ctx.dst_height = 480;
g_ctx.batch = 24;
} else if (argc > 1) {
if (checkCmdLineFlag(argc, (const char **)argv, "width")) {
g_ctx.width = getCmdLineArgumentInt(argc, (const char **)argv, "width");
if ((arg && (!strcmp(arg, "-h") || !strcmp(arg, "--help")))) {
printHelp(argv[0]);
return -1;
}
if (checkCmdLineFlag(argc, (const char **)argv, "height")) {
g_ctx.height = getCmdLineArgumentInt(argc, (const char **)argv, "height");
if (argc == 1) {
// Run using default arguments
g_ctx.input_nv12_file = sdkFindFilePath("test1920x1080.nv12", argv[0]);
if (g_ctx.input_nv12_file == NULL) {
printf("Cannot find input file test1920x1080.nv12\n Exiting\n");
return EXIT_FAILURE;
}
g_ctx.width = 1920;
g_ctx.height = 1080;
g_ctx.dst_width = 640;
g_ctx.dst_height = 480;
g_ctx.batch = 24;
}
else if (argc > 1) {
if (checkCmdLineFlag(argc, (const char **)argv, "width")) {
g_ctx.width = getCmdLineArgumentInt(argc, (const char **)argv, "width");
}
if (checkCmdLineFlag(argc, (const char **)argv, "height")) {
g_ctx.height = getCmdLineArgumentInt(argc, (const char **)argv, "height");
}
if (checkCmdLineFlag(argc, (const char **)argv, "pitch")) {
g_ctx.pitch = getCmdLineArgumentInt(argc, (const char **)argv, "pitch");
}
if (checkCmdLineFlag(argc, (const char **)argv, "input")) {
getCmdLineArgumentString(argc, (const char **)argv, "input", (char **)&g_ctx.input_nv12_file);
}
if (checkCmdLineFlag(argc, (const char **)argv, "dst_width")) {
g_ctx.dst_width = getCmdLineArgumentInt(argc, (const char **)argv, "dst_width");
}
if (checkCmdLineFlag(argc, (const char **)argv, "dst_height")) {
g_ctx.dst_height = getCmdLineArgumentInt(argc, (const char **)argv, "dst_height");
}
if (checkCmdLineFlag(argc, (const char **)argv, "dst_pitch")) {
g_ctx.dst_pitch = getCmdLineArgumentInt(argc, (const char **)argv, "dst_pitch");
}
if (checkCmdLineFlag(argc, (const char **)argv, "batch")) {
g_ctx.batch = getCmdLineArgumentInt(argc, (const char **)argv, "batch");
}
}
if (checkCmdLineFlag(argc, (const char **)argv, "pitch")) {
g_ctx.pitch = getCmdLineArgumentInt(argc, (const char **)argv, "pitch");
g_ctx.device = findCudaDevice(argc, (const char **)argv);
if ((g_ctx.width == 0) || (g_ctx.height == 0) || (g_ctx.dst_width == 0) || (g_ctx.dst_height == 0)
|| !g_ctx.input_nv12_file) {
printHelp(argv[0]);
return -1;
}
if (checkCmdLineFlag(argc, (const char **)argv, "input")) {
getCmdLineArgumentString(argc, (const char **)argv, "input",
(char **)&g_ctx.input_nv12_file);
}
if (g_ctx.pitch == 0)
g_ctx.pitch = g_ctx.width;
if (g_ctx.dst_pitch == 0)
g_ctx.dst_pitch = g_ctx.dst_width;
if (checkCmdLineFlag(argc, (const char **)argv, "dst_width")) {
g_ctx.dst_width =
getCmdLineArgumentInt(argc, (const char **)argv, "dst_width");
}
if (checkCmdLineFlag(argc, (const char **)argv, "dst_height")) {
g_ctx.dst_height =
getCmdLineArgumentInt(argc, (const char **)argv, "dst_height");
}
if (checkCmdLineFlag(argc, (const char **)argv, "dst_pitch")) {
g_ctx.dst_pitch =
getCmdLineArgumentInt(argc, (const char **)argv, "dst_pitch");
}
if (checkCmdLineFlag(argc, (const char **)argv, "batch")) {
g_ctx.batch = getCmdLineArgumentInt(argc, (const char **)argv, "batch");
}
}
g_ctx.device = findCudaDevice(argc, (const char **)argv);
if ((g_ctx.width == 0) || (g_ctx.height == 0) || (g_ctx.dst_width == 0) ||
(g_ctx.dst_height == 0) || !g_ctx.input_nv12_file) {
printHelp(argv[0]);
return -1;
}
if (g_ctx.pitch == 0) g_ctx.pitch = g_ctx.width;
if (g_ctx.dst_pitch == 0) g_ctx.dst_pitch = g_ctx.dst_width;
return 0;
return 0;
}
/*
load nv12 yuvfile data into GPU device memory with batch of copy
*/
static int loadNV12Frame(unsigned char *d_inputNV12) {
unsigned char *pNV12FrameData;
unsigned char *d_nv12;
int frameSize;
std::ifstream nv12File(g_ctx.input_nv12_file, std::ifstream::in | std::ios::binary);
static int loadNV12Frame(unsigned char *d_inputNV12)
{
unsigned char *pNV12FrameData;
unsigned char *d_nv12;
int frameSize;
std::ifstream nv12File(g_ctx.input_nv12_file, std::ifstream::in | std::ios::binary);
if (!nv12File.is_open()) {
std::cerr << "Can't open files\n";
return -1;
}
if (!nv12File.is_open()) {
std::cerr << "Can't open files\n";
return -1;
}
frameSize = g_ctx.pitch * g_ctx.ctx_heights;
frameSize = g_ctx.pitch * g_ctx.ctx_heights;
#if USE_UVM_MEM
pNV12FrameData = d_inputNV12;
pNV12FrameData = d_inputNV12;
#else
pNV12FrameData = (unsigned char *)malloc(frameSize);
if (pNV12FrameData == NULL) {
std::cerr << "Failed to malloc pNV12FrameData\n";
return -1;
}
pNV12FrameData = (unsigned char *)malloc(frameSize);
if (pNV12FrameData == NULL) {
std::cerr << "Failed to malloc pNV12FrameData\n";
return -1;
}
#endif
nv12File.read((char *)pNV12FrameData, frameSize);
nv12File.read((char *)pNV12FrameData, frameSize);
if (nv12File.gcount() < frameSize) {
std::cerr << "can't get one frame!\n";
return -1;
}
if (nv12File.gcount() < frameSize) {
std::cerr << "can't get one frame!\n";
return -1;
}
#if USE_UVM_MEM
// Prefetch to GPU for following GPU operation
cudaStreamAttachMemAsync(NULL, pNV12FrameData, 0, cudaMemAttachGlobal);
// Prefetch to GPU for following GPU operation
cudaStreamAttachMemAsync(NULL, pNV12FrameData, 0, cudaMemAttachGlobal);
#endif
// expand one frame to multi frames for batch processing
d_nv12 = d_inputNV12;
for (int i = 0; i < g_ctx.batch; i++) {
checkCudaErrors(cudaMemcpy2D((void *)d_nv12, g_ctx.ctx_pitch,
pNV12FrameData, g_ctx.width, g_ctx.width,
g_ctx.ctx_heights, cudaMemcpyHostToDevice));
// expand one frame to multi frames for batch processing
d_nv12 = d_inputNV12;
for (int i = 0; i < g_ctx.batch; i++) {
checkCudaErrors(cudaMemcpy2D((void *)d_nv12,
g_ctx.ctx_pitch,
pNV12FrameData,
g_ctx.width,
g_ctx.width,
g_ctx.ctx_heights,
cudaMemcpyHostToDevice));
d_nv12 += g_ctx.ctx_pitch * g_ctx.ctx_heights;
}
d_nv12 += g_ctx.ctx_pitch * g_ctx.ctx_heights;
}
#if (USE_UVM_MEM == 0)
free(pNV12FrameData);
free(pNV12FrameData);
#endif
nv12File.close();
nv12File.close();
return 0;
return 0;
}
/*
1. resize interlace nv12 to target size
2. convert nv12 to bgr 3 progressive planars
*/
void nv12ResizeAndNV12ToBGR(unsigned char *d_inputNV12) {
unsigned char *d_resizedNV12;
float *d_outputBGR;
int size;
char filename[40];
void nv12ResizeAndNV12ToBGR(unsigned char *d_inputNV12)
{
unsigned char *d_resizedNV12;
float *d_outputBGR;
int size;
char filename[40];
/* allocate device memory for resized nv12 output */
size = g_ctx.dst_width * ceil(g_ctx.dst_height * 3.0f / 2.0f) * g_ctx.batch *
sizeof(unsigned char);
checkCudaErrors(cudaMalloc((void **)&d_resizedNV12, size));
/* allocate device memory for resized nv12 output */
size = g_ctx.dst_width * ceil(g_ctx.dst_height * 3.0f / 2.0f) * g_ctx.batch * sizeof(unsigned char);
checkCudaErrors(cudaMalloc((void **)&d_resizedNV12, size));
/* allocate device memory for bgr output */
size = g_ctx.dst_pitch * g_ctx.dst_height * 3 * g_ctx.batch * sizeof(float);
checkCudaErrors(cudaMalloc((void **)&d_outputBGR, size));
/* allocate device memory for bgr output */
size = g_ctx.dst_pitch * g_ctx.dst_height * 3 * g_ctx.batch * sizeof(float);
checkCudaErrors(cudaMalloc((void **)&d_outputBGR, size));
cudaStream_t stream;
checkCudaErrors(cudaStreamCreate(&stream));
/* create cuda event handles */
cudaEvent_t start, stop;
checkCudaErrors(cudaEventCreate(&start));
checkCudaErrors(cudaEventCreate(&stop));
float elapsedTime = 0.0f;
cudaStream_t stream;
checkCudaErrors(cudaStreamCreate(&stream));
/* create cuda event handles */
cudaEvent_t start, stop;
checkCudaErrors(cudaEventCreate(&start));
checkCudaErrors(cudaEventCreate(&stop));
float elapsedTime = 0.0f;
/* resize interlace nv12 */
/* resize interlace nv12 */
cudaEventRecord(start, 0);
for (int i = 0; i < TEST_LOOP; i++) {
resizeNV12Batch(d_inputNV12, g_ctx.ctx_pitch, g_ctx.width, g_ctx.height,
d_resizedNV12, g_ctx.dst_width, g_ctx.dst_width,
g_ctx.dst_height, g_ctx.batch);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventRecord(start, 0);
for (int i = 0; i < TEST_LOOP; i++) {
resizeNV12Batch(d_inputNV12,
g_ctx.ctx_pitch,
g_ctx.width,
g_ctx.height,
d_resizedNV12,
g_ctx.dst_width,
g_ctx.dst_width,
g_ctx.dst_height,
g_ctx.batch);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
printf(
" CUDA resize nv12(%dx%d --> %dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.width, g_ctx.height, g_ctx.dst_width, g_ctx.dst_height, g_ctx.batch,
(elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
cudaEventElapsedTime(&elapsedTime, start, stop);
printf(" CUDA resize nv12(%dx%d --> %dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.width,
g_ctx.height,
g_ctx.dst_width,
g_ctx.dst_height,
g_ctx.batch,
(elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
sprintf(filename, "resized_nv12_%dx%d", g_ctx.dst_width, g_ctx.dst_height);
sprintf(filename, "resized_nv12_%dx%d", g_ctx.dst_width, g_ctx.dst_height);
/* convert nv12 to bgr 3 progressive planars */
cudaEventRecord(start, 0);
for (int i = 0; i < TEST_LOOP; i++) {
nv12ToBGRplanarBatch(d_resizedNV12, g_ctx.dst_pitch, // intput
d_outputBGR,
g_ctx.dst_pitch * sizeof(float), // output
g_ctx.dst_width, g_ctx.dst_height, // output
g_ctx.batch, 0);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
/* convert nv12 to bgr 3 progressive planars */
cudaEventRecord(start, 0);
for (int i = 0; i < TEST_LOOP; i++) {
nv12ToBGRplanarBatch(d_resizedNV12,
g_ctx.dst_pitch, // intput
d_outputBGR,
g_ctx.dst_pitch * sizeof(float), // output
g_ctx.dst_width,
g_ctx.dst_height, // output
g_ctx.batch,
0);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
printf(
" CUDA convert nv12(%dx%d) to bgr(%dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.dst_width, g_ctx.dst_height, g_ctx.dst_width, g_ctx.dst_height,
g_ctx.batch, (elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
printf(" CUDA convert nv12(%dx%d) to bgr(%dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.dst_width,
g_ctx.dst_height,
g_ctx.dst_width,
g_ctx.dst_height,
g_ctx.batch,
(elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
sprintf(filename, "converted_bgr_%dx%d", g_ctx.dst_width, g_ctx.dst_height);
dumpBGR(d_outputBGR, g_ctx.dst_pitch, g_ctx.dst_width, g_ctx.dst_height,
g_ctx.batch, (char *)"t1", filename);
sprintf(filename, "converted_bgr_%dx%d", g_ctx.dst_width, g_ctx.dst_height);
dumpBGR(d_outputBGR, g_ctx.dst_pitch, g_ctx.dst_width, g_ctx.dst_height, g_ctx.batch, (char *)"t1", filename);
/* release resources */
checkCudaErrors(cudaEventDestroy(start));
checkCudaErrors(cudaEventDestroy(stop));
checkCudaErrors(cudaStreamDestroy(stream));
checkCudaErrors(cudaFree(d_resizedNV12));
checkCudaErrors(cudaFree(d_outputBGR));
/* release resources */
checkCudaErrors(cudaEventDestroy(start));
checkCudaErrors(cudaEventDestroy(stop));
checkCudaErrors(cudaStreamDestroy(stream));
checkCudaErrors(cudaFree(d_resizedNV12));
checkCudaErrors(cudaFree(d_outputBGR));
}
/*
1. convert nv12 to bgr 3 progressive planars
2. resize bgr 3 planars to target size
*/
void nv12ToBGRandBGRresize(unsigned char *d_inputNV12) {
float *d_bgr;
float *d_resizedBGR;
int size;
char filename[40];
void nv12ToBGRandBGRresize(unsigned char *d_inputNV12)
{
float *d_bgr;
float *d_resizedBGR;
int size;
char filename[40];
/* allocate device memory for bgr output */
size = g_ctx.ctx_pitch * g_ctx.height * 3 * g_ctx.batch * sizeof(float);
checkCudaErrors(cudaMalloc((void **)&d_bgr, size));
/* allocate device memory for bgr output */
size = g_ctx.ctx_pitch * g_ctx.height * 3 * g_ctx.batch * sizeof(float);
checkCudaErrors(cudaMalloc((void **)&d_bgr, size));
/* allocate device memory for resized bgr output */
size = g_ctx.dst_width * g_ctx.dst_height * 3 * g_ctx.batch * sizeof(float);
checkCudaErrors(cudaMalloc((void **)&d_resizedBGR, size));
/* allocate device memory for resized bgr output */
size = g_ctx.dst_width * g_ctx.dst_height * 3 * g_ctx.batch * sizeof(float);
checkCudaErrors(cudaMalloc((void **)&d_resizedBGR, size));
cudaStream_t stream;
checkCudaErrors(cudaStreamCreate(&stream));
/* create cuda event handles */
cudaEvent_t start, stop;
checkCudaErrors(cudaEventCreate(&start));
checkCudaErrors(cudaEventCreate(&stop));
float elapsedTime = 0.0f;
cudaStream_t stream;
checkCudaErrors(cudaStreamCreate(&stream));
/* create cuda event handles */
cudaEvent_t start, stop;
checkCudaErrors(cudaEventCreate(&start));
checkCudaErrors(cudaEventCreate(&stop));
float elapsedTime = 0.0f;
/* convert interlace nv12 to bgr 3 progressive planars */
cudaEventRecord(start, 0);
cudaDeviceSynchronize();
for (int i = 0; i < TEST_LOOP; i++) {
nv12ToBGRplanarBatch(d_inputNV12, g_ctx.ctx_pitch, d_bgr,
g_ctx.ctx_pitch * sizeof(float), g_ctx.width,
g_ctx.height, g_ctx.batch, 0);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
/* convert interlace nv12 to bgr 3 progressive planars */
cudaEventRecord(start, 0);
cudaDeviceSynchronize();
for (int i = 0; i < TEST_LOOP; i++) {
nv12ToBGRplanarBatch(d_inputNV12,
g_ctx.ctx_pitch,
d_bgr,
g_ctx.ctx_pitch * sizeof(float),
g_ctx.width,
g_ctx.height,
g_ctx.batch,
0);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
printf(
" CUDA convert nv12(%dx%d) to bgr(%dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.width, g_ctx.height, g_ctx.width, g_ctx.height, g_ctx.batch,
(elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
cudaEventElapsedTime(&elapsedTime, start, stop);
printf(" CUDA convert nv12(%dx%d) to bgr(%dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.width,
g_ctx.height,
g_ctx.width,
g_ctx.height,
g_ctx.batch,
(elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
sprintf(filename, "converted_bgr_%dx%d", g_ctx.width, g_ctx.height);
sprintf(filename, "converted_bgr_%dx%d", g_ctx.width, g_ctx.height);
/* resize bgr 3 progressive planars */
cudaEventRecord(start, 0);
for (int i = 0; i < TEST_LOOP; i++) {
resizeBGRplanarBatch(d_bgr, g_ctx.ctx_pitch, g_ctx.width, g_ctx.height,
d_resizedBGR, g_ctx.dst_width, g_ctx.dst_width,
g_ctx.dst_height, g_ctx.batch);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
/* resize bgr 3 progressive planars */
cudaEventRecord(start, 0);
for (int i = 0; i < TEST_LOOP; i++) {
resizeBGRplanarBatch(d_bgr,
g_ctx.ctx_pitch,
g_ctx.width,
g_ctx.height,
d_resizedBGR,
g_ctx.dst_width,
g_ctx.dst_width,
g_ctx.dst_height,
g_ctx.batch);
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
printf(
" CUDA resize bgr(%dx%d --> %dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.width, g_ctx.height, g_ctx.dst_width, g_ctx.dst_height, g_ctx.batch,
(elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
cudaEventElapsedTime(&elapsedTime, start, stop);
printf(" CUDA resize bgr(%dx%d --> %dx%d), batch: %d,"
" average time: %.3f ms ==> %.3f ms/frame\n",
g_ctx.width,
g_ctx.height,
g_ctx.dst_width,
g_ctx.dst_height,
g_ctx.batch,
(elapsedTime / (TEST_LOOP * 1.0f)),
(elapsedTime / (TEST_LOOP * 1.0f)) / g_ctx.batch);
memset(filename, 0, sizeof(filename));
sprintf(filename, "resized_bgr_%dx%d", g_ctx.dst_width, g_ctx.dst_height);
dumpBGR(d_resizedBGR, g_ctx.dst_pitch, g_ctx.dst_width, g_ctx.dst_height,
g_ctx.batch, (char *)"t2", filename);
memset(filename, 0, sizeof(filename));
sprintf(filename, "resized_bgr_%dx%d", g_ctx.dst_width, g_ctx.dst_height);
dumpBGR(d_resizedBGR, g_ctx.dst_pitch, g_ctx.dst_width, g_ctx.dst_height, g_ctx.batch, (char *)"t2", filename);
/* release resources */
checkCudaErrors(cudaEventDestroy(start));
checkCudaErrors(cudaEventDestroy(stop));
checkCudaErrors(cudaStreamDestroy(stream));
checkCudaErrors(cudaFree(d_bgr));
checkCudaErrors(cudaFree(d_resizedBGR));
/* release resources */
checkCudaErrors(cudaEventDestroy(start));
checkCudaErrors(cudaEventDestroy(stop));
checkCudaErrors(cudaStreamDestroy(stream));
checkCudaErrors(cudaFree(d_bgr));
checkCudaErrors(cudaFree(d_resizedBGR));
}
int main(int argc, char *argv[]) {
unsigned char *d_inputNV12;
int main(int argc, char *argv[])
{
unsigned char *d_inputNV12;
if (parseCmdLine(argc, argv) < 0) return EXIT_FAILURE;
if (parseCmdLine(argc, argv) < 0)
return EXIT_FAILURE;
g_ctx.ctx_pitch = g_ctx.width;
int ctx_alignment = 32;
g_ctx.ctx_pitch += (g_ctx.ctx_pitch % ctx_alignment != 0)
? (ctx_alignment - g_ctx.ctx_pitch % ctx_alignment)
: 0;
g_ctx.ctx_pitch = g_ctx.width;
int ctx_alignment = 32;
g_ctx.ctx_pitch += (g_ctx.ctx_pitch % ctx_alignment != 0) ? (ctx_alignment - g_ctx.ctx_pitch % ctx_alignment) : 0;
g_ctx.ctx_heights = ceil(g_ctx.height * 3.0f / 2.0f);
g_ctx.ctx_heights = ceil(g_ctx.height * 3.0f / 2.0f);
/* load nv12 yuv data into d_inputNV12 with batch of copies */
/* load nv12 yuv data into d_inputNV12 with batch of copies */
#if USE_UVM_MEM
checkCudaErrors(cudaMallocManaged(
(void **)&d_inputNV12,
(g_ctx.ctx_pitch * g_ctx.ctx_heights * g_ctx.batch), cudaMemAttachHost));
printf("\nUSE_UVM_MEM\n");
checkCudaErrors(cudaMallocManaged(
(void **)&d_inputNV12, (g_ctx.ctx_pitch * g_ctx.ctx_heights * g_ctx.batch), cudaMemAttachHost));
printf("\nUSE_UVM_MEM\n");
#else
checkCudaErrors(
cudaMalloc((void **)&d_inputNV12,
(g_ctx.ctx_pitch * g_ctx.ctx_heights * g_ctx.batch)));
checkCudaErrors(cudaMalloc((void **)&d_inputNV12, (g_ctx.ctx_pitch * g_ctx.ctx_heights * g_ctx.batch)));
#endif
if (loadNV12Frame(d_inputNV12)) {
std::cerr << "failed to load batch data!\n";
return EXIT_FAILURE;
}
if (loadNV12Frame(d_inputNV12)) {
std::cerr << "failed to load batch data!\n";
return EXIT_FAILURE;
}
/* firstly resize nv12, then convert nv12 to bgr */
printf("\nTEST#1:\n");
nv12ResizeAndNV12ToBGR(d_inputNV12);
/* firstly resize nv12, then convert nv12 to bgr */
printf("\nTEST#1:\n");
nv12ResizeAndNV12ToBGR(d_inputNV12);
/* first convert nv12 to bgr, then resize bgr */
printf("\nTEST#2:\n");
nv12ToBGRandBGRresize(d_inputNV12);
/* first convert nv12 to bgr, then resize bgr */
printf("\nTEST#2:\n");
nv12ToBGRandBGRresize(d_inputNV12);
checkCudaErrors(cudaFree(d_inputNV12));
checkCudaErrors(cudaFree(d_inputNV12));
return EXIT_SUCCESS;
return EXIT_SUCCESS;
}

View File

@@ -25,140 +25,124 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cuda.h>
#include <cuda_runtime.h>
#include <fstream>
#include <iostream>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
#include <iostream>
#include <cuda.h>
#include <cuda_runtime.h>
#include "resize_convert.h"
#include "utils.h"
__global__ void floatToChar(float *src, unsigned char *dst, int height,
int width, int batchSize)
__global__ void floatToChar(float *src, unsigned char *dst, int height, int width, int batchSize)
{
int x = threadIdx.x + blockIdx.x * blockDim.x;
int x = threadIdx.x + blockIdx.x * blockDim.x;
if (x >= height * width)
return;
if (x >= height * width)
return;
int offset = height * width * 3;
int offset = height * width * 3;
for (int j = 0; j < batchSize; j++)
{
// b
*(dst + j * offset + x * 3 + 0) =
(unsigned char)*(src + j * offset + height * width * 0 + x);
// g
*(dst + j * offset + x * 3 + 1) =
(unsigned char)*(src + j * offset + height * width * 1 + x);
// r
*(dst + j * offset + x * 3 + 2) =
(unsigned char)*(src + j * offset + height * width * 2 + x);
}
for (int j = 0; j < batchSize; j++) {
// b
*(dst + j * offset + x * 3 + 0) = (unsigned char)*(src + j * offset + height * width * 0 + x);
// g
*(dst + j * offset + x * 3 + 1) = (unsigned char)*(src + j * offset + height * width * 1 + x);
// r
*(dst + j * offset + x * 3 + 2) = (unsigned char)*(src + j * offset + height * width * 2 + x);
}
}
void floatPlanarToChar(float *src, unsigned char *dst, int height, int width,
int batchSize)
void floatPlanarToChar(float *src, unsigned char *dst, int height, int width, int batchSize)
{
floatToChar<<<(height * width - 1) / 1024 + 1, 1024, 0, NULL>>>(
src, dst, height, width, batchSize);
floatToChar<<<(height * width - 1) / 1024 + 1, 1024, 0, NULL>>>(src, dst, height, width, batchSize);
}
void dumpRawBGR(float *d_srcBGR, int pitch, int width, int height,
int batchSize, char *folder, char *tag)
void dumpRawBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize, char *folder, char *tag)
{
float *bgr, *d_bgr;
int frameSize;
char directory[120];
char mkdir_cmd[256];
float *bgr, *d_bgr;
int frameSize;
char directory[120];
char mkdir_cmd[256];
#if !defined(_WIN32)
sprintf(directory, "output/%s", folder);
sprintf(mkdir_cmd, "mkdir -p %s 2> /dev/null", directory);
sprintf(directory, "output/%s", folder);
sprintf(mkdir_cmd, "mkdir -p %s 2> /dev/null", directory);
#else
sprintf(directory, "output\\%s", folder);
sprintf(mkdir_cmd, "mkdir %s 2> nul", directory);
sprintf(directory, "output\\%s", folder);
sprintf(mkdir_cmd, "mkdir %s 2> nul", directory);
#endif
int ret = system(mkdir_cmd);
int ret = system(mkdir_cmd);
frameSize = width * height * 3 * sizeof(float);
bgr = (float *)malloc(frameSize);
if (bgr == NULL)
{
std::cerr << "Failed malloc for bgr\n";
return;
}
d_bgr = d_srcBGR;
for (int i = 0; i < batchSize; i++)
{
char filename[256];
std::ofstream *outputFile;
checkCudaErrors(cudaMemcpy((void *)bgr, (void *)d_bgr, frameSize,
cudaMemcpyDeviceToHost));
snprintf(filename, sizeof(filename), "%s/%s_%d.raw", directory, tag, (i + 1));
outputFile = new std::ofstream(filename);
if (outputFile)
{
outputFile->write((char *)bgr, frameSize);
delete outputFile;
frameSize = width * height * 3 * sizeof(float);
bgr = (float *)malloc(frameSize);
if (bgr == NULL) {
std::cerr << "Failed malloc for bgr\n";
return;
}
d_bgr += pitch * height * 3;
}
d_bgr = d_srcBGR;
for (int i = 0; i < batchSize; i++) {
char filename[256];
std::ofstream *outputFile;
free(bgr);
checkCudaErrors(cudaMemcpy((void *)bgr, (void *)d_bgr, frameSize, cudaMemcpyDeviceToHost));
snprintf(filename, sizeof(filename), "%s/%s_%d.raw", directory, tag, (i + 1));
outputFile = new std::ofstream(filename);
if (outputFile) {
outputFile->write((char *)bgr, frameSize);
delete outputFile;
}
d_bgr += pitch * height * 3;
}
free(bgr);
}
void dumpBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize,
char *folder, char *tag)
void dumpBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize, char *folder, char *tag)
{
dumpRawBGR(d_srcBGR, pitch, width, height, batchSize, folder, tag);
dumpRawBGR(d_srcBGR, pitch, width, height, batchSize, folder, tag);
}
void dumpYUV(unsigned char *d_nv12, int size, char *folder, char *tag)
{
unsigned char *nv12Data;
std::ofstream *nv12File;
char filename[256];
char directory[60];
char mkdir_cmd[256];
unsigned char *nv12Data;
std::ofstream *nv12File;
char filename[256];
char directory[60];
char mkdir_cmd[256];
#if !defined(_WIN32)
sprintf(directory, "output/%s", folder);
sprintf(mkdir_cmd, "mkdir -p %s 2> /dev/null", directory);
sprintf(directory, "output/%s", folder);
sprintf(mkdir_cmd, "mkdir -p %s 2> /dev/null", directory);
#else
sprintf(directory, "output\\%s", folder);
sprintf(mkdir_cmd, "mkdir %s 2> nul", directory);
sprintf(directory, "output\\%s", folder);
sprintf(mkdir_cmd, "mkdir %s 2> nul", directory);
#endif
int ret = system(mkdir_cmd);
int ret = system(mkdir_cmd);
snprintf(filename, sizeof(filename), "%s/%s.nv12", directory, tag);
snprintf(filename, sizeof(filename), "%s/%s.nv12", directory, tag);
nv12File = new std::ofstream(filename);
if (nv12File == NULL)
{
std::cerr << "Failed to new " << filename;
return;
}
nv12File = new std::ofstream(filename);
if (nv12File == NULL) {
std::cerr << "Failed to new " << filename;
return;
}
nv12Data = (unsigned char *)malloc(size * (sizeof(char)));
if (nv12Data == NULL)
{
std::cerr << "Failed to allcoate memory\n";
return;
}
nv12Data = (unsigned char *)malloc(size * (sizeof(char)));
if (nv12Data == NULL) {
std::cerr << "Failed to allcoate memory\n";
return;
}
cudaMemcpy((void *)nv12Data, (void *)d_nv12, size, cudaMemcpyDeviceToHost);
cudaMemcpy((void *)nv12Data, (void *)d_nv12, size, cudaMemcpyDeviceToHost);
nv12File->write((const char *)nv12Data, size);
nv12File->write((const char *)nv12Data, size);
free(nv12Data);
delete nv12File;
free(nv12Data);
delete nv12File;
}

View File

@@ -29,9 +29,6 @@
#ifndef __H_UTIL_
#define __H_UTIL_
extern "C"
void dumpBGR(float *d_srcBGR, int pitch, int width, int height,
int batchSize, char *folder, char *tag);
extern "C"
void dumpYUV(unsigned char *d_nv12, int size, char *folder, char *tag);
extern "C" void dumpBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize, char *folder, char *tag);
extern "C" void dumpYUV(unsigned char *d_nv12, int size, char *folder, char *tag);
#endif

View File

@@ -18,7 +18,7 @@ This sample evaluates fair call and put prices for a given set of European optio
### [BlackScholes_nvrtc](./BlackScholes_nvrtc)
This sample evaluates fair call and put prices for a given set of European options by Black-Scholes formula, compiling the CUDA kernels involved at runtime using NVRTC.
### [convolutionFFT2D](./convolutionFFT2D)
This sample demonstrates how 2D convolutions with very large kernel sizes can be efficiently implemented using FFT transformations.
@@ -152,4 +152,3 @@ This sample demonstrates basic volume rendering using 3D Textures.
### [vulkanImageCUDA](./vulkanImageCUDA)
This sample demonstrates Vulkan Image - CUDA Interop. CUDA imports the Vulkan image buffer, performs box filtering over it, and synchronizes with Vulkan through vulkan semaphores imported by CUDA. This sample depends on Vulkan SDK, GLFW3 libraries, for building this sample please refer to "Build_instructions.txt" provided in this sample's directory

View File

@@ -34,4 +34,3 @@ Download and install the [CUDA Toolkit 12.5](https://developer.nvidia.com/cuda-d
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
## References (for more details)

View File

@@ -38,22 +38,21 @@
#endif
// CUDA utilities and system includes
#include <cuda_runtime.h>
#include <cuda_gl_interop.h>
#include <cuda_runtime.h>
// Includes
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SobelFilter_kernels.h"
// includes, project
#include <helper_functions.h> // includes for SDK helper functions
#include <helper_cuda.h> // includes for cuda initialization and error checking
#include <helper_cuda.h> // includes for cuda initialization and error checking
#include <helper_functions.h> // includes for SDK helper functions
const char *filterMode[] = {"No Filtering", "Sobel Texture",
"Sobel SMEM+Texture", NULL};
const char *filterMode[] = {"No Filtering", "Sobel Texture", "Sobel SMEM+Texture", NULL};
//
// Cuda example code that implements the Sobel edge detection
@@ -70,38 +69,37 @@ void cleanup(void);
void initializeData(char *file);
#define MAX_EPSILON_ERROR 5.0f
#define REFRESH_DELAY 10 // ms
#define REFRESH_DELAY 10 // ms
const char *sSDKsample = "CUDA Sobel Edge-Detection";
static int wWidth = 512; // Window width
static int wHeight = 512; // Window height
static int imWidth = 0; // Image width
static int wWidth = 512; // Window width
static int wHeight = 512; // Window height
static int imWidth = 0; // Image width
static int imHeight = 0; // Image height
// Code to handle Auto verification
const int frameCheckNumber = 4;
int fpsCount = 0; // FPS count for averaging
int fpsLimit = 8; // FPS limit for sampling
unsigned int frameCount = 0;
unsigned int g_TotalErrors = 0;
StopWatchInterface *timer = NULL;
unsigned int g_Bpp;
unsigned int g_Index = 0;
const int frameCheckNumber = 4;
int fpsCount = 0; // FPS count for averaging
int fpsLimit = 8; // FPS limit for sampling
unsigned int frameCount = 0;
unsigned int g_TotalErrors = 0;
StopWatchInterface *timer = NULL;
unsigned int g_Bpp;
unsigned int g_Index = 0;
bool g_bQAReadback = false;
// Display Data
static GLuint pbo_buffer = 0; // Front and back CA buffers
struct cudaGraphicsResource
*cuda_pbo_resource; // CUDA Graphics Resource (to transfer PBO)
static GLuint pbo_buffer = 0; // Front and back CA buffers
struct cudaGraphicsResource *cuda_pbo_resource; // CUDA Graphics Resource (to transfer PBO)
static GLuint texid = 0; // Texture for display
unsigned char *pixels = NULL; // Image pixel data on the host
float imageScale = 1.f; // Image exposure
static GLuint texid = 0; // Texture for display
unsigned char *pixels = NULL; // Image pixel data on the host
float imageScale = 1.f; // Image exposure
enum SobelDisplayMode g_SobelDisplayMode;
int *pArgc = NULL;
int *pArgc = NULL;
char **pArgv = NULL;
extern "C" void runAutoTest(int argc, char **argv);
@@ -109,376 +107,381 @@ extern "C" void runAutoTest(int argc, char **argv);
#define OFFSET(i) ((char *)NULL + (i))
#define MAX(a, b) ((a > b) ? a : b)
void computeFPS() {
frameCount++;
fpsCount++;
void computeFPS()
{
frameCount++;
fpsCount++;
if (fpsCount == fpsLimit) {
char fps[256];
float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);
sprintf(fps, "CUDA Edge Detection (%s): %3.1f fps",
filterMode[g_SobelDisplayMode], ifps);
if (fpsCount == fpsLimit) {
char fps[256];
float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);
sprintf(fps, "CUDA Edge Detection (%s): %3.1f fps", filterMode[g_SobelDisplayMode], ifps);
glutSetWindowTitle(fps);
fpsCount = 0;
glutSetWindowTitle(fps);
fpsCount = 0;
sdkResetTimer(&timer);
}
sdkResetTimer(&timer);
}
}
// This is the normal display path
void display(void) {
sdkStartTimer(&timer);
void display(void)
{
sdkStartTimer(&timer);
// Sobel operation
Pixel *data = NULL;
// Sobel operation
Pixel *data = NULL;
// map PBO to get CUDA device pointer
checkCudaErrors(cudaGraphicsMapResources(1, &cuda_pbo_resource, 0));
size_t num_bytes;
checkCudaErrors(cudaGraphicsResourceGetMappedPointer(
(void **)&data, &num_bytes, cuda_pbo_resource));
// printf("CUDA mapped PBO: May access %ld bytes\n", num_bytes);
// map PBO to get CUDA device pointer
checkCudaErrors(cudaGraphicsMapResources(1, &cuda_pbo_resource, 0));
size_t num_bytes;
checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&data, &num_bytes, cuda_pbo_resource));
// printf("CUDA mapped PBO: May access %ld bytes\n", num_bytes);
sobelFilter(data, imWidth, imHeight, g_SobelDisplayMode, imageScale);
checkCudaErrors(cudaGraphicsUnmapResources(1, &cuda_pbo_resource, 0));
sobelFilter(data, imWidth, imHeight, g_SobelDisplayMode, imageScale);
checkCudaErrors(cudaGraphicsUnmapResources(1, &cuda_pbo_resource, 0));
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texid);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imWidth, imHeight, GL_LUMINANCE,
GL_UNSIGNED_BYTE, OFFSET(0));
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, texid);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imWidth, imHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, OFFSET(0));
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBegin(GL_QUADS);
glVertex2f(0, 0);
glTexCoord2f(0, 0);
glVertex2f(0, 1);
glTexCoord2f(1, 0);
glVertex2f(1, 1);
glTexCoord2f(1, 1);
glVertex2f(1, 0);
glTexCoord2f(0, 1);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glutSwapBuffers();
glBegin(GL_QUADS);
glVertex2f(0, 0);
glTexCoord2f(0, 0);
glVertex2f(0, 1);
glTexCoord2f(1, 0);
glVertex2f(1, 1);
glTexCoord2f(1, 1);
glVertex2f(1, 0);
glTexCoord2f(0, 1);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glutSwapBuffers();
sdkStopTimer(&timer);
sdkStopTimer(&timer);
computeFPS();
computeFPS();
}
void timerEvent(int value) {
if (glutGetWindow()) {
glutPostRedisplay();
glutTimerFunc(REFRESH_DELAY, timerEvent, 0);
}
void timerEvent(int value)
{
if (glutGetWindow()) {
glutPostRedisplay();
glutTimerFunc(REFRESH_DELAY, timerEvent, 0);
}
}
void keyboard(unsigned char key, int /*x*/, int /*y*/) {
char temp[256];
void keyboard(unsigned char key, int /*x*/, int /*y*/)
{
char temp[256];
switch (key) {
switch (key) {
case 27:
case 'q':
case 'Q':
printf("Shutting down...\n");
printf("Shutting down...\n");
#if defined(__APPLE__) || defined(MACOSX)
exit(EXIT_SUCCESS);
exit(EXIT_SUCCESS);
#else
glutDestroyWindow(glutGetWindow());
return;
glutDestroyWindow(glutGetWindow());
return;
#endif
break;
break;
case '-':
imageScale -= 0.1f;
printf("brightness = %4.2f\n", imageScale);
break;
imageScale -= 0.1f;
printf("brightness = %4.2f\n", imageScale);
break;
case '=':
imageScale += 0.1f;
printf("brightness = %4.2f\n", imageScale);
break;
imageScale += 0.1f;
printf("brightness = %4.2f\n", imageScale);
break;
case 'i':
case 'I':
g_SobelDisplayMode = SOBELDISPLAY_IMAGE;
sprintf(temp, "CUDA Edge Detection (%s)", filterMode[g_SobelDisplayMode]);
glutSetWindowTitle(temp);
break;
g_SobelDisplayMode = SOBELDISPLAY_IMAGE;
sprintf(temp, "CUDA Edge Detection (%s)", filterMode[g_SobelDisplayMode]);
glutSetWindowTitle(temp);
break;
case 's':
case 'S':
g_SobelDisplayMode = SOBELDISPLAY_SOBELSHARED;
sprintf(temp, "CUDA Edge Detection (%s)", filterMode[g_SobelDisplayMode]);
glutSetWindowTitle(temp);
break;
g_SobelDisplayMode = SOBELDISPLAY_SOBELSHARED;
sprintf(temp, "CUDA Edge Detection (%s)", filterMode[g_SobelDisplayMode]);
glutSetWindowTitle(temp);
break;
case 't':
case 'T':
g_SobelDisplayMode = SOBELDISPLAY_SOBELTEX;
sprintf(temp, "CUDA Edge Detection (%s)", filterMode[g_SobelDisplayMode]);
glutSetWindowTitle(temp);
break;
g_SobelDisplayMode = SOBELDISPLAY_SOBELTEX;
sprintf(temp, "CUDA Edge Detection (%s)", filterMode[g_SobelDisplayMode]);
glutSetWindowTitle(temp);
break;
default:
break;
}
break;
}
}
void reshape(int x, int y) {
glViewport(0, 0, x, y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
void reshape(int x, int y)
{
glViewport(0, 0, x, y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void cleanup(void) {
cudaGraphicsUnregisterResource(cuda_pbo_resource);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pbo_buffer);
glDeleteTextures(1, &texid);
deleteTexture();
sdkDeleteTimer(&timer);
}
void initializeData(char *file) {
GLint bsize;
unsigned int w, h;
size_t file_length = strlen(file);
if (!strcmp(&file[file_length - 3], "pgm")) {
if (sdkLoadPGM<unsigned char>(file, &pixels, &w, &h) != true) {
printf("Failed to load PGM image file: %s\n", file);
exit(EXIT_FAILURE);
}
g_Bpp = 1;
} else if (!strcmp(&file[file_length - 3], "ppm")) {
if (sdkLoadPPM4(file, &pixels, &w, &h) != true) {
printf("Failed to load PPM image file: %s\n", file);
exit(EXIT_FAILURE);
}
g_Bpp = 4;
} else {
exit(EXIT_FAILURE);
}
imWidth = (int)w;
imHeight = (int)h;
setupTexture(imWidth, imHeight, pixels, g_Bpp);
memset(pixels, 0x0, g_Bpp * sizeof(Pixel) * imWidth * imHeight);
if (!g_bQAReadback) {
// use OpenGL Path
glGenBuffers(1, &pbo_buffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);
glBufferData(GL_PIXEL_UNPACK_BUFFER,
g_Bpp * sizeof(Pixel) * imWidth * imHeight, pixels,
GL_STREAM_DRAW);
glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &bsize);
if ((GLuint)bsize != (g_Bpp * sizeof(Pixel) * imWidth * imHeight)) {
printf("Buffer object (%d) has incorrect size (%d).\n",
(unsigned)pbo_buffer, (unsigned)bsize);
exit(EXIT_FAILURE);
}
void cleanup(void)
{
cudaGraphicsUnregisterResource(cuda_pbo_resource);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pbo_buffer);
glDeleteTextures(1, &texid);
deleteTexture();
// register this buffer object with CUDA
checkCudaErrors(cudaGraphicsGLRegisterBuffer(
&cuda_pbo_resource, pbo_buffer, cudaGraphicsMapFlagsWriteDiscard));
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexImage2D(GL_TEXTURE_2D, 0, ((g_Bpp == 1) ? GL_LUMINANCE : GL_BGRA),
imWidth, imHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
}
sdkDeleteTimer(&timer);
}
void loadDefaultImage(char *loc_exec) {
printf("Reading image: teapot.pgm\n");
const char *image_filename = "teapot.pgm";
char *image_path = sdkFindFilePath(image_filename, loc_exec);
void initializeData(char *file)
{
GLint bsize;
unsigned int w, h;
size_t file_length = strlen(file);
if (image_path == NULL) {
printf("Failed to read image file: <%s>\n", image_filename);
exit(EXIT_FAILURE);
}
if (!strcmp(&file[file_length - 3], "pgm")) {
if (sdkLoadPGM<unsigned char>(file, &pixels, &w, &h) != true) {
printf("Failed to load PGM image file: %s\n", file);
exit(EXIT_FAILURE);
}
initializeData(image_path);
free(image_path);
g_Bpp = 1;
}
else if (!strcmp(&file[file_length - 3], "ppm")) {
if (sdkLoadPPM4(file, &pixels, &w, &h) != true) {
printf("Failed to load PPM image file: %s\n", file);
exit(EXIT_FAILURE);
}
g_Bpp = 4;
}
else {
exit(EXIT_FAILURE);
}
imWidth = (int)w;
imHeight = (int)h;
setupTexture(imWidth, imHeight, pixels, g_Bpp);
memset(pixels, 0x0, g_Bpp * sizeof(Pixel) * imWidth * imHeight);
if (!g_bQAReadback) {
// use OpenGL Path
glGenBuffers(1, &pbo_buffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_buffer);
glBufferData(GL_PIXEL_UNPACK_BUFFER, g_Bpp * sizeof(Pixel) * imWidth * imHeight, pixels, GL_STREAM_DRAW);
glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, &bsize);
if ((GLuint)bsize != (g_Bpp * sizeof(Pixel) * imWidth * imHeight)) {
printf("Buffer object (%d) has incorrect size (%d).\n", (unsigned)pbo_buffer, (unsigned)bsize);
exit(EXIT_FAILURE);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
// register this buffer object with CUDA
checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_pbo_resource, pbo_buffer, cudaGraphicsMapFlagsWriteDiscard));
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexImage2D(GL_TEXTURE_2D,
0,
((g_Bpp == 1) ? GL_LUMINANCE : GL_BGRA),
imWidth,
imHeight,
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
}
}
void initGL(int *argc, char **argv) {
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(wWidth, wHeight);
glutCreateWindow("CUDA Edge Detection");
void loadDefaultImage(char *loc_exec)
{
printf("Reading image: teapot.pgm\n");
const char *image_filename = "teapot.pgm";
char *image_path = sdkFindFilePath(image_filename, loc_exec);
if (!isGLVersionSupported(1, 5) ||
!areGLExtensionsSupported(
"GL_ARB_vertex_buffer_object GL_ARB_pixel_buffer_object")) {
fprintf(stderr, "Error: failed to get minimal extensions for demo\n");
fprintf(stderr, "This sample requires:\n");
fprintf(stderr, " OpenGL version 1.5\n");
fprintf(stderr, " GL_ARB_vertex_buffer_object\n");
fprintf(stderr, " GL_ARB_pixel_buffer_object\n");
exit(EXIT_FAILURE);
}
if (image_path == NULL) {
printf("Failed to read image file: <%s>\n", image_filename);
exit(EXIT_FAILURE);
}
initializeData(image_path);
free(image_path);
}
void runAutoTest(int argc, char *argv[]) {
printf("[%s] (automated testing w/ readback)\n", sSDKsample);
int devID = findCudaDevice(argc, (const char **)argv);
void initGL(int *argc, char **argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(wWidth, wHeight);
glutCreateWindow("CUDA Edge Detection");
loadDefaultImage(argv[0]);
if (!isGLVersionSupported(1, 5)
|| !areGLExtensionsSupported("GL_ARB_vertex_buffer_object GL_ARB_pixel_buffer_object")) {
fprintf(stderr, "Error: failed to get minimal extensions for demo\n");
fprintf(stderr, "This sample requires:\n");
fprintf(stderr, " OpenGL version 1.5\n");
fprintf(stderr, " GL_ARB_vertex_buffer_object\n");
fprintf(stderr, " GL_ARB_pixel_buffer_object\n");
exit(EXIT_FAILURE);
}
}
Pixel *d_result;
checkCudaErrors(
cudaMalloc((void **)&d_result, imWidth * imHeight * sizeof(Pixel)));
void runAutoTest(int argc, char *argv[])
{
printf("[%s] (automated testing w/ readback)\n", sSDKsample);
int devID = findCudaDevice(argc, (const char **)argv);
char *ref_file = NULL;
char dump_file[256];
loadDefaultImage(argv[0]);
int mode = 0;
mode = getCmdLineArgumentInt(argc, (const char **)argv, "mode");
getCmdLineArgumentString(argc, (const char **)argv, "file", &ref_file);
Pixel *d_result;
checkCudaErrors(cudaMalloc((void **)&d_result, imWidth * imHeight * sizeof(Pixel)));
switch (mode) {
char *ref_file = NULL;
char dump_file[256];
int mode = 0;
mode = getCmdLineArgumentInt(argc, (const char **)argv, "mode");
getCmdLineArgumentString(argc, (const char **)argv, "file", &ref_file);
switch (mode) {
case 0:
g_SobelDisplayMode = SOBELDISPLAY_IMAGE;
sprintf(dump_file, "teapot_orig.pgm");
break;
g_SobelDisplayMode = SOBELDISPLAY_IMAGE;
sprintf(dump_file, "teapot_orig.pgm");
break;
case 1:
g_SobelDisplayMode = SOBELDISPLAY_SOBELTEX;
sprintf(dump_file, "teapot_tex.pgm");
break;
g_SobelDisplayMode = SOBELDISPLAY_SOBELTEX;
sprintf(dump_file, "teapot_tex.pgm");
break;
case 2:
g_SobelDisplayMode = SOBELDISPLAY_SOBELSHARED;
sprintf(dump_file, "teapot_shared.pgm");
break;
g_SobelDisplayMode = SOBELDISPLAY_SOBELSHARED;
sprintf(dump_file, "teapot_shared.pgm");
break;
default:
printf("Invalid Filter Mode File\n");
exit(EXIT_FAILURE);
break;
}
printf("Invalid Filter Mode File\n");
exit(EXIT_FAILURE);
break;
}
printf("AutoTest: %s <%s>\n", sSDKsample, filterMode[g_SobelDisplayMode]);
sobelFilter(d_result, imWidth, imHeight, g_SobelDisplayMode, imageScale);
checkCudaErrors(cudaDeviceSynchronize());
printf("AutoTest: %s <%s>\n", sSDKsample, filterMode[g_SobelDisplayMode]);
sobelFilter(d_result, imWidth, imHeight, g_SobelDisplayMode, imageScale);
checkCudaErrors(cudaDeviceSynchronize());
unsigned char *h_result =
(unsigned char *)malloc(imWidth * imHeight * sizeof(Pixel));
checkCudaErrors(cudaMemcpy(h_result, d_result,
imWidth * imHeight * sizeof(Pixel),
cudaMemcpyDeviceToHost));
sdkSavePGM(dump_file, h_result, imWidth, imHeight);
unsigned char *h_result = (unsigned char *)malloc(imWidth * imHeight * sizeof(Pixel));
checkCudaErrors(cudaMemcpy(h_result, d_result, imWidth * imHeight * sizeof(Pixel), cudaMemcpyDeviceToHost));
sdkSavePGM(dump_file, h_result, imWidth, imHeight);
if (!sdkComparePGM(dump_file, sdkFindFilePath(ref_file, argv[0]),
MAX_EPSILON_ERROR, 0.15f, false)) {
g_TotalErrors++;
}
if (!sdkComparePGM(dump_file, sdkFindFilePath(ref_file, argv[0]), MAX_EPSILON_ERROR, 0.15f, false)) {
g_TotalErrors++;
}
checkCudaErrors(cudaFree(d_result));
free(h_result);
checkCudaErrors(cudaFree(d_result));
free(h_result);
if (g_TotalErrors != 0) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
if (g_TotalErrors != 0) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
printf("Test passed!\n");
exit(EXIT_SUCCESS);
printf("Test passed!\n");
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv) {
pArgc = &argc;
pArgv = argv;
int main(int argc, char **argv)
{
pArgc = &argc;
pArgv = argv;
#if defined(__linux__)
setenv("DISPLAY", ":0", 0);
setenv("DISPLAY", ":0", 0);
#endif
printf("%s Starting...\n\n", sSDKsample);
printf("%s Starting...\n\n", sSDKsample);
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printf("\nUsage: SobelFilter <options>\n");
printf("\t\t-mode=n (0=original, 1=texture, 2=smem + texture)\n");
printf("\t\t-file=ref_orig.pgm (ref_tex.pgm, ref_shared.pgm)\n\n");
exit(EXIT_SUCCESS);
}
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printf("\nUsage: SobelFilter <options>\n");
printf("\t\t-mode=n (0=original, 1=texture, 2=smem + texture)\n");
printf("\t\t-file=ref_orig.pgm (ref_tex.pgm, ref_shared.pgm)\n\n");
exit(EXIT_SUCCESS);
}
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
g_bQAReadback = true;
runAutoTest(argc, argv);
}
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
g_bQAReadback = true;
runAutoTest(argc, argv);
}
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
if (checkCmdLineFlag(argc, (const char **)argv, "device")) {
printf(
" This SDK does not explicitly support -device=n when running with "
"OpenGL.\n");
printf(
" When specifying -device=n (n=0,1,2,....) the sample must not use "
"OpenGL.\n");
printf(" See details below to run without OpenGL:\n\n");
printf(" > %s -device=n\n\n", argv[0]);
printf("exiting...\n");
exit(EXIT_SUCCESS);
}
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
if (checkCmdLineFlag(argc, (const char **)argv, "device")) {
printf(" This SDK does not explicitly support -device=n when running with "
"OpenGL.\n");
printf(" When specifying -device=n (n=0,1,2,....) the sample must not use "
"OpenGL.\n");
printf(" See details below to run without OpenGL:\n\n");
printf(" > %s -device=n\n\n", argv[0]);
printf("exiting...\n");
exit(EXIT_SUCCESS);
}
initGL(&argc, argv);
findCudaDevice(argc, (const char **)argv);
initGL(&argc, argv);
findCudaDevice(argc, (const char **)argv);
sdkCreateTimer(&timer);
sdkResetTimer(&timer);
sdkCreateTimer(&timer);
sdkResetTimer(&timer);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutReshapeFunc(reshape);
loadDefaultImage(argv[0]);
loadDefaultImage(argv[0]);
// If code is not printing the usage, then we execute this path.
printf("I: display Image (no filtering)\n");
printf("T: display Sobel Edge Detection (Using Texture)\n");
printf("S: display Sobel Edge Detection (Using SMEM+Texture)\n");
printf("Use the '-' and '=' keys to change the brightness.\n");
fflush(stdout);
// If code is not printing the usage, then we execute this path.
printf("I: display Image (no filtering)\n");
printf("T: display Sobel Edge Detection (Using Texture)\n");
printf("S: display Sobel Edge Detection (Using SMEM+Texture)\n");
printf("Use the '-' and '=' keys to change the brightness.\n");
fflush(stdout);
#if defined(__APPLE__) || defined(MACOSX)
atexit(cleanup);
atexit(cleanup);
#else
glutCloseFunc(cleanup);
glutCloseFunc(cleanup);
#endif
glutTimerFunc(REFRESH_DELAY, timerEvent, 0);
glutMainLoop();
glutTimerFunc(REFRESH_DELAY, timerEvent, 0);
glutMainLoop();
}

View File

@@ -25,10 +25,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cooperative_groups.h>
#include <cuda_runtime.h>
#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>
#include <cooperative_groups.h>
namespace cg = cooperative_groups;
@@ -37,14 +37,14 @@ namespace cg = cooperative_groups;
#include "SobelFilter_kernels.h"
// Texture object for reading image
cudaTextureObject_t texObject;
cudaTextureObject_t texObject;
extern __shared__ unsigned char LocalBlock[];
static cudaArray *array = NULL;
static cudaArray *array = NULL;
#define RADIUS 1
#ifdef FIXED_BLOCKWIDTH
#define BlockWidth 80
#define BlockWidth 80
#define SharedPitch 384
#endif
@@ -52,241 +52,232 @@ static cudaArray *array = NULL;
// call returns an error
#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)
inline void __checkCudaErrors(cudaError err, const char *file, const int line) {
if (cudaSuccess != err) {
fprintf(stderr, "%s(%i) : CUDA Runtime API error %d: %s.\n", file, line,
(int)err, cudaGetErrorString(err));
exit(EXIT_FAILURE);
}
inline void __checkCudaErrors(cudaError err, const char *file, const int line)
{
if (cudaSuccess != err) {
fprintf(stderr, "%s(%i) : CUDA Runtime API error %d: %s.\n", file, line, (int)err, cudaGetErrorString(err));
exit(EXIT_FAILURE);
}
}
__device__ unsigned char ComputeSobel(unsigned char ul, // upper left
unsigned char um, // upper middle
unsigned char ur, // upper right
unsigned char ml, // middle left
unsigned char mm, // middle (unused)
unsigned char mr, // middle right
unsigned char ll, // lower left
unsigned char lm, // lower middle
unsigned char lr, // lower right
float fScale) {
short Horz = ur + 2 * mr + lr - ul - 2 * ml - ll;
short Vert = ul + 2 * um + ur - ll - 2 * lm - lr;
short Sum = (short)(fScale * (abs((int)Horz) + abs((int)Vert)));
__device__ unsigned char ComputeSobel(unsigned char ul, // upper left
unsigned char um, // upper middle
unsigned char ur, // upper right
unsigned char ml, // middle left
unsigned char mm, // middle (unused)
unsigned char mr, // middle right
unsigned char ll, // lower left
unsigned char lm, // lower middle
unsigned char lr, // lower right
float fScale)
{
short Horz = ur + 2 * mr + lr - ul - 2 * ml - ll;
short Vert = ul + 2 * um + ur - ll - 2 * lm - lr;
short Sum = (short)(fScale * (abs((int)Horz) + abs((int)Vert)));
if (Sum < 0) {
return 0;
} else if (Sum > 0xff) {
return 0xff;
}
if (Sum < 0) {
return 0;
}
else if (Sum > 0xff) {
return 0xff;
}
return (unsigned char)Sum;
return (unsigned char)Sum;
}
__global__ void SobelShared(uchar4 *pSobelOriginal, unsigned short SobelPitch,
__global__ void SobelShared(uchar4 *pSobelOriginal,
unsigned short SobelPitch,
#ifndef FIXED_BLOCKWIDTH
short BlockWidth, short SharedPitch,
short BlockWidth,
short SharedPitch,
#endif
short w, short h, float fScale,
cudaTextureObject_t tex) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
short u = 4 * blockIdx.x * BlockWidth;
short v = blockIdx.y * blockDim.y + threadIdx.y;
short ib;
short w,
short h,
float fScale,
cudaTextureObject_t tex)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
short u = 4 * blockIdx.x * BlockWidth;
short v = blockIdx.y * blockDim.y + threadIdx.y;
short ib;
int SharedIdx = threadIdx.y * SharedPitch;
for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {
LocalBlock[SharedIdx + 4 * ib + 0] = tex2D<unsigned char>(
tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 1] = tex2D<unsigned char>(
tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 2] = tex2D<unsigned char>(
tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 3] = tex2D<unsigned char>(
tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v - RADIUS));
}
if (threadIdx.y < RADIUS * 2) {
//
// copy trailing RADIUS*2 rows of pixels into shared
//
SharedIdx = (blockDim.y + threadIdx.y) * SharedPitch;
int SharedIdx = threadIdx.y * SharedPitch;
for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {
LocalBlock[SharedIdx + 4 * ib + 0] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0),
(float)(v + blockDim.y - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 1] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1),
(float)(v + blockDim.y - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 2] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2),
(float)(v + blockDim.y - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 3] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3),
(float)(v + blockDim.y - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 0] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 1] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 2] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 3] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v - RADIUS));
}
}
cg::sync(cta);
if (threadIdx.y < RADIUS * 2) {
//
// copy trailing RADIUS*2 rows of pixels into shared
//
SharedIdx = (blockDim.y + threadIdx.y) * SharedPitch;
u >>= 2; // index as uchar4 from here
uchar4 *pSobel = (uchar4 *)(((char *)pSobelOriginal) + v * SobelPitch);
SharedIdx = threadIdx.y * SharedPitch;
for (ib = threadIdx.x; ib < BlockWidth; ib += blockDim.x) {
unsigned char pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 0];
unsigned char pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 1];
unsigned char pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 2];
unsigned char pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 0];
unsigned char pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 1];
unsigned char pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 2];
unsigned char pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 0];
unsigned char pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 1];
unsigned char pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 2];
uchar4 out;
out.x = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21,
pix22, fScale);
pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 3];
pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 3];
pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 3];
out.y = ComputeSobel(pix01, pix02, pix00, pix11, pix12, pix10, pix21, pix22,
pix20, fScale);
pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 4];
pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 4];
pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 4];
out.z = ComputeSobel(pix02, pix00, pix01, pix12, pix10, pix11, pix22, pix20,
pix21, fScale);
pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 5];
pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 5];
pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 5];
out.w = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21,
pix22, fScale);
if (u + ib < w / 4 && v < h) {
pSobel[u + ib] = out;
for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {
LocalBlock[SharedIdx + 4 * ib + 0] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v + blockDim.y - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 1] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v + blockDim.y - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 2] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v + blockDim.y - RADIUS));
LocalBlock[SharedIdx + 4 * ib + 3] =
tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v + blockDim.y - RADIUS));
}
}
}
cg::sync(cta);
cg::sync(cta);
u >>= 2; // index as uchar4 from here
uchar4 *pSobel = (uchar4 *)(((char *)pSobelOriginal) + v * SobelPitch);
SharedIdx = threadIdx.y * SharedPitch;
for (ib = threadIdx.x; ib < BlockWidth; ib += blockDim.x) {
unsigned char pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 0];
unsigned char pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 1];
unsigned char pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 2];
unsigned char pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 0];
unsigned char pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 1];
unsigned char pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 2];
unsigned char pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 0];
unsigned char pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 1];
unsigned char pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 2];
uchar4 out;
out.x = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);
pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 3];
pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 3];
pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 3];
out.y = ComputeSobel(pix01, pix02, pix00, pix11, pix12, pix10, pix21, pix22, pix20, fScale);
pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 4];
pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 4];
pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 4];
out.z = ComputeSobel(pix02, pix00, pix01, pix12, pix10, pix11, pix22, pix20, pix21, fScale);
pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 5];
pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 5];
pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 5];
out.w = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);
if (u + ib < w / 4 && v < h) {
pSobel[u + ib] = out;
}
}
cg::sync(cta);
}
__global__ void SobelCopyImage(Pixel *pSobelOriginal, unsigned int Pitch, int w,
int h, float fscale, cudaTextureObject_t tex) {
unsigned char *pSobel =
(unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);
__global__ void
SobelCopyImage(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fscale, cudaTextureObject_t tex)
{
unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);
for (int i = threadIdx.x; i < w; i += blockDim.x) {
pSobel[i] = min(
max((tex2D<unsigned char>(tex, (float)i, (float)blockIdx.x) * fscale),
0.f),
255.f);
}
for (int i = threadIdx.x; i < w; i += blockDim.x) {
pSobel[i] = min(max((tex2D<unsigned char>(tex, (float)i, (float)blockIdx.x) * fscale), 0.f), 255.f);
}
}
__global__ void SobelTex(Pixel *pSobelOriginal, unsigned int Pitch, int w,
int h, float fScale, cudaTextureObject_t tex) {
unsigned char *pSobel =
(unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);
__global__ void SobelTex(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fScale, cudaTextureObject_t tex)
{
unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);
for (int i = threadIdx.x; i < w; i += blockDim.x) {
unsigned char pix00 =
tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x - 1);
unsigned char pix01 =
tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x - 1);
unsigned char pix02 =
tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x - 1);
unsigned char pix10 =
tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 0);
unsigned char pix11 =
tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 0);
unsigned char pix12 =
tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 0);
unsigned char pix20 =
tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 1);
unsigned char pix21 =
tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 1);
unsigned char pix22 =
tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 1);
pSobel[i] = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20,
pix21, pix22, fScale);
}
for (int i = threadIdx.x; i < w; i += blockDim.x) {
unsigned char pix00 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x - 1);
unsigned char pix01 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x - 1);
unsigned char pix02 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x - 1);
unsigned char pix10 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 0);
unsigned char pix11 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 0);
unsigned char pix12 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 0);
unsigned char pix20 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 1);
unsigned char pix21 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 1);
unsigned char pix22 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 1);
pSobel[i] = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);
}
}
extern "C" void setupTexture(int iw, int ih, Pixel *data, int Bpp) {
cudaChannelFormatDesc desc;
extern "C" void setupTexture(int iw, int ih, Pixel *data, int Bpp)
{
cudaChannelFormatDesc desc;
if (Bpp == 1) {
desc = cudaCreateChannelDesc<unsigned char>();
} else {
desc = cudaCreateChannelDesc<uchar4>();
}
if (Bpp == 1) {
desc = cudaCreateChannelDesc<unsigned char>();
}
else {
desc = cudaCreateChannelDesc<uchar4>();
}
checkCudaErrors(cudaMallocArray(&array, &desc, iw, ih));
checkCudaErrors(cudaMemcpy2DToArray(
array, 0, 0, data, iw * Bpp * sizeof(Pixel), iw * Bpp * sizeof(Pixel), ih,
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMallocArray(&array, &desc, iw, ih));
checkCudaErrors(cudaMemcpy2DToArray(
array, 0, 0, data, iw * Bpp * sizeof(Pixel), iw * Bpp * sizeof(Pixel), ih, cudaMemcpyHostToDevice));
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeArray;
texRes.res.array.array = array;
texRes.resType = cudaResourceTypeArray;
texRes.res.array.array = array;
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModePoint;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModePoint;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texObject, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texObject, &texRes, &texDescr, NULL));
}
extern "C" void deleteTexture(void) {
checkCudaErrors(cudaFreeArray(array));
checkCudaErrors(cudaDestroyTextureObject(texObject));
extern "C" void deleteTexture(void)
{
checkCudaErrors(cudaFreeArray(array));
checkCudaErrors(cudaDestroyTextureObject(texObject));
}
// Wrapper for the __global__ call that sets up the texture and threads
extern "C" void sobelFilter(Pixel *odata, int iw, int ih,
enum SobelDisplayMode mode, float fScale) {
switch (mode) {
extern "C" void sobelFilter(Pixel *odata, int iw, int ih, enum SobelDisplayMode mode, float fScale)
{
switch (mode) {
case SOBELDISPLAY_IMAGE:
SobelCopyImage<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);
break;
SobelCopyImage<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);
break;
case SOBELDISPLAY_SOBELTEX:
SobelTex<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);
break;
SobelTex<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);
break;
case SOBELDISPLAY_SOBELSHARED: {
dim3 threads(16, 4);
dim3 threads(16, 4);
#ifndef FIXED_BLOCKWIDTH
int BlockWidth = 80; // must be divisible by 16 for coalescing
int BlockWidth = 80; // must be divisible by 16 for coalescing
#endif
dim3 blocks = dim3(iw / (4 * BlockWidth) + (0 != iw % (4 * BlockWidth)),
ih / threads.y + (0 != ih % threads.y));
int SharedPitch = ~0x3f & (4 * (BlockWidth + 2 * RADIUS) + 0x3f);
int sharedMem = SharedPitch * (threads.y + 2 * RADIUS);
dim3 blocks =
dim3(iw / (4 * BlockWidth) + (0 != iw % (4 * BlockWidth)), ih / threads.y + (0 != ih % threads.y));
int SharedPitch = ~0x3f & (4 * (BlockWidth + 2 * RADIUS) + 0x3f);
int sharedMem = SharedPitch * (threads.y + 2 * RADIUS);
// for the shared kernel, width must be divisible by 4
iw &= ~3;
// for the shared kernel, width must be divisible by 4
iw &= ~3;
SobelShared<<<blocks, threads, sharedMem>>>((uchar4 *)odata, iw,
SobelShared<<<blocks, threads, sharedMem>>>((uchar4 *)odata,
iw,
#ifndef FIXED_BLOCKWIDTH
BlockWidth, SharedPitch,
BlockWidth,
SharedPitch,
#endif
iw, ih, fScale, texObject);
iw,
ih,
fScale,
texObject);
} break;
}
}
}

View File

@@ -31,16 +31,11 @@
typedef unsigned char Pixel;
// global determines which filter to invoke
enum SobelDisplayMode {
SOBELDISPLAY_IMAGE = 0,
SOBELDISPLAY_SOBELTEX,
SOBELDISPLAY_SOBELSHARED
};
enum SobelDisplayMode { SOBELDISPLAY_IMAGE = 0, SOBELDISPLAY_SOBELTEX, SOBELDISPLAY_SOBELSHARED };
extern enum SobelDisplayMode g_SobelDisplayMode;
extern "C" void sobelFilter(Pixel *odata, int iw, int ih,
enum SobelDisplayMode mode, float fScale);
extern "C" void sobelFilter(Pixel *odata, int iw, int ih, enum SobelDisplayMode mode, float fScale);
extern "C" void setupTexture(int iw, int ih, Pixel *data, int Bpp);
extern "C" void deleteTexture(void);
extern "C" void initFilter(void);

View File

@@ -53,16 +53,15 @@
* ACM Trans. on Math. Software, 29(1):49-57, 2003
*/
#include <iostream>
#include <cuda_runtime.h> // CUDA Runtime Functions
#include <helper_cuda.h> // helper functions for CUDA error checking and initialization
#include <helper_functions.h> // helper functions
#include <stdexcept>
#include <math.h>
#include "sobol.h"
#include <cuda_runtime.h> // CUDA Runtime Functions
#include <helper_cuda.h> // helper functions for CUDA error checking and initialization
#include <helper_functions.h> // helper functions
#include <iostream>
#include <math.h>
#include <stdexcept>
#include "sobol_gold.h"
#include "sobol_gpu.h"
@@ -70,236 +69,238 @@
const char *sSDKsample = "Sobol Quasi-Random Number Generator";
void printHelp(int argc, char *argv[]) {
if (argc > 0) {
std::cout << "\nUsage: " << argv[0] << " <options>\n\n";
} else {
std::cout << "\nUsage: <program name> <options>\n\n";
}
void printHelp(int argc, char *argv[])
{
if (argc > 0) {
std::cout << "\nUsage: " << argv[0] << " <options>\n\n";
}
else {
std::cout << "\nUsage: <program name> <options>\n\n";
}
std::cout << "\t--vectors=M specify number of vectors (required)\n";
std::cout << "\t The generator will output M vectors\n\n";
std::cout << "\t--dimensions=N specify number of dimensions (required)\n";
std::cout << "\t Each vector will consist of N components\n\n";
std::cout << std::endl;
std::cout << "\t--vectors=M specify number of vectors (required)\n";
std::cout << "\t The generator will output M vectors\n\n";
std::cout << "\t--dimensions=N specify number of dimensions (required)\n";
std::cout << "\t Each vector will consist of N components\n\n";
std::cout << std::endl;
}
int main(int argc, char *argv[]) {
bool ok = true;
int main(int argc, char *argv[])
{
bool ok = true;
// We will generate n_vectors vectors of n_dimensions numbers
int n_vectors = 100000;
int n_dimensions = 100;
// We will generate n_vectors vectors of n_dimensions numbers
int n_vectors = 100000;
int n_dimensions = 100;
printf("%s Starting...\n\n", sSDKsample);
printf("%s Starting...\n\n", sSDKsample);
// Print help if requested
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printHelp(argc, argv);
return 0;
}
// Print help if requested
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
printHelp(argc, argv);
return 0;
}
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
// For QA testing set a default number of vectors and dimensions
n_vectors = 100000;
n_dimensions = 100;
} else {
// Parse the command line to determine the required number of vectors
if (checkCmdLineFlag(argc, (const char **)argv, "vectors")) {
n_vectors = getCmdLineArgumentInt(argc, (const char **)argv, "vectors");
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
// For QA testing set a default number of vectors and dimensions
n_vectors = 100000;
n_dimensions = 100;
}
else {
// Parse the command line to determine the required number of vectors
if (checkCmdLineFlag(argc, (const char **)argv, "vectors")) {
n_vectors = getCmdLineArgumentInt(argc, (const char **)argv, "vectors");
if (n_vectors < 1) {
std::cerr << "Illegal argument: number of vectors must be positive "
"(--vectors=N)"
if (n_vectors < 1) {
std::cerr << "Illegal argument: number of vectors must be positive "
"(--vectors=N)"
<< std::endl;
ok = false;
}
}
std::cout << "> number of vectors = " << n_vectors << std::endl;
// Parse the command line to determine the number of dimensions in each
// vector
if (checkCmdLineFlag(argc, (const char **)argv, "dimensions")) {
n_dimensions = getCmdLineArgumentInt(argc, (const char **)argv, "dimensions");
if (n_dimensions < 1) {
std::cerr << "Illegal argument: number of dimensions must be positive "
"(--dimensions=N)"
<< std::endl;
ok = false;
}
}
std::cout << "> number of dimensions = " << n_dimensions << std::endl;
}
// If any of the command line checks failed, exit
if (!ok) {
return -1;
}
// Use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
// Create a timer to measure performance
StopWatchInterface *hTimer = NULL;
double time;
sdkCreateTimer(&hTimer);
// Allocate memory for the arrays
std::cout << "Allocating CPU memory..." << std::endl;
unsigned int *h_directions = 0;
float *h_outputCPU = 0;
float *h_outputGPU = 0;
try {
h_directions = new unsigned int[n_dimensions * n_directions];
h_outputCPU = new float[n_vectors * n_dimensions];
h_outputGPU = new float[n_vectors * n_dimensions];
}
catch (std::exception e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
std::cerr << "Unable to allocate CPU memory (try running with fewer "
"vectors/dimensions)"
<< std::endl;
ok = false;
}
exit(EXIT_FAILURE);
}
std::cout << "> number of vectors = " << n_vectors << std::endl;
std::cout << "Allocating GPU memory..." << std::endl;
unsigned int *d_directions;
float *d_output;
// Parse the command line to determine the number of dimensions in each
// vector
if (checkCmdLineFlag(argc, (const char **)argv, "dimensions")) {
n_dimensions =
getCmdLineArgumentInt(argc, (const char **)argv, "dimensions");
try {
cudaError_t cudaResult;
cudaResult = cudaMalloc((void **)&d_directions, n_dimensions * n_directions * sizeof(unsigned int));
if (n_dimensions < 1) {
std::cerr << "Illegal argument: number of dimensions must be positive "
"(--dimensions=N)"
if (cudaResult != cudaSuccess) {
throw std::runtime_error(cudaGetErrorString(cudaResult));
}
cudaResult = cudaMalloc((void **)&d_output, n_vectors * n_dimensions * sizeof(float));
if (cudaResult != cudaSuccess) {
throw std::runtime_error(cudaGetErrorString(cudaResult));
}
}
catch (std::runtime_error e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
std::cerr << "Unable to allocate GPU memory (try running with fewer "
"vectors/dimensions)"
<< std::endl;
ok = false;
}
exit(EXIT_FAILURE);
}
std::cout << "> number of dimensions = " << n_dimensions << std::endl;
}
// Initialize the direction numbers (done on the host)
std::cout << "Initializing direction numbers..." << std::endl;
initSobolDirectionVectors(n_dimensions, h_directions);
// If any of the command line checks failed, exit
if (!ok) {
return -1;
}
// Copy the direction numbers to the device
std::cout << "Copying direction numbers to device..." << std::endl;
checkCudaErrors(cudaMemcpy(
d_directions, h_directions, n_dimensions * n_directions * sizeof(unsigned int), cudaMemcpyHostToDevice));
checkCudaErrors(cudaDeviceSynchronize());
// Use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
// Execute the QRNG on the device
std::cout << "Executing QRNG on GPU..." << std::endl;
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
sobolGPU(n_vectors, n_dimensions, d_directions, d_output);
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
time = sdkGetTimerValue(&hTimer);
// Create a timer to measure performance
StopWatchInterface *hTimer = NULL;
double time;
sdkCreateTimer(&hTimer);
// Allocate memory for the arrays
std::cout << "Allocating CPU memory..." << std::endl;
unsigned int *h_directions = 0;
float *h_outputCPU = 0;
float *h_outputGPU = 0;
try {
h_directions = new unsigned int[n_dimensions * n_directions];
h_outputCPU = new float[n_vectors * n_dimensions];
h_outputGPU = new float[n_vectors * n_dimensions];
} catch (std::exception e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
std::cerr << "Unable to allocate CPU memory (try running with fewer "
"vectors/dimensions)"
<< std::endl;
exit(EXIT_FAILURE);
}
std::cout << "Allocating GPU memory..." << std::endl;
unsigned int *d_directions;
float *d_output;
try {
cudaError_t cudaResult;
cudaResult = cudaMalloc((void **)&d_directions,
n_dimensions * n_directions * sizeof(unsigned int));
if (cudaResult != cudaSuccess) {
throw std::runtime_error(cudaGetErrorString(cudaResult));
if (time < 1e-6) {
std::cout << "Gsamples/s: problem size too small to measure, try "
"increasing number of vectors or dimensions"
<< std::endl;
}
else {
std::cout << "Gsamples/s: " << (double)n_vectors * (double)n_dimensions * 1E-9 / (time * 1E-3) << std::endl;
}
cudaResult = cudaMalloc((void **)&d_output,
n_vectors * n_dimensions * sizeof(float));
std::cout << "Reading results from GPU..." << std::endl;
checkCudaErrors(
cudaMemcpy(h_outputGPU, d_output, n_vectors * n_dimensions * sizeof(float), cudaMemcpyDeviceToHost));
if (cudaResult != cudaSuccess) {
throw std::runtime_error(cudaGetErrorString(cudaResult));
std::cout << std::endl;
// Execute the QRNG on the host
std::cout << "Executing QRNG on CPU..." << std::endl;
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
sobolCPU(n_vectors, n_dimensions, h_directions, h_outputCPU);
sdkStopTimer(&hTimer);
time = sdkGetTimerValue(&hTimer);
if (time < 1e-6) {
std::cout << "Gsamples/s: problem size too small to measure, try "
"increasing number of vectors or dimensions"
<< std::endl;
}
} catch (std::runtime_error e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
std::cerr << "Unable to allocate GPU memory (try running with fewer "
"vectors/dimensions)"
<< std::endl;
exit(EXIT_FAILURE);
}
// Initialize the direction numbers (done on the host)
std::cout << "Initializing direction numbers..." << std::endl;
initSobolDirectionVectors(n_dimensions, h_directions);
// Copy the direction numbers to the device
std::cout << "Copying direction numbers to device..." << std::endl;
checkCudaErrors(cudaMemcpy(d_directions, h_directions,
n_dimensions * n_directions * sizeof(unsigned int),
cudaMemcpyHostToDevice));
checkCudaErrors(cudaDeviceSynchronize());
// Execute the QRNG on the device
std::cout << "Executing QRNG on GPU..." << std::endl;
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
sobolGPU(n_vectors, n_dimensions, d_directions, d_output);
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
time = sdkGetTimerValue(&hTimer);
if (time < 1e-6) {
std::cout << "Gsamples/s: problem size too small to measure, try "
"increasing number of vectors or dimensions"
<< std::endl;
} else {
std::cout << "Gsamples/s: "
<< (double)n_vectors * (double)n_dimensions * 1E-9 / (time * 1E-3)
<< std::endl;
}
std::cout << "Reading results from GPU..." << std::endl;
checkCudaErrors(cudaMemcpy(h_outputGPU, d_output,
n_vectors * n_dimensions * sizeof(float),
cudaMemcpyDeviceToHost));
std::cout << std::endl;
// Execute the QRNG on the host
std::cout << "Executing QRNG on CPU..." << std::endl;
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
sobolCPU(n_vectors, n_dimensions, h_directions, h_outputCPU);
sdkStopTimer(&hTimer);
time = sdkGetTimerValue(&hTimer);
if (time < 1e-6) {
std::cout << "Gsamples/s: problem size too small to measure, try "
"increasing number of vectors or dimensions"
<< std::endl;
} else {
std::cout << "Gsamples/s: "
<< (double)n_vectors * (double)n_dimensions * 1E-9 / (time * 1E-3)
<< std::endl;
}
// Check the results
std::cout << "Checking results..." << std::endl;
float l1norm_diff = 0.0F;
float l1norm_ref = 0.0F;
float l1error;
// Special case if n_vectors is 1, when the vector should be exactly 0
if (n_vectors == 1) {
for (int d = 0, v = 0; d < n_dimensions; d++) {
float ref = h_outputCPU[d * n_vectors + v];
l1norm_diff += fabs(h_outputGPU[d * n_vectors + v] - ref);
l1norm_ref += fabs(ref);
else {
std::cout << "Gsamples/s: " << (double)n_vectors * (double)n_dimensions * 1E-9 / (time * 1E-3) << std::endl;
}
// Output the L1-Error
l1error = l1norm_diff;
// Check the results
std::cout << "Checking results..." << std::endl;
float l1norm_diff = 0.0F;
float l1norm_ref = 0.0F;
float l1error;
if (l1norm_ref != 0) {
std::cerr << "Error: L1-Norm of the reference is not zero (for single "
"vector), golden generator appears broken\n";
} else {
std::cout << "L1-Error: " << l1error << std::endl;
// Special case if n_vectors is 1, when the vector should be exactly 0
if (n_vectors == 1) {
for (int d = 0, v = 0; d < n_dimensions; d++) {
float ref = h_outputCPU[d * n_vectors + v];
l1norm_diff += fabs(h_outputGPU[d * n_vectors + v] - ref);
l1norm_ref += fabs(ref);
}
// Output the L1-Error
l1error = l1norm_diff;
if (l1norm_ref != 0) {
std::cerr << "Error: L1-Norm of the reference is not zero (for single "
"vector), golden generator appears broken\n";
}
else {
std::cout << "L1-Error: " << l1error << std::endl;
}
}
} else {
for (int d = 0; d < n_dimensions; d++) {
for (int v = 0; v < n_vectors; v++) {
float ref = h_outputCPU[d * n_vectors + v];
l1norm_diff += fabs(h_outputGPU[d * n_vectors + v] - ref);
l1norm_ref += fabs(ref);
}
else {
for (int d = 0; d < n_dimensions; d++) {
for (int v = 0; v < n_vectors; v++) {
float ref = h_outputCPU[d * n_vectors + v];
l1norm_diff += fabs(h_outputGPU[d * n_vectors + v] - ref);
l1norm_ref += fabs(ref);
}
}
// Output the L1-Error
l1error = l1norm_diff / l1norm_ref;
if (l1norm_ref == 0) {
std::cerr << "Error: L1-Norm of the reference is zero, golden generator "
"appears broken\n";
}
else {
std::cout << "L1-Error: " << l1error << std::endl;
}
}
// Output the L1-Error
l1error = l1norm_diff / l1norm_ref;
// Cleanup and terminate
std::cout << "Shutting down..." << std::endl;
sdkDeleteTimer(&hTimer);
delete h_directions;
delete h_outputCPU;
delete h_outputGPU;
checkCudaErrors(cudaFree(d_directions));
checkCudaErrors(cudaFree(d_output));
if (l1norm_ref == 0) {
std::cerr << "Error: L1-Norm of the reference is zero, golden generator "
"appears broken\n";
} else {
std::cout << "L1-Error: " << l1error << std::endl;
}
}
// Cleanup and terminate
std::cout << "Shutting down..." << std::endl;
sdkDeleteTimer(&hTimer);
delete h_directions;
delete h_outputCPU;
delete h_outputGPU;
checkCudaErrors(cudaFree(d_directions));
checkCudaErrors(cudaFree(d_output));
// Check pass/fail using L1 error
exit(l1error < L1ERROR_TOLERANCE ? EXIT_SUCCESS : EXIT_FAILURE);
// Check pass/fail using L1 error
exit(l1error < L1ERROR_TOLERANCE ? EXIT_SUCCESS : EXIT_FAILURE);
}

View File

@@ -53,13 +53,14 @@
* ACM Trans. on Math. Software, 29(1):49-57, 2003
*/
#include "sobol_gold.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "sobol.h"
#include "sobol_gold.h"
#include "sobol_primitives.h"
#define k_2powneg32 2.3283064E-10F
@@ -68,106 +69,110 @@
// fairly simple implementation.
// WIN32 is defined on 32 and 64 bit Windows
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
int ffs(const unsigned int &i) {
unsigned int v = i;
unsigned int count;
int ffs(const unsigned int &i)
{
unsigned int v = i;
unsigned int count;
if (!v) {
count = 0;
} else {
count = 2;
if (!v) {
count = 0;
}
else {
count = 2;
if ((v & 0xffff) == 0) {
v >>= 16;
count += 16;
if ((v & 0xffff) == 0) {
v >>= 16;
count += 16;
}
if ((v & 0xff) == 0) {
v >>= 8;
count += 8;
}
if ((v & 0xf) == 0) {
v >>= 4;
count += 4;
}
if ((v & 0x3) == 0) {
v >>= 2;
count += 2;
}
count -= v & 0x1;
}
if ((v & 0xff) == 0) {
v >>= 8;
count += 8;
}
if ((v & 0xf) == 0) {
v >>= 4;
count += 4;
}
if ((v & 0x3) == 0) {
v >>= 2;
count += 2;
}
count -= v & 0x1;
}
return count;
return count;
}
#endif
// Create the direction numbers, based on the primitive polynomials.
void initSobolDirectionVectors(int n_dimensions, unsigned int *directions) {
unsigned int *v = directions;
void initSobolDirectionVectors(int n_dimensions, unsigned int *directions)
{
unsigned int *v = directions;
for (int dim = 0; dim < n_dimensions; dim++) {
// First dimension is a special case
if (dim == 0) {
for (int i = 0; i < n_directions; i++) {
// All m's are 1
v[i] = 1 << (31 - i);
}
} else {
int d = sobol_primitives[dim].degree;
// The first direction numbers (up to the degree of the polynomial)
// are simply v[i] = m[i] / 2^i (stored in Q0.32 format)
for (int i = 0; i < d; i++) {
v[i] = sobol_primitives[dim].m[i] << (31 - i);
}
// The remaining direction numbers are computed as described in
// the Bratley and Fox paper.
// v[i] = a[1]v[i-1] ^ a[2]v[i-2] ^ ... ^ a[v-1]v[i-d+1] ^ v[i-d] ^
// v[i-d]/2^d
for (int i = d; i < n_directions; i++) {
// First do the v[i-d] ^ v[i-d]/2^d part
v[i] = v[i - d] ^ (v[i - d] >> d);
// Now do the a[1]v[i-1] ^ a[2]v[i-2] ^ ... part
// Note that the coefficients a[] are zero or one and for compactness in
// the input tables they are stored as bits of a single integer. To
// extract the relevant bit we use right shift and mask with 1.
// For example, for a 10 degree polynomial there are ten useful bits in
// a, so to get a[2] we need to right shift 7 times (to get the 8th bit
// into the LSB) and then mask with 1.
for (int j = 1; j < d; j++) {
v[i] ^= (((sobol_primitives[dim].a >> (d - 1 - j)) & 1) * v[i - j]);
for (int dim = 0; dim < n_dimensions; dim++) {
// First dimension is a special case
if (dim == 0) {
for (int i = 0; i < n_directions; i++) {
// All m's are 1
v[i] = 1 << (31 - i);
}
}
}
}
else {
int d = sobol_primitives[dim].degree;
v += n_directions;
}
// The first direction numbers (up to the degree of the polynomial)
// are simply v[i] = m[i] / 2^i (stored in Q0.32 format)
for (int i = 0; i < d; i++) {
v[i] = sobol_primitives[dim].m[i] << (31 - i);
}
// The remaining direction numbers are computed as described in
// the Bratley and Fox paper.
// v[i] = a[1]v[i-1] ^ a[2]v[i-2] ^ ... ^ a[v-1]v[i-d+1] ^ v[i-d] ^
// v[i-d]/2^d
for (int i = d; i < n_directions; i++) {
// First do the v[i-d] ^ v[i-d]/2^d part
v[i] = v[i - d] ^ (v[i - d] >> d);
// Now do the a[1]v[i-1] ^ a[2]v[i-2] ^ ... part
// Note that the coefficients a[] are zero or one and for compactness in
// the input tables they are stored as bits of a single integer. To
// extract the relevant bit we use right shift and mask with 1.
// For example, for a 10 degree polynomial there are ten useful bits in
// a, so to get a[2] we need to right shift 7 times (to get the 8th bit
// into the LSB) and then mask with 1.
for (int j = 1; j < d; j++) {
v[i] ^= (((sobol_primitives[dim].a >> (d - 1 - j)) & 1) * v[i - j]);
}
}
}
v += n_directions;
}
}
// Reference model for generating Sobol numbers on the host
void sobolCPU(int n_vectors, int n_dimensions, unsigned int *directions,
float *output) {
unsigned int *v = directions;
void sobolCPU(int n_vectors, int n_dimensions, unsigned int *directions, float *output)
{
unsigned int *v = directions;
for (int d = 0; d < n_dimensions; d++) {
unsigned int X = 0;
// x[0] is zero (in all dimensions)
output[n_vectors * d] = 0.0;
for (int d = 0; d < n_dimensions; d++) {
unsigned int X = 0;
// x[0] is zero (in all dimensions)
output[n_vectors * d] = 0.0;
for (int i = 1; i < n_vectors; i++) {
// x[i] = x[i-1] ^ v[c]
// where c is the index of the rightmost zero bit in i
// minus 1 (since C arrays count from zero)
// In the Bratley and Fox paper this is equation (**)
X ^= v[ffs(~(i - 1)) - 1];
output[i + n_vectors * d] = (float)X * k_2powneg32;
for (int i = 1; i < n_vectors; i++) {
// x[i] = x[i-1] ^ v[c]
// where c is the index of the rightmost zero bit in i
// minus 1 (since C arrays count from zero)
// In the Bratley and Fox paper this is equation (**)
X ^= v[ffs(~(i - 1)) - 1];
output[i + n_vectors * d] = (float)X * k_2powneg32;
}
v += n_directions;
}
v += n_directions;
}
}

View File

@@ -58,7 +58,6 @@
#define SOBOL_GOLD_H
void initSobolDirectionVectors(int n_dimensions, unsigned int *directions);
void sobolCPU(int n_vectors, int n_dimensions, unsigned int *directions,
float *output);
void sobolCPU(int n_vectors, int n_dimensions, unsigned int *directions, float *output);
#endif

View File

@@ -54,154 +54,155 @@
*
*/
#include <cooperative_groups.h>
#include "sobol.h"
#include "sobol_gpu.h"
#include <cooperative_groups.h>
namespace cg = cooperative_groups;
#include <helper_cuda.h>
#define k_2powneg32 2.3283064E-10F
__global__ void sobolGPU_kernel(unsigned n_vectors, unsigned n_dimensions,
unsigned *d_directions, float *d_output) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__shared__ unsigned int v[n_directions];
__global__ void sobolGPU_kernel(unsigned n_vectors, unsigned n_dimensions, unsigned *d_directions, float *d_output)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__shared__ unsigned int v[n_directions];
// Offset into the correct dimension as specified by the
// block y coordinate
d_directions = d_directions + n_directions * blockIdx.y;
d_output = d_output + n_vectors * blockIdx.y;
// Offset into the correct dimension as specified by the
// block y coordinate
d_directions = d_directions + n_directions * blockIdx.y;
d_output = d_output + n_vectors * blockIdx.y;
// Copy the direction numbers for this dimension into shared
// memory - there are only 32 direction numbers so only the
// first 32 (n_directions) threads need participate.
if (threadIdx.x < n_directions) {
v[threadIdx.x] = d_directions[threadIdx.x];
}
// Copy the direction numbers for this dimension into shared
// memory - there are only 32 direction numbers so only the
// first 32 (n_directions) threads need participate.
if (threadIdx.x < n_directions) {
v[threadIdx.x] = d_directions[threadIdx.x];
}
cg::sync(cta);
cg::sync(cta);
// Set initial index (i.e. which vector this thread is
// computing first) and stride (i.e. step to the next vector
// for this thread)
int i0 = threadIdx.x + blockIdx.x * blockDim.x;
int stride = gridDim.x * blockDim.x;
// Set initial index (i.e. which vector this thread is
// computing first) and stride (i.e. step to the next vector
// for this thread)
int i0 = threadIdx.x + blockIdx.x * blockDim.x;
int stride = gridDim.x * blockDim.x;
// Get the gray code of the index
// c.f. Numerical Recipes in C, chapter 20
// http://www.nrbook.com/a/bookcpdf/c20-2.pdf
unsigned int g = i0 ^ (i0 >> 1);
// Get the gray code of the index
// c.f. Numerical Recipes in C, chapter 20
// http://www.nrbook.com/a/bookcpdf/c20-2.pdf
unsigned int g = i0 ^ (i0 >> 1);
// Initialisation for first point x[i0]
// In the Bratley and Fox paper this is equation (*), where
// we are computing the value for x[n] without knowing the
// value of x[n-1].
unsigned int X = 0;
unsigned int mask;
// Initialisation for first point x[i0]
// In the Bratley and Fox paper this is equation (*), where
// we are computing the value for x[n] without knowing the
// value of x[n-1].
unsigned int X = 0;
unsigned int mask;
for (unsigned int k = 0; k < __ffs(stride) - 1; k++) {
// We want X ^= g_k * v[k], where g_k is one or zero.
// We do this by setting a mask with all bits equal to
// g_k. In reality we keep shifting g so that g_k is the
// LSB of g. This way we avoid multiplication.
mask = -(g & 1);
X ^= mask & v[k];
g = g >> 1;
}
for (unsigned int k = 0; k < __ffs(stride) - 1; k++) {
// We want X ^= g_k * v[k], where g_k is one or zero.
// We do this by setting a mask with all bits equal to
// g_k. In reality we keep shifting g so that g_k is the
// LSB of g. This way we avoid multiplication.
mask = -(g & 1);
X ^= mask & v[k];
g = g >> 1;
}
if (i0 < n_vectors) {
d_output[i0] = (float)X * k_2powneg32;
}
if (i0 < n_vectors) {
d_output[i0] = (float)X * k_2powneg32;
}
// Now do rest of points, using the stride
// Here we want to generate x[i] from x[i-stride] where we
// don't have any of the x in between, therefore we have to
// revisit the equation (**), this is easiest with an example
// so assume stride is 16.
// From x[n] to x[n+16] there will be:
// 8 changes in the first bit
// 4 changes in the second bit
// 2 changes in the third bit
// 1 change in the fourth
// 1 change in one of the remaining bits
//
// What this means is that in the equation:
// x[n+1] = x[n] ^ v[p]
// x[n+2] = x[n+1] ^ v[q] = x[n] ^ v[p] ^ v[q]
// ...
// We will apply xor with v[1] eight times, v[2] four times,
// v[3] twice, v[4] once and one other direction number once.
// Since two xors cancel out, we can skip even applications
// and just apply xor with v[4] (i.e. log2(16)) and with
// the current applicable direction number.
// Note that all these indices count from 1, so we need to
// subtract 1 from them all to account for C arrays counting
// from zero.
unsigned int v_log2stridem1 = v[__ffs(stride) - 2];
unsigned int v_stridemask = stride - 1;
// Now do rest of points, using the stride
// Here we want to generate x[i] from x[i-stride] where we
// don't have any of the x in between, therefore we have to
// revisit the equation (**), this is easiest with an example
// so assume stride is 16.
// From x[n] to x[n+16] there will be:
// 8 changes in the first bit
// 4 changes in the second bit
// 2 changes in the third bit
// 1 change in the fourth
// 1 change in one of the remaining bits
//
// What this means is that in the equation:
// x[n+1] = x[n] ^ v[p]
// x[n+2] = x[n+1] ^ v[q] = x[n] ^ v[p] ^ v[q]
// ...
// We will apply xor with v[1] eight times, v[2] four times,
// v[3] twice, v[4] once and one other direction number once.
// Since two xors cancel out, we can skip even applications
// and just apply xor with v[4] (i.e. log2(16)) and with
// the current applicable direction number.
// Note that all these indices count from 1, so we need to
// subtract 1 from them all to account for C arrays counting
// from zero.
unsigned int v_log2stridem1 = v[__ffs(stride) - 2];
unsigned int v_stridemask = stride - 1;
for (unsigned int i = i0 + stride; i < n_vectors; i += stride) {
// x[i] = x[i-stride] ^ v[b] ^ v[c]
// where b is log2(stride) minus 1 for C array indexing
// where c is the index of the rightmost zero bit in i,
// not including the bottom log2(stride) bits, minus 1
// for C array indexing
// In the Bratley and Fox paper this is equation (**)
X ^= v_log2stridem1 ^ v[__ffs(~((i - stride) | v_stridemask)) - 1];
d_output[i] = (float)X * k_2powneg32;
}
for (unsigned int i = i0 + stride; i < n_vectors; i += stride) {
// x[i] = x[i-stride] ^ v[b] ^ v[c]
// where b is log2(stride) minus 1 for C array indexing
// where c is the index of the rightmost zero bit in i,
// not including the bottom log2(stride) bits, minus 1
// for C array indexing
// In the Bratley and Fox paper this is equation (**)
X ^= v_log2stridem1 ^ v[__ffs(~((i - stride) | v_stridemask)) - 1];
d_output[i] = (float)X * k_2powneg32;
}
}
extern "C" void sobolGPU(int n_vectors, int n_dimensions,
unsigned int *d_directions, float *d_output) {
const int threadsperblock = 64;
extern "C" void sobolGPU(int n_vectors, int n_dimensions, unsigned int *d_directions, float *d_output)
{
const int threadsperblock = 64;
// Set up the execution configuration
dim3 dimGrid;
dim3 dimBlock;
// Set up the execution configuration
dim3 dimGrid;
dim3 dimBlock;
int device;
cudaDeviceProp prop;
checkCudaErrors(cudaGetDevice(&device));
checkCudaErrors(cudaGetDeviceProperties(&prop, device));
int device;
cudaDeviceProp prop;
checkCudaErrors(cudaGetDevice(&device));
checkCudaErrors(cudaGetDeviceProperties(&prop, device));
// This implementation of the generator outputs all the draws for
// one dimension in a contiguous region of memory, followed by the
// next dimension and so on.
// Therefore all threads within a block will be processing different
// vectors from the same dimension. As a result we want the total
// number of blocks to be a multiple of the number of dimensions.
dimGrid.y = n_dimensions;
// This implementation of the generator outputs all the draws for
// one dimension in a contiguous region of memory, followed by the
// next dimension and so on.
// Therefore all threads within a block will be processing different
// vectors from the same dimension. As a result we want the total
// number of blocks to be a multiple of the number of dimensions.
dimGrid.y = n_dimensions;
// If the number of dimensions is large then we will set the number
// of blocks to equal the number of dimensions (i.e. dimGrid.x = 1)
// but if the number of dimensions is small (e.g. less than four per
// multiprocessor) then we'll partition the vectors across blocks
// (as well as threads).
if (n_dimensions < (4 * prop.multiProcessorCount)) {
dimGrid.x = 4 * prop.multiProcessorCount;
} else {
dimGrid.x = 1;
}
// If the number of dimensions is large then we will set the number
// of blocks to equal the number of dimensions (i.e. dimGrid.x = 1)
// but if the number of dimensions is small (e.g. less than four per
// multiprocessor) then we'll partition the vectors across blocks
// (as well as threads).
if (n_dimensions < (4 * prop.multiProcessorCount)) {
dimGrid.x = 4 * prop.multiProcessorCount;
}
else {
dimGrid.x = 1;
}
// Cap the dimGrid.x if the number of vectors is small
if (dimGrid.x > (unsigned int)(n_vectors / threadsperblock)) {
dimGrid.x = (n_vectors + threadsperblock - 1) / threadsperblock;
}
// Cap the dimGrid.x if the number of vectors is small
if (dimGrid.x > (unsigned int)(n_vectors / threadsperblock)) {
dimGrid.x = (n_vectors + threadsperblock - 1) / threadsperblock;
}
// Round up to a power of two, required for the algorithm so that
// stride is a power of two.
unsigned int targetDimGridX = dimGrid.x;
// Round up to a power of two, required for the algorithm so that
// stride is a power of two.
unsigned int targetDimGridX = dimGrid.x;
for (dimGrid.x = 1; dimGrid.x < targetDimGridX; dimGrid.x *= 2)
;
for (dimGrid.x = 1; dimGrid.x < targetDimGridX; dimGrid.x *= 2)
;
// Fix the number of threads
dimBlock.x = threadsperblock;
// Fix the number of threads
dimBlock.x = threadsperblock;
// Execute GPU kernel
sobolGPU_kernel<<<dimGrid, dimBlock>>>(n_vectors, n_dimensions, d_directions,
d_output);
// Execute GPU kernel
sobolGPU_kernel<<<dimGrid, dimBlock>>>(n_vectors, n_dimensions, d_directions, d_output);
}

View File

@@ -57,7 +57,6 @@
#ifndef SOBOL_GPU_H
#define SOBOL_GPU_H
extern "C" void sobolGPU(int n_vectors, int n_dimensions,
unsigned int *d_directions, float *d_output);
extern "C" void sobolGPU(int n_vectors, int n_dimensions, unsigned int *d_directions, float *d_output);
#endif

View File

@@ -65,8 +65,7 @@
// The primitives are based on those generated by Stephen Joe and
// Frances Kuo in the joe-kuo-6.10200 set.
// c.f. http://web.maths.unsw.edu.au/~fkuo/sobol/index.html
const struct primitive sobol_primitives[] =
{
const struct primitive sobol_primitives[] = {
// First dimension is a special case so this entry is actually ignored
{1, 0, 0, {}},
{2, 1, 0, {1}},

View File

@@ -64,11 +64,12 @@
// degree is the degree of the polynomial
// a is a binary word representing the coefficients
// m is the array of m values
struct primitive {
unsigned int dimension;
unsigned int degree;
unsigned int a;
unsigned int m[max_m];
struct primitive
{
unsigned int dimension;
unsigned int degree;
unsigned int a;
unsigned int m[max_m];
};
extern const struct primitive sobol_primitives[];

View File

@@ -34,4 +34,3 @@ Download and install the [CUDA Toolkit 12.5](https://developer.nvidia.com/cuda-d
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
## References (for more details)

File diff suppressed because it is too large Load Diff

View File

@@ -28,101 +28,107 @@
#ifndef _BICUBICTEXTURE_CU_
#define _BICUBICTEXTURE_CU_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <helper_math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// includes, cuda
#include <helper_cuda.h>
typedef unsigned int uint;
typedef unsigned int uint;
typedef unsigned char uchar;
#include "bicubicTexture_kernel.cuh"
cudaArray *d_imageArray = 0;
extern "C" void initTexture(int imageWidth, int imageHeight, uchar *h_data) {
// allocate array and copy image data
cudaChannelFormatDesc channelDesc =
cudaCreateChannelDesc(8, 0, 0, 0, cudaChannelFormatKindUnsigned);
checkCudaErrors(
cudaMallocArray(&d_imageArray, &channelDesc, imageWidth, imageHeight));
checkCudaErrors(cudaMemcpy2DToArray(
d_imageArray, 0, 0, h_data, imageWidth * sizeof(uchar),
imageWidth * sizeof(uchar), imageHeight, cudaMemcpyHostToDevice));
free(h_data);
extern "C" void initTexture(int imageWidth, int imageHeight, uchar *h_data)
{
// allocate array and copy image data
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(8, 0, 0, 0, cudaChannelFormatKindUnsigned);
checkCudaErrors(cudaMallocArray(&d_imageArray, &channelDesc, imageWidth, imageHeight));
checkCudaErrors(cudaMemcpy2DToArray(d_imageArray,
0,
0,
h_data,
imageWidth * sizeof(uchar),
imageWidth * sizeof(uchar),
imageHeight,
cudaMemcpyHostToDevice));
free(h_data);
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeArray;
texRes.res.array.array = d_imageArray;
texRes.resType = cudaResourceTypeArray;
texRes.res.array.array = d_imageArray;
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeClamp;
texDescr.addressMode[1] = cudaAddressModeClamp;
texDescr.readMode = cudaReadModeNormalizedFloat;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeClamp;
texDescr.addressMode[1] = cudaAddressModeClamp;
texDescr.readMode = cudaReadModeNormalizedFloat;
checkCudaErrors(
cudaCreateTextureObject(&texObjLinear, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texObjLinear, &texRes, &texDescr, NULL));
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModePoint;
texDescr.addressMode[0] = cudaAddressModeClamp;
texDescr.addressMode[1] = cudaAddressModeClamp;
texDescr.readMode = cudaReadModeNormalizedFloat;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModePoint;
texDescr.addressMode[0] = cudaAddressModeClamp;
texDescr.addressMode[1] = cudaAddressModeClamp;
texDescr.readMode = cudaReadModeNormalizedFloat;
checkCudaErrors(
cudaCreateTextureObject(&texObjPoint, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texObjPoint, &texRes, &texDescr, NULL));
}
extern "C" void freeTexture() {
checkCudaErrors(cudaDestroyTextureObject(texObjPoint));
checkCudaErrors(cudaDestroyTextureObject(texObjLinear));
checkCudaErrors(cudaFreeArray(d_imageArray));
extern "C" void freeTexture()
{
checkCudaErrors(cudaDestroyTextureObject(texObjPoint));
checkCudaErrors(cudaDestroyTextureObject(texObjLinear));
checkCudaErrors(cudaFreeArray(d_imageArray));
}
// render image using CUDA
extern "C" void render(int width, int height, float tx, float ty, float scale,
float cx, float cy, dim3 blockSize, dim3 gridSize,
int filter_mode, uchar4 *output) {
// call CUDA kernel, writing results to PBO memory
switch (filter_mode) {
extern "C" void render(int width,
int height,
float tx,
float ty,
float scale,
float cx,
float cy,
dim3 blockSize,
dim3 gridSize,
int filter_mode,
uchar4 *output)
{
// call CUDA kernel, writing results to PBO memory
switch (filter_mode) {
case MODE_NEAREST:
d_render<<<gridSize, blockSize>>>(output, width, height, tx, ty, scale,
cx, cy, texObjPoint);
break;
d_render<<<gridSize, blockSize>>>(output, width, height, tx, ty, scale, cx, cy, texObjPoint);
break;
case MODE_BILINEAR:
d_render<<<gridSize, blockSize>>>(output, width, height, tx, ty, scale,
cx, cy, texObjLinear);
break;
d_render<<<gridSize, blockSize>>>(output, width, height, tx, ty, scale, cx, cy, texObjLinear);
break;
case MODE_BICUBIC:
d_renderBicubic<<<gridSize, blockSize>>>(output, width, height, tx, ty,
scale, cx, cy, texObjPoint);
break;
d_renderBicubic<<<gridSize, blockSize>>>(output, width, height, tx, ty, scale, cx, cy, texObjPoint);
break;
case MODE_FAST_BICUBIC:
d_renderFastBicubic<<<gridSize, blockSize>>>(
output, width, height, tx, ty, scale, cx, cy, texObjLinear);
break;
d_renderFastBicubic<<<gridSize, blockSize>>>(output, width, height, tx, ty, scale, cx, cy, texObjLinear);
break;
case MODE_CATROM:
d_renderCatRom<<<gridSize, blockSize>>>(output, width, height, tx, ty,
scale, cx, cy, texObjPoint);
break;
}
d_renderCatRom<<<gridSize, blockSize>>>(output, width, height, tx, ty, scale, cx, cy, texObjPoint);
break;
}
getLastCudaError("kernel failed");
getLastCudaError("kernel failed");
}
#endif

View File

@@ -36,30 +36,27 @@
#ifndef _BICUBICTEXTURE_KERNEL_CUH_
#define _BICUBICTEXTURE_KERNEL_CUH_
enum Mode {
MODE_NEAREST,
MODE_BILINEAR,
MODE_BICUBIC,
MODE_FAST_BICUBIC,
MODE_CATROM
};
enum Mode { MODE_NEAREST, MODE_BILINEAR, MODE_BICUBIC, MODE_FAST_BICUBIC, MODE_CATROM };
cudaTextureObject_t texObjPoint, texObjLinear;
// w0, w1, w2, and w3 are the four cubic B-spline basis functions
__host__ __device__ float w0(float a) {
// return (1.0f/6.0f)*(-a*a*a + 3.0f*a*a - 3.0f*a + 1.0f);
return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f); // optimized
__host__ __device__ float w0(float a)
{
// return (1.0f/6.0f)*(-a*a*a + 3.0f*a*a - 3.0f*a + 1.0f);
return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f); // optimized
}
__host__ __device__ float w1(float a) {
// return (1.0f/6.0f)*(3.0f*a*a*a - 6.0f*a*a + 4.0f);
return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
__host__ __device__ float w1(float a)
{
// return (1.0f/6.0f)*(3.0f*a*a*a - 6.0f*a*a + 4.0f);
return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
}
__host__ __device__ float w2(float a) {
// return (1.0f/6.0f)*(-3.0f*a*a*a + 3.0f*a*a + 3.0f*a + 1.0f);
return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
__host__ __device__ float w2(float a)
{
// return (1.0f/6.0f)*(-3.0f*a*a*a + 3.0f*a*a + 3.0f*a + 1.0f);
return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
}
__host__ __device__ float w3(float a) { return (1.0f / 6.0f) * (a * a * a); }
@@ -70,91 +67,99 @@ __device__ float g0(float a) { return w0(a) + w1(a); }
__device__ float g1(float a) { return w2(a) + w3(a); }
// h0 and h1 are the two offset functions
__device__ float h0(float a) {
// note +0.5 offset to compensate for CUDA linear filtering convention
return -1.0f + w1(a) / (w0(a) + w1(a)) + 0.5f;
__device__ float h0(float a)
{
// note +0.5 offset to compensate for CUDA linear filtering convention
return -1.0f + w1(a) / (w0(a) + w1(a)) + 0.5f;
}
__device__ float h1(float a) { return 1.0f + w3(a) / (w2(a) + w3(a)) + 0.5f; }
// filter 4 values using cubic splines
template <class T>
__device__ T cubicFilter(float x, T c0, T c1, T c2, T c3) {
T r;
r = c0 * w0(x);
r += c1 * w1(x);
r += c2 * w2(x);
r += c3 * w3(x);
return r;
template <class T> __device__ T cubicFilter(float x, T c0, T c1, T c2, T c3)
{
T r;
r = c0 * w0(x);
r += c1 * w1(x);
r += c2 * w2(x);
r += c3 * w3(x);
return r;
}
// slow but precise bicubic lookup using 16 texture lookups
template <class T, class R> // texture data type, return type
__device__ R tex2DBicubic(const cudaTextureObject_t tex, float x, float y) {
x -= 0.5f;
y -= 0.5f;
float px = floorf(x);
float py = floorf(y);
float fx = x - px;
float fy = y - py;
template <class T, class R> // texture data type, return type
__device__ R tex2DBicubic(const cudaTextureObject_t tex, float x, float y)
{
x -= 0.5f;
y -= 0.5f;
float px = floorf(x);
float py = floorf(y);
float fx = x - px;
float fy = y - py;
return cubicFilter<R>(
fy, cubicFilter<R>(
fx, tex2D<R>(tex, px - 1, py - 1), tex2D<R>(tex, px, py - 1),
tex2D<R>(tex, px + 1, py - 1), tex2D<R>(tex, px + 2, py - 1)),
cubicFilter<R>(fx, tex2D<R>(tex, px - 1, py), tex2D<R>(tex, px, py),
tex2D<R>(tex, px + 1, py), tex2D<R>(tex, px + 2, py)),
cubicFilter<R>(fx, tex2D<R>(tex, px - 1, py + 1),
tex2D<R>(tex, px, py + 1), tex2D<R>(tex, px + 1, py + 1),
tex2D<R>(tex, px + 2, py + 1)),
cubicFilter<R>(fx, tex2D<R>(tex, px - 1, py + 2),
tex2D<R>(tex, px, py + 2), tex2D<R>(tex, px + 1, py + 2),
tex2D<R>(tex, px + 2, py + 2)));
return cubicFilter<R>(
fy,
cubicFilter<R>(fx,
tex2D<R>(tex, px - 1, py - 1),
tex2D<R>(tex, px, py - 1),
tex2D<R>(tex, px + 1, py - 1),
tex2D<R>(tex, px + 2, py - 1)),
cubicFilter<R>(
fx, tex2D<R>(tex, px - 1, py), tex2D<R>(tex, px, py), tex2D<R>(tex, px + 1, py), tex2D<R>(tex, px + 2, py)),
cubicFilter<R>(fx,
tex2D<R>(tex, px - 1, py + 1),
tex2D<R>(tex, px, py + 1),
tex2D<R>(tex, px + 1, py + 1),
tex2D<R>(tex, px + 2, py + 1)),
cubicFilter<R>(fx,
tex2D<R>(tex, px - 1, py + 2),
tex2D<R>(tex, px, py + 2),
tex2D<R>(tex, px + 1, py + 2),
tex2D<R>(tex, px + 2, py + 2)));
}
// fast bicubic texture lookup using 4 bilinear lookups
// assumes texture is set to non-normalized coordinates, point sampling
template <class T, class R> // texture data type, return type
__device__ R tex2DFastBicubic(const cudaTextureObject_t tex, float x, float y) {
x -= 0.5f;
y -= 0.5f;
float px = floorf(x);
float py = floorf(y);
float fx = x - px;
float fy = y - py;
template <class T, class R> // texture data type, return type
__device__ R tex2DFastBicubic(const cudaTextureObject_t tex, float x, float y)
{
x -= 0.5f;
y -= 0.5f;
float px = floorf(x);
float py = floorf(y);
float fx = x - px;
float fy = y - py;
// note: we could store these functions in a lookup table texture, but maths
// is cheap
float g0x = g0(fx);
float g1x = g1(fx);
float h0x = h0(fx);
float h1x = h1(fx);
float h0y = h0(fy);
float h1y = h1(fy);
// note: we could store these functions in a lookup table texture, but maths
// is cheap
float g0x = g0(fx);
float g1x = g1(fx);
float h0x = h0(fx);
float h1x = h1(fx);
float h0y = h0(fy);
float h1y = h1(fy);
R r = g0(fy) * (g0x * tex2D<R>(tex, px + h0x, py + h0y) +
g1x * tex2D<R>(tex, px + h1x, py + h0y)) +
g1(fy) * (g0x * tex2D<R>(tex, px + h0x, py + h1y) +
g1x * tex2D<R>(tex, px + h1x, py + h1y));
return r;
R r = g0(fy) * (g0x * tex2D<R>(tex, px + h0x, py + h0y) + g1x * tex2D<R>(tex, px + h1x, py + h0y))
+ g1(fy) * (g0x * tex2D<R>(tex, px + h0x, py + h1y) + g1x * tex2D<R>(tex, px + h1x, py + h1y));
return r;
}
// higher-precision 2D bilinear lookup
template <class T, class R> // texture data type, return type
__device__ R tex2DBilinear(const cudaTextureObject_t tex, float x, float y) {
x -= 0.5f;
y -= 0.5f;
float px = floorf(x); // integer position
float py = floorf(y);
float fx = x - px; // fractional position
float fy = y - py;
px += 0.5f;
py += 0.5f;
template <class T, class R> // texture data type, return type
__device__ R tex2DBilinear(const cudaTextureObject_t tex, float x, float y)
{
x -= 0.5f;
y -= 0.5f;
float px = floorf(x); // integer position
float py = floorf(y);
float fx = x - px; // fractional position
float fy = y - py;
px += 0.5f;
py += 0.5f;
return lerp(lerp(tex2D<R>(tex, px, py), tex2D<R>(tex, px + 1.0f, py), fx),
lerp(tex2D<R>(tex, px, py + 1.0f),
tex2D<R>(tex, px + 1.0f, py + 1.0f), fx),
fy);
return lerp(lerp(tex2D<R>(tex, px, py), tex2D<R>(tex, px + 1.0f, py), fx),
lerp(tex2D<R>(tex, px, py + 1.0f), tex2D<R>(tex, px + 1.0f, py + 1.0f), fx),
fy);
}
#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 200
@@ -174,154 +179,193 @@ __device__ R tex2DBilinear(const cudaTextureObject_t tex, float x, float y) {
w: (0, 0)
*/
template <class T, class R> // texture data type, return type
__device__ float tex2DBilinearGather(const cudaTextureObject_t tex, float x,
float y, int comp = 0) {
x -= 0.5f;
y -= 0.5f;
float px = floorf(x); // integer position
float py = floorf(y);
float fx = x - px; // fractional position
float fy = y - py;
template <class T, class R> // texture data type, return type
__device__ float tex2DBilinearGather(const cudaTextureObject_t tex, float x, float y, int comp = 0)
{
x -= 0.5f;
y -= 0.5f;
float px = floorf(x); // integer position
float py = floorf(y);
float fx = x - px; // fractional position
float fy = y - py;
R samples = tex2Dgather<R>(tex, px + 0.5f, py + 0.5f, comp);
R samples = tex2Dgather<R>(tex, px + 0.5f, py + 0.5f, comp);
return lerp(lerp((float)samples.w, (float)samples.z, fx),
lerp((float)samples.x, (float)samples.y, fx), fy);
return lerp(lerp((float)samples.w, (float)samples.z, fx), lerp((float)samples.x, (float)samples.y, fx), fy);
}
#endif
// Catmull-Rom interpolation
__host__ __device__ float catrom_w0(float a) {
// return -0.5f*a + a*a - 0.5f*a*a*a;
return a * (-0.5f + a * (1.0f - 0.5f * a));
__host__ __device__ float catrom_w0(float a)
{
// return -0.5f*a + a*a - 0.5f*a*a*a;
return a * (-0.5f + a * (1.0f - 0.5f * a));
}
__host__ __device__ float catrom_w1(float a) {
// return 1.0f - 2.5f*a*a + 1.5f*a*a*a;
return 1.0f + a * a * (-2.5f + 1.5f * a);
__host__ __device__ float catrom_w1(float a)
{
// return 1.0f - 2.5f*a*a + 1.5f*a*a*a;
return 1.0f + a * a * (-2.5f + 1.5f * a);
}
__host__ __device__ float catrom_w2(float a) {
// return 0.5f*a + 2.0f*a*a - 1.5f*a*a*a;
return a * (0.5f + a * (2.0f - 1.5f * a));
__host__ __device__ float catrom_w2(float a)
{
// return 0.5f*a + 2.0f*a*a - 1.5f*a*a*a;
return a * (0.5f + a * (2.0f - 1.5f * a));
}
__host__ __device__ float catrom_w3(float a) {
// return -0.5f*a*a + 0.5f*a*a*a;
return a * a * (-0.5f + 0.5f * a);
__host__ __device__ float catrom_w3(float a)
{
// return -0.5f*a*a + 0.5f*a*a*a;
return a * a * (-0.5f + 0.5f * a);
}
template <class T>
__device__ T catRomFilter(float x, T c0, T c1, T c2, T c3) {
T r;
r = c0 * catrom_w0(x);
r += c1 * catrom_w1(x);
r += c2 * catrom_w2(x);
r += c3 * catrom_w3(x);
return r;
template <class T> __device__ T catRomFilter(float x, T c0, T c1, T c2, T c3)
{
T r;
r = c0 * catrom_w0(x);
r += c1 * catrom_w1(x);
r += c2 * catrom_w2(x);
r += c3 * catrom_w3(x);
return r;
}
// Note - can't use bilinear trick here because of negative lobes
template <class T, class R> // texture data type, return type
__device__ R tex2DCatRom(const cudaTextureObject_t tex, float x, float y) {
x -= 0.5f;
y -= 0.5f;
float px = floorf(x);
float py = floorf(y);
float fx = x - px;
float fy = y - py;
template <class T, class R> // texture data type, return type
__device__ R tex2DCatRom(const cudaTextureObject_t tex, float x, float y)
{
x -= 0.5f;
y -= 0.5f;
float px = floorf(x);
float py = floorf(y);
float fx = x - px;
float fy = y - py;
return catRomFilter<R>(
fy, catRomFilter<R>(
fx, tex2D<R>(tex, px - 1, py - 1), tex2D<R>(tex, px, py - 1),
tex2D<R>(tex, px + 1, py - 1), tex2D<R>(tex, px + 2, py - 1)),
catRomFilter<R>(fx, tex2D<R>(tex, px - 1, py), tex2D<R>(tex, px, py),
tex2D<R>(tex, px + 1, py), tex2D<R>(tex, px + 2, py)),
catRomFilter<R>(fx, tex2D<R>(tex, px - 1, py + 1),
tex2D<R>(tex, px, py + 1), tex2D<R>(tex, px + 1, py + 1),
tex2D<R>(tex, px + 2, py + 1)),
catRomFilter<R>(fx, tex2D<R>(tex, px - 1, py + 2),
tex2D<R>(tex, px, py + 2), tex2D<R>(tex, px + 1, py + 2),
tex2D<R>(tex, px + 2, py + 2)));
return catRomFilter<R>(
fy,
catRomFilter<R>(fx,
tex2D<R>(tex, px - 1, py - 1),
tex2D<R>(tex, px, py - 1),
tex2D<R>(tex, px + 1, py - 1),
tex2D<R>(tex, px + 2, py - 1)),
catRomFilter<R>(
fx, tex2D<R>(tex, px - 1, py), tex2D<R>(tex, px, py), tex2D<R>(tex, px + 1, py), tex2D<R>(tex, px + 2, py)),
catRomFilter<R>(fx,
tex2D<R>(tex, px - 1, py + 1),
tex2D<R>(tex, px, py + 1),
tex2D<R>(tex, px + 1, py + 1),
tex2D<R>(tex, px + 2, py + 1)),
catRomFilter<R>(fx,
tex2D<R>(tex, px - 1, py + 2),
tex2D<R>(tex, px, py + 2),
tex2D<R>(tex, px + 1, py + 2),
tex2D<R>(tex, px + 2, py + 2)));
}
// test functions
// render image using normal bilinear texture lookup
__global__ void d_render(uchar4 *d_output, uint width, uint height, float tx,
float ty, float scale, float cx, float cy,
cudaTextureObject_t texObj) {
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
__global__ void d_render(uchar4 *d_output,
uint width,
uint height,
float tx,
float ty,
float scale,
float cx,
float cy,
cudaTextureObject_t texObj)
{
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
if ((x < width) && (y < height)) {
// write output color
float c = tex2D<float>(texObj, u, v);
// float c = tex2DBilinear<uchar, float>(tex, u, v);
// float c = tex2DBilinearGather<uchar, uchar4>(tex2, u, v, 0) / 255.0f;
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
if ((x < width) && (y < height)) {
// write output color
float c = tex2D<float>(texObj, u, v);
// float c = tex2DBilinear<uchar, float>(tex, u, v);
// float c = tex2DBilinearGather<uchar, uchar4>(tex2, u, v, 0) / 255.0f;
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
}
// render image using bicubic texture lookup
__global__ void d_renderBicubic(uchar4 *d_output, uint width, uint height,
float tx, float ty, float scale, float cx,
float cy, cudaTextureObject_t texObj) {
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
__global__ void d_renderBicubic(uchar4 *d_output,
uint width,
uint height,
float tx,
float ty,
float scale,
float cx,
float cy,
cudaTextureObject_t texObj)
{
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
if ((x < width) && (y < height)) {
// write output color
float c = tex2DBicubic<uchar, float>(texObj, u, v);
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
if ((x < width) && (y < height)) {
// write output color
float c = tex2DBicubic<uchar, float>(texObj, u, v);
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
}
// render image using fast bicubic texture lookup
__global__ void d_renderFastBicubic(uchar4 *d_output, uint width, uint height,
float tx, float ty, float scale, float cx,
float cy, cudaTextureObject_t texObj) {
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
__global__ void d_renderFastBicubic(uchar4 *d_output,
uint width,
uint height,
float tx,
float ty,
float scale,
float cx,
float cy,
cudaTextureObject_t texObj)
{
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
if ((x < width) && (y < height)) {
// write output color
float c = tex2DFastBicubic<uchar, float>(texObj, u, v);
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
if ((x < width) && (y < height)) {
// write output color
float c = tex2DFastBicubic<uchar, float>(texObj, u, v);
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
}
// render image using Catmull-Rom texture lookup
__global__ void d_renderCatRom(uchar4 *d_output, uint width, uint height,
float tx, float ty, float scale, float cx,
float cy, cudaTextureObject_t texObj) {
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
__global__ void d_renderCatRom(uchar4 *d_output,
uint width,
uint height,
float tx,
float ty,
float scale,
float cx,
float cy,
cudaTextureObject_t texObj)
{
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint i = __umul24(y, width) + x;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
float u = (x - cx) * scale + cx + tx;
float v = (y - cy) * scale + cy + ty;
if ((x < width) && (y < height)) {
// write output color
float c = tex2DCatRom<uchar, float>(texObj, u, v);
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
if ((x < width) && (y < height)) {
// write output color
float c = tex2DCatRom<uchar, float>(texObj, u, v);
d_output[i] = make_uchar4(c * 0xff, c * 0xff, c * 0xff, 0);
}
}
#endif // _BICUBICTEXTURE_KERNEL_CUH_
#endif // _BICUBICTEXTURE_KERNEL_CUH_

View File

@@ -34,4 +34,3 @@ Download and install the [CUDA Toolkit 12.5](https://developer.nvidia.com/cuda-d
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
## References (for more details)

File diff suppressed because it is too large Load Diff

View File

@@ -32,122 +32,129 @@
// export C interface
#define EPSILON 1e-3
extern "C" void updateGaussianGold(float delta, int radius);
extern "C" void bilateralFilterGold(unsigned int *pSrc, unsigned int *pDest,
float e_d, int w, int h, int r);
extern "C" void bilateralFilterGold(unsigned int *pSrc, unsigned int *pDest, float e_d, int w, int h, int r);
// variables
float gaussian[50];
struct float4 {
float x;
float y;
float z;
float w;
struct float4
{
float x;
float y;
float z;
float w;
float4(){};
float4(float value) { x = y = z = w = value; }
float4() {};
float4(float value) { x = y = z = w = value; }
};
void updateGaussianGold(float delta, int radius) {
for (int i = 0; i < 2 * radius + 1; i++) {
int x = i - radius;
gaussian[i] = expf(-(x * x) / (2 * delta * delta));
}
}
float heuclideanLen(float4 a, float4 b, float d) {
float mod = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) +
(b.z - a.z) * (b.z - a.z) + (b.w - a.w) * (b.w - a.w);
return expf(-mod / (2 * d * d));
}
unsigned int hrgbaFloatToInt(float4 rgba) {
unsigned int w = (((unsigned int)(fabs(rgba.w) * 255.0f)) & 0xff) << 24;
unsigned int z = (((unsigned int)(fabs(rgba.z) * 255.0f)) & 0xff) << 16;
unsigned int y = (((unsigned int)(fabs(rgba.y) * 255.0f)) & 0xff) << 8;
unsigned int x = ((unsigned int)(fabs(rgba.x) * 255.0f)) & 0xff;
return (w | z | y | x);
}
float4 hrgbaIntToFloat(unsigned int c) {
float4 rgba;
rgba.x = (c & 0xff) * 0.003921568627f; // /255.0f;
rgba.y = ((c >> 8) & 0xff) * 0.003921568627f; // /255.0f;
rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; // /255.0f;
rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; // /255.0f;
return rgba;
}
float4 mul(float a, float4 b) {
float4 ans;
ans.x = a * b.x;
ans.y = a * b.y;
ans.z = a * b.z;
ans.w = a * b.w;
return ans;
}
float4 add4(float4 a, float4 b) {
float4 ans;
ans.x = a.x + b.x;
ans.y = a.y + b.y;
ans.z = a.z + b.z;
ans.w = a.w + b.w;
return ans;
}
void bilateralFilterGold(unsigned int *pSrc, unsigned int *pDest, float e_d,
int w, int h, int r) {
float4 *hImage = new float4[w * h];
float domainDist, colorDist, factor;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
hImage[y * w + x] = hrgbaIntToFloat(pSrc[y * w + x]);
void updateGaussianGold(float delta, int radius)
{
for (int i = 0; i < 2 * radius + 1; i++) {
int x = i - radius;
gaussian[i] = expf(-(x * x) / (2 * delta * delta));
}
}
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
float4 t(0.0f);
float sum = 0.0f;
for (int i = -r; i <= r; i++) {
int neighborY = y + i;
// clamp the neighbor pixel, prevent overflow
if (neighborY < 0) {
neighborY = 0;
} else if (neighborY >= h) {
neighborY = h - 1;
}
for (int j = -r; j <= r; j++) {
domainDist = gaussian[r + i] * gaussian[r + j];
// clamp the neighbor pixel, prevent overflow
int neighborX = x + j;
if (neighborX < 0) {
neighborX = 0;
} else if (neighborX >= w) {
neighborX = w - 1;
}
colorDist = heuclideanLen(hImage[neighborY * w + neighborX],
hImage[y * w + x], e_d);
factor = domainDist * colorDist;
sum += factor;
t = add4(t, mul(factor, hImage[neighborY * w + neighborX]));
}
}
pDest[y * w + x] = hrgbaFloatToInt(mul(1 / sum, t));
}
}
delete[] hImage;
}
float heuclideanLen(float4 a, float4 b, float d)
{
float mod =
(b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) + (b.z - a.z) * (b.z - a.z) + (b.w - a.w) * (b.w - a.w);
return expf(-mod / (2 * d * d));
}
unsigned int hrgbaFloatToInt(float4 rgba)
{
unsigned int w = (((unsigned int)(fabs(rgba.w) * 255.0f)) & 0xff) << 24;
unsigned int z = (((unsigned int)(fabs(rgba.z) * 255.0f)) & 0xff) << 16;
unsigned int y = (((unsigned int)(fabs(rgba.y) * 255.0f)) & 0xff) << 8;
unsigned int x = ((unsigned int)(fabs(rgba.x) * 255.0f)) & 0xff;
return (w | z | y | x);
}
float4 hrgbaIntToFloat(unsigned int c)
{
float4 rgba;
rgba.x = (c & 0xff) * 0.003921568627f; // /255.0f;
rgba.y = ((c >> 8) & 0xff) * 0.003921568627f; // /255.0f;
rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; // /255.0f;
rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; // /255.0f;
return rgba;
}
float4 mul(float a, float4 b)
{
float4 ans;
ans.x = a * b.x;
ans.y = a * b.y;
ans.z = a * b.z;
ans.w = a * b.w;
return ans;
}
float4 add4(float4 a, float4 b)
{
float4 ans;
ans.x = a.x + b.x;
ans.y = a.y + b.y;
ans.z = a.z + b.z;
ans.w = a.w + b.w;
return ans;
}
void bilateralFilterGold(unsigned int *pSrc, unsigned int *pDest, float e_d, int w, int h, int r)
{
float4 *hImage = new float4[w * h];
float domainDist, colorDist, factor;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
hImage[y * w + x] = hrgbaIntToFloat(pSrc[y * w + x]);
}
}
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
float4 t(0.0f);
float sum = 0.0f;
for (int i = -r; i <= r; i++) {
int neighborY = y + i;
// clamp the neighbor pixel, prevent overflow
if (neighborY < 0) {
neighborY = 0;
}
else if (neighborY >= h) {
neighborY = h - 1;
}
for (int j = -r; j <= r; j++) {
domainDist = gaussian[r + i] * gaussian[r + j];
// clamp the neighbor pixel, prevent overflow
int neighborX = x + j;
if (neighborX < 0) {
neighborX = 0;
}
else if (neighborX >= w) {
neighborX = w - 1;
}
colorDist = heuclideanLen(hImage[neighborY * w + neighborX], hImage[y * w + x], e_d);
factor = domainDist * colorDist;
sum += factor;
t = add4(t, mul(factor, hImage[neighborY * w + neighborX]));
}
}
pDest[y * w + x] = hrgbaFloatToInt(mul(1 / sum, t));
}
}
delete[] hImage;
}

View File

@@ -25,17 +25,17 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <helper_math.h>
#include <helper_cuda.h> // CUDA device initialization helper functions
#include <helper_functions.h>
#include <helper_cuda.h> // CUDA device initialization helper functions
#include <helper_math.h>
__constant__ float cGaussian[64]; // gaussian array in device side
__constant__ float cGaussian[64]; // gaussian array in device side
cudaTextureObject_t rgbaTexdImage;
cudaTextureObject_t rgbaTexdTemp;
uint *dImage = NULL; // original image
uint *dTemp = NULL; // temp array for iterations
uint *dImage = NULL; // original image
uint *dTemp = NULL; // temp array for iterations
size_t pitch;
/*
@@ -69,118 +69,117 @@ size_t pitch;
*/
// Euclidean Distance (x, y, d) = exp((|x - y| / d)^2 / 2)
__device__ float euclideanLen(float4 a, float4 b, float d) {
float mod = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) +
(b.z - a.z) * (b.z - a.z);
__device__ float euclideanLen(float4 a, float4 b, float d)
{
float mod = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) + (b.z - a.z) * (b.z - a.z);
return __expf(-mod / (2.f * d * d));
return __expf(-mod / (2.f * d * d));
}
__device__ uint rgbaFloatToInt(float4 rgba) {
rgba.x = __saturatef(fabs(rgba.x)); // clamp to [0.0, 1.0]
rgba.y = __saturatef(fabs(rgba.y));
rgba.z = __saturatef(fabs(rgba.z));
rgba.w = __saturatef(fabs(rgba.w));
return (uint(rgba.w * 255.0f) << 24) | (uint(rgba.z * 255.0f) << 16) |
(uint(rgba.y * 255.0f) << 8) | uint(rgba.x * 255.0f);
__device__ uint rgbaFloatToInt(float4 rgba)
{
rgba.x = __saturatef(fabs(rgba.x)); // clamp to [0.0, 1.0]
rgba.y = __saturatef(fabs(rgba.y));
rgba.z = __saturatef(fabs(rgba.z));
rgba.w = __saturatef(fabs(rgba.w));
return (uint(rgba.w * 255.0f) << 24) | (uint(rgba.z * 255.0f) << 16) | (uint(rgba.y * 255.0f) << 8)
| uint(rgba.x * 255.0f);
}
__device__ float4 rgbaIntToFloat(uint c) {
float4 rgba;
rgba.x = (c & 0xff) * 0.003921568627f; // /255.0f;
rgba.y = ((c >> 8) & 0xff) * 0.003921568627f; // /255.0f;
rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; // /255.0f;
rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; // /255.0f;
return rgba;
__device__ float4 rgbaIntToFloat(uint c)
{
float4 rgba;
rgba.x = (c & 0xff) * 0.003921568627f; // /255.0f;
rgba.y = ((c >> 8) & 0xff) * 0.003921568627f; // /255.0f;
rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; // /255.0f;
rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; // /255.0f;
return rgba;
}
// column pass using coalesced global memory reads
__global__ void d_bilateral_filter(uint *od, int w, int h, float e_d, int r,
cudaTextureObject_t rgbaTex) {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
__global__ void d_bilateral_filter(uint *od, int w, int h, float e_d, int r, cudaTextureObject_t rgbaTex)
{
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if (x >= w || y >= h) {
return;
}
float sum = 0.0f;
float factor;
float4 t = {0.f, 0.f, 0.f, 0.f};
float4 center = tex2D<float4>(rgbaTex, x, y);
for (int i = -r; i <= r; i++) {
for (int j = -r; j <= r; j++) {
float4 curPix = tex2D<float4>(rgbaTex, x + j, y + i);
factor = cGaussian[i + r] * cGaussian[j + r] * // domain factor
euclideanLen(curPix, center, e_d); // range factor
t += factor * curPix;
sum += factor;
if (x >= w || y >= h) {
return;
}
}
od[y * w + x] = rgbaFloatToInt(t / sum);
float sum = 0.0f;
float factor;
float4 t = {0.f, 0.f, 0.f, 0.f};
float4 center = tex2D<float4>(rgbaTex, x, y);
for (int i = -r; i <= r; i++) {
for (int j = -r; j <= r; j++) {
float4 curPix = tex2D<float4>(rgbaTex, x + j, y + i);
factor = cGaussian[i + r] * cGaussian[j + r] * // domain factor
euclideanLen(curPix, center, e_d); // range factor
t += factor * curPix;
sum += factor;
}
}
od[y * w + x] = rgbaFloatToInt(t / sum);
}
extern "C" void initTexture(int width, int height, uint *hImage) {
// copy image data to array
checkCudaErrors(
cudaMallocPitch(&dImage, &pitch, sizeof(uint) * width, height));
checkCudaErrors(
cudaMallocPitch(&dTemp, &pitch, sizeof(uint) * width, height));
checkCudaErrors(cudaMemcpy2D(dImage, pitch, hImage, sizeof(uint) * width,
sizeof(uint) * width, height,
cudaMemcpyHostToDevice));
extern "C" void initTexture(int width, int height, uint *hImage)
{
// copy image data to array
checkCudaErrors(cudaMallocPitch(&dImage, &pitch, sizeof(uint) * width, height));
checkCudaErrors(cudaMallocPitch(&dTemp, &pitch, sizeof(uint) * width, height));
checkCudaErrors(cudaMemcpy2D(
dImage, pitch, hImage, sizeof(uint) * width, sizeof(uint) * width, height, cudaMemcpyHostToDevice));
// texture<uchar4, 2, cudaReadModeNormalizedFloat> rgbaTex;
cudaChannelFormatDesc desc = cudaCreateChannelDesc<uchar4>();
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
// texture<uchar4, 2, cudaReadModeNormalizedFloat> rgbaTex;
cudaChannelFormatDesc desc = cudaCreateChannelDesc<uchar4>();
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = dImage;
texRes.res.pitch2D.desc = desc;
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = pitch;
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = dImage;
texRes.res.pitch2D.desc = desc;
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = pitch;
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModePoint;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.addressMode[1] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeNormalizedFloat;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModePoint;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.addressMode[1] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeNormalizedFloat;
checkCudaErrors(
cudaCreateTextureObject(&rgbaTexdImage, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&rgbaTexdImage, &texRes, &texDescr, NULL));
memset(&texRes, 0, sizeof(cudaResourceDesc));
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = dTemp;
texRes.res.pitch2D.desc = desc;
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = pitch;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texRes.resType = cudaResourceTypePitch2D;
texRes.res.pitch2D.devPtr = dTemp;
texRes.res.pitch2D.desc = desc;
texRes.res.pitch2D.width = width;
texRes.res.pitch2D.height = height;
texRes.res.pitch2D.pitchInBytes = pitch;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.addressMode[1] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeNormalizedFloat;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.addressMode[1] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeNormalizedFloat;
checkCudaErrors(
cudaCreateTextureObject(&rgbaTexdTemp, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&rgbaTexdTemp, &texRes, &texDescr, NULL));
}
extern "C" void freeTextures() {
checkCudaErrors(cudaDestroyTextureObject(rgbaTexdImage));
checkCudaErrors(cudaDestroyTextureObject(rgbaTexdTemp));
checkCudaErrors(cudaFree(dImage));
checkCudaErrors(cudaFree(dTemp));
extern "C" void freeTextures()
{
checkCudaErrors(cudaDestroyTextureObject(rgbaTexdImage));
checkCudaErrors(cudaDestroyTextureObject(rgbaTexdTemp));
checkCudaErrors(cudaFree(dImage));
checkCudaErrors(cudaFree(dTemp));
}
/*
@@ -197,16 +196,16 @@ extern "C" void freeTextures() {
radius - half of the filter size
(total filter size = 2 * radius + 1)
*/
extern "C" void updateGaussian(float delta, int radius) {
float fGaussian[64];
extern "C" void updateGaussian(float delta, int radius)
{
float fGaussian[64];
for (int i = 0; i < 2 * radius + 1; ++i) {
float x = (float)(i - radius);
fGaussian[i] = expf(-(x * x) / (2 * delta * delta));
}
for (int i = 0; i < 2 * radius + 1; ++i) {
float x = (float)(i - radius);
fGaussian[i] = expf(-(x * x) / (2 * delta * delta));
}
checkCudaErrors(cudaMemcpyToSymbol(cGaussian, fGaussian,
sizeof(float) * (2 * radius + 1)));
checkCudaErrors(cudaMemcpyToSymbol(cGaussian, fGaussian, sizeof(float) * (2 * radius + 1)));
}
/*
@@ -222,40 +221,43 @@ extern "C" void updateGaussian(float delta, int radius) {
*/
// RGBA version
extern "C" double bilateralFilterRGBA(uint *dDest, int width, int height,
float e_d, int radius, int iterations,
StopWatchInterface *timer) {
// var for kernel computation timing
double dKernelTime;
extern "C" double bilateralFilterRGBA(uint *dDest,
int width,
int height,
float e_d,
int radius,
int iterations,
StopWatchInterface *timer)
{
// var for kernel computation timing
double dKernelTime;
for (int i = 0; i < iterations; i++) {
// sync host and start kernel computation timer
dKernelTime = 0.0;
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&timer);
for (int i = 0; i < iterations; i++) {
// sync host and start kernel computation timer
dKernelTime = 0.0;
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&timer);
dim3 gridSize((width + 16 - 1) / 16, (height + 16 - 1) / 16);
dim3 blockSize(16, 16);
dim3 gridSize((width + 16 - 1) / 16, (height + 16 - 1) / 16);
dim3 blockSize(16, 16);
if (iterations > 1) {
d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d,
radius, rgbaTexdTemp);
} else {
d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d,
radius, rgbaTexdImage);
if (iterations > 1) {
d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d, radius, rgbaTexdTemp);
}
else {
d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d, radius, rgbaTexdImage);
}
// sync host and stop computation timer
checkCudaErrors(cudaDeviceSynchronize());
dKernelTime += sdkGetTimerValue(&timer);
if (iterations > 1) {
// copy result back from global memory to array
checkCudaErrors(cudaMemcpy2D(
dTemp, pitch, dDest, sizeof(int) * width, sizeof(int) * width, height, cudaMemcpyDeviceToDevice));
}
}
// sync host and stop computation timer
checkCudaErrors(cudaDeviceSynchronize());
dKernelTime += sdkGetTimerValue(&timer);
if (iterations > 1) {
// copy result back from global memory to array
checkCudaErrors(cudaMemcpy2D(dTemp, pitch, dDest, sizeof(int) * width,
sizeof(int) * width, height,
cudaMemcpyDeviceToDevice));
}
}
return ((dKernelTime / 1000.) / (double)iterations);
return ((dKernelTime / 1000.) / (double)iterations);
}

View File

@@ -29,104 +29,110 @@
#include <stdlib.h>
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
#pragma warning(disable : 4996) // disable deprecated warning
#pragma warning(disable : 4996) // disable deprecated warning
#endif
#pragma pack(1)
typedef struct {
short type;
int size;
short reserved1;
short reserved2;
int offset;
typedef struct
{
short type;
int size;
short reserved1;
short reserved2;
int offset;
} BMPHeader;
typedef struct {
int size;
int width;
int height;
short planes;
short bitsPerPixel;
unsigned compression;
unsigned imageSize;
int xPelsPerMeter;
int yPelsPerMeter;
int clrUsed;
int clrImportant;
typedef struct
{
int size;
int width;
int height;
short planes;
short bitsPerPixel;
unsigned compression;
unsigned imageSize;
int xPelsPerMeter;
int yPelsPerMeter;
int clrUsed;
int clrImportant;
} BMPInfoHeader;
// Isolated definition
typedef struct { unsigned char x, y, z, w; } uchar4;
typedef struct
{
unsigned char x, y, z, w;
} uchar4;
extern "C" void LoadBMPFile(uchar4 **dst, unsigned int *width,
unsigned int *height, const char *name) {
BMPHeader hdr;
BMPInfoHeader infoHdr;
int x, y;
extern "C" void LoadBMPFile(uchar4 **dst, unsigned int *width, unsigned int *height, const char *name)
{
BMPHeader hdr;
BMPInfoHeader infoHdr;
int x, y;
FILE *fd;
FILE *fd;
printf("Loading %s...\n", name);
printf("Loading %s...\n", name);
if (sizeof(uchar4) != 4) {
printf("***Bad uchar4 size***\n");
exit(EXIT_SUCCESS);
}
if (!(fd = fopen(name, "rb"))) {
printf("***BMP load error: file access denied***\n");
exit(EXIT_SUCCESS);
}
fread(&hdr, sizeof(hdr), 1, fd);
if (hdr.type != 0x4D42) {
printf("***BMP load error: bad file format***\n");
exit(EXIT_SUCCESS);
}
fread(&infoHdr, sizeof(infoHdr), 1, fd);
if (infoHdr.bitsPerPixel != 24) {
printf("***BMP load error: invalid color depth***\n");
exit(EXIT_SUCCESS);
}
if (infoHdr.compression) {
printf("***BMP load error: compressed image***\n");
exit(EXIT_SUCCESS);
}
*width = infoHdr.width;
*height = infoHdr.height;
*dst = (uchar4 *)malloc(*width * *height * 4);
printf("BMP width: %u\n", infoHdr.width);
printf("BMP height: %u\n", infoHdr.height);
fseek(fd, hdr.offset - sizeof(hdr) - sizeof(infoHdr), SEEK_CUR);
for (y = 0; y < infoHdr.height; y++) {
for (x = 0; x < infoHdr.width; x++) {
(*dst)[(y * infoHdr.width + x)].w = 0;
(*dst)[(y * infoHdr.width + x)].z = fgetc(fd);
(*dst)[(y * infoHdr.width + x)].y = fgetc(fd);
(*dst)[(y * infoHdr.width + x)].x = fgetc(fd);
if (sizeof(uchar4) != 4) {
printf("***Bad uchar4 size***\n");
exit(EXIT_SUCCESS);
}
for (x = 0; x < (4 - (3 * infoHdr.width) % 4) % 4; x++) {
fgetc(fd);
if (!(fd = fopen(name, "rb"))) {
printf("***BMP load error: file access denied***\n");
exit(EXIT_SUCCESS);
}
}
if (ferror(fd)) {
printf("***Unknown BMP load error.***\n");
free(*dst);
exit(EXIT_SUCCESS);
} else {
printf("BMP file loaded successfully!\n");
}
fread(&hdr, sizeof(hdr), 1, fd);
fclose(fd);
if (hdr.type != 0x4D42) {
printf("***BMP load error: bad file format***\n");
exit(EXIT_SUCCESS);
}
fread(&infoHdr, sizeof(infoHdr), 1, fd);
if (infoHdr.bitsPerPixel != 24) {
printf("***BMP load error: invalid color depth***\n");
exit(EXIT_SUCCESS);
}
if (infoHdr.compression) {
printf("***BMP load error: compressed image***\n");
exit(EXIT_SUCCESS);
}
*width = infoHdr.width;
*height = infoHdr.height;
*dst = (uchar4 *)malloc(*width * *height * 4);
printf("BMP width: %u\n", infoHdr.width);
printf("BMP height: %u\n", infoHdr.height);
fseek(fd, hdr.offset - sizeof(hdr) - sizeof(infoHdr), SEEK_CUR);
for (y = 0; y < infoHdr.height; y++) {
for (x = 0; x < infoHdr.width; x++) {
(*dst)[(y * infoHdr.width + x)].w = 0;
(*dst)[(y * infoHdr.width + x)].z = fgetc(fd);
(*dst)[(y * infoHdr.width + x)].y = fgetc(fd);
(*dst)[(y * infoHdr.width + x)].x = fgetc(fd);
}
for (x = 0; x < (4 - (3 * infoHdr.width) % 4) % 4; x++) {
fgetc(fd);
}
}
if (ferror(fd)) {
printf("***Unknown BMP load error.***\n");
free(*dst);
exit(EXIT_SUCCESS);
}
else {
printf("BMP file loaded successfully!\n");
}
fclose(fd);
}

View File

@@ -31,14 +31,13 @@
* See supplied whitepaper for more explanations.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <cuda_runtime.h>
#include <helper_functions.h>
#include <helper_cuda.h>
#include <helper_functions.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "binomialOptions_common.h"
#include "realtype.h"
@@ -57,132 +56,134 @@ extern "C" void binomialOptionsCPU(real &callResult, TOptionData optionData);
////////////////////////////////////////////////////////////////////////////////
// Process an array of OptN options on GPU
////////////////////////////////////////////////////////////////////////////////
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData,
int optN);
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData, int optN);
////////////////////////////////////////////////////////////////////////////////
// Helper function, returning uniformly distributed
// random float in [low, high] range
////////////////////////////////////////////////////////////////////////////////
real randData(real low, real high) {
real t = (real)rand() / (real)RAND_MAX;
return ((real)1.0 - t) * low + t * high;
real randData(real low, real high)
{
real t = (real)rand() / (real)RAND_MAX;
return ((real)1.0 - t) * low + t * high;
}
////////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
printf("[%s] - Starting...\n", argv[0]);
int main(int argc, char **argv)
{
printf("[%s] - Starting...\n", argv[0]);
int devID = findCudaDevice(argc, (const char **)argv);
int devID = findCudaDevice(argc, (const char **)argv);
const int OPT_N = MAX_OPTIONS;
const int OPT_N = MAX_OPTIONS;
TOptionData optionData[MAX_OPTIONS];
real callValueBS[MAX_OPTIONS], callValueGPU[MAX_OPTIONS],
callValueCPU[MAX_OPTIONS];
TOptionData optionData[MAX_OPTIONS];
real callValueBS[MAX_OPTIONS], callValueGPU[MAX_OPTIONS], callValueCPU[MAX_OPTIONS];
real sumDelta, sumRef, gpuTime, errorVal;
real sumDelta, sumRef, gpuTime, errorVal;
StopWatchInterface *hTimer = NULL;
int i;
StopWatchInterface *hTimer = NULL;
int i;
sdkCreateTimer(&hTimer);
sdkCreateTimer(&hTimer);
printf("Generating input data...\n");
// Generate options set
srand(123);
printf("Generating input data...\n");
// Generate options set
srand(123);
for (i = 0; i < OPT_N; i++) {
optionData[i].S = randData(5.0f, 30.0f);
optionData[i].X = randData(1.0f, 100.0f);
optionData[i].T = randData(0.25f, 10.0f);
optionData[i].R = 0.06f;
optionData[i].V = 0.10f;
BlackScholesCall(callValueBS[i], optionData[i]);
}
for (i = 0; i < OPT_N; i++) {
optionData[i].S = randData(5.0f, 30.0f);
optionData[i].X = randData(1.0f, 100.0f);
optionData[i].T = randData(0.25f, 10.0f);
optionData[i].R = 0.06f;
optionData[i].V = 0.10f;
BlackScholesCall(callValueBS[i], optionData[i]);
}
printf("Running GPU binomial tree...\n");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
printf("Running GPU binomial tree...\n");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
binomialOptionsGPU(callValueGPU, optionData, OPT_N);
binomialOptionsGPU(callValueGPU, optionData, OPT_N);
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer);
printf("Options count : %i \n", OPT_N);
printf("Time steps : %i \n", NUM_STEPS);
printf("binomialOptionsGPU() time: %f msec\n", gpuTime);
printf("Options per second : %f \n", OPT_N / (gpuTime * 0.001));
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer);
printf("Options count : %i \n", OPT_N);
printf("Time steps : %i \n", NUM_STEPS);
printf("binomialOptionsGPU() time: %f msec\n", gpuTime);
printf("Options per second : %f \n", OPT_N / (gpuTime * 0.001));
printf("Running CPU binomial tree...\n");
printf("Running CPU binomial tree...\n");
for (i = 0; i < OPT_N; i++) {
binomialOptionsCPU(callValueCPU[i], optionData[i]);
}
for (i = 0; i < OPT_N; i++) {
binomialOptionsCPU(callValueCPU[i], optionData[i]);
}
printf("Comparing the results...\n");
sumDelta = 0;
sumRef = 0;
printf("GPU binomial vs. Black-Scholes\n");
printf("Comparing the results...\n");
sumDelta = 0;
sumRef = 0;
printf("GPU binomial vs. Black-Scholes\n");
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueGPU[i]);
sumRef += fabs(callValueBS[i]);
}
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueGPU[i]);
sumRef += fabs(callValueBS[i]);
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", (double)(sumDelta / sumRef));
} else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", (double)(sumDelta / sumRef));
}
else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
printf("CPU binomial vs. Black-Scholes\n");
sumDelta = 0;
sumRef = 0;
printf("CPU binomial vs. Black-Scholes\n");
sumDelta = 0;
sumRef = 0;
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueCPU[i]);
sumRef += fabs(callValueBS[i]);
}
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueCPU[i]);
sumRef += fabs(callValueBS[i]);
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", sumDelta / sumRef);
} else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", sumDelta / sumRef);
}
else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
printf("CPU binomial vs. GPU binomial\n");
sumDelta = 0;
sumRef = 0;
printf("CPU binomial vs. GPU binomial\n");
sumDelta = 0;
sumRef = 0;
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueGPU[i] - callValueCPU[i]);
sumRef += callValueCPU[i];
}
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueGPU[i] - callValueCPU[i]);
sumRef += callValueCPU[i];
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", errorVal = sumDelta / sumRef);
} else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", errorVal = sumDelta / sumRef);
}
else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
printf("Shutting down...\n");
printf("Shutting down...\n");
sdkDeleteTimer(&hTimer);
sdkDeleteTimer(&hTimer);
printf(
"\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
printf("\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
if (errorVal > 5e-4) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
if (errorVal > 5e-4) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
printf("Test passed\n");
exit(EXIT_SUCCESS);
printf("Test passed\n");
exit(EXIT_SUCCESS);
}

View File

@@ -33,12 +33,13 @@
////////////////////////////////////////////////////////////////////////////////
// Global types
////////////////////////////////////////////////////////////////////////////////
typedef struct {
real S;
real X;
real T;
real R;
real V;
typedef struct
{
real S;
real X;
real T;
real R;
real V;
} TOptionData;
////////////////////////////////////////////////////////////////////////////////

View File

@@ -25,96 +25,102 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include "binomialOptions_common.h"
#include "realtype.h"
///////////////////////////////////////////////////////////////////////////////
// Polynomial approximation of cumulative normal distribution function
///////////////////////////////////////////////////////////////////////////////
static real CND(real d) {
const real A1 = (real)0.31938153;
const real A2 = (real)-0.356563782;
const real A3 = (real)1.781477937;
const real A4 = (real)-1.821255978;
const real A5 = (real)1.330274429;
const real RSQRT2PI = (real)0.39894228040143267793994605993438;
static real CND(real d)
{
const real A1 = (real)0.31938153;
const real A2 = (real)-0.356563782;
const real A3 = (real)1.781477937;
const real A4 = (real)-1.821255978;
const real A5 = (real)1.330274429;
const real RSQRT2PI = (real)0.39894228040143267793994605993438;
real K = (real)(1.0 / (1.0 + 0.2316419 * (real)fabs(d)));
real K = (real)(1.0 / (1.0 + 0.2316419 * (real)fabs(d)));
real cnd = (real)RSQRT2PI * (real)exp(-0.5 * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
real cnd = (real)RSQRT2PI * (real)exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (d > 0) cnd = (real)1.0 - cnd;
if (d > 0)
cnd = (real)1.0 - cnd;
return cnd;
return cnd;
}
extern "C" void BlackScholesCall(real &callResult, TOptionData optionData) {
real S = optionData.S;
real X = optionData.X;
real T = optionData.T;
real R = optionData.R;
real V = optionData.V;
extern "C" void BlackScholesCall(real &callResult, TOptionData optionData)
{
real S = optionData.S;
real X = optionData.X;
real T = optionData.T;
real R = optionData.R;
real V = optionData.V;
real sqrtT = (real)sqrt(T);
real d1 = (real)(log(S / X) + (R + (real)0.5 * V * V) * T) / (V * sqrtT);
real d2 = d1 - V * sqrtT;
real CNDD1 = CND(d1);
real CNDD2 = CND(d2);
real sqrtT = (real)sqrt(T);
real d1 = (real)(log(S / X) + (R + (real)0.5 * V * V) * T) / (V * sqrtT);
real d2 = d1 - V * sqrtT;
real CNDD1 = CND(d1);
real CNDD2 = CND(d2);
// Calculate Call and Put simultaneously
real expRT = (real)exp(-R * T);
callResult = (real)(S * CNDD1 - X * expRT * CNDD2);
// Calculate Call and Put simultaneously
real expRT = (real)exp(-R * T);
callResult = (real)(S * CNDD1 - X * expRT * CNDD2);
}
////////////////////////////////////////////////////////////////////////////////
// Process an array of OptN options on CPU
// Note that CPU code is for correctness testing only and not for benchmarking.
////////////////////////////////////////////////////////////////////////////////
static real expiryCallValue(real S, real X, real vDt, int i) {
real d = S * (real)exp(vDt * (real)(2 * i - NUM_STEPS)) - X;
return (d > (real)0) ? d : (real)0;
static real expiryCallValue(real S, real X, real vDt, int i)
{
real d = S * (real)exp(vDt * (real)(2 * i - NUM_STEPS)) - X;
return (d > (real)0) ? d : (real)0;
}
extern "C" void binomialOptionsCPU(real &callResult, TOptionData optionData) {
static real Call[NUM_STEPS + 1];
extern "C" void binomialOptionsCPU(real &callResult, TOptionData optionData)
{
static real Call[NUM_STEPS + 1];
const real S = optionData.S;
const real X = optionData.X;
const real T = optionData.T;
const real R = optionData.R;
const real V = optionData.V;
const real S = optionData.S;
const real X = optionData.X;
const real T = optionData.T;
const real R = optionData.R;
const real V = optionData.V;
const real dt = T / (real)NUM_STEPS;
const real vDt = (real)V * (real)sqrt(dt);
const real rDt = R * dt;
// Per-step interest and discount factors
const real If = (real)exp(rDt);
const real Df = (real)exp(-rDt);
// Values and pseudoprobabilities of upward and downward moves
const real u = (real)exp(vDt);
const real d = (real)exp(-vDt);
const real pu = (If - d) / (u - d);
const real pd = (real)1.0 - pu;
const real puByDf = pu * Df;
const real pdByDf = pd * Df;
const real dt = T / (real)NUM_STEPS;
const real vDt = (real)V * (real)sqrt(dt);
const real rDt = R * dt;
// Per-step interest and discount factors
const real If = (real)exp(rDt);
const real Df = (real)exp(-rDt);
// Values and pseudoprobabilities of upward and downward moves
const real u = (real)exp(vDt);
const real d = (real)exp(-vDt);
const real pu = (If - d) / (u - d);
const real pd = (real)1.0 - pu;
const real puByDf = pu * Df;
const real pdByDf = pd * Df;
///////////////////////////////////////////////////////////////////////
// Compute values at expiration date:
// call option value at period end is V(T) = S(T) - X
// if S(T) is greater than X, or zero otherwise.
// The computation is similar for put options.
///////////////////////////////////////////////////////////////////////
for (int i = 0; i <= NUM_STEPS; i++) Call[i] = expiryCallValue(S, X, vDt, i);
///////////////////////////////////////////////////////////////////////
// Compute values at expiration date:
// call option value at period end is V(T) = S(T) - X
// if S(T) is greater than X, or zero otherwise.
// The computation is similar for put options.
///////////////////////////////////////////////////////////////////////
for (int i = 0; i <= NUM_STEPS; i++)
Call[i] = expiryCallValue(S, X, vDt, i);
////////////////////////////////////////////////////////////////////////
// Walk backwards up binomial tree
////////////////////////////////////////////////////////////////////////
for (int i = NUM_STEPS; i > 0; i--)
for (int j = 0; j <= i - 1; j++)
Call[j] = puByDf * Call[j + 1] + pdByDf * Call[j];
////////////////////////////////////////////////////////////////////////
// Walk backwards up binomial tree
////////////////////////////////////////////////////////////////////////
for (int i = NUM_STEPS; i > 0; i--)
for (int j = 0; j <= i - 1; j++)
Call[j] = puByDf * Call[j + 1] + pdByDf * Call[j];
callResult = (real)Call[0];
callResult = (real)Call[0];
}

View File

@@ -28,40 +28,43 @@
////////////////////////////////////////////////////////////////////////////////
// Global types and parameters
////////////////////////////////////////////////////////////////////////////////
#include <cooperative_groups.h>
#include <stdio.h>
#include <stdlib.h>
#include <cooperative_groups.h>
namespace cg = cooperative_groups;
#include <helper_cuda.h>
#include "binomialOptions_common.h"
#include "realtype.h"
// Preprocessed input option data
typedef struct {
real S;
real X;
real vDt;
real puByDf;
real pdByDf;
typedef struct
{
real S;
real X;
real vDt;
real puByDf;
real pdByDf;
} __TOptionData;
static __constant__ __TOptionData d_OptionData[MAX_OPTIONS];
static __device__ real d_CallValue[MAX_OPTIONS];
static __device__ real d_CallValue[MAX_OPTIONS];
////////////////////////////////////////////////////////////////////////////////
// Overloaded shortcut functions for different precision modes
////////////////////////////////////////////////////////////////////////////////
#ifndef DOUBLE_PRECISION
__device__ inline float expiryCallValue(float S, float X, float vDt, int i) {
float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;
return (d > 0.0F) ? d : 0.0F;
__device__ inline float expiryCallValue(float S, float X, float vDt, int i)
{
float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;
return (d > 0.0F) ? d : 0.0F;
}
#else
__device__ inline double expiryCallValue(double S, double X, double vDt,
int i) {
double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;
return (d > 0.0) ? d : 0.0;
__device__ inline double expiryCallValue(double S, double X, double vDt, int i)
{
double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;
return (d > 0.0) ? d : 0.0;
}
#endif
@@ -74,84 +77,83 @@ __device__ inline double expiryCallValue(double S, double X, double vDt,
#error Bad constants
#endif
__global__ void binomialOptionsKernel() {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__shared__ real call_exchange[THREADBLOCK_SIZE + 1];
__global__ void binomialOptionsKernel()
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__shared__ real call_exchange[THREADBLOCK_SIZE + 1];
const int tid = threadIdx.x;
const real S = d_OptionData[blockIdx.x].S;
const real X = d_OptionData[blockIdx.x].X;
const real vDt = d_OptionData[blockIdx.x].vDt;
const real puByDf = d_OptionData[blockIdx.x].puByDf;
const real pdByDf = d_OptionData[blockIdx.x].pdByDf;
const int tid = threadIdx.x;
const real S = d_OptionData[blockIdx.x].S;
const real X = d_OptionData[blockIdx.x].X;
const real vDt = d_OptionData[blockIdx.x].vDt;
const real puByDf = d_OptionData[blockIdx.x].puByDf;
const real pdByDf = d_OptionData[blockIdx.x].pdByDf;
real call[ELEMS_PER_THREAD + 1];
real call[ELEMS_PER_THREAD + 1];
#pragma unroll
for (int i = 0; i < ELEMS_PER_THREAD; ++i)
call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);
for (int i = 0; i < ELEMS_PER_THREAD; ++i)
call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);
if (tid == 0)
call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);
if (tid == 0)
call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);
int final_it = max(0, tid * ELEMS_PER_THREAD - 1);
int final_it = max(0, tid * ELEMS_PER_THREAD - 1);
#pragma unroll 16
for (int i = NUM_STEPS; i > 0; --i) {
call_exchange[tid] = call[0];
cg::sync(cta);
call[ELEMS_PER_THREAD] = call_exchange[tid + 1];
cg::sync(cta);
for (int i = NUM_STEPS; i > 0; --i) {
call_exchange[tid] = call[0];
cg::sync(cta);
call[ELEMS_PER_THREAD] = call_exchange[tid + 1];
cg::sync(cta);
if (i > final_it) {
if (i > final_it) {
#pragma unroll
for (int j = 0; j < ELEMS_PER_THREAD; ++j)
call[j] = puByDf * call[j + 1] + pdByDf * call[j];
for (int j = 0; j < ELEMS_PER_THREAD; ++j)
call[j] = puByDf * call[j + 1] + pdByDf * call[j];
}
}
}
if (tid == 0) {
d_CallValue[blockIdx.x] = call[0];
}
if (tid == 0) {
d_CallValue[blockIdx.x] = call[0];
}
}
////////////////////////////////////////////////////////////////////////////////
// Host-side interface to GPU binomialOptions
////////////////////////////////////////////////////////////////////////////////
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData,
int optN) {
__TOptionData h_OptionData[MAX_OPTIONS];
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData, int optN)
{
__TOptionData h_OptionData[MAX_OPTIONS];
for (int i = 0; i < optN; i++) {
const real T = optionData[i].T;
const real R = optionData[i].R;
const real V = optionData[i].V;
for (int i = 0; i < optN; i++) {
const real T = optionData[i].T;
const real R = optionData[i].R;
const real V = optionData[i].V;
const real dt = T / (real)NUM_STEPS;
const real vDt = V * sqrt(dt);
const real rDt = R * dt;
// Per-step interest and discount factors
const real If = exp(rDt);
const real Df = exp(-rDt);
// Values and pseudoprobabilities of upward and downward moves
const real u = exp(vDt);
const real d = exp(-vDt);
const real pu = (If - d) / (u - d);
const real pd = (real)1.0 - pu;
const real puByDf = pu * Df;
const real pdByDf = pd * Df;
const real dt = T / (real)NUM_STEPS;
const real vDt = V * sqrt(dt);
const real rDt = R * dt;
// Per-step interest and discount factors
const real If = exp(rDt);
const real Df = exp(-rDt);
// Values and pseudoprobabilities of upward and downward moves
const real u = exp(vDt);
const real d = exp(-vDt);
const real pu = (If - d) / (u - d);
const real pd = (real)1.0 - pu;
const real puByDf = pu * Df;
const real pdByDf = pd * Df;
h_OptionData[i].S = (real)optionData[i].S;
h_OptionData[i].X = (real)optionData[i].X;
h_OptionData[i].vDt = (real)vDt;
h_OptionData[i].puByDf = (real)puByDf;
h_OptionData[i].pdByDf = (real)pdByDf;
}
h_OptionData[i].S = (real)optionData[i].S;
h_OptionData[i].X = (real)optionData[i].X;
h_OptionData[i].vDt = (real)vDt;
h_OptionData[i].puByDf = (real)puByDf;
h_OptionData[i].pdByDf = (real)pdByDf;
}
checkCudaErrors(cudaMemcpyToSymbol(d_OptionData, h_OptionData,
optN * sizeof(__TOptionData)));
binomialOptionsKernel<<<optN, THREADBLOCK_SIZE>>>();
getLastCudaError("binomialOptionsKernel() execution failed.\n");
checkCudaErrors(
cudaMemcpyFromSymbol(callValue, d_CallValue, optN * sizeof(real)));
checkCudaErrors(cudaMemcpyToSymbol(d_OptionData, h_OptionData, optN * sizeof(__TOptionData)));
binomialOptionsKernel<<<optN, THREADBLOCK_SIZE>>>();
getLastCudaError("binomialOptionsKernel() execution failed.\n");
checkCudaErrors(cudaMemcpyFromSymbol(callValue, d_CallValue, optN * sizeof(real)));
}

View File

@@ -28,7 +28,7 @@
#ifndef REALTYPE_H
#define REALTYPE_H
//#define DOUBLE_PRECISION
// #define DOUBLE_PRECISION
#ifndef DOUBLE_PRECISION
typedef float real;

View File

@@ -31,15 +31,13 @@
* See supplied whitepaper for more explanations.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <cuda_runtime.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <helper_functions.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "binomialOptions_common.h"
#include "realtype.h"
@@ -61,138 +59,140 @@ extern "C" void binomialOptionsCPU(real &callResult, TOptionData optionData);
// Process an array of OptN options on GPU
////////////////////////////////////////////////////////////////////////////////
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData,
int optN, int argc, char **argv);
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData, int optN, int argc, char **argv);
////////////////////////////////////////////////////////////////////////////////
// Helper function, returning uniformly distributed
// random float in [low, high] range
////////////////////////////////////////////////////////////////////////////////
real randData(real low, real high) {
real t = (real)rand() / (real)RAND_MAX;
return ((real)1.0 - t) * low + t * high;
real randData(real low, real high)
{
real t = (real)rand() / (real)RAND_MAX;
return ((real)1.0 - t) * low + t * high;
}
////////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
printf("[%s] - Starting...\n", argv[0]);
int main(int argc, char **argv)
{
printf("[%s] - Starting...\n", argv[0]);
const int OPT_N = MAX_OPTIONS;
const int OPT_N = MAX_OPTIONS;
TOptionData optionData[MAX_OPTIONS];
real callValueBS[MAX_OPTIONS], callValueGPU[MAX_OPTIONS],
callValueCPU[MAX_OPTIONS];
TOptionData optionData[MAX_OPTIONS];
real callValueBS[MAX_OPTIONS], callValueGPU[MAX_OPTIONS], callValueCPU[MAX_OPTIONS];
real sumDelta, sumRef, gpuTime, errorVal;
real sumDelta, sumRef, gpuTime, errorVal;
StopWatchInterface *hTimer = NULL;
StopWatchInterface *hTimer = NULL;
int i;
int i;
sdkCreateTimer(&hTimer);
sdkCreateTimer(&hTimer);
printf("Generating input data...\n");
printf("Generating input data...\n");
// Generate options set
srand(123);
// Generate options set
srand(123);
for (i = 0; i < OPT_N; i++) {
optionData[i].S = randData(5.0f, 30.0f);
optionData[i].X = randData(1.0f, 100.0f);
optionData[i].T = randData(0.25f, 10.0f);
optionData[i].R = 0.06f;
optionData[i].V = 0.10f;
for (i = 0; i < OPT_N; i++) {
optionData[i].S = randData(5.0f, 30.0f);
optionData[i].X = randData(1.0f, 100.0f);
optionData[i].T = randData(0.25f, 10.0f);
optionData[i].R = 0.06f;
optionData[i].V = 0.10f;
BlackScholesCall(callValueBS[i], optionData[i]);
}
BlackScholesCall(callValueBS[i], optionData[i]);
}
printf("Running GPU binomial tree...\n");
printf("Running GPU binomial tree...\n");
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
binomialOptionsGPU(callValueGPU, optionData, OPT_N, argc, argv);
binomialOptionsGPU(callValueGPU, optionData, OPT_N, argc, argv);
sdkStopTimer(&hTimer);
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer);
printf("Options count : %i \n", OPT_N);
printf("Time steps : %i \n", NUM_STEPS);
printf("binomialOptionsGPU() time: %f msec\n", gpuTime);
printf("Options per second : %f \n", OPT_N / (gpuTime * 0.001));
printf("Options count : %i \n", OPT_N);
printf("Time steps : %i \n", NUM_STEPS);
printf("binomialOptionsGPU() time: %f msec\n", gpuTime);
printf("Options per second : %f \n", OPT_N / (gpuTime * 0.001));
printf("Running CPU binomial tree...\n");
printf("Running CPU binomial tree...\n");
for (i = 0; i < OPT_N; i++) {
binomialOptionsCPU(callValueCPU[i], optionData[i]);
}
for (i = 0; i < OPT_N; i++) {
binomialOptionsCPU(callValueCPU[i], optionData[i]);
}
printf("Comparing the results...\n");
printf("Comparing the results...\n");
sumDelta = 0;
sumRef = 0;
printf("GPU binomial vs. Black-Scholes\n");
sumDelta = 0;
sumRef = 0;
printf("GPU binomial vs. Black-Scholes\n");
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueGPU[i]);
sumRef += fabs(callValueBS[i]);
}
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueGPU[i]);
sumRef += fabs(callValueBS[i]);
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", (double)(sumDelta / sumRef));
} else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", (double)(sumDelta / sumRef));
}
else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
printf("CPU binomial vs. Black-Scholes\n");
sumDelta = 0;
sumRef = 0;
printf("CPU binomial vs. Black-Scholes\n");
sumDelta = 0;
sumRef = 0;
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueCPU[i]);
sumRef += fabs(callValueBS[i]);
}
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueBS[i] - callValueCPU[i]);
sumRef += fabs(callValueBS[i]);
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", sumDelta / sumRef);
} else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", sumDelta / sumRef);
}
else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
printf("CPU binomial vs. GPU binomial\n");
sumDelta = 0;
sumRef = 0;
printf("CPU binomial vs. GPU binomial\n");
sumDelta = 0;
sumRef = 0;
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueGPU[i] - callValueCPU[i]);
sumRef += callValueCPU[i];
}
for (i = 0; i < OPT_N; i++) {
sumDelta += fabs(callValueGPU[i] - callValueCPU[i]);
sumRef += callValueCPU[i];
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", errorVal = sumDelta / sumRef);
} else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
if (sumRef > 1E-5) {
printf("L1 norm: %E\n", errorVal = sumDelta / sumRef);
}
else {
printf("Avg. diff: %E\n", (double)(sumDelta / (real)OPT_N));
}
printf("Shutting down...\n");
printf("Shutting down...\n");
sdkDeleteTimer(&hTimer);
sdkDeleteTimer(&hTimer);
printf(
"\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
printf("\nNOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
if (errorVal > 5e-4) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
if (errorVal > 5e-4) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
printf("Test passed\n");
printf("Test passed\n");
exit(EXIT_SUCCESS);
exit(EXIT_SUCCESS);
}

View File

@@ -32,12 +32,13 @@
// Global types
////////////////////////////////////////////////////////////////////////////////
typedef struct {
float S;
float X;
float T;
float R;
float V;
typedef struct
{
float S;
float X;
float T;
float R;
float V;
} TOptionData;
////////////////////////////////////////////////////////////////////////////////

View File

@@ -25,50 +25,53 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include "binomialOptions_common.h"
///////////////////////////////////////////////////////////////////////////////
// Polynomial approximation of cumulative normal distribution function
///////////////////////////////////////////////////////////////////////////////
static double CND(double d) {
const double A1 = 0.31938153;
const double A2 = -0.356563782;
const double A3 = 1.781477937;
const double A4 = -1.821255978;
const double A5 = 1.330274429;
const double RSQRT2PI = 0.39894228040143267793994605993438;
static double CND(double d)
{
const double A1 = 0.31938153;
const double A2 = -0.356563782;
const double A3 = 1.781477937;
const double A4 = -1.821255978;
const double A5 = 1.330274429;
const double RSQRT2PI = 0.39894228040143267793994605993438;
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
double cnd = RSQRT2PI * exp(-0.5 * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
double cnd = RSQRT2PI * exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (d > 0) cnd = 1.0 - cnd;
if (d > 0)
cnd = 1.0 - cnd;
return cnd;
return cnd;
}
extern "C" void BlackScholesCall(float &callResult, TOptionData optionData) {
double S = optionData.S;
double X = optionData.X;
double T = optionData.T;
double R = optionData.R;
double V = optionData.V;
double sqrtT = sqrt(T);
extern "C" void BlackScholesCall(float &callResult, TOptionData optionData)
{
double S = optionData.S;
double X = optionData.X;
double T = optionData.T;
double R = optionData.R;
double V = optionData.V;
double sqrtT = sqrt(T);
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
double d2 = d1 - V * sqrtT;
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
double CNDD1 = CND(d1);
double CNDD2 = CND(d2);
// Calculate Call and Put simultaneously
double expRT = exp(-R * T);
// Calculate Call and Put simultaneously
double expRT = exp(-R * T);
callResult = (float)(S * CNDD1 - X * expRT * CNDD2);
callResult = (float)(S * CNDD1 - X * expRT * CNDD2);
}
////////////////////////////////////////////////////////////////////////////////
@@ -76,51 +79,54 @@ extern "C" void BlackScholesCall(float &callResult, TOptionData optionData) {
// Note that CPU code is for correctness testing only and not for benchmarking.
////////////////////////////////////////////////////////////////////////////////
static double expiryCallValue(double S, double X, double vDt, int i) {
double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;
return (d > 0) ? d : 0;
static double expiryCallValue(double S, double X, double vDt, int i)
{
double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;
return (d > 0) ? d : 0;
}
extern "C" void binomialOptionsCPU(float &callResult, TOptionData optionData) {
static double Call[NUM_STEPS + 1];
const double S = optionData.S;
const double X = optionData.X;
const double T = optionData.T;
const double R = optionData.R;
const double V = optionData.V;
extern "C" void binomialOptionsCPU(float &callResult, TOptionData optionData)
{
static double Call[NUM_STEPS + 1];
const double S = optionData.S;
const double X = optionData.X;
const double T = optionData.T;
const double R = optionData.R;
const double V = optionData.V;
const double dt = T / (double)NUM_STEPS;
const double vDt = V * sqrt(dt);
const double rDt = R * dt;
const double dt = T / (double)NUM_STEPS;
const double vDt = V * sqrt(dt);
const double rDt = R * dt;
// Per-step interest and discount factors
const double If = exp(rDt);
const double Df = exp(-rDt);
// Per-step interest and discount factors
const double If = exp(rDt);
const double Df = exp(-rDt);
// Values and pseudoprobabilities of upward and downward moves
const double u = exp(vDt);
const double d = exp(-vDt);
const double pu = (If - d) / (u - d);
const double pd = 1.0 - pu;
const double puByDf = pu * Df;
const double pdByDf = pd * Df;
// Values and pseudoprobabilities of upward and downward moves
const double u = exp(vDt);
const double d = exp(-vDt);
const double pu = (If - d) / (u - d);
const double pd = 1.0 - pu;
const double puByDf = pu * Df;
const double pdByDf = pd * Df;
///////////////////////////////////////////////////////////////////////
// Compute values at expiration date:
// call option value at period end is V(T) = S(T) - X
// if S(T) is greater than X, or zero otherwise.
// The computation is similar for put options.
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// Compute values at expiration date:
// call option value at period end is V(T) = S(T) - X
// if S(T) is greater than X, or zero otherwise.
// The computation is similar for put options.
///////////////////////////////////////////////////////////////////////
for (int i = 0; i <= NUM_STEPS; i++) Call[i] = expiryCallValue(S, X, vDt, i);
for (int i = 0; i <= NUM_STEPS; i++)
Call[i] = expiryCallValue(S, X, vDt, i);
////////////////////////////////////////////////////////////////////////
// Walk backwards up binomial tree
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Walk backwards up binomial tree
////////////////////////////////////////////////////////////////////////
for (int i = NUM_STEPS; i > 0; i--)
for (int j = 0; j <= i - 1; j++)
Call[j] = puByDf * Call[j + 1] + pdByDf * Call[j];
for (int i = NUM_STEPS; i > 0; i--)
for (int j = 0; j <= i - 1; j++)
Call[j] = puByDf * Call[j + 1] + pdByDf * Call[j];
callResult = (float)Call[0];
callResult = (float)Call[0];
}

View File

@@ -29,99 +29,104 @@
// Global types and parameters
////////////////////////////////////////////////////////////////////////////////
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
// Other helpers
#include <helper_cuda.h>
#include <nvrtc_helper.h>
// CUDA runtime
#include <cuda_runtime.h>
#include "binomialOptions_common.h"
#include "common_gpu_header.h"
#include "realtype.h"
// Preprocessed input option data
typedef struct {
real S;
real X;
real vDt;
real puByDf;
real pdByDf;
typedef struct
{
real S;
real X;
real vDt;
real puByDf;
real pdByDf;
} __TOptionData;
static bool moduleLoaded = false;
char *cubin, *kernel_file;
size_t cubinSize;
CUmodule module;
char *cubin, *kernel_file;
size_t cubinSize;
CUmodule module;
////////////////////////////////////////////////////////////////////////////////
// Host-side interface to GPU binomialOptions
////////////////////////////////////////////////////////////////////////////////
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData,
int optN, int argc, char **argv) {
if (!moduleLoaded) {
kernel_file = sdkFindFilePath("binomialOptions_kernel.cu", argv[0]);
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
module = loadCUBIN(cubin, argc, argv);
moduleLoaded = true;
}
extern "C" void binomialOptionsGPU(real *callValue, TOptionData *optionData, int optN, int argc, char **argv)
{
if (!moduleLoaded) {
kernel_file = sdkFindFilePath("binomialOptions_kernel.cu", argv[0]);
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
module = loadCUBIN(cubin, argc, argv);
moduleLoaded = true;
}
__TOptionData h_OptionData[MAX_OPTIONS];
__TOptionData h_OptionData[MAX_OPTIONS];
for (int i = 0; i < optN; i++) {
const real T = optionData[i].T;
const real R = optionData[i].R;
const real V = optionData[i].V;
for (int i = 0; i < optN; i++) {
const real T = optionData[i].T;
const real R = optionData[i].R;
const real V = optionData[i].V;
const real dt = T / (real)NUM_STEPS;
const real vDt = V * sqrt(dt);
const real rDt = R * dt;
// Per-step interest and discount factors
const real If = exp(rDt);
const real Df = exp(-rDt);
// Values and pseudoprobabilities of upward and downward moves
const real u = exp(vDt);
const real d = exp(-vDt);
const real pu = (If - d) / (u - d);
const real pd = (real)1.0 - pu;
const real puByDf = pu * Df;
const real pdByDf = pd * Df;
const real dt = T / (real)NUM_STEPS;
const real vDt = V * sqrt(dt);
const real rDt = R * dt;
// Per-step interest and discount factors
const real If = exp(rDt);
const real Df = exp(-rDt);
// Values and pseudoprobabilities of upward and downward moves
const real u = exp(vDt);
const real d = exp(-vDt);
const real pu = (If - d) / (u - d);
const real pd = (real)1.0 - pu;
const real puByDf = pu * Df;
const real pdByDf = pd * Df;
h_OptionData[i].S = (real)optionData[i].S;
h_OptionData[i].X = (real)optionData[i].X;
h_OptionData[i].vDt = (real)vDt;
h_OptionData[i].puByDf = (real)puByDf;
h_OptionData[i].pdByDf = (real)pdByDf;
}
h_OptionData[i].S = (real)optionData[i].S;
h_OptionData[i].X = (real)optionData[i].X;
h_OptionData[i].vDt = (real)vDt;
h_OptionData[i].puByDf = (real)puByDf;
h_OptionData[i].pdByDf = (real)pdByDf;
}
CUfunction kernel_addr;
checkCudaErrors(
cuModuleGetFunction(&kernel_addr, module, "binomialOptionsKernel"));
CUfunction kernel_addr;
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "binomialOptionsKernel"));
CUdeviceptr d_OptionData;
checkCudaErrors(
cuModuleGetGlobal(&d_OptionData, NULL, module, "d_OptionData"));
checkCudaErrors(
cuMemcpyHtoD(d_OptionData, h_OptionData, optN * sizeof(__TOptionData)));
CUdeviceptr d_OptionData;
checkCudaErrors(cuModuleGetGlobal(&d_OptionData, NULL, module, "d_OptionData"));
checkCudaErrors(cuMemcpyHtoD(d_OptionData, h_OptionData, optN * sizeof(__TOptionData)));
dim3 cudaBlockSize(128, 1, 1);
dim3 cudaGridSize(optN, 1, 1);
dim3 cudaBlockSize(128, 1, 1);
dim3 cudaGridSize(optN, 1, 1);
checkCudaErrors(cuLaunchKernel(kernel_addr, cudaGridSize.x, cudaGridSize.y,
cudaGridSize.z, /* grid dim */
cudaBlockSize.x, cudaBlockSize.y,
cudaBlockSize.z, /* block dim */
0, 0, /* shared mem, stream */
NULL, /* arguments */
0));
checkCudaErrors(cuLaunchKernel(kernel_addr,
cudaGridSize.x,
cudaGridSize.y,
cudaGridSize.z, /* grid dim */
cudaBlockSize.x,
cudaBlockSize.y,
cudaBlockSize.z, /* block dim */
0,
0, /* shared mem, stream */
NULL, /* arguments */
0));
checkCudaErrors(cuCtxSynchronize());
checkCudaErrors(cuCtxSynchronize());
CUdeviceptr d_CallValue;
checkCudaErrors(cuModuleGetGlobal(&d_CallValue, NULL, module, "d_CallValue"));
checkCudaErrors(cuMemcpyDtoH(callValue, d_CallValue, optN * sizeof(real)));
CUdeviceptr d_CallValue;
checkCudaErrors(cuModuleGetGlobal(&d_CallValue, NULL, module, "d_CallValue"));
checkCudaErrors(cuMemcpyDtoH(callValue, d_CallValue, optN * sizeof(real)));
}

View File

@@ -25,20 +25,21 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "common_gpu_header.h"
#include "binomialOptions_common.h"
#include "common_gpu_header.h"
#include "realtype.h"
// Preprocessed input option data
typedef struct {
real S;
real X;
real vDt;
real puByDf;
real pdByDf;
typedef struct
{
real S;
real X;
real vDt;
real puByDf;
real pdByDf;
} __TOptionData;
static __constant__ __TOptionData d_OptionData[MAX_OPTIONS];
__device__ real d_CallValue[MAX_OPTIONS];
__device__ real d_CallValue[MAX_OPTIONS];
#define THREADBLOCK_SIZE 128
#define ELEMS_PER_THREAD (NUM_STEPS / THREADBLOCK_SIZE)
@@ -51,57 +52,59 @@ __device__ real d_CallValue[MAX_OPTIONS];
////////////////////////////////////////////////////////////////////////////////
#ifndef DOUBLE_PRECISION
__device__ inline float expiryCallValue(float S, float X, float vDt, int i) {
float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;
return (d > 0.0F) ? d : 0.0F;
__device__ inline float expiryCallValue(float S, float X, float vDt, int i)
{
float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;
return (d > 0.0F) ? d : 0.0F;
}
#else
__device__ inline double expiryCallValue(double S, double X, double vDt,
int i) {
double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;
return (d > 0.0) ? d : 0.0;
__device__ inline double expiryCallValue(double S, double X, double vDt, int i)
{
double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;
return (d > 0.0) ? d : 0.0;
}
#endif
////////////////////////////////////////////////////////////////////////////////
// GPU kernel
////////////////////////////////////////////////////////////////////////////////
extern "C" __global__ void binomialOptionsKernel() {
__shared__ real call_exchange[THREADBLOCK_SIZE + 1];
extern "C" __global__ void binomialOptionsKernel()
{
__shared__ real call_exchange[THREADBLOCK_SIZE + 1];
const int tid = threadIdx.x;
const real S = d_OptionData[blockIdx.x].S;
const real X = d_OptionData[blockIdx.x].X;
const real vDt = d_OptionData[blockIdx.x].vDt;
const real puByDf = d_OptionData[blockIdx.x].puByDf;
const real pdByDf = d_OptionData[blockIdx.x].pdByDf;
const int tid = threadIdx.x;
const real S = d_OptionData[blockIdx.x].S;
const real X = d_OptionData[blockIdx.x].X;
const real vDt = d_OptionData[blockIdx.x].vDt;
const real puByDf = d_OptionData[blockIdx.x].puByDf;
const real pdByDf = d_OptionData[blockIdx.x].pdByDf;
real call[ELEMS_PER_THREAD + 1];
real call[ELEMS_PER_THREAD + 1];
#pragma unroll
for (int i = 0; i < ELEMS_PER_THREAD; ++i)
call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);
for (int i = 0; i < ELEMS_PER_THREAD; ++i)
call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);
if (tid == 0)
call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);
if (tid == 0)
call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);
int final_it = max(0, tid * ELEMS_PER_THREAD - 1);
int final_it = max(0, tid * ELEMS_PER_THREAD - 1);
#pragma unroll 16
for (int i = NUM_STEPS; i > 0; --i) {
call_exchange[tid] = call[0];
__syncthreads();
call[ELEMS_PER_THREAD] = call_exchange[tid + 1];
__syncthreads();
for (int i = NUM_STEPS; i > 0; --i) {
call_exchange[tid] = call[0];
__syncthreads();
call[ELEMS_PER_THREAD] = call_exchange[tid + 1];
__syncthreads();
if (i > final_it) {
if (i > final_it) {
#pragma unroll
for (int j = 0; j < ELEMS_PER_THREAD; ++j)
call[j] = puByDf * call[j + 1] + pdByDf * call[j];
for (int j = 0; j < ELEMS_PER_THREAD; ++j)
call[j] = puByDf * call[j + 1] + pdByDf * call[j];
}
}
}
if (tid == 0) {
d_CallValue[blockIdx.x] = call[0];
}
if (tid == 0) {
d_CallValue[blockIdx.x] = call[0];
}
}

View File

@@ -8,7 +8,7 @@
* is strictly prohibited.
*
*/
#if !defined(__COMMON_GPU_HEADER_H)
#define __COMMON_GPU_HEADER_H
@@ -16,16 +16,16 @@
// Internal GPU-side constants and data structures
////////////////////////////////////////////////////////////////////////////////
#define TIME_STEPS 16
#define TIME_STEPS 16
#define CACHE_DELTA (2 * TIME_STEPS)
#define CACHE_SIZE (256)
#define CACHE_SIZE (256)
#define CACHE_STEP (CACHE_SIZE - CACHE_DELTA)
#define CACHE_STEP (CACHE_SIZE - CACHE_DELTA)
#if NUM_STEPS % CACHE_DELTA
#error Bad constants
#endif
#endif
#endif

View File

@@ -30,7 +30,7 @@
// To use double precision uncomment the macro DOUBLE_PRECISION below, default
// is single precision.
//#define DOUBLE_PRECISION
// #define DOUBLE_PRECISION
#ifndef DOUBLE_PRECISION
typedef float real;

View File

@@ -29,98 +29,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Other helpers
#include <helper_cuda.h>
#include "convolutionFFT2D_common.h"
// Project includes
#include "convolutionFFT2D.cuh"
#include "convolutionFFT2D_common.h"
////////////////////////////////////////////////////////////////////////////////
/// Position convolution kernel center at (0, 0) in the image
////////////////////////////////////////////////////////////////////////////////
extern "C" void padKernel(float *d_Dst, float *d_Src, int fftH, int fftW,
int kernelH, int kernelW, int kernelY, int kernelX) {
assert(d_Src != d_Dst);
dim3 threads(32, 8);
dim3 grid(iDivUp(kernelW, threads.x), iDivUp(kernelH, threads.y));
extern "C" void
padKernel(float *d_Dst, float *d_Src, int fftH, int fftW, int kernelH, int kernelW, int kernelY, int kernelX)
{
assert(d_Src != d_Dst);
dim3 threads(32, 8);
dim3 grid(iDivUp(kernelW, threads.x), iDivUp(kernelH, threads.y));
SET_FLOAT_BASE;
SET_FLOAT_BASE;
#if (USE_TEXTURE)
cudaTextureObject_t texFloat;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texFloat;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(float) * kernelH * kernelW;
texRes.res.linear.desc = cudaCreateChannelDesc<float>();
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(float) * kernelH * kernelW;
texRes.res.linear.desc = cudaCreateChannelDesc<float>();
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(cudaCreateTextureObject(&texFloat, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texFloat, &texRes, &texDescr, NULL));
#endif
padKernel_kernel<<<grid, threads>>>(d_Dst, d_Src, fftH, fftW, kernelH,
kernelW, kernelY, kernelX
padKernel_kernel<<<grid, threads>>>(d_Dst,
d_Src,
fftH,
fftW,
kernelH,
kernelW,
kernelY,
kernelX
#if (USE_TEXTURE)
,
texFloat
,
texFloat
#endif
);
getLastCudaError("padKernel_kernel<<<>>> execution failed\n");
);
getLastCudaError("padKernel_kernel<<<>>> execution failed\n");
#if (USE_TEXTURE)
checkCudaErrors(cudaDestroyTextureObject(texFloat));
checkCudaErrors(cudaDestroyTextureObject(texFloat));
#endif
}
////////////////////////////////////////////////////////////////////////////////
// Prepare data for "pad to border" addressing mode
////////////////////////////////////////////////////////////////////////////////
extern "C" void padDataClampToBorder(float *d_Dst, float *d_Src, int fftH,
int fftW, int dataH, int dataW,
int kernelW, int kernelH, int kernelY,
int kernelX) {
assert(d_Src != d_Dst);
dim3 threads(32, 8);
dim3 grid(iDivUp(fftW, threads.x), iDivUp(fftH, threads.y));
extern "C" void padDataClampToBorder(float *d_Dst,
float *d_Src,
int fftH,
int fftW,
int dataH,
int dataW,
int kernelW,
int kernelH,
int kernelY,
int kernelX)
{
assert(d_Src != d_Dst);
dim3 threads(32, 8);
dim3 grid(iDivUp(fftW, threads.x), iDivUp(fftH, threads.y));
#if (USE_TEXTURE)
cudaTextureObject_t texFloat;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texFloat;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(float) * dataH * dataW;
texRes.res.linear.desc = cudaCreateChannelDesc<float>();
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(float) * dataH * dataW;
texRes.res.linear.desc = cudaCreateChannelDesc<float>();
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(cudaCreateTextureObject(&texFloat, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texFloat, &texRes, &texDescr, NULL));
#endif
padDataClampToBorder_kernel<<<grid, threads>>>(
d_Dst, d_Src, fftH, fftW, dataH, dataW, kernelH, kernelW, kernelY, kernelX
padDataClampToBorder_kernel<<<grid, threads>>>(d_Dst,
d_Src,
fftH,
fftW,
dataH,
dataW,
kernelH,
kernelW,
kernelY,
kernelX
#if (USE_TEXTURE)
,
texFloat
,
texFloat
#endif
);
getLastCudaError("padDataClampToBorder_kernel<<<>>> execution failed\n");
);
getLastCudaError("padDataClampToBorder_kernel<<<>>> execution failed\n");
#if (USE_TEXTURE)
checkCudaErrors(cudaDestroyTextureObject(texFloat));
checkCudaErrors(cudaDestroyTextureObject(texFloat));
#endif
}
@@ -128,192 +154,201 @@ extern "C" void padDataClampToBorder(float *d_Dst, float *d_Src, int fftH,
// Modulate Fourier image of padded data by Fourier image of padded kernel
// and normalize by FFT size
////////////////////////////////////////////////////////////////////////////////
extern "C" void modulateAndNormalize(fComplex *d_Dst, fComplex *d_Src, int fftH,
int fftW, int padding) {
assert(fftW % 2 == 0);
const int dataSize = fftH * (fftW / 2 + padding);
extern "C" void modulateAndNormalize(fComplex *d_Dst, fComplex *d_Src, int fftH, int fftW, int padding)
{
assert(fftW % 2 == 0);
const int dataSize = fftH * (fftW / 2 + padding);
modulateAndNormalize_kernel<<<iDivUp(dataSize, 256), 256>>>(
d_Dst, d_Src, dataSize, 1.0f / (float)(fftW * fftH));
getLastCudaError("modulateAndNormalize() execution failed\n");
modulateAndNormalize_kernel<<<iDivUp(dataSize, 256), 256>>>(d_Dst, d_Src, dataSize, 1.0f / (float)(fftW * fftH));
getLastCudaError("modulateAndNormalize() execution failed\n");
}
////////////////////////////////////////////////////////////////////////////////
// 2D R2C / C2R post/preprocessing kernels
////////////////////////////////////////////////////////////////////////////////
static const double PI = 3.1415926535897932384626433832795;
static const uint BLOCKDIM = 256;
static const double PI = 3.1415926535897932384626433832795;
static const uint BLOCKDIM = 256;
extern "C" void spPostprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX,
uint padding, int dir) {
assert(d_Src != d_Dst);
assert(DX % 2 == 0);
extern "C" void spPostprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX, uint padding, int dir)
{
assert(d_Src != d_Dst);
assert(DX % 2 == 0);
#if (POWER_OF_TWO)
uint log2DX, log2DY;
uint factorizationRemX = factorRadix2(log2DX, DX);
uint factorizationRemY = factorRadix2(log2DY, DY);
assert(factorizationRemX == 1 && factorizationRemY == 1);
uint log2DX, log2DY;
uint factorizationRemX = factorRadix2(log2DX, DX);
uint factorizationRemY = factorRadix2(log2DY, DY);
assert(factorizationRemX == 1 && factorizationRemY == 1);
#endif
const uint threadCount = DY * (DX / 2);
const double phaseBase = dir * PI / (double)DX;
const uint threadCount = DY * (DX / 2);
const double phaseBase = dir * PI / (double)DX;
#if (USE_TEXTURE)
cudaTextureObject_t texComplex;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texComplex;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * (DX + padding);
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * (DX + padding);
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texComplex, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texComplex, &texRes, &texDescr, NULL));
#endif
spPostprocess2D_kernel<<<iDivUp(threadCount, BLOCKDIM), BLOCKDIM>>>(
(fComplex *)d_Dst, (fComplex *)d_Src, DY, DX, threadCount, padding,
(float)phaseBase
spPostprocess2D_kernel<<<iDivUp(threadCount, BLOCKDIM), BLOCKDIM>>>((fComplex *)d_Dst,
(fComplex *)d_Src,
DY,
DX,
threadCount,
padding,
(float)phaseBase
#if (USE_TEXTURE)
,
texComplex
,
texComplex
#endif
);
getLastCudaError("spPostprocess2D_kernel<<<>>> execution failed\n");
);
getLastCudaError("spPostprocess2D_kernel<<<>>> execution failed\n");
#if (USE_TEXTURE)
checkCudaErrors(cudaDestroyTextureObject(texComplex));
checkCudaErrors(cudaDestroyTextureObject(texComplex));
#endif
}
extern "C" void spPreprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX,
uint padding, int dir) {
assert(d_Src != d_Dst);
assert(DX % 2 == 0);
extern "C" void spPreprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX, uint padding, int dir)
{
assert(d_Src != d_Dst);
assert(DX % 2 == 0);
#if (POWER_OF_TWO)
uint log2DX, log2DY;
uint factorizationRemX = factorRadix2(log2DX, DX);
uint factorizationRemY = factorRadix2(log2DY, DY);
assert(factorizationRemX == 1 && factorizationRemY == 1);
uint log2DX, log2DY;
uint factorizationRemX = factorRadix2(log2DX, DX);
uint factorizationRemY = factorRadix2(log2DY, DY);
assert(factorizationRemX == 1 && factorizationRemY == 1);
#endif
const uint threadCount = DY * (DX / 2);
const double phaseBase = -dir * PI / (double)DX;
const uint threadCount = DY * (DX / 2);
const double phaseBase = -dir * PI / (double)DX;
#if (USE_TEXTURE)
cudaTextureObject_t texComplex;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texComplex;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * (DX + padding);
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_Src;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * (DX + padding);
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texComplex, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texComplex, &texRes, &texDescr, NULL));
#endif
spPreprocess2D_kernel<<<iDivUp(threadCount, BLOCKDIM), BLOCKDIM>>>(
(fComplex *)d_Dst, (fComplex *)d_Src, DY, DX, threadCount, padding,
(float)phaseBase
spPreprocess2D_kernel<<<iDivUp(threadCount, BLOCKDIM), BLOCKDIM>>>((fComplex *)d_Dst,
(fComplex *)d_Src,
DY,
DX,
threadCount,
padding,
(float)phaseBase
#if (USE_TEXTURE)
,
texComplex
,
texComplex
#endif
);
getLastCudaError("spPreprocess2D_kernel<<<>>> execution failed\n");
);
getLastCudaError("spPreprocess2D_kernel<<<>>> execution failed\n");
#if (USE_TEXTURE)
checkCudaErrors(cudaDestroyTextureObject(texComplex));
checkCudaErrors(cudaDestroyTextureObject(texComplex));
#endif
}
////////////////////////////////////////////////////////////////////////////////
// Combined spPostprocess2D + modulateAndNormalize + spPreprocess2D
////////////////////////////////////////////////////////////////////////////////
extern "C" void spProcess2D(void *d_Dst, void *d_SrcA, void *d_SrcB, uint DY,
uint DX, int dir) {
assert(DY % 2 == 0);
extern "C" void spProcess2D(void *d_Dst, void *d_SrcA, void *d_SrcB, uint DY, uint DX, int dir)
{
assert(DY % 2 == 0);
#if (POWER_OF_TWO)
uint log2DX, log2DY;
uint factorizationRemX = factorRadix2(log2DX, DX);
uint factorizationRemY = factorRadix2(log2DY, DY);
assert(factorizationRemX == 1 && factorizationRemY == 1);
uint log2DX, log2DY;
uint factorizationRemX = factorRadix2(log2DX, DX);
uint factorizationRemY = factorRadix2(log2DY, DY);
assert(factorizationRemX == 1 && factorizationRemY == 1);
#endif
const uint threadCount = (DY / 2) * DX;
const double phaseBase = dir * PI / (double)DX;
const uint threadCount = (DY / 2) * DX;
const double phaseBase = dir * PI / (double)DX;
#if (USE_TEXTURE)
cudaTextureObject_t texComplexA, texComplexB;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaTextureObject_t texComplexA, texComplexB;
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_SrcA;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * DX;
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_SrcA;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * DX;
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texComplexA, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texComplexA, &texRes, &texDescr, NULL));
memset(&texRes, 0, sizeof(cudaResourceDesc));
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_SrcB;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * DX;
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
texRes.resType = cudaResourceTypeLinear;
texRes.res.linear.devPtr = d_SrcB;
texRes.res.linear.sizeInBytes = sizeof(fComplex) * DY * DX;
texRes.res.linear.desc = cudaCreateChannelDesc<fComplex>();
memset(&texDescr, 0, sizeof(cudaTextureDesc));
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(
cudaCreateTextureObject(&texComplexB, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texComplexB, &texRes, &texDescr, NULL));
#endif
spProcess2D_kernel<<<iDivUp(threadCount, BLOCKDIM), BLOCKDIM>>>(
(fComplex *)d_Dst, (fComplex *)d_SrcA, (fComplex *)d_SrcB, DY, DX,
threadCount, (float)phaseBase, 0.5f / (float)(DY * DX)
spProcess2D_kernel<<<iDivUp(threadCount, BLOCKDIM), BLOCKDIM>>>((fComplex *)d_Dst,
(fComplex *)d_SrcA,
(fComplex *)d_SrcB,
DY,
DX,
threadCount,
(float)phaseBase,
0.5f / (float)(DY * DX)
#if (USE_TEXTURE)
,
texComplexA, texComplexB
,
texComplexA,
texComplexB
#endif
);
getLastCudaError("spProcess2D_kernel<<<>>> execution failed\n");
);
getLastCudaError("spProcess2D_kernel<<<>>> execution failed\n");
#if (USE_TEXTURE)
checkCudaErrors(cudaDestroyTextureObject(texComplexA));
checkCudaErrors(cudaDestroyTextureObject(texComplexB));
checkCudaErrors(cudaDestroyTextureObject(texComplexA));
checkCudaErrors(cudaDestroyTextureObject(texComplexB));
#endif
}

View File

@@ -25,7 +25,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define USE_TEXTURE 1
#define USE_TEXTURE 1
#define POWER_OF_TWO 1
#if (USE_TEXTURE)
@@ -36,116 +36,131 @@
#define SET_FLOAT_BASE
#endif
#include "convolutionFFT2D_common.h"
////////////////////////////////////////////////////////////////////////////////
/// Position convolution kernel center at (0, 0) in the image
////////////////////////////////////////////////////////////////////////////////
__global__ void padKernel_kernel(float *d_Dst, float *d_Src, int fftH, int fftW,
int kernelH, int kernelW, int kernelY,
int kernelX
__global__ void padKernel_kernel(float *d_Dst,
float *d_Src,
int fftH,
int fftW,
int kernelH,
int kernelW,
int kernelY,
int kernelX
#if (USE_TEXTURE)
,
cudaTextureObject_t texFloat
#endif
) {
const int y = blockDim.y * blockIdx.y + threadIdx.y;
const int x = blockDim.x * blockIdx.x + threadIdx.x;
)
{
const int y = blockDim.y * blockIdx.y + threadIdx.y;
const int x = blockDim.x * blockIdx.x + threadIdx.x;
if (y < kernelH && x < kernelW) {
int ky = y - kernelY;
if (y < kernelH && x < kernelW) {
int ky = y - kernelY;
if (ky < 0) {
ky += fftH;
if (ky < 0) {
ky += fftH;
}
int kx = x - kernelX;
if (kx < 0) {
kx += fftW;
}
d_Dst[ky * fftW + kx] = LOAD_FLOAT(y * kernelW + x);
}
int kx = x - kernelX;
if (kx < 0) {
kx += fftW;
}
d_Dst[ky * fftW + kx] = LOAD_FLOAT(y * kernelW + x);
}
}
////////////////////////////////////////////////////////////////////////////////
// Prepare data for "pad to border" addressing mode
////////////////////////////////////////////////////////////////////////////////
__global__ void padDataClampToBorder_kernel(float *d_Dst, float *d_Src,
int fftH, int fftW, int dataH,
int dataW, int kernelH, int kernelW,
int kernelY, int kernelX
__global__ void padDataClampToBorder_kernel(float *d_Dst,
float *d_Src,
int fftH,
int fftW,
int dataH,
int dataW,
int kernelH,
int kernelW,
int kernelY,
int kernelX
#if (USE_TEXTURE)
,
cudaTextureObject_t texFloat
#endif
) {
const int y = blockDim.y * blockIdx.y + threadIdx.y;
const int x = blockDim.x * blockIdx.x + threadIdx.x;
const int borderH = dataH + kernelY;
const int borderW = dataW + kernelX;
)
{
const int y = blockDim.y * blockIdx.y + threadIdx.y;
const int x = blockDim.x * blockIdx.x + threadIdx.x;
const int borderH = dataH + kernelY;
const int borderW = dataW + kernelX;
if (y < fftH && x < fftW) {
int dy, dx;
if (y < fftH && x < fftW) {
int dy, dx;
if (y < dataH) {
dy = y;
if (y < dataH) {
dy = y;
}
if (x < dataW) {
dx = x;
}
if (y >= dataH && y < borderH) {
dy = dataH - 1;
}
if (x >= dataW && x < borderW) {
dx = dataW - 1;
}
if (y >= borderH) {
dy = 0;
}
if (x >= borderW) {
dx = 0;
}
d_Dst[y * fftW + x] = LOAD_FLOAT(dy * dataW + dx);
}
if (x < dataW) {
dx = x;
}
if (y >= dataH && y < borderH) {
dy = dataH - 1;
}
if (x >= dataW && x < borderW) {
dx = dataW - 1;
}
if (y >= borderH) {
dy = 0;
}
if (x >= borderW) {
dx = 0;
}
d_Dst[y * fftW + x] = LOAD_FLOAT(dy * dataW + dx);
}
}
////////////////////////////////////////////////////////////////////////////////
// Modulate Fourier image of padded data by Fourier image of padded kernel
// and normalize by FFT size
////////////////////////////////////////////////////////////////////////////////
inline __device__ void mulAndScale(fComplex &a, const fComplex &b,
const float &c) {
fComplex t = {c * (a.x * b.x - a.y * b.y), c * (a.y * b.x + a.x * b.y)};
a = t;
inline __device__ void mulAndScale(fComplex &a, const fComplex &b, const float &c)
{
fComplex t = {c * (a.x * b.x - a.y * b.y), c * (a.y * b.x + a.x * b.y)};
a = t;
}
__global__ void modulateAndNormalize_kernel(fComplex *d_Dst, fComplex *d_Src,
int dataSize, float c) {
const int i = blockDim.x * blockIdx.x + threadIdx.x;
__global__ void modulateAndNormalize_kernel(fComplex *d_Dst, fComplex *d_Src, int dataSize, float c)
{
const int i = blockDim.x * blockIdx.x + threadIdx.x;
if (i >= dataSize) {
return;
}
if (i >= dataSize) {
return;
}
fComplex a = d_Src[i];
fComplex b = d_Dst[i];
fComplex a = d_Src[i];
fComplex b = d_Dst[i];
mulAndScale(a, b, c);
mulAndScale(a, b, c);
d_Dst[i] = a;
d_Dst[i] = a;
}
////////////////////////////////////////////////////////////////////////////////
// 2D R2C / C2R post/preprocessing kernels
////////////////////////////////////////////////////////////////////////////////
#if (USE_TEXTURE)
#define LOAD_FCOMPLEX(i) tex1Dfetch<fComplex>(texComplex, i)
#define LOAD_FCOMPLEX(i) tex1Dfetch<fComplex>(texComplex, i)
#define LOAD_FCOMPLEX_A(i) tex1Dfetch<fComplex>(texComplexA, i)
#define LOAD_FCOMPLEX_B(i) tex1Dfetch<fComplex>(texComplexB, i)
@@ -153,7 +168,7 @@ __global__ void modulateAndNormalize_kernel(fComplex *d_Dst, fComplex *d_Src,
#define SET_FCOMPLEX_BASE_A
#define SET_FCOMPLEX_BASE_B
#else
#define LOAD_FCOMPLEX(i) d_Src[i]
#define LOAD_FCOMPLEX(i) d_Src[i]
#define LOAD_FCOMPLEX_A(i) d_SrcA[i]
#define LOAD_FCOMPLEX_B(i) d_SrcB[i]
@@ -162,260 +177,278 @@ __global__ void modulateAndNormalize_kernel(fComplex *d_Dst, fComplex *d_Src,
#define SET_FCOMPLEX_BASE_B
#endif
inline __device__ void spPostprocessC2C(fComplex &D1, fComplex &D2,
const fComplex &twiddle) {
float A1 = 0.5f * (D1.x + D2.x);
float B1 = 0.5f * (D1.y - D2.y);
float A2 = 0.5f * (D1.y + D2.y);
float B2 = 0.5f * (D1.x - D2.x);
inline __device__ void spPostprocessC2C(fComplex &D1, fComplex &D2, const fComplex &twiddle)
{
float A1 = 0.5f * (D1.x + D2.x);
float B1 = 0.5f * (D1.y - D2.y);
float A2 = 0.5f * (D1.y + D2.y);
float B2 = 0.5f * (D1.x - D2.x);
D1.x = A1 + (A2 * twiddle.x + B2 * twiddle.y);
D1.y = (A2 * twiddle.y - B2 * twiddle.x) + B1;
D2.x = A1 - (A2 * twiddle.x + B2 * twiddle.y);
D2.y = (A2 * twiddle.y - B2 * twiddle.x) - B1;
D1.x = A1 + (A2 * twiddle.x + B2 * twiddle.y);
D1.y = (A2 * twiddle.y - B2 * twiddle.x) + B1;
D2.x = A1 - (A2 * twiddle.x + B2 * twiddle.y);
D2.y = (A2 * twiddle.y - B2 * twiddle.x) - B1;
}
// Premultiply by 2 to account for 1.0 / (DZ * DY * DX) normalization
inline __device__ void spPreprocessC2C(fComplex &D1, fComplex &D2,
const fComplex &twiddle) {
float A1 = /* 0.5f * */ (D1.x + D2.x);
float B1 = /* 0.5f * */ (D1.y - D2.y);
float A2 = /* 0.5f * */ (D1.y + D2.y);
float B2 = /* 0.5f * */ (D1.x - D2.x);
inline __device__ void spPreprocessC2C(fComplex &D1, fComplex &D2, const fComplex &twiddle)
{
float A1 = /* 0.5f * */ (D1.x + D2.x);
float B1 = /* 0.5f * */ (D1.y - D2.y);
float A2 = /* 0.5f * */ (D1.y + D2.y);
float B2 = /* 0.5f * */ (D1.x - D2.x);
D1.x = A1 - (A2 * twiddle.x - B2 * twiddle.y);
D1.y = (B2 * twiddle.x + A2 * twiddle.y) + B1;
D2.x = A1 + (A2 * twiddle.x - B2 * twiddle.y);
D2.y = (B2 * twiddle.x + A2 * twiddle.y) - B1;
D1.x = A1 - (A2 * twiddle.x - B2 * twiddle.y);
D1.y = (B2 * twiddle.x + A2 * twiddle.y) + B1;
D2.x = A1 + (A2 * twiddle.x - B2 * twiddle.y);
D2.y = (B2 * twiddle.x + A2 * twiddle.y) - B1;
}
inline __device__ void getTwiddle(fComplex &twiddle, float phase) {
__sincosf(phase, &twiddle.y, &twiddle.x);
inline __device__ void getTwiddle(fComplex &twiddle, float phase) { __sincosf(phase, &twiddle.y, &twiddle.x); }
inline __device__ uint mod(uint a, uint DA)
{
//(DA - a) % DA, assuming a <= DA
return a ? (DA - a) : a;
}
inline __device__ uint mod(uint a, uint DA) {
//(DA - a) % DA, assuming a <= DA
return a ? (DA - a) : a;
static inline uint factorRadix2(uint &log2N, uint n)
{
if (!n) {
log2N = 0;
return 0;
}
else {
for (log2N = 0; n % 2 == 0; n /= 2, log2N++)
;
return n;
}
}
static inline uint factorRadix2(uint &log2N, uint n) {
if (!n) {
log2N = 0;
return 0;
} else {
for (log2N = 0; n % 2 == 0; n /= 2, log2N++)
;
return n;
}
}
inline __device__ void udivmod(uint &dividend, uint divisor, uint &rem) {
inline __device__ void udivmod(uint &dividend, uint divisor, uint &rem)
{
#if (!POWER_OF_TWO)
rem = dividend % divisor;
dividend /= divisor;
rem = dividend % divisor;
dividend /= divisor;
#else
rem = dividend & (divisor - 1);
dividend >>= (__ffs(divisor) - 1);
rem = dividend & (divisor - 1);
dividend >>= (__ffs(divisor) - 1);
#endif
}
__global__ void spPostprocess2D_kernel(fComplex *d_Dst, fComplex *d_Src,
uint DY, uint DX, uint threadCount,
uint padding, float phaseBase
__global__ void spPostprocess2D_kernel(fComplex *d_Dst,
fComplex *d_Src,
uint DY,
uint DX,
uint threadCount,
uint padding,
float phaseBase
#if (USE_TEXTURE)
,
cudaTextureObject_t texComplex
#endif
) {
const uint threadId = blockIdx.x * blockDim.x + threadIdx.x;
)
{
const uint threadId = blockIdx.x * blockDim.x + threadIdx.x;
if (threadId >= threadCount) {
return;
}
if (threadId >= threadCount) {
return;
}
uint x, y, i = threadId;
udivmod(i, DX / 2, x);
udivmod(i, DY, y);
uint x, y, i = threadId;
udivmod(i, DX / 2, x);
udivmod(i, DY, y);
// Avoid overwrites in columns DX / 2 by different threads
if ((x == 0) && (y > DY / 2)) {
return;
}
// Avoid overwrites in columns DX / 2 by different threads
if ((x == 0) && (y > DY / 2)) {
return;
}
const uint srcOffset = i * DY * DX;
const uint dstOffset = i * DY * (DX + padding);
const uint srcOffset = i * DY * DX;
const uint dstOffset = i * DY * (DX + padding);
// Process x = [0 .. DX / 2 - 1] U [DX / 2 + 1 .. DX]
{
const uint loadPos1 = srcOffset + y * DX + x;
const uint loadPos2 = srcOffset + mod(y, DY) * DX + mod(x, DX);
const uint storePos1 = dstOffset + y * (DX + padding) + x;
const uint storePos2 = dstOffset + mod(y, DY) * (DX + padding) + (DX - x);
// Process x = [0 .. DX / 2 - 1] U [DX / 2 + 1 .. DX]
{
const uint loadPos1 = srcOffset + y * DX + x;
const uint loadPos2 = srcOffset + mod(y, DY) * DX + mod(x, DX);
const uint storePos1 = dstOffset + y * (DX + padding) + x;
const uint storePos2 = dstOffset + mod(y, DY) * (DX + padding) + (DX - x);
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
fComplex twiddle;
getTwiddle(twiddle, phaseBase * (float)x);
spPostprocessC2C(D1, D2, twiddle);
fComplex twiddle;
getTwiddle(twiddle, phaseBase * (float)x);
spPostprocessC2C(D1, D2, twiddle);
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
// Process x = DX / 2
if (x == 0) {
const uint loadPos1 = srcOffset + y * DX + DX / 2;
const uint loadPos2 = srcOffset + mod(y, DY) * DX + DX / 2;
const uint storePos1 = dstOffset + y * (DX + padding) + DX / 2;
const uint storePos2 = dstOffset + mod(y, DY) * (DX + padding) + DX / 2;
// Process x = DX / 2
if (x == 0) {
const uint loadPos1 = srcOffset + y * DX + DX / 2;
const uint loadPos2 = srcOffset + mod(y, DY) * DX + DX / 2;
const uint storePos1 = dstOffset + y * (DX + padding) + DX / 2;
const uint storePos2 = dstOffset + mod(y, DY) * (DX + padding) + DX / 2;
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
// twiddle = getTwiddle(phaseBase * (DX / 2)) = exp(dir * j * PI / 2)
fComplex twiddle = {0, (phaseBase > 0) ? 1.0f : -1.0f};
spPostprocessC2C(D1, D2, twiddle);
// twiddle = getTwiddle(phaseBase * (DX / 2)) = exp(dir * j * PI / 2)
fComplex twiddle = {0, (phaseBase > 0) ? 1.0f : -1.0f};
spPostprocessC2C(D1, D2, twiddle);
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
}
__global__ void spPreprocess2D_kernel(fComplex *d_Dst, fComplex *d_Src, uint DY,
uint DX, uint threadCount, uint padding,
float phaseBase
__global__ void spPreprocess2D_kernel(fComplex *d_Dst,
fComplex *d_Src,
uint DY,
uint DX,
uint threadCount,
uint padding,
float phaseBase
#if (USE_TEXTURE)
,
cudaTextureObject_t texComplex
#endif
) {
const uint threadId = blockIdx.x * blockDim.x + threadIdx.x;
)
{
const uint threadId = blockIdx.x * blockDim.x + threadIdx.x;
if (threadId >= threadCount) {
return;
}
if (threadId >= threadCount) {
return;
}
uint x, y, i = threadId;
udivmod(i, DX / 2, x);
udivmod(i, DY, y);
uint x, y, i = threadId;
udivmod(i, DX / 2, x);
udivmod(i, DY, y);
// Avoid overwrites in columns 0 and DX / 2 by different threads (lower and
// upper halves)
if ((x == 0) && (y > DY / 2)) {
return;
}
// Avoid overwrites in columns 0 and DX / 2 by different threads (lower and
// upper halves)
if ((x == 0) && (y > DY / 2)) {
return;
}
const uint srcOffset = i * DY * (DX + padding);
const uint dstOffset = i * DY * DX;
const uint srcOffset = i * DY * (DX + padding);
const uint dstOffset = i * DY * DX;
// Process x = [0 .. DX / 2 - 1] U [DX / 2 + 1 .. DX]
{
const uint loadPos1 = srcOffset + y * (DX + padding) + x;
const uint loadPos2 = srcOffset + mod(y, DY) * (DX + padding) + (DX - x);
const uint storePos1 = dstOffset + y * DX + x;
const uint storePos2 = dstOffset + mod(y, DY) * DX + mod(x, DX);
// Process x = [0 .. DX / 2 - 1] U [DX / 2 + 1 .. DX]
{
const uint loadPos1 = srcOffset + y * (DX + padding) + x;
const uint loadPos2 = srcOffset + mod(y, DY) * (DX + padding) + (DX - x);
const uint storePos1 = dstOffset + y * DX + x;
const uint storePos2 = dstOffset + mod(y, DY) * DX + mod(x, DX);
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
fComplex twiddle;
getTwiddle(twiddle, phaseBase * (float)x);
spPreprocessC2C(D1, D2, twiddle);
fComplex twiddle;
getTwiddle(twiddle, phaseBase * (float)x);
spPreprocessC2C(D1, D2, twiddle);
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
// Process x = DX / 2
if (x == 0) {
const uint loadPos1 = srcOffset + y * (DX + padding) + DX / 2;
const uint loadPos2 = srcOffset + mod(y, DY) * (DX + padding) + DX / 2;
const uint storePos1 = dstOffset + y * DX + DX / 2;
const uint storePos2 = dstOffset + mod(y, DY) * DX + DX / 2;
// Process x = DX / 2
if (x == 0) {
const uint loadPos1 = srcOffset + y * (DX + padding) + DX / 2;
const uint loadPos2 = srcOffset + mod(y, DY) * (DX + padding) + DX / 2;
const uint storePos1 = dstOffset + y * DX + DX / 2;
const uint storePos2 = dstOffset + mod(y, DY) * DX + DX / 2;
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
fComplex D1 = LOAD_FCOMPLEX(loadPos1);
fComplex D2 = LOAD_FCOMPLEX(loadPos2);
// twiddle = getTwiddle(phaseBase * (DX / 2)) = exp(-dir * j * PI / 2)
fComplex twiddle = {0, (phaseBase > 0) ? 1.0f : -1.0f};
spPreprocessC2C(D1, D2, twiddle);
// twiddle = getTwiddle(phaseBase * (DX / 2)) = exp(-dir * j * PI / 2)
fComplex twiddle = {0, (phaseBase > 0) ? 1.0f : -1.0f};
spPreprocessC2C(D1, D2, twiddle);
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
d_Dst[storePos1] = D1;
d_Dst[storePos2] = D2;
}
}
////////////////////////////////////////////////////////////////////////////////
// Combined spPostprocess2D + modulateAndNormalize + spPreprocess2D
////////////////////////////////////////////////////////////////////////////////
__global__ void spProcess2D_kernel(fComplex *d_Dst, fComplex *d_SrcA,
fComplex *d_SrcB, uint DY, uint DX,
uint threadCount, float phaseBase, float c
__global__ void spProcess2D_kernel(fComplex *d_Dst,
fComplex *d_SrcA,
fComplex *d_SrcB,
uint DY,
uint DX,
uint threadCount,
float phaseBase,
float c
#if (USE_TEXTURE)
,
cudaTextureObject_t texComplexA,
cudaTextureObject_t texComplexB
#endif
) {
const uint threadId = blockIdx.x * blockDim.x + threadIdx.x;
)
{
const uint threadId = blockIdx.x * blockDim.x + threadIdx.x;
if (threadId >= threadCount) {
return;
}
if (threadId >= threadCount) {
return;
}
uint x, y, i = threadId;
udivmod(i, DX, x);
udivmod(i, DY / 2, y);
uint x, y, i = threadId;
udivmod(i, DX, x);
udivmod(i, DY / 2, y);
const uint offset = i * DY * DX;
const uint offset = i * DY * DX;
// Avoid overwrites in rows 0 and DY / 2 by different threads (left and right
// halves) Otherwise correctness for in-place transformations is affected
if ((y == 0) && (x > DX / 2)) {
return;
}
// Avoid overwrites in rows 0 and DY / 2 by different threads (left and right
// halves) Otherwise correctness for in-place transformations is affected
if ((y == 0) && (x > DX / 2)) {
return;
}
fComplex twiddle;
fComplex twiddle;
// Process y = [0 .. DY / 2 - 1] U [DY - (DY / 2) + 1 .. DY - 1]
{
const uint pos1 = offset + y * DX + x;
const uint pos2 = offset + mod(y, DY) * DX + mod(x, DX);
// Process y = [0 .. DY / 2 - 1] U [DY - (DY / 2) + 1 .. DY - 1]
{
const uint pos1 = offset + y * DX + x;
const uint pos2 = offset + mod(y, DY) * DX + mod(x, DX);
fComplex D1 = LOAD_FCOMPLEX_A(pos1);
fComplex D2 = LOAD_FCOMPLEX_A(pos2);
fComplex K1 = LOAD_FCOMPLEX_B(pos1);
fComplex K2 = LOAD_FCOMPLEX_B(pos2);
getTwiddle(twiddle, phaseBase * (float)x);
fComplex D1 = LOAD_FCOMPLEX_A(pos1);
fComplex D2 = LOAD_FCOMPLEX_A(pos2);
fComplex K1 = LOAD_FCOMPLEX_B(pos1);
fComplex K2 = LOAD_FCOMPLEX_B(pos2);
getTwiddle(twiddle, phaseBase * (float)x);
spPostprocessC2C(D1, D2, twiddle);
spPostprocessC2C(K1, K2, twiddle);
mulAndScale(D1, K1, c);
mulAndScale(D2, K2, c);
spPreprocessC2C(D1, D2, twiddle);
spPostprocessC2C(D1, D2, twiddle);
spPostprocessC2C(K1, K2, twiddle);
mulAndScale(D1, K1, c);
mulAndScale(D2, K2, c);
spPreprocessC2C(D1, D2, twiddle);
d_Dst[pos1] = D1;
d_Dst[pos2] = D2;
}
d_Dst[pos1] = D1;
d_Dst[pos2] = D2;
}
if (y == 0) {
const uint pos1 = offset + (DY / 2) * DX + x;
const uint pos2 = offset + (DY / 2) * DX + mod(x, DX);
if (y == 0) {
const uint pos1 = offset + (DY / 2) * DX + x;
const uint pos2 = offset + (DY / 2) * DX + mod(x, DX);
fComplex D1 = LOAD_FCOMPLEX_A(pos1);
fComplex D2 = LOAD_FCOMPLEX_A(pos2);
fComplex K1 = LOAD_FCOMPLEX_B(pos1);
fComplex K2 = LOAD_FCOMPLEX_B(pos2);
fComplex D1 = LOAD_FCOMPLEX_A(pos1);
fComplex D2 = LOAD_FCOMPLEX_A(pos2);
fComplex K1 = LOAD_FCOMPLEX_B(pos1);
fComplex K2 = LOAD_FCOMPLEX_B(pos2);
spPostprocessC2C(D1, D2, twiddle);
spPostprocessC2C(K1, K2, twiddle);
mulAndScale(D1, K1, c);
mulAndScale(D2, K2, c);
spPreprocessC2C(D1, D2, twiddle);
spPostprocessC2C(D1, D2, twiddle);
spPostprocessC2C(K1, K2, twiddle);
mulAndScale(D1, K1, c);
mulAndScale(D2, K2, c);
spPreprocessC2C(D1, D2, twiddle);
d_Dst[pos1] = D1;
d_Dst[pos2] = D2;
}
d_Dst[pos1] = D1;
d_Dst[pos2] = D2;
}
}

View File

@@ -33,9 +33,10 @@ typedef unsigned int uint;
#ifdef __CUDACC__
typedef float2 fComplex;
#else
typedef struct {
float x;
float y;
typedef struct
{
float x;
float y;
} fComplex;
#endif
@@ -48,30 +49,42 @@ inline int iDivUp(int a, int b) { return (a % b != 0) ? (a / b + 1) : (a / b); }
// Align a to nearest higher multiple of b
inline int iAlignUp(int a, int b) { return (a % b != 0) ? (a - a % b + b) : a; }
extern "C" void convolutionClampToBorderCPU(float *h_Result, float *h_Data,
float *h_Kernel, int dataH,
int dataW, int kernelH, int kernelW,
int kernelY, int kernelX);
extern "C" void convolutionClampToBorderCPU(float *h_Result,
float *h_Data,
float *h_Kernel,
int dataH,
int dataW,
int kernelH,
int kernelW,
int kernelY,
int kernelX);
extern "C" void padKernel(float *d_PaddedKernel, float *d_Kernel, int fftH,
int fftW, int kernelH, int kernelW, int kernelY,
int kernelX);
extern "C" void padKernel(float *d_PaddedKernel,
float *d_Kernel,
int fftH,
int fftW,
int kernelH,
int kernelW,
int kernelY,
int kernelX);
extern "C" void padDataClampToBorder(float *d_PaddedData, float *d_Data,
int fftH, int fftW, int dataH, int dataW,
int kernelH, int kernelW, int kernelY,
int kernelX);
extern "C" void padDataClampToBorder(float *d_PaddedData,
float *d_Data,
int fftH,
int fftW,
int dataH,
int dataW,
int kernelH,
int kernelW,
int kernelY,
int kernelX);
extern "C" void modulateAndNormalize(fComplex *d_Dst, fComplex *d_Src, int fftH,
int fftW, int padding);
extern "C" void modulateAndNormalize(fComplex *d_Dst, fComplex *d_Src, int fftH, int fftW, int padding);
extern "C" void spPostprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX,
uint padding, int dir);
extern "C" void spPostprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX, uint padding, int dir);
extern "C" void spPreprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX,
uint padding, int dir);
extern "C" void spPreprocess2D(void *d_Dst, void *d_Src, uint DY, uint DX, uint padding, int dir);
extern "C" void spProcess2D(void *d_Data, void *d_Data0, void *d_Kernel0,
uint DY, uint DX, int dir);
extern "C" void spProcess2D(void *d_Data, void *d_Data0, void *d_Kernel0, uint DY, uint DX, int dir);
#endif // CONVOLUTIONFFT2D_COMMON_H
#endif // CONVOLUTIONFFT2D_COMMON_H

View File

@@ -26,36 +26,46 @@
*/
#include <assert.h>
#include "convolutionFFT2D_common.h"
////////////////////////////////////////////////////////////////////////////////
// Reference straightforward CPU convolution
////////////////////////////////////////////////////////////////////////////////
extern "C" void convolutionClampToBorderCPU(float *h_Result, float *h_Data,
float *h_Kernel, int dataH,
int dataW, int kernelH, int kernelW,
int kernelY, int kernelX) {
for (int y = 0; y < dataH; y++)
for (int x = 0; x < dataW; x++) {
double sum = 0;
extern "C" void convolutionClampToBorderCPU(float *h_Result,
float *h_Data,
float *h_Kernel,
int dataH,
int dataW,
int kernelH,
int kernelW,
int kernelY,
int kernelX)
{
for (int y = 0; y < dataH; y++)
for (int x = 0; x < dataW; x++) {
double sum = 0;
for (int ky = -(kernelH - kernelY - 1); ky <= kernelY; ky++)
for (int kx = -(kernelW - kernelX - 1); kx <= kernelX; kx++) {
int dy = y + ky;
int dx = x + kx;
for (int ky = -(kernelH - kernelY - 1); ky <= kernelY; ky++)
for (int kx = -(kernelW - kernelX - 1); kx <= kernelX; kx++) {
int dy = y + ky;
int dx = x + kx;
if (dy < 0) dy = 0;
if (dy < 0)
dy = 0;
if (dx < 0) dx = 0;
if (dx < 0)
dx = 0;
if (dy >= dataH) dy = dataH - 1;
if (dy >= dataH)
dy = dataH - 1;
if (dx >= dataW) dx = dataW - 1;
if (dx >= dataW)
dx = dataW - 1;
sum += h_Data[dy * dataW + dx] *
h_Kernel[(kernelY - ky) * kernelW + (kernelX - kx)];
sum += h_Data[dy * dataW + dx] * h_Kernel[(kernelY - ky) * kernelW + (kernelX - kx)];
}
h_Result[y * dataW + x] = (float)sum;
}
h_Result[y * dataW + x] = (float)sum;
}
}

View File

@@ -42,545 +42,489 @@
#include <cufft.h>
// Helper functions for CUDA
#include <helper_functions.h>
#include <helper_cuda.h>
#include <helper_functions.h>
#include "convolutionFFT2D_common.h"
////////////////////////////////////////////////////////////////////////////////
// Helper functions
////////////////////////////////////////////////////////////////////////////////
int snapTransformSize(int dataSize) {
int hiBit;
unsigned int lowPOT, hiPOT;
int snapTransformSize(int dataSize)
{
int hiBit;
unsigned int lowPOT, hiPOT;
dataSize = iAlignUp(dataSize, 16);
dataSize = iAlignUp(dataSize, 16);
for (hiBit = 31; hiBit >= 0; hiBit--)
if (dataSize & (1U << hiBit)) {
break;
for (hiBit = 31; hiBit >= 0; hiBit--)
if (dataSize & (1U << hiBit)) {
break;
}
lowPOT = 1U << hiBit;
if (lowPOT == (unsigned int)dataSize) {
return dataSize;
}
lowPOT = 1U << hiBit;
hiPOT = 1U << (hiBit + 1);
if (lowPOT == (unsigned int)dataSize) {
return dataSize;
}
hiPOT = 1U << (hiBit + 1);
if (hiPOT <= 1024) {
return hiPOT;
} else {
return iAlignUp(dataSize, 512);
}
if (hiPOT <= 1024) {
return hiPOT;
}
else {
return iAlignUp(dataSize, 512);
}
}
float getRand(void) { return (float)(rand() % 16); }
bool test0(void) {
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
bool test0(void)
{
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
float *d_Data, *d_PaddedData, *d_Kernel, *d_PaddedKernel;
float *d_Data, *d_PaddedData, *d_Kernel, *d_PaddedKernel;
fComplex *d_DataSpectrum, *d_KernelSpectrum;
fComplex *d_DataSpectrum, *d_KernelSpectrum;
cufftHandle fftPlanFwd, fftPlanInv;
cufftHandle fftPlanFwd, fftPlanInv;
bool bRetVal;
StopWatchInterface *hTimer = NULL;
sdkCreateTimer(&hTimer);
bool bRetVal;
StopWatchInterface *hTimer = NULL;
sdkCreateTimer(&hTimer);
printf("Testing built-in R2C / C2R FFT-based convolution\n");
const int kernelH = 7;
const int kernelW = 6;
const int kernelY = 3;
const int kernelX = 4;
const int dataH = 2000;
const int dataW = 2000;
const int fftH = snapTransformSize(dataH + kernelH - 1);
const int fftW = snapTransformSize(dataW + kernelW - 1);
printf("Testing built-in R2C / C2R FFT-based convolution\n");
const int kernelH = 7;
const int kernelW = 6;
const int kernelY = 3;
const int kernelX = 4;
const int dataH = 2000;
const int dataW = 2000;
const int fftH = snapTransformSize(dataH + kernelH - 1);
const int fftW = snapTransformSize(dataW + kernelW - 1);
printf("...allocating memory\n");
h_Data = (float *)malloc(dataH * dataW * sizeof(float));
h_Kernel = (float *)malloc(kernelH * kernelW * sizeof(float));
h_ResultCPU = (float *)malloc(dataH * dataW * sizeof(float));
h_ResultGPU = (float *)malloc(fftH * fftW * sizeof(float));
printf("...allocating memory\n");
h_Data = (float *)malloc(dataH * dataW * sizeof(float));
h_Kernel = (float *)malloc(kernelH * kernelW * sizeof(float));
h_ResultCPU = (float *)malloc(dataH * dataW * sizeof(float));
h_ResultGPU = (float *)malloc(fftH * fftW * sizeof(float));
checkCudaErrors(cudaMalloc((void **)&d_Data, dataH * dataW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_Kernel, kernelH * kernelW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_Data, dataH * dataW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_Kernel, kernelH * kernelW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_PaddedData, fftH * fftW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_PaddedKernel, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_PaddedData, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_PaddedKernel, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_DataSpectrum,
fftH * (fftW / 2 + 1) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_KernelSpectrum,
fftH * (fftW / 2 + 1) * sizeof(fComplex)));
checkCudaErrors(cudaMemset(d_KernelSpectrum, 0,
fftH * (fftW / 2 + 1) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_DataSpectrum, fftH * (fftW / 2 + 1) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_KernelSpectrum, fftH * (fftW / 2 + 1) * sizeof(fComplex)));
checkCudaErrors(cudaMemset(d_KernelSpectrum, 0, fftH * (fftW / 2 + 1) * sizeof(fComplex)));
printf("...generating random input data\n");
srand(2010);
printf("...generating random input data\n");
srand(2010);
for (int i = 0; i < dataH * dataW; i++) {
h_Data[i] = getRand();
}
for (int i = 0; i < kernelH * kernelW; i++) {
h_Kernel[i] = getRand();
}
printf("...creating R2C & C2R FFT plans for %i x %i\n", fftH, fftW);
checkCudaErrors(cufftPlan2d(&fftPlanFwd, fftH, fftW, CUFFT_R2C));
checkCudaErrors(cufftPlan2d(&fftPlanInv, fftH, fftW, CUFFT_C2R));
printf("...uploading to GPU and padding convolution kernel and input data\n");
checkCudaErrors(cudaMemcpy(d_Kernel, h_Kernel,
kernelH * kernelW * sizeof(float),
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_Data, h_Data, dataH * dataW * sizeof(float),
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemset(d_PaddedKernel, 0, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMemset(d_PaddedData, 0, fftH * fftW * sizeof(float)));
padKernel(d_PaddedKernel, d_Kernel, fftH, fftW, kernelH, kernelW, kernelY,
kernelX);
padDataClampToBorder(d_PaddedData, d_Data, fftH, fftW, dataH, dataW, kernelH,
kernelW, kernelY, kernelX);
// Not including kernel transformation into time measurement,
// since convolution kernel is not changed very frequently
printf("...transforming convolution kernel\n");
checkCudaErrors(cufftExecR2C(fftPlanFwd, (cufftReal *)d_PaddedKernel,
(cufftComplex *)d_KernelSpectrum));
printf("...running GPU FFT convolution: ");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
checkCudaErrors(cufftExecR2C(fftPlanFwd, (cufftReal *)d_PaddedData,
(cufftComplex *)d_DataSpectrum));
modulateAndNormalize(d_DataSpectrum, d_KernelSpectrum, fftH, fftW, 1);
checkCudaErrors(cufftExecC2R(fftPlanInv, (cufftComplex *)d_DataSpectrum,
(cufftReal *)d_PaddedData));
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
double gpuTime = sdkGetTimerValue(&hTimer);
printf("%f MPix/s (%f ms)\n",
(double)dataH * (double)dataW * 1e-6 / (gpuTime * 0.001), gpuTime);
printf("...reading back GPU convolution results\n");
checkCudaErrors(cudaMemcpy(h_ResultGPU, d_PaddedData,
fftH * fftW * sizeof(float),
cudaMemcpyDeviceToHost));
printf("...running reference CPU convolution\n");
convolutionClampToBorderCPU(h_ResultCPU, h_Data, h_Kernel, dataH, dataW,
kernelH, kernelW, kernelY, kernelX);
printf("...comparing the results: ");
double sum_delta2 = 0;
double sum_ref2 = 0;
double max_delta_ref = 0;
for (int y = 0; y < dataH; y++)
for (int x = 0; x < dataW; x++) {
double rCPU = (double)h_ResultCPU[y * dataW + x];
double rGPU = (double)h_ResultGPU[y * fftW + x];
double delta = (rCPU - rGPU) * (rCPU - rGPU);
double ref = rCPU * rCPU + rCPU * rCPU;
if ((delta / ref) > max_delta_ref) {
max_delta_ref = delta / ref;
}
sum_delta2 += delta;
sum_ref2 += ref;
for (int i = 0; i < dataH * dataW; i++) {
h_Data[i] = getRand();
}
double L2norm = sqrt(sum_delta2 / sum_ref2);
printf("rel L2 = %E (max delta = %E)\n", L2norm, sqrt(max_delta_ref));
bRetVal = (L2norm < 1e-6) ? true : false;
printf(bRetVal ? "L2norm Error OK\n" : "L2norm Error too high!\n");
printf("...shutting down\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cufftDestroy(fftPlanInv));
checkCudaErrors(cufftDestroy(fftPlanFwd));
checkCudaErrors(cudaFree(d_DataSpectrum));
checkCudaErrors(cudaFree(d_KernelSpectrum));
checkCudaErrors(cudaFree(d_PaddedData));
checkCudaErrors(cudaFree(d_PaddedKernel));
checkCudaErrors(cudaFree(d_Data));
checkCudaErrors(cudaFree(d_Kernel));
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Data);
free(h_Kernel);
return bRetVal;
}
bool test1(void) {
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
float *d_Data, *d_Kernel, *d_PaddedData, *d_PaddedKernel;
fComplex *d_DataSpectrum0, *d_KernelSpectrum0, *d_DataSpectrum,
*d_KernelSpectrum;
cufftHandle fftPlan;
bool bRetVal;
StopWatchInterface *hTimer = NULL;
sdkCreateTimer(&hTimer);
printf("Testing custom R2C / C2R FFT-based convolution\n");
const uint fftPadding = 16;
const int kernelH = 7;
const int kernelW = 6;
const int kernelY = 3;
const int kernelX = 4;
const int dataH = 2000;
const int dataW = 2000;
const int fftH = snapTransformSize(dataH + kernelH - 1);
const int fftW = snapTransformSize(dataW + kernelW - 1);
printf("...allocating memory\n");
h_Data = (float *)malloc(dataH * dataW * sizeof(float));
h_Kernel = (float *)malloc(kernelH * kernelW * sizeof(float));
h_ResultCPU = (float *)malloc(dataH * dataW * sizeof(float));
h_ResultGPU = (float *)malloc(fftH * fftW * sizeof(float));
checkCudaErrors(cudaMalloc((void **)&d_Data, dataH * dataW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_Kernel, kernelH * kernelW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_PaddedData, fftH * fftW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_PaddedKernel, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_DataSpectrum0,
fftH * (fftW / 2) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_KernelSpectrum0,
fftH * (fftW / 2) * sizeof(fComplex)));
checkCudaErrors(
cudaMalloc((void **)&d_DataSpectrum,
fftH * (fftW / 2 + fftPadding) * sizeof(fComplex)));
checkCudaErrors(
cudaMalloc((void **)&d_KernelSpectrum,
fftH * (fftW / 2 + fftPadding) * sizeof(fComplex)));
printf("...generating random input data\n");
srand(2010);
for (int i = 0; i < dataH * dataW; i++) {
h_Data[i] = getRand();
}
for (int i = 0; i < kernelH * kernelW; i++) {
h_Kernel[i] = getRand();
}
printf("...creating C2C FFT plan for %i x %i\n", fftH, fftW / 2);
checkCudaErrors(cufftPlan2d(&fftPlan, fftH, fftW / 2, CUFFT_C2C));
printf("...uploading to GPU and padding convolution kernel and input data\n");
checkCudaErrors(cudaMemcpy(d_Data, h_Data, dataH * dataW * sizeof(float),
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_Kernel, h_Kernel,
kernelH * kernelW * sizeof(float),
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemset(d_PaddedData, 0, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMemset(d_PaddedKernel, 0, fftH * fftW * sizeof(float)));
padDataClampToBorder(d_PaddedData, d_Data, fftH, fftW, dataH, dataW, kernelH,
kernelW, kernelY, kernelX);
padKernel(d_PaddedKernel, d_Kernel, fftH, fftW, kernelH, kernelW, kernelY,
kernelX);
// CUFFT_INVERSE works just as well...
const int FFT_DIR = CUFFT_FORWARD;
// Not including kernel transformation into time measurement,
// since convolution kernel is not changed very frequently
printf("...transforming convolution kernel\n");
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedKernel,
(cufftComplex *)d_KernelSpectrum0, FFT_DIR));
spPostprocess2D(d_KernelSpectrum, d_KernelSpectrum0, fftH, fftW / 2,
fftPadding, FFT_DIR);
printf("...running GPU FFT convolution: ");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedData,
(cufftComplex *)d_DataSpectrum0, FFT_DIR));
spPostprocess2D(d_DataSpectrum, d_DataSpectrum0, fftH, fftW / 2, fftPadding,
FFT_DIR);
modulateAndNormalize(d_DataSpectrum, d_KernelSpectrum, fftH, fftW,
fftPadding);
spPreprocess2D(d_DataSpectrum0, d_DataSpectrum, fftH, fftW / 2, fftPadding,
-FFT_DIR);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_DataSpectrum0,
(cufftComplex *)d_PaddedData, -FFT_DIR));
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
double gpuTime = sdkGetTimerValue(&hTimer);
printf("%f MPix/s (%f ms)\n",
(double)dataH * (double)dataW * 1e-6 / (gpuTime * 0.001), gpuTime);
printf("...reading back GPU FFT results\n");
checkCudaErrors(cudaMemcpy(h_ResultGPU, d_PaddedData,
fftH * fftW * sizeof(float),
cudaMemcpyDeviceToHost));
printf("...running reference CPU convolution\n");
convolutionClampToBorderCPU(h_ResultCPU, h_Data, h_Kernel, dataH, dataW,
kernelH, kernelW, kernelY, kernelX);
printf("...comparing the results: ");
double sum_delta2 = 0;
double sum_ref2 = 0;
double max_delta_ref = 0;
for (int y = 0; y < dataH; y++)
for (int x = 0; x < dataW; x++) {
double rCPU = (double)h_ResultCPU[y * dataW + x];
double rGPU = (double)h_ResultGPU[y * fftW + x];
double delta = (rCPU - rGPU) * (rCPU - rGPU);
double ref = rCPU * rCPU + rCPU * rCPU;
if ((delta / ref) > max_delta_ref) {
max_delta_ref = delta / ref;
}
sum_delta2 += delta;
sum_ref2 += ref;
for (int i = 0; i < kernelH * kernelW; i++) {
h_Kernel[i] = getRand();
}
double L2norm = sqrt(sum_delta2 / sum_ref2);
printf("rel L2 = %E (max delta = %E)\n", L2norm, sqrt(max_delta_ref));
bRetVal = (L2norm < 1e-6) ? true : false;
printf(bRetVal ? "L2norm Error OK\n" : "L2norm Error too high!\n");
printf("...creating R2C & C2R FFT plans for %i x %i\n", fftH, fftW);
checkCudaErrors(cufftPlan2d(&fftPlanFwd, fftH, fftW, CUFFT_R2C));
checkCudaErrors(cufftPlan2d(&fftPlanInv, fftH, fftW, CUFFT_C2R));
printf("...shutting down\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cufftDestroy(fftPlan));
printf("...uploading to GPU and padding convolution kernel and input data\n");
checkCudaErrors(cudaMemcpy(d_Kernel, h_Kernel, kernelH * kernelW * sizeof(float), cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_Data, h_Data, dataH * dataW * sizeof(float), cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemset(d_PaddedKernel, 0, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMemset(d_PaddedData, 0, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaFree(d_KernelSpectrum));
checkCudaErrors(cudaFree(d_DataSpectrum));
checkCudaErrors(cudaFree(d_KernelSpectrum0));
checkCudaErrors(cudaFree(d_DataSpectrum0));
checkCudaErrors(cudaFree(d_PaddedKernel));
checkCudaErrors(cudaFree(d_PaddedData));
checkCudaErrors(cudaFree(d_Kernel));
checkCudaErrors(cudaFree(d_Data));
padKernel(d_PaddedKernel, d_Kernel, fftH, fftW, kernelH, kernelW, kernelY, kernelX);
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Kernel);
free(h_Data);
padDataClampToBorder(d_PaddedData, d_Data, fftH, fftW, dataH, dataW, kernelH, kernelW, kernelY, kernelX);
return bRetVal;
// Not including kernel transformation into time measurement,
// since convolution kernel is not changed very frequently
printf("...transforming convolution kernel\n");
checkCudaErrors(cufftExecR2C(fftPlanFwd, (cufftReal *)d_PaddedKernel, (cufftComplex *)d_KernelSpectrum));
printf("...running GPU FFT convolution: ");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
checkCudaErrors(cufftExecR2C(fftPlanFwd, (cufftReal *)d_PaddedData, (cufftComplex *)d_DataSpectrum));
modulateAndNormalize(d_DataSpectrum, d_KernelSpectrum, fftH, fftW, 1);
checkCudaErrors(cufftExecC2R(fftPlanInv, (cufftComplex *)d_DataSpectrum, (cufftReal *)d_PaddedData));
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
double gpuTime = sdkGetTimerValue(&hTimer);
printf("%f MPix/s (%f ms)\n", (double)dataH * (double)dataW * 1e-6 / (gpuTime * 0.001), gpuTime);
printf("...reading back GPU convolution results\n");
checkCudaErrors(cudaMemcpy(h_ResultGPU, d_PaddedData, fftH * fftW * sizeof(float), cudaMemcpyDeviceToHost));
printf("...running reference CPU convolution\n");
convolutionClampToBorderCPU(h_ResultCPU, h_Data, h_Kernel, dataH, dataW, kernelH, kernelW, kernelY, kernelX);
printf("...comparing the results: ");
double sum_delta2 = 0;
double sum_ref2 = 0;
double max_delta_ref = 0;
for (int y = 0; y < dataH; y++)
for (int x = 0; x < dataW; x++) {
double rCPU = (double)h_ResultCPU[y * dataW + x];
double rGPU = (double)h_ResultGPU[y * fftW + x];
double delta = (rCPU - rGPU) * (rCPU - rGPU);
double ref = rCPU * rCPU + rCPU * rCPU;
if ((delta / ref) > max_delta_ref) {
max_delta_ref = delta / ref;
}
sum_delta2 += delta;
sum_ref2 += ref;
}
double L2norm = sqrt(sum_delta2 / sum_ref2);
printf("rel L2 = %E (max delta = %E)\n", L2norm, sqrt(max_delta_ref));
bRetVal = (L2norm < 1e-6) ? true : false;
printf(bRetVal ? "L2norm Error OK\n" : "L2norm Error too high!\n");
printf("...shutting down\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cufftDestroy(fftPlanInv));
checkCudaErrors(cufftDestroy(fftPlanFwd));
checkCudaErrors(cudaFree(d_DataSpectrum));
checkCudaErrors(cudaFree(d_KernelSpectrum));
checkCudaErrors(cudaFree(d_PaddedData));
checkCudaErrors(cudaFree(d_PaddedKernel));
checkCudaErrors(cudaFree(d_Data));
checkCudaErrors(cudaFree(d_Kernel));
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Data);
free(h_Kernel);
return bRetVal;
}
bool test2(void) {
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
bool test1(void)
{
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
float *d_Data, *d_Kernel, *d_PaddedData, *d_PaddedKernel;
float *d_Data, *d_Kernel, *d_PaddedData, *d_PaddedKernel;
fComplex *d_DataSpectrum0, *d_KernelSpectrum0;
fComplex *d_DataSpectrum0, *d_KernelSpectrum0, *d_DataSpectrum, *d_KernelSpectrum;
cufftHandle fftPlan;
cufftHandle fftPlan;
bool bRetVal;
StopWatchInterface *hTimer = NULL;
sdkCreateTimer(&hTimer);
bool bRetVal;
StopWatchInterface *hTimer = NULL;
sdkCreateTimer(&hTimer);
printf("Testing updated custom R2C / C2R FFT-based convolution\n");
const int kernelH = 7;
const int kernelW = 6;
const int kernelY = 3;
const int kernelX = 4;
const int dataH = 2000;
const int dataW = 2000;
const int fftH = snapTransformSize(dataH + kernelH - 1);
const int fftW = snapTransformSize(dataW + kernelW - 1);
printf("Testing custom R2C / C2R FFT-based convolution\n");
const uint fftPadding = 16;
const int kernelH = 7;
const int kernelW = 6;
const int kernelY = 3;
const int kernelX = 4;
const int dataH = 2000;
const int dataW = 2000;
const int fftH = snapTransformSize(dataH + kernelH - 1);
const int fftW = snapTransformSize(dataW + kernelW - 1);
printf("...allocating memory\n");
h_Data = (float *)malloc(dataH * dataW * sizeof(float));
h_Kernel = (float *)malloc(kernelH * kernelW * sizeof(float));
h_ResultCPU = (float *)malloc(dataH * dataW * sizeof(float));
h_ResultGPU = (float *)malloc(fftH * fftW * sizeof(float));
printf("...allocating memory\n");
h_Data = (float *)malloc(dataH * dataW * sizeof(float));
h_Kernel = (float *)malloc(kernelH * kernelW * sizeof(float));
h_ResultCPU = (float *)malloc(dataH * dataW * sizeof(float));
h_ResultGPU = (float *)malloc(fftH * fftW * sizeof(float));
checkCudaErrors(cudaMalloc((void **)&d_Data, dataH * dataW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_Kernel, kernelH * kernelW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_Data, dataH * dataW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_Kernel, kernelH * kernelW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_PaddedData, fftH * fftW * sizeof(float)));
checkCudaErrors(
cudaMalloc((void **)&d_PaddedKernel, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_PaddedData, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_PaddedKernel, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_DataSpectrum0,
fftH * (fftW / 2) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_KernelSpectrum0,
fftH * (fftW / 2) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_DataSpectrum0, fftH * (fftW / 2) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_KernelSpectrum0, fftH * (fftW / 2) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_DataSpectrum, fftH * (fftW / 2 + fftPadding) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_KernelSpectrum, fftH * (fftW / 2 + fftPadding) * sizeof(fComplex)));
printf("...generating random input data\n");
srand(2010);
printf("...generating random input data\n");
srand(2010);
for (int i = 0; i < dataH * dataW; i++) {
h_Data[i] = getRand();
}
for (int i = 0; i < kernelH * kernelW; i++) {
h_Kernel[i] = getRand();
}
printf("...creating C2C FFT plan for %i x %i\n", fftH, fftW / 2);
checkCudaErrors(cufftPlan2d(&fftPlan, fftH, fftW / 2, CUFFT_C2C));
printf("...uploading to GPU and padding convolution kernel and input data\n");
checkCudaErrors(cudaMemcpy(d_Data, h_Data, dataH * dataW * sizeof(float),
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_Kernel, h_Kernel,
kernelH * kernelW * sizeof(float),
cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemset(d_PaddedData, 0, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMemset(d_PaddedKernel, 0, fftH * fftW * sizeof(float)));
padDataClampToBorder(d_PaddedData, d_Data, fftH, fftW, dataH, dataW, kernelH,
kernelW, kernelY, kernelX);
padKernel(d_PaddedKernel, d_Kernel, fftH, fftW, kernelH, kernelW, kernelY,
kernelX);
// CUFFT_INVERSE works just as well...
const int FFT_DIR = CUFFT_FORWARD;
// Not including kernel transformation into time measurement,
// since convolution kernel is not changed very frequently
printf("...transforming convolution kernel\n");
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedKernel,
(cufftComplex *)d_KernelSpectrum0, FFT_DIR));
printf("...running GPU FFT convolution: ");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedData,
(cufftComplex *)d_DataSpectrum0, FFT_DIR));
spProcess2D(d_DataSpectrum0, d_DataSpectrum0, d_KernelSpectrum0, fftH,
fftW / 2, FFT_DIR);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_DataSpectrum0,
(cufftComplex *)d_PaddedData, -FFT_DIR));
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
double gpuTime = sdkGetTimerValue(&hTimer);
printf("%f MPix/s (%f ms)\n",
(double)dataH * (double)dataW * 1e-6 / (gpuTime * 0.001), gpuTime);
printf("...reading back GPU FFT results\n");
checkCudaErrors(cudaMemcpy(h_ResultGPU, d_PaddedData,
fftH * fftW * sizeof(float),
cudaMemcpyDeviceToHost));
printf("...running reference CPU convolution\n");
convolutionClampToBorderCPU(h_ResultCPU, h_Data, h_Kernel, dataH, dataW,
kernelH, kernelW, kernelY, kernelX);
printf("...comparing the results: ");
double sum_delta2 = 0;
double sum_ref2 = 0;
double max_delta_ref = 0;
for (int y = 0; y < dataH; y++) {
for (int x = 0; x < dataW; x++) {
double rCPU = (double)h_ResultCPU[y * dataW + x];
double rGPU = (double)h_ResultGPU[y * fftW + x];
double delta = (rCPU - rGPU) * (rCPU - rGPU);
double ref = rCPU * rCPU + rCPU * rCPU;
if ((delta / ref) > max_delta_ref) {
max_delta_ref = delta / ref;
}
sum_delta2 += delta;
sum_ref2 += ref;
for (int i = 0; i < dataH * dataW; i++) {
h_Data[i] = getRand();
}
}
double L2norm = sqrt(sum_delta2 / sum_ref2);
printf("rel L2 = %E (max delta = %E)\n", L2norm, sqrt(max_delta_ref));
bRetVal = (L2norm < 1e-6) ? true : false;
printf(bRetVal ? "L2norm Error OK\n" : "L2norm Error too high!\n");
for (int i = 0; i < kernelH * kernelW; i++) {
h_Kernel[i] = getRand();
}
printf("...shutting down\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cufftDestroy(fftPlan));
printf("...creating C2C FFT plan for %i x %i\n", fftH, fftW / 2);
checkCudaErrors(cufftPlan2d(&fftPlan, fftH, fftW / 2, CUFFT_C2C));
checkCudaErrors(cudaFree(d_KernelSpectrum0));
checkCudaErrors(cudaFree(d_DataSpectrum0));
checkCudaErrors(cudaFree(d_PaddedKernel));
checkCudaErrors(cudaFree(d_PaddedData));
checkCudaErrors(cudaFree(d_Kernel));
checkCudaErrors(cudaFree(d_Data));
printf("...uploading to GPU and padding convolution kernel and input data\n");
checkCudaErrors(cudaMemcpy(d_Data, h_Data, dataH * dataW * sizeof(float), cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_Kernel, h_Kernel, kernelH * kernelW * sizeof(float), cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemset(d_PaddedData, 0, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMemset(d_PaddedKernel, 0, fftH * fftW * sizeof(float)));
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Kernel);
free(h_Data);
padDataClampToBorder(d_PaddedData, d_Data, fftH, fftW, dataH, dataW, kernelH, kernelW, kernelY, kernelX);
return bRetVal;
padKernel(d_PaddedKernel, d_Kernel, fftH, fftW, kernelH, kernelW, kernelY, kernelX);
// CUFFT_INVERSE works just as well...
const int FFT_DIR = CUFFT_FORWARD;
// Not including kernel transformation into time measurement,
// since convolution kernel is not changed very frequently
printf("...transforming convolution kernel\n");
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedKernel, (cufftComplex *)d_KernelSpectrum0, FFT_DIR));
spPostprocess2D(d_KernelSpectrum, d_KernelSpectrum0, fftH, fftW / 2, fftPadding, FFT_DIR);
printf("...running GPU FFT convolution: ");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedData, (cufftComplex *)d_DataSpectrum0, FFT_DIR));
spPostprocess2D(d_DataSpectrum, d_DataSpectrum0, fftH, fftW / 2, fftPadding, FFT_DIR);
modulateAndNormalize(d_DataSpectrum, d_KernelSpectrum, fftH, fftW, fftPadding);
spPreprocess2D(d_DataSpectrum0, d_DataSpectrum, fftH, fftW / 2, fftPadding, -FFT_DIR);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_DataSpectrum0, (cufftComplex *)d_PaddedData, -FFT_DIR));
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
double gpuTime = sdkGetTimerValue(&hTimer);
printf("%f MPix/s (%f ms)\n", (double)dataH * (double)dataW * 1e-6 / (gpuTime * 0.001), gpuTime);
printf("...reading back GPU FFT results\n");
checkCudaErrors(cudaMemcpy(h_ResultGPU, d_PaddedData, fftH * fftW * sizeof(float), cudaMemcpyDeviceToHost));
printf("...running reference CPU convolution\n");
convolutionClampToBorderCPU(h_ResultCPU, h_Data, h_Kernel, dataH, dataW, kernelH, kernelW, kernelY, kernelX);
printf("...comparing the results: ");
double sum_delta2 = 0;
double sum_ref2 = 0;
double max_delta_ref = 0;
for (int y = 0; y < dataH; y++)
for (int x = 0; x < dataW; x++) {
double rCPU = (double)h_ResultCPU[y * dataW + x];
double rGPU = (double)h_ResultGPU[y * fftW + x];
double delta = (rCPU - rGPU) * (rCPU - rGPU);
double ref = rCPU * rCPU + rCPU * rCPU;
if ((delta / ref) > max_delta_ref) {
max_delta_ref = delta / ref;
}
sum_delta2 += delta;
sum_ref2 += ref;
}
double L2norm = sqrt(sum_delta2 / sum_ref2);
printf("rel L2 = %E (max delta = %E)\n", L2norm, sqrt(max_delta_ref));
bRetVal = (L2norm < 1e-6) ? true : false;
printf(bRetVal ? "L2norm Error OK\n" : "L2norm Error too high!\n");
printf("...shutting down\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cufftDestroy(fftPlan));
checkCudaErrors(cudaFree(d_KernelSpectrum));
checkCudaErrors(cudaFree(d_DataSpectrum));
checkCudaErrors(cudaFree(d_KernelSpectrum0));
checkCudaErrors(cudaFree(d_DataSpectrum0));
checkCudaErrors(cudaFree(d_PaddedKernel));
checkCudaErrors(cudaFree(d_PaddedData));
checkCudaErrors(cudaFree(d_Kernel));
checkCudaErrors(cudaFree(d_Data));
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Kernel);
free(h_Data);
return bRetVal;
}
int main(int argc, char **argv) {
printf("[%s] - Starting...\n", argv[0]);
bool test2(void)
{
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
// Use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
float *d_Data, *d_Kernel, *d_PaddedData, *d_PaddedKernel;
int nFailures = 0;
fComplex *d_DataSpectrum0, *d_KernelSpectrum0;
if (!test0()) {
nFailures++;
}
cufftHandle fftPlan;
if (!test1()) {
nFailures++;
}
bool bRetVal;
StopWatchInterface *hTimer = NULL;
sdkCreateTimer(&hTimer);
if (!test2()) {
nFailures++;
}
printf("Testing updated custom R2C / C2R FFT-based convolution\n");
const int kernelH = 7;
const int kernelW = 6;
const int kernelY = 3;
const int kernelX = 4;
const int dataH = 2000;
const int dataW = 2000;
const int fftH = snapTransformSize(dataH + kernelH - 1);
const int fftW = snapTransformSize(dataW + kernelW - 1);
printf("Test Summary: %d errors\n", nFailures);
printf("...allocating memory\n");
h_Data = (float *)malloc(dataH * dataW * sizeof(float));
h_Kernel = (float *)malloc(kernelH * kernelW * sizeof(float));
h_ResultCPU = (float *)malloc(dataH * dataW * sizeof(float));
h_ResultGPU = (float *)malloc(fftH * fftW * sizeof(float));
if (nFailures > 0) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
checkCudaErrors(cudaMalloc((void **)&d_Data, dataH * dataW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_Kernel, kernelH * kernelW * sizeof(float)));
printf("Test passed\n");
exit(EXIT_SUCCESS);
checkCudaErrors(cudaMalloc((void **)&d_PaddedData, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_PaddedKernel, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&d_DataSpectrum0, fftH * (fftW / 2) * sizeof(fComplex)));
checkCudaErrors(cudaMalloc((void **)&d_KernelSpectrum0, fftH * (fftW / 2) * sizeof(fComplex)));
printf("...generating random input data\n");
srand(2010);
for (int i = 0; i < dataH * dataW; i++) {
h_Data[i] = getRand();
}
for (int i = 0; i < kernelH * kernelW; i++) {
h_Kernel[i] = getRand();
}
printf("...creating C2C FFT plan for %i x %i\n", fftH, fftW / 2);
checkCudaErrors(cufftPlan2d(&fftPlan, fftH, fftW / 2, CUFFT_C2C));
printf("...uploading to GPU and padding convolution kernel and input data\n");
checkCudaErrors(cudaMemcpy(d_Data, h_Data, dataH * dataW * sizeof(float), cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_Kernel, h_Kernel, kernelH * kernelW * sizeof(float), cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemset(d_PaddedData, 0, fftH * fftW * sizeof(float)));
checkCudaErrors(cudaMemset(d_PaddedKernel, 0, fftH * fftW * sizeof(float)));
padDataClampToBorder(d_PaddedData, d_Data, fftH, fftW, dataH, dataW, kernelH, kernelW, kernelY, kernelX);
padKernel(d_PaddedKernel, d_Kernel, fftH, fftW, kernelH, kernelW, kernelY, kernelX);
// CUFFT_INVERSE works just as well...
const int FFT_DIR = CUFFT_FORWARD;
// Not including kernel transformation into time measurement,
// since convolution kernel is not changed very frequently
printf("...transforming convolution kernel\n");
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedKernel, (cufftComplex *)d_KernelSpectrum0, FFT_DIR));
printf("...running GPU FFT convolution: ");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_PaddedData, (cufftComplex *)d_DataSpectrum0, FFT_DIR));
spProcess2D(d_DataSpectrum0, d_DataSpectrum0, d_KernelSpectrum0, fftH, fftW / 2, FFT_DIR);
checkCudaErrors(cufftExecC2C(fftPlan, (cufftComplex *)d_DataSpectrum0, (cufftComplex *)d_PaddedData, -FFT_DIR));
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
double gpuTime = sdkGetTimerValue(&hTimer);
printf("%f MPix/s (%f ms)\n", (double)dataH * (double)dataW * 1e-6 / (gpuTime * 0.001), gpuTime);
printf("...reading back GPU FFT results\n");
checkCudaErrors(cudaMemcpy(h_ResultGPU, d_PaddedData, fftH * fftW * sizeof(float), cudaMemcpyDeviceToHost));
printf("...running reference CPU convolution\n");
convolutionClampToBorderCPU(h_ResultCPU, h_Data, h_Kernel, dataH, dataW, kernelH, kernelW, kernelY, kernelX);
printf("...comparing the results: ");
double sum_delta2 = 0;
double sum_ref2 = 0;
double max_delta_ref = 0;
for (int y = 0; y < dataH; y++) {
for (int x = 0; x < dataW; x++) {
double rCPU = (double)h_ResultCPU[y * dataW + x];
double rGPU = (double)h_ResultGPU[y * fftW + x];
double delta = (rCPU - rGPU) * (rCPU - rGPU);
double ref = rCPU * rCPU + rCPU * rCPU;
if ((delta / ref) > max_delta_ref) {
max_delta_ref = delta / ref;
}
sum_delta2 += delta;
sum_ref2 += ref;
}
}
double L2norm = sqrt(sum_delta2 / sum_ref2);
printf("rel L2 = %E (max delta = %E)\n", L2norm, sqrt(max_delta_ref));
bRetVal = (L2norm < 1e-6) ? true : false;
printf(bRetVal ? "L2norm Error OK\n" : "L2norm Error too high!\n");
printf("...shutting down\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cufftDestroy(fftPlan));
checkCudaErrors(cudaFree(d_KernelSpectrum0));
checkCudaErrors(cudaFree(d_DataSpectrum0));
checkCudaErrors(cudaFree(d_PaddedKernel));
checkCudaErrors(cudaFree(d_PaddedData));
checkCudaErrors(cudaFree(d_Kernel));
checkCudaErrors(cudaFree(d_Data));
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Kernel);
free(h_Data);
return bRetVal;
}
int main(int argc, char **argv)
{
printf("[%s] - Starting...\n", argv[0]);
// Use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
int nFailures = 0;
if (!test0()) {
nFailures++;
}
if (!test1()) {
nFailures++;
}
if (!test2()) {
nFailures++;
}
printf("Test Summary: %d errors\n", nFailures);
if (nFailures > 0) {
printf("Test failed!\n");
exit(EXIT_FAILURE);
}
printf("Test passed\n");
exit(EXIT_SUCCESS);
}

View File

@@ -84,20 +84,20 @@ decomposition.
#endif
// includes, system
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// includes, project
#include <helper_functions.h>
#include <helper_cuda.h>
#include <helper_functions.h>
// constants which are used in host and device code
#define INV_SQRT_2 0.70710678118654752440f;
const unsigned int LOG_NUM_BANKS = 4;
const unsigned int NUM_BANKS = 16;
const unsigned int NUM_BANKS = 16;
////////////////////////////////////////////////////////////////////////////////
// includes, kernels
@@ -111,256 +111,252 @@ bool getLevels(unsigned int len, unsigned int *levels);
////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
// run test
runTest(argc, argv);
int main(int argc, char **argv)
{
// run test
runTest(argc, argv);
}
////////////////////////////////////////////////////////////////////////////////
//! Perform the wavelet decomposition
////////////////////////////////////////////////////////////////////////////////
void runTest(int argc, char **argv) {
bool bResult = false; // flag for final validation of the results
void runTest(int argc, char **argv)
{
bool bResult = false; // flag for final validation of the results
char *s_fname = NULL, *r_gold_fname = NULL;
char r_fname[256];
const char usage[] = {
"\nUsage:\n"
" dwtHaar1D --signal=<signal_file> --result=<result_file> "
"--gold=<gold_file>\n\n"
" <signal_file> Input file containing the signal\n"
" <result_file> Output file storing the result of the wavelet "
"decomposition\n"
" <gold_file> Input file containing the reference result of the "
"wavelet decomposition\n"
"\nExample:\n"
" ./dwtHaar1D\n"
" --signal=signal.dat\n"
" --result=result.dat\n"
" --gold=regression.gold.dat\n"};
char *s_fname = NULL, *r_gold_fname = NULL;
char r_fname[256];
const char usage[] = {"\nUsage:\n"
" dwtHaar1D --signal=<signal_file> --result=<result_file> "
"--gold=<gold_file>\n\n"
" <signal_file> Input file containing the signal\n"
" <result_file> Output file storing the result of the wavelet "
"decomposition\n"
" <gold_file> Input file containing the reference result of the "
"wavelet decomposition\n"
"\nExample:\n"
" ./dwtHaar1D\n"
" --signal=signal.dat\n"
" --result=result.dat\n"
" --gold=regression.gold.dat\n"};
printf("%s Starting...\n\n", argv[0]);
printf("%s Starting...\n\n", argv[0]);
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
// file names, either specified as cmd line args or use default
if (argc == 4) {
char *tmp_sfname, *tmp_rfname, *tmp_goldfname;
// file names, either specified as cmd line args or use default
if (argc == 4) {
char *tmp_sfname, *tmp_rfname, *tmp_goldfname;
if ((getCmdLineArgumentString(argc, (const char **)argv, "signal",
&tmp_sfname) != true) ||
(getCmdLineArgumentString(argc, (const char **)argv, "result",
&tmp_rfname) != true) ||
(getCmdLineArgumentString(argc, (const char **)argv, "gold",
&tmp_goldfname) != true)) {
fprintf(stderr, "Invalid input syntax.\n%s", usage);
exit(EXIT_FAILURE);
if ((getCmdLineArgumentString(argc, (const char **)argv, "signal", &tmp_sfname) != true)
|| (getCmdLineArgumentString(argc, (const char **)argv, "result", &tmp_rfname) != true)
|| (getCmdLineArgumentString(argc, (const char **)argv, "gold", &tmp_goldfname) != true)) {
fprintf(stderr, "Invalid input syntax.\n%s", usage);
exit(EXIT_FAILURE);
}
s_fname = sdkFindFilePath(tmp_sfname, argv[0]);
r_gold_fname = sdkFindFilePath(tmp_goldfname, argv[0]);
strcpy(r_fname, tmp_rfname);
}
else {
s_fname = sdkFindFilePath("signal.dat", argv[0]);
r_gold_fname = sdkFindFilePath("regression.gold.dat", argv[0]);
strcpy(r_fname, "result.dat");
}
s_fname = sdkFindFilePath(tmp_sfname, argv[0]);
r_gold_fname = sdkFindFilePath(tmp_goldfname, argv[0]);
strcpy(r_fname, tmp_rfname);
} else {
s_fname = sdkFindFilePath("signal.dat", argv[0]);
r_gold_fname = sdkFindFilePath("regression.gold.dat", argv[0]);
strcpy(r_fname, "result.dat");
}
printf("source file = \"%s\"\n", s_fname);
printf("reference file = \"%s\"\n", r_fname);
printf("gold file = \"%s\"\n", r_gold_fname);
printf("source file = \"%s\"\n", s_fname);
printf("reference file = \"%s\"\n", r_fname);
printf("gold file = \"%s\"\n", r_gold_fname);
// read in signal
unsigned int slength = 0;
float *signal = NULL;
// read in signal
unsigned int slength = 0;
float *signal = NULL;
if (s_fname == NULL) {
fprintf(stderr, "Cannot find the file containing the signal.\n%s", usage);
if (s_fname == NULL) {
fprintf(stderr, "Cannot find the file containing the signal.\n%s", usage);
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
if (sdkReadFile(s_fname, &signal, &slength, false) == true) {
printf("Reading signal from \"%s\"\n", s_fname);
}
else {
exit(EXIT_FAILURE);
}
if (sdkReadFile(s_fname, &signal, &slength, false) == true) {
printf("Reading signal from \"%s\"\n", s_fname);
} else {
exit(EXIT_FAILURE);
}
// get the number of decompositions necessary to perform a full decomposition
unsigned int dlevels_complete = 0;
// get the number of decompositions necessary to perform a full decomposition
unsigned int dlevels_complete = 0;
if (true != getLevels(slength, &dlevels_complete)) {
// error message
fprintf(stderr, "Signal length not supported.\n");
// cleanup and abort
free(signal);
exit(EXIT_FAILURE);
}
// device in data
float *d_idata = NULL;
// device out data
float *d_odata = NULL;
// device approx_final data
float *approx_final = NULL;
// The very final approximation coefficient has to be written to the output
// data, all others are reused as input data in the next global step and
// therefore have to be written to the input data again.
// The following flag indicates where to copy approx_final data
// - 0 is input, 1 is output
int approx_is_input;
// allocate device mem
const unsigned int smem_size = sizeof(float) * slength;
checkCudaErrors(cudaMalloc((void **)&d_idata, smem_size));
checkCudaErrors(cudaMalloc((void **)&d_odata, smem_size));
checkCudaErrors(cudaMalloc((void **)&approx_final, smem_size));
// copy input data to device
checkCudaErrors(cudaMemcpy(d_idata, signal, smem_size, cudaMemcpyHostToDevice));
// total number of threads
// in the first decomposition step always one thread computes the average and
// detail signal for one pair of adjacent values
unsigned int num_threads_total_left = slength / 2;
// decomposition levels performed in the current / next step
unsigned int dlevels_step = dlevels_complete;
// 1D signal so the arrangement of elements is also 1D
dim3 block_size;
dim3 grid_size;
// number of decomposition levels left after one iteration on the device
unsigned int dlevels_left = dlevels_complete;
// if less or equal 1k elements, then the data can be processed in one block,
// this avoids the Wait-For-Idle (WFI) on host side which is necessary if the
// computation is split across multiple SM's if enough input data
if (dlevels_complete <= 10) {
// decomposition can be performed at once
block_size.x = num_threads_total_left;
approx_is_input = 0;
}
else {
// 512 threads per block
grid_size.x = (num_threads_total_left / 512);
block_size.x = 512;
// 512 threads corresponds to 10 decomposition steps
dlevels_step = 10;
dlevels_left -= 10;
approx_is_input = 1;
}
// Initialize d_odata to 0.0f
initValue<<<grid_size, block_size>>>(d_odata, 0.0f);
// do until full decomposition is accomplished
while (0 != num_threads_total_left) {
// double the number of threads as bytes
unsigned int mem_shared = (2 * block_size.x) * sizeof(float);
// extra memory requirements to avoid bank conflicts
mem_shared += ((2 * block_size.x) / NUM_BANKS) * sizeof(float);
// run kernel
dwtHaar1D<<<grid_size, block_size, mem_shared>>>(
d_idata, d_odata, approx_final, dlevels_step, num_threads_total_left, block_size.x);
// Copy approx_final to appropriate location
if (approx_is_input) {
checkCudaErrors(cudaMemcpy(d_idata, approx_final, grid_size.x * 4, cudaMemcpyDeviceToDevice));
}
else {
checkCudaErrors(cudaMemcpy(d_odata, approx_final, grid_size.x * 4, cudaMemcpyDeviceToDevice));
}
// update level variables
if (dlevels_left < 10) {
// approx_final = d_odata;
approx_is_input = 0;
}
// more global steps necessary
dlevels_step = (dlevels_left > 10) ? dlevels_left - 10 : dlevels_left;
dlevels_left -= 10;
// after each step only half the threads are used any longer
// therefore after 10 steps 2^10 less threads
num_threads_total_left = num_threads_total_left >> 10;
// update block and grid size
grid_size.x = (num_threads_total_left / 512) + (0 != (num_threads_total_left % 512)) ? 1 : 0;
if (grid_size.x <= 1) {
block_size.x = num_threads_total_left;
}
}
// get the result back from the server
// allocate mem for the result
float *odata = (float *)malloc(smem_size);
checkCudaErrors(cudaMemcpy(odata, d_odata, smem_size, cudaMemcpyDeviceToHost));
// post processing
// write file for regression test
if (r_fname == NULL) {
fprintf(stderr,
"Cannot write the output file storing the result of the wavelet "
"decomposition.\n%s",
usage);
exit(EXIT_FAILURE);
}
if (sdkWriteFile(r_fname, odata, slength, 0.001f, false) == true) {
printf("Writing result to \"%s\"\n", r_fname);
}
else {
exit(EXIT_FAILURE);
}
// load the reference solution
unsigned int len_reference = 0;
float *reference = NULL;
if (r_gold_fname == NULL) {
fprintf(stderr,
"Cannot read the file containing the reference result of the "
"wavelet decomposition.\n%s",
usage);
exit(EXIT_FAILURE);
}
if (sdkReadFile(r_gold_fname, &reference, &len_reference, false) == true) {
printf("Reading reference result from \"%s\"\n", r_gold_fname);
}
else {
exit(EXIT_FAILURE);
}
assert(slength == len_reference);
// compare the computed solution and the reference
bResult = (bool)sdkCompareL2fe(reference, odata, slength, 0.001f);
free(reference);
// free allocated host and device memory
checkCudaErrors(cudaFree(d_odata));
checkCudaErrors(cudaFree(d_idata));
checkCudaErrors(cudaFree(approx_final));
if (true != getLevels(slength, &dlevels_complete)) {
// error message
fprintf(stderr, "Signal length not supported.\n");
// cleanup and abort
free(signal);
exit(EXIT_FAILURE);
}
free(odata);
free(s_fname);
free(r_gold_fname);
// device in data
float *d_idata = NULL;
// device out data
float *d_odata = NULL;
// device approx_final data
float *approx_final = NULL;
// The very final approximation coefficient has to be written to the output
// data, all others are reused as input data in the next global step and
// therefore have to be written to the input data again.
// The following flag indicates where to copy approx_final data
// - 0 is input, 1 is output
int approx_is_input;
// allocate device mem
const unsigned int smem_size = sizeof(float) * slength;
checkCudaErrors(cudaMalloc((void **)&d_idata, smem_size));
checkCudaErrors(cudaMalloc((void **)&d_odata, smem_size));
checkCudaErrors(cudaMalloc((void **)&approx_final, smem_size));
// copy input data to device
checkCudaErrors(
cudaMemcpy(d_idata, signal, smem_size, cudaMemcpyHostToDevice));
// total number of threads
// in the first decomposition step always one thread computes the average and
// detail signal for one pair of adjacent values
unsigned int num_threads_total_left = slength / 2;
// decomposition levels performed in the current / next step
unsigned int dlevels_step = dlevels_complete;
// 1D signal so the arrangement of elements is also 1D
dim3 block_size;
dim3 grid_size;
// number of decomposition levels left after one iteration on the device
unsigned int dlevels_left = dlevels_complete;
// if less or equal 1k elements, then the data can be processed in one block,
// this avoids the Wait-For-Idle (WFI) on host side which is necessary if the
// computation is split across multiple SM's if enough input data
if (dlevels_complete <= 10) {
// decomposition can be performed at once
block_size.x = num_threads_total_left;
approx_is_input = 0;
} else {
// 512 threads per block
grid_size.x = (num_threads_total_left / 512);
block_size.x = 512;
// 512 threads corresponds to 10 decomposition steps
dlevels_step = 10;
dlevels_left -= 10;
approx_is_input = 1;
}
// Initialize d_odata to 0.0f
initValue<<<grid_size, block_size>>>(d_odata, 0.0f);
// do until full decomposition is accomplished
while (0 != num_threads_total_left) {
// double the number of threads as bytes
unsigned int mem_shared = (2 * block_size.x) * sizeof(float);
// extra memory requirements to avoid bank conflicts
mem_shared += ((2 * block_size.x) / NUM_BANKS) * sizeof(float);
// run kernel
dwtHaar1D<<<grid_size, block_size, mem_shared>>>(
d_idata, d_odata, approx_final, dlevels_step, num_threads_total_left,
block_size.x);
// Copy approx_final to appropriate location
if (approx_is_input) {
checkCudaErrors(cudaMemcpy(d_idata, approx_final, grid_size.x * 4,
cudaMemcpyDeviceToDevice));
} else {
checkCudaErrors(cudaMemcpy(d_odata, approx_final, grid_size.x * 4,
cudaMemcpyDeviceToDevice));
}
// update level variables
if (dlevels_left < 10) {
// approx_final = d_odata;
approx_is_input = 0;
}
// more global steps necessary
dlevels_step = (dlevels_left > 10) ? dlevels_left - 10 : dlevels_left;
dlevels_left -= 10;
// after each step only half the threads are used any longer
// therefore after 10 steps 2^10 less threads
num_threads_total_left = num_threads_total_left >> 10;
// update block and grid size
grid_size.x =
(num_threads_total_left / 512) + (0 != (num_threads_total_left % 512))
? 1
: 0;
if (grid_size.x <= 1) {
block_size.x = num_threads_total_left;
}
}
// get the result back from the server
// allocate mem for the result
float *odata = (float *)malloc(smem_size);
checkCudaErrors(
cudaMemcpy(odata, d_odata, smem_size, cudaMemcpyDeviceToHost));
// post processing
// write file for regression test
if (r_fname == NULL) {
fprintf(stderr,
"Cannot write the output file storing the result of the wavelet "
"decomposition.\n%s",
usage);
exit(EXIT_FAILURE);
}
if (sdkWriteFile(r_fname, odata, slength, 0.001f, false) == true) {
printf("Writing result to \"%s\"\n", r_fname);
} else {
exit(EXIT_FAILURE);
}
// load the reference solution
unsigned int len_reference = 0;
float *reference = NULL;
if (r_gold_fname == NULL) {
fprintf(stderr,
"Cannot read the file containing the reference result of the "
"wavelet decomposition.\n%s",
usage);
exit(EXIT_FAILURE);
}
if (sdkReadFile(r_gold_fname, &reference, &len_reference, false) == true) {
printf("Reading reference result from \"%s\"\n", r_gold_fname);
} else {
exit(EXIT_FAILURE);
}
assert(slength == len_reference);
// compare the computed solution and the reference
bResult = (bool)sdkCompareL2fe(reference, odata, slength, 0.001f);
free(reference);
// free allocated host and device memory
checkCudaErrors(cudaFree(d_odata));
checkCudaErrors(cudaFree(d_idata));
checkCudaErrors(cudaFree(approx_final));
free(signal);
free(odata);
free(s_fname);
free(r_gold_fname);
printf(bResult ? "Test success!\n" : "Test failure!\n");
printf(bResult ? "Test success!\n" : "Test failure!\n");
}
////////////////////////////////////////////////////////////////////////////////
@@ -373,17 +369,18 @@ void runTest(int argc, char **argv) {
//! @param levels number of decomposition levels necessary to perform a full
//! decomposition
////////////////////////////////////////////////////////////////////////////////
bool getLevels(unsigned int len, unsigned int *levels) {
bool retval = false;
bool getLevels(unsigned int len, unsigned int *levels)
{
bool retval = false;
// currently signals up to a length of 2^20 supported
for (unsigned int i = 0; i < 20; ++i) {
if (len == (1 << i)) {
*levels = i;
retval = true;
break;
// currently signals up to a length of 2^20 supported
for (unsigned int i = 0; i < 20; ++i) {
if (len == (1 << i)) {
*levels = i;
retval = true;
break;
}
}
}
return retval;
return retval;
}

View File

@@ -91,16 +91,17 @@ namespace cg = cooperative_groups;
//! @param od output data
//! @param value
////////////////////////////////////////////////////////////////////////////////
__global__ void initValue(float *od, float value) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
// position of write into global memory
unsigned int index = (blockIdx.x * blockDim.x) + threadIdx.x;
__global__ void initValue(float *od, float value)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
// position of write into global memory
unsigned int index = (blockIdx.x * blockDim.x) + threadIdx.x;
od[index] = value;
od[index] = value;
// sync after each decomposition step
cg::sync(cta);
// sync after each decomposition step
cg::sync(cta);
}
////////////////////////////////////////////////////////////////////////////////
@@ -118,127 +119,130 @@ __global__ void initValue(float *od, float value) {
//! global memory
//! @param bdim block dimension
////////////////////////////////////////////////////////////////////////////////
__global__ void dwtHaar1D(float *id, float *od, float *approx_final,
__global__ void dwtHaar1D(float *id,
float *od,
float *approx_final,
const unsigned int dlevels,
const unsigned int slength_step_half,
const int bdim) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
const int bdim)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
// shared memory for part of the signal
extern __shared__ float shared[];
// shared memory for part of the signal
extern __shared__ float shared[];
// thread runtime environment, 1D parametrization
const int gdim = gridDim.x;
// const int bdim = blockDim.x;
const int bid = blockIdx.x;
const int tid = threadIdx.x;
// thread runtime environment, 1D parametrization
const int gdim = gridDim.x;
// const int bdim = blockDim.x;
const int bid = blockIdx.x;
const int tid = threadIdx.x;
// global thread id (w.r.t. to total data set)
const int tid_global = (bid * bdim) + tid;
unsigned int idata = (bid * (2 * bdim)) + tid;
// global thread id (w.r.t. to total data set)
const int tid_global = (bid * bdim) + tid;
unsigned int idata = (bid * (2 * bdim)) + tid;
// read data from global memory
shared[tid] = id[idata];
shared[tid + bdim] = id[idata + bdim];
cg::sync(cta);
// read data from global memory
shared[tid] = id[idata];
shared[tid + bdim] = id[idata + bdim];
cg::sync(cta);
// this operation has a two way bank conflicts for all threads, this are two
// additional cycles for each warp -- all alternatives to avoid this bank
// conflict are more expensive than the one cycle introduced by serialization
float data0 = shared[2 * tid];
float data1 = shared[(2 * tid) + 1];
cg::sync(cta);
// this operation has a two way bank conflicts for all threads, this are two
// additional cycles for each warp -- all alternatives to avoid this bank
// conflict are more expensive than the one cycle introduced by serialization
float data0 = shared[2 * tid];
float data1 = shared[(2 * tid) + 1];
cg::sync(cta);
// detail coefficient, not further referenced so directly store in
// global memory
od[tid_global + slength_step_half] = (data0 - data1) * INV_SQRT_2;
// detail coefficient, not further referenced so directly store in
// global memory
od[tid_global + slength_step_half] = (data0 - data1) * INV_SQRT_2;
// offset to avoid bank conflicts
// see the scan example for a more detailed description
unsigned int atid = tid + (tid >> LOG_NUM_BANKS);
// offset to avoid bank conflicts
// see the scan example for a more detailed description
unsigned int atid = tid + (tid >> LOG_NUM_BANKS);
// approximation coefficient
// store in shared memory for further decomposition steps in this global step
shared[atid] = (data0 + data1) * INV_SQRT_2;
// approximation coefficient
// store in shared memory for further decomposition steps in this global step
shared[atid] = (data0 + data1) * INV_SQRT_2;
// all threads have to write approximation coefficient to shared memory before
// next steps can take place
cg::sync(cta);
// all threads have to write approximation coefficient to shared memory before
// next steps can take place
cg::sync(cta);
// early out if possible
// the compiler removes this part from the source because dlevels is
// a constant shader input
// note: syncthreads in bodies of branches can lead to dead-locks unless
// the condition evaluates the same way for ALL threads of a block, as in
// this case
if (dlevels > 1) {
// offset to second element in shared element which has to be used for the
// decomposition, effectively 2^(i - 1)
unsigned int offset_neighbor = 1;
// number of active threads per decomposition level
// identical to the offset for the detail coefficients
unsigned int num_threads = bdim >> 1;
// early out if possible
// the compiler removes this part from the source because dlevels is
// a constant shader input
// note: syncthreads in bodies of branches can lead to dead-locks unless
// the condition evaluates the same way for ALL threads of a block, as in
// this case
if (dlevels > 1) {
// offset to second element in shared element which has to be used for the
// decomposition, effectively 2^(i - 1)
unsigned int offset_neighbor = 1;
// number of active threads per decomposition level
// identical to the offset for the detail coefficients
unsigned int num_threads = bdim >> 1;
// index for the first element of the pair to process
// the representation is still compact (and therefore still tid * 2)
// because the first step operated on registers and only the result has been
// written to shared memory
unsigned int idata0 = tid * 2;
// index for the first element of the pair to process
// the representation is still compact (and therefore still tid * 2)
// because the first step operated on registers and only the result has been
// written to shared memory
unsigned int idata0 = tid * 2;
// offset levels to make the loop more efficient
for (unsigned int i = 1; i < dlevels; ++i) {
// Non-coalesced writes occur if the number of active threads becomes
// less than 16 for a block because the start address for the first
// block is not always aligned with 64 byte which is necessary for
// coalesced access. However, the problem only occurs at high levels
// with only a small number of active threads so that the total number of
// non-coalesced access is rather small and does not justify the
// computations which are necessary to avoid these uncoalesced writes
// (this has been tested and verified)
if (tid < num_threads) {
// update stride, with each decomposition level the stride grows by a
// factor of 2
unsigned int idata1 = idata0 + offset_neighbor;
// offset levels to make the loop more efficient
for (unsigned int i = 1; i < dlevels; ++i) {
// Non-coalesced writes occur if the number of active threads becomes
// less than 16 for a block because the start address for the first
// block is not always aligned with 64 byte which is necessary for
// coalesced access. However, the problem only occurs at high levels
// with only a small number of active threads so that the total number of
// non-coalesced access is rather small and does not justify the
// computations which are necessary to avoid these uncoalesced writes
// (this has been tested and verified)
if (tid < num_threads) {
// update stride, with each decomposition level the stride grows by a
// factor of 2
unsigned int idata1 = idata0 + offset_neighbor;
// position of write into global memory
unsigned int g_wpos = (num_threads * gdim) + (bid * num_threads) + tid;
// position of write into global memory
unsigned int g_wpos = (num_threads * gdim) + (bid * num_threads) + tid;
// compute wavelet decomposition step
// compute wavelet decomposition step
// offset to avoid bank conflicts
unsigned int c_idata0 = idata0 + (idata0 >> LOG_NUM_BANKS);
unsigned int c_idata1 = idata1 + (idata1 >> LOG_NUM_BANKS);
// offset to avoid bank conflicts
unsigned int c_idata0 = idata0 + (idata0 >> LOG_NUM_BANKS);
unsigned int c_idata1 = idata1 + (idata1 >> LOG_NUM_BANKS);
// detail coefficient, not further modified so directly store
// in global memory
od[g_wpos] = (shared[c_idata0] - shared[c_idata1]) * INV_SQRT_2;
// detail coefficient, not further modified so directly store
// in global memory
od[g_wpos] = (shared[c_idata0] - shared[c_idata1]) * INV_SQRT_2;
// approximation coefficient
// note that the representation in shared memory becomes rather sparse
// (with a lot of holes inbetween) but the storing scheme in global
// memory guarantees that the common representation (approx, detail_0,
// detail_1, ...)
// is achieved
shared[c_idata0] = (shared[c_idata0] + shared[c_idata1]) * INV_SQRT_2;
// approximation coefficient
// note that the representation in shared memory becomes rather sparse
// (with a lot of holes inbetween) but the storing scheme in global
// memory guarantees that the common representation (approx, detail_0,
// detail_1, ...)
// is achieved
shared[c_idata0] = (shared[c_idata0] + shared[c_idata1]) * INV_SQRT_2;
// update storage offset for details
num_threads = num_threads >> 1; // div 2
offset_neighbor <<= 1; // mul 2
idata0 = idata0 << 1; // mul 2
}
// update storage offset for details
num_threads = num_threads >> 1; // div 2
offset_neighbor <<= 1; // mul 2
idata0 = idata0 << 1; // mul 2
}
// sync after each decomposition step
cg::sync(cta);
}
// sync after each decomposition step
cg::sync(cta);
}
// write the top most level element for the next decomposition steps
// which are performed after an interlock synchronization on host side
if (0 == tid) {
approx_final[bid] = shared[0];
}
// write the top most level element for the next decomposition steps
// which are performed after an interlock synchronization on host side
if (0 == tid) {
approx_final[bid] = shared[0];
}
} // end early out if possible
} // end early out if possible
}
#endif // #ifndef _DWTHAAR1D_KERNEL_H_
#endif // #ifndef _DWTHAAR1D_KERNEL_H_

View File

@@ -36,71 +36,72 @@ namespace cg = cooperative_groups;
// Use power method to find the first eigenvector.
// https://en.wikipedia.org/wiki/Power_iteration
inline __device__ __host__ float3 firstEigenVector(float matrix[6]) {
// 8 iterations seems to be more than enough.
inline __device__ __host__ float3 firstEigenVector(float matrix[6])
{
// 8 iterations seems to be more than enough.
float3 v = make_float3(1.0f, 1.0f, 1.0f);
float3 v = make_float3(1.0f, 1.0f, 1.0f);
for (int i = 0; i < 8; i++) {
float x = v.x * matrix[0] + v.y * matrix[1] + v.z * matrix[2];
float y = v.x * matrix[1] + v.y * matrix[3] + v.z * matrix[4];
float z = v.x * matrix[2] + v.y * matrix[4] + v.z * matrix[5];
float m = max(max(x, y), z);
float iv = 1.0f / m;
v = make_float3(x * iv, y * iv, z * iv);
}
return v;
}
inline __device__ void colorSums(const float3 *colors, float3 *sums,
cg::thread_group tile) {
const int idx = threadIdx.x;
sums[idx] = colors[idx];
cg::sync(tile);
sums[idx] += sums[idx ^ 8];
cg::sync(tile);
sums[idx] += sums[idx ^ 4];
cg::sync(tile);
sums[idx] += sums[idx ^ 2];
cg::sync(tile);
sums[idx] += sums[idx ^ 1];
}
inline __device__ float3 bestFitLine(const float3 *colors, float3 color_sum,
cg::thread_group tile) {
// Compute covariance matrix of the given colors.
const int idx = threadIdx.x;
float3 diff = colors[idx] - color_sum * (1.0f / 16.0f);
// @@ Eliminate two-way bank conflicts here.
// @@ It seems that doing that and unrolling the reduction doesn't help...
__shared__ float covariance[16 * 6];
covariance[6 * idx + 0] = diff.x * diff.x; // 0, 6, 12, 2, 8, 14, 4, 10, 0
covariance[6 * idx + 1] = diff.x * diff.y;
covariance[6 * idx + 2] = diff.x * diff.z;
covariance[6 * idx + 3] = diff.y * diff.y;
covariance[6 * idx + 4] = diff.y * diff.z;
covariance[6 * idx + 5] = diff.z * diff.z;
cg::sync(tile);
for (int d = 8; d > 0; d >>= 1) {
if (idx < d) {
covariance[6 * idx + 0] += covariance[6 * (idx + d) + 0];
covariance[6 * idx + 1] += covariance[6 * (idx + d) + 1];
covariance[6 * idx + 2] += covariance[6 * (idx + d) + 2];
covariance[6 * idx + 3] += covariance[6 * (idx + d) + 3];
covariance[6 * idx + 4] += covariance[6 * (idx + d) + 4];
covariance[6 * idx + 5] += covariance[6 * (idx + d) + 5];
for (int i = 0; i < 8; i++) {
float x = v.x * matrix[0] + v.y * matrix[1] + v.z * matrix[2];
float y = v.x * matrix[1] + v.y * matrix[3] + v.z * matrix[4];
float z = v.x * matrix[2] + v.y * matrix[4] + v.z * matrix[5];
float m = max(max(x, y), z);
float iv = 1.0f / m;
v = make_float3(x * iv, y * iv, z * iv);
}
cg::sync(tile);
}
// Compute first eigen vector.
return firstEigenVector(covariance);
return v;
}
#endif // CUDAMATH_H
inline __device__ void colorSums(const float3 *colors, float3 *sums, cg::thread_group tile)
{
const int idx = threadIdx.x;
sums[idx] = colors[idx];
cg::sync(tile);
sums[idx] += sums[idx ^ 8];
cg::sync(tile);
sums[idx] += sums[idx ^ 4];
cg::sync(tile);
sums[idx] += sums[idx ^ 2];
cg::sync(tile);
sums[idx] += sums[idx ^ 1];
}
inline __device__ float3 bestFitLine(const float3 *colors, float3 color_sum, cg::thread_group tile)
{
// Compute covariance matrix of the given colors.
const int idx = threadIdx.x;
float3 diff = colors[idx] - color_sum * (1.0f / 16.0f);
// @@ Eliminate two-way bank conflicts here.
// @@ It seems that doing that and unrolling the reduction doesn't help...
__shared__ float covariance[16 * 6];
covariance[6 * idx + 0] = diff.x * diff.x; // 0, 6, 12, 2, 8, 14, 4, 10, 0
covariance[6 * idx + 1] = diff.x * diff.y;
covariance[6 * idx + 2] = diff.x * diff.z;
covariance[6 * idx + 3] = diff.y * diff.y;
covariance[6 * idx + 4] = diff.y * diff.z;
covariance[6 * idx + 5] = diff.z * diff.z;
cg::sync(tile);
for (int d = 8; d > 0; d >>= 1) {
if (idx < d) {
covariance[6 * idx + 0] += covariance[6 * (idx + d) + 0];
covariance[6 * idx + 1] += covariance[6 * (idx + d) + 1];
covariance[6 * idx + 2] += covariance[6 * (idx + d) + 2];
covariance[6 * idx + 3] += covariance[6 * (idx + d) + 3];
covariance[6 * idx + 4] += covariance[6 * (idx + d) + 4];
covariance[6 * idx + 5] += covariance[6 * (idx + d) + 5];
}
cg::sync(tile);
}
// Compute first eigen vector.
return firstEigenVector(covariance);
}
#endif // CUDAMATH_H

View File

@@ -29,56 +29,58 @@
#define DDS_H
#if !defined(MAKEFOURCC)
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((unsigned int)(ch0) | ((unsigned int)(ch1) << 8) | \
((unsigned int)(ch2) << 16) | ((unsigned int)(ch3) << 24))
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((unsigned int)(ch0) | ((unsigned int)(ch1) << 8) | ((unsigned int)(ch2) << 16) | ((unsigned int)(ch3) << 24))
#endif
typedef unsigned int uint;
typedef unsigned int uint;
typedef unsigned short ushort;
struct DDSPixelFormat {
uint size;
uint flags;
uint fourcc;
uint bitcount;
uint rmask;
uint gmask;
uint bmask;
uint amask;
struct DDSPixelFormat
{
uint size;
uint flags;
uint fourcc;
uint bitcount;
uint rmask;
uint gmask;
uint bmask;
uint amask;
};
struct DDSCaps {
uint caps1;
uint caps2;
uint caps3;
uint caps4;
struct DDSCaps
{
uint caps1;
uint caps2;
uint caps3;
uint caps4;
};
/// DDS file header.
struct DDSHeader {
uint fourcc;
uint size;
uint flags;
uint height;
uint width;
uint pitch;
uint depth;
uint mipmapcount;
uint reserved[11];
DDSPixelFormat pf;
DDSCaps caps;
uint notused;
struct DDSHeader
{
uint fourcc;
uint size;
uint flags;
uint height;
uint width;
uint pitch;
uint depth;
uint mipmapcount;
uint reserved[11];
DDSPixelFormat pf;
DDSCaps caps;
uint notused;
};
static const uint FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
static const uint FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
static const uint DDSD_WIDTH = 0x00000004U;
static const uint DDSD_HEIGHT = 0x00000002U;
static const uint DDSD_CAPS = 0x00000001U;
static const uint FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
static const uint FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
static const uint DDSD_WIDTH = 0x00000004U;
static const uint DDSD_HEIGHT = 0x00000002U;
static const uint DDSD_CAPS = 0x00000001U;
static const uint DDSD_PIXELFORMAT = 0x00001000U;
static const uint DDSCAPS_TEXTURE = 0x00001000U;
static const uint DDPF_FOURCC = 0x00000004U;
static const uint DDSD_LINEARSIZE = 0x00080000U;
static const uint DDSCAPS_TEXTURE = 0x00001000U;
static const uint DDPF_FOURCC = 0x00000004U;
static const uint DDSD_LINEARSIZE = 0x00080000U;
#endif // DDS_H
#endif // DDS_H

File diff suppressed because it is too large Load Diff

View File

@@ -28,116 +28,118 @@
#ifndef PERMUTATIONS_H
#define PERMUTATIONS_H
#include <helper_cuda.h> // assert
#include <helper_cuda.h> // assert
static void computePermutations(uint permutations[1024]) {
int indices[16];
int num = 0;
static void computePermutations(uint permutations[1024])
{
int indices[16];
int num = 0;
// 3 element permutations:
// 3 element permutations:
// first cluster [0,i) is at the start
for (int m = 0; m < 16; ++m) {
indices[m] = 0;
}
const int imax = 15;
for (int i = imax; i >= 0; --i) {
// second cluster [i,j) is half along
for (int m = i; m < 16; ++m) {
indices[m] = 2;
// first cluster [0,i) is at the start
for (int m = 0; m < 16; ++m) {
indices[m] = 0;
}
const int jmax = (i == 0) ? 15 : 16;
const int imax = 15;
for (int j = jmax; j >= i; --j) {
// last cluster [j,k) is at the end
if (j < 16) {
indices[j] = 1;
}
uint permutation = 0;
for (int p = 0; p < 16; p++) {
permutation |= indices[p] << (p * 2);
// permutation |= indices[15-p] << (p * 2);
}
permutations[num] = permutation;
num++;
}
}
assert(num == 151);
for (int i = 0; i < 9; i++) {
permutations[num] = 0x000AA555;
num++;
}
assert(num == 160);
// Append 4 element permutations:
// first cluster [0,i) is at the start
for (int m = 0; m < 16; ++m) {
indices[m] = 0;
}
for (int i = imax; i >= 0; --i) {
// second cluster [i,j) is one third along
for (int m = i; m < 16; ++m) {
indices[m] = 2;
}
const int jmax = (i == 0) ? 15 : 16;
for (int j = jmax; j >= i; --j) {
// third cluster [j,k) is two thirds along
for (int m = j; m < 16; ++m) {
indices[m] = 3;
}
int kmax = (j == 0) ? 15 : 16;
for (int k = kmax; k >= j; --k) {
// last cluster [k,n) is at the end
if (k < 16) {
indices[k] = 1;
for (int i = imax; i >= 0; --i) {
// second cluster [i,j) is half along
for (int m = i; m < 16; ++m) {
indices[m] = 2;
}
uint permutation = 0;
const int jmax = (i == 0) ? 15 : 16;
bool hasThree = false;
for (int j = jmax; j >= i; --j) {
// last cluster [j,k) is at the end
if (j < 16) {
indices[j] = 1;
}
for (int p = 0; p < 16; p++) {
permutation |= indices[p] << (p * 2);
// permutation |= indices[15-p] << (p * 2);
uint permutation = 0;
if (indices[p] == 3) hasThree = true;
for (int p = 0; p < 16; p++) {
permutation |= indices[p] << (p * 2);
// permutation |= indices[15-p] << (p * 2);
}
permutations[num] = permutation;
num++;
}
if (hasThree) {
permutations[num] = permutation;
num++;
}
}
}
}
assert(num == 975);
assert(num == 151);
// 1024 - 969 - 7 = 48 extra elements
for (int i = 0; i < 9; i++) {
permutations[num] = 0x000AA555;
num++;
}
// It would be nice to set these extra elements with better values...
for (int i = 0; i < 49; i++) {
permutations[num] = 0x00AAFF55;
num++;
}
assert(num == 160);
assert(num == 1024);
// Append 4 element permutations:
// first cluster [0,i) is at the start
for (int m = 0; m < 16; ++m) {
indices[m] = 0;
}
for (int i = imax; i >= 0; --i) {
// second cluster [i,j) is one third along
for (int m = i; m < 16; ++m) {
indices[m] = 2;
}
const int jmax = (i == 0) ? 15 : 16;
for (int j = jmax; j >= i; --j) {
// third cluster [j,k) is two thirds along
for (int m = j; m < 16; ++m) {
indices[m] = 3;
}
int kmax = (j == 0) ? 15 : 16;
for (int k = kmax; k >= j; --k) {
// last cluster [k,n) is at the end
if (k < 16) {
indices[k] = 1;
}
uint permutation = 0;
bool hasThree = false;
for (int p = 0; p < 16; p++) {
permutation |= indices[p] << (p * 2);
// permutation |= indices[15-p] << (p * 2);
if (indices[p] == 3)
hasThree = true;
}
if (hasThree) {
permutations[num] = permutation;
num++;
}
}
}
}
assert(num == 975);
// 1024 - 969 - 7 = 48 extra elements
// It would be nice to set these extra elements with better values...
for (int i = 0; i < 49; i++) {
permutations[num] = 0x00AAFF55;
num++;
}
assert(num == 1024);
}
#endif // PERMUTATIONS_H
#endif // PERMUTATIONS_H

View File

@@ -38,20 +38,18 @@
* Victor Podlozhnyuk (vpodlozhnyuk@nvidia.com)
*/
#include <helper_cuda.h>
#include <helper_functions.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <helper_functions.h>
#include <helper_cuda.h>
////////////////////////////////////////////////////////////////////////////////
// Reference CPU FWT
////////////////////////////////////////////////////////////////////////////////
extern "C" void fwtCPU(float *h_Output, float *h_Input, int log2N);
extern "C" void slowWTcpu(float *h_Output, float *h_Input, int log2N);
extern "C" void dyadicConvolutionCPU(float *h_Result, float *h_Data,
float *h_Kernel, int log2dataN,
int log2kernelN);
extern "C" void dyadicConvolutionCPU(float *h_Result, float *h_Data, float *h_Kernel, int log2dataN, int log2kernelN);
////////////////////////////////////////////////////////////////////////////////
// GPU FWT
@@ -62,12 +60,12 @@ extern "C" void dyadicConvolutionCPU(float *h_Result, float *h_Data,
// Data configuration
////////////////////////////////////////////////////////////////////////////////
const int log2Kernel = 7;
const int log2Data = 23;
const int log2Data = 23;
const int dataN = 1 << log2Data;
const int dataN = 1 << log2Data;
const int kernelN = 1 << log2Kernel;
const int DATA_SIZE = dataN * sizeof(float);
const int DATA_SIZE = dataN * sizeof(float);
const int KERNEL_SIZE = kernelN * sizeof(float);
const double NOPS = 3.0 * (double)dataN * (double)log2Data / 2.0;
@@ -75,95 +73,92 @@ const double NOPS = 3.0 * (double)dataN * (double)log2Data / 2.0;
////////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[]) {
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
int main(int argc, char *argv[])
{
float *h_Data, *h_Kernel, *h_ResultCPU, *h_ResultGPU;
float *d_Data, *d_Kernel;
float *d_Data, *d_Kernel;
double delta, ref, sum_delta2, sum_ref2, L2norm, gpuTime;
double delta, ref, sum_delta2, sum_ref2, L2norm, gpuTime;
StopWatchInterface *hTimer = NULL;
int i;
StopWatchInterface *hTimer = NULL;
int i;
printf("%s Starting...\n\n", argv[0]);
printf("%s Starting...\n\n", argv[0]);
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
findCudaDevice(argc, (const char **)argv);
sdkCreateTimer(&hTimer);
sdkCreateTimer(&hTimer);
printf("Initializing data...\n");
printf("...allocating CPU memory\n");
h_Kernel = (float *)malloc(KERNEL_SIZE);
h_Data = (float *)malloc(DATA_SIZE);
h_ResultCPU = (float *)malloc(DATA_SIZE);
h_ResultGPU = (float *)malloc(DATA_SIZE);
printf("...allocating GPU memory\n");
checkCudaErrors(cudaMalloc((void **)&d_Kernel, DATA_SIZE));
checkCudaErrors(cudaMalloc((void **)&d_Data, DATA_SIZE));
printf("Initializing data...\n");
printf("...allocating CPU memory\n");
h_Kernel = (float *)malloc(KERNEL_SIZE);
h_Data = (float *)malloc(DATA_SIZE);
h_ResultCPU = (float *)malloc(DATA_SIZE);
h_ResultGPU = (float *)malloc(DATA_SIZE);
printf("...allocating GPU memory\n");
checkCudaErrors(cudaMalloc((void **)&d_Kernel, DATA_SIZE));
checkCudaErrors(cudaMalloc((void **)&d_Data, DATA_SIZE));
printf("...generating data\n");
printf("Data length: %i; kernel length: %i\n", dataN, kernelN);
srand(2007);
printf("...generating data\n");
printf("Data length: %i; kernel length: %i\n", dataN, kernelN);
srand(2007);
for (i = 0; i < kernelN; i++) {
h_Kernel[i] = (float)rand() / (float)RAND_MAX;
}
for (i = 0; i < kernelN; i++) {
h_Kernel[i] = (float)rand() / (float)RAND_MAX;
}
for (i = 0; i < dataN; i++) {
h_Data[i] = (float)rand() / (float)RAND_MAX;
}
for (i = 0; i < dataN; i++) {
h_Data[i] = (float)rand() / (float)RAND_MAX;
}
checkCudaErrors(cudaMemset(d_Kernel, 0, DATA_SIZE));
checkCudaErrors(
cudaMemcpy(d_Kernel, h_Kernel, KERNEL_SIZE, cudaMemcpyHostToDevice));
checkCudaErrors(
cudaMemcpy(d_Data, h_Data, DATA_SIZE, cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemset(d_Kernel, 0, DATA_SIZE));
checkCudaErrors(cudaMemcpy(d_Kernel, h_Kernel, KERNEL_SIZE, cudaMemcpyHostToDevice));
checkCudaErrors(cudaMemcpy(d_Data, h_Data, DATA_SIZE, cudaMemcpyHostToDevice));
printf("Running GPU dyadic convolution using Fast Walsh Transform...\n");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
fwtBatchGPU(d_Data, 1, log2Data);
fwtBatchGPU(d_Kernel, 1, log2Data);
modulateGPU(d_Data, d_Kernel, dataN);
fwtBatchGPU(d_Data, 1, log2Data);
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer);
printf("GPU time: %f ms; GOP/s: %f\n", gpuTime,
NOPS / (gpuTime * 0.001 * 1E+9));
printf("Running GPU dyadic convolution using Fast Walsh Transform...\n");
checkCudaErrors(cudaDeviceSynchronize());
sdkResetTimer(&hTimer);
sdkStartTimer(&hTimer);
fwtBatchGPU(d_Data, 1, log2Data);
fwtBatchGPU(d_Kernel, 1, log2Data);
modulateGPU(d_Data, d_Kernel, dataN);
fwtBatchGPU(d_Data, 1, log2Data);
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&hTimer);
gpuTime = sdkGetTimerValue(&hTimer);
printf("GPU time: %f ms; GOP/s: %f\n", gpuTime, NOPS / (gpuTime * 0.001 * 1E+9));
printf("Reading back GPU results...\n");
checkCudaErrors(
cudaMemcpy(h_ResultGPU, d_Data, DATA_SIZE, cudaMemcpyDeviceToHost));
printf("Reading back GPU results...\n");
checkCudaErrors(cudaMemcpy(h_ResultGPU, d_Data, DATA_SIZE, cudaMemcpyDeviceToHost));
printf("Running straightforward CPU dyadic convolution...\n");
dyadicConvolutionCPU(h_ResultCPU, h_Data, h_Kernel, log2Data, log2Kernel);
printf("Running straightforward CPU dyadic convolution...\n");
dyadicConvolutionCPU(h_ResultCPU, h_Data, h_Kernel, log2Data, log2Kernel);
printf("Comparing the results...\n");
sum_delta2 = 0;
sum_ref2 = 0;
printf("Comparing the results...\n");
sum_delta2 = 0;
sum_ref2 = 0;
for (i = 0; i < dataN; i++) {
delta = h_ResultCPU[i] - h_ResultGPU[i];
ref = h_ResultCPU[i];
sum_delta2 += delta * delta;
sum_ref2 += ref * ref;
}
for (i = 0; i < dataN; i++) {
delta = h_ResultCPU[i] - h_ResultGPU[i];
ref = h_ResultCPU[i];
sum_delta2 += delta * delta;
sum_ref2 += ref * ref;
}
L2norm = sqrt(sum_delta2 / sum_ref2);
L2norm = sqrt(sum_delta2 / sum_ref2);
printf("Shutting down...\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cudaFree(d_Data));
checkCudaErrors(cudaFree(d_Kernel));
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Data);
free(h_Kernel);
printf("Shutting down...\n");
sdkDeleteTimer(&hTimer);
checkCudaErrors(cudaFree(d_Data));
checkCudaErrors(cudaFree(d_Kernel));
free(h_ResultGPU);
free(h_ResultCPU);
free(h_Data);
free(h_Kernel);
printf("L2 norm: %E\n", L2norm);
printf(L2norm < 1e-6 ? "Test passed\n" : "Test failed!\n");
printf("L2 norm: %E\n", L2norm);
printf(L2norm < 1e-6 ? "Test passed\n" : "Test failed!\n");
}

View File

@@ -28,68 +28,72 @@
///////////////////////////////////////////////////////////////////////////////
// CPU Fast Walsh Transform
///////////////////////////////////////////////////////////////////////////////
extern "C" void fwtCPU(float *h_Output, float *h_Input, int log2N) {
const int N = 1 << log2N;
extern "C" void fwtCPU(float *h_Output, float *h_Input, int log2N)
{
const int N = 1 << log2N;
for (int pos = 0; pos < N; pos++) h_Output[pos] = h_Input[pos];
for (int pos = 0; pos < N; pos++)
h_Output[pos] = h_Input[pos];
// Cycle through stages with different butterfly strides
for (int stride = N / 2; stride >= 1; stride >>= 1) {
// Cycle through subvectors of (2 * stride) elements
for (int base = 0; base < N; base += 2 * stride)
// Cycle through stages with different butterfly strides
for (int stride = N / 2; stride >= 1; stride >>= 1) {
// Cycle through subvectors of (2 * stride) elements
for (int base = 0; base < N; base += 2 * stride)
// Butterfly index within subvector of (2 * stride) size
for (int j = 0; j < stride; j++) {
int i0 = base + j + 0;
int i1 = base + j + stride;
// Butterfly index within subvector of (2 * stride) size
for (int j = 0; j < stride; j++) {
int i0 = base + j + 0;
int i1 = base + j + stride;
float T1 = h_Output[i0];
float T2 = h_Output[i1];
h_Output[i0] = T1 + T2;
h_Output[i1] = T1 - T2;
}
}
float T1 = h_Output[i0];
float T2 = h_Output[i1];
h_Output[i0] = T1 + T2;
h_Output[i1] = T1 - T2;
}
}
}
///////////////////////////////////////////////////////////////////////////////
// Straightforward Walsh Transform: used to test both CPU and GPU FWT
// Slow. Uses doubles because of straightforward accumulation
///////////////////////////////////////////////////////////////////////////////
extern "C" void slowWTcpu(float *h_Output, float *h_Input, int log2N) {
const int N = 1 << log2N;
extern "C" void slowWTcpu(float *h_Output, float *h_Input, int log2N)
{
const int N = 1 << log2N;
for (int i = 0; i < N; i++) {
double sum = 0;
for (int i = 0; i < N; i++) {
double sum = 0;
for (int j = 0; j < N; j++) {
// Walsh-Hadamard quotient
double q = 1.0;
for (int j = 0; j < N; j++) {
// Walsh-Hadamard quotient
double q = 1.0;
for (int t = i & j; t != 0; t >>= 1)
if (t & 1) q = -q;
for (int t = i & j; t != 0; t >>= 1)
if (t & 1)
q = -q;
sum += q * h_Input[j];
sum += q * h_Input[j];
}
h_Output[i] = (float)sum;
}
h_Output[i] = (float)sum;
}
}
////////////////////////////////////////////////////////////////////////////////
// Reference CPU dyadic convolution.
// Extremely slow because of non-linear memory access patterns (cache thrashing)
////////////////////////////////////////////////////////////////////////////////
extern "C" void dyadicConvolutionCPU(float *h_Result, float *h_Data,
float *h_Kernel, int log2dataN,
int log2kernelN) {
const int dataN = 1 << log2dataN;
const int kernelN = 1 << log2kernelN;
extern "C" void dyadicConvolutionCPU(float *h_Result, float *h_Data, float *h_Kernel, int log2dataN, int log2kernelN)
{
const int dataN = 1 << log2dataN;
const int kernelN = 1 << log2kernelN;
for (int i = 0; i < dataN; i++) {
double sum = 0;
for (int i = 0; i < dataN; i++) {
double sum = 0;
for (int j = 0; j < kernelN; j++) sum += h_Data[i ^ j] * h_Kernel[j];
for (int j = 0; j < kernelN; j++)
sum += h_Data[i ^ j] * h_Kernel[j];
h_Result[i] = (float)sum;
}
h_Result[i] = (float)sum;
}
}

View File

@@ -40,146 +40,148 @@ namespace cg = cooperative_groups;
///////////////////////////////////////////////////////////////////////////////
#define ELEMENTARY_LOG2SIZE 11
__global__ void fwtBatch1Kernel(float *d_Output, float *d_Input, int log2N) {
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
const int N = 1 << log2N;
const int base = blockIdx.x << log2N;
__global__ void fwtBatch1Kernel(float *d_Output, float *d_Input, int log2N)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
const int N = 1 << log2N;
const int base = blockIdx.x << log2N;
//(2 ** 11) * 4 bytes == 8KB -- maximum s_data[] size for G80
extern __shared__ float s_data[];
float *d_Src = d_Input + base;
float *d_Dst = d_Output + base;
//(2 ** 11) * 4 bytes == 8KB -- maximum s_data[] size for G80
extern __shared__ float s_data[];
float *d_Src = d_Input + base;
float *d_Dst = d_Output + base;
for (int pos = threadIdx.x; pos < N; pos += blockDim.x) {
s_data[pos] = d_Src[pos];
}
// Main radix-4 stages
const int pos = threadIdx.x;
for (int stride = N >> 2; stride > 0; stride >>= 2) {
int lo = pos & (stride - 1);
int i0 = ((pos - lo) << 2) + lo;
int i1 = i0 + stride;
int i2 = i1 + stride;
int i3 = i2 + stride;
cg::sync(cta);
float D0 = s_data[i0];
float D1 = s_data[i1];
float D2 = s_data[i2];
float D3 = s_data[i3];
float T;
T = D0;
D0 = D0 + D2;
D2 = T - D2;
T = D1;
D1 = D1 + D3;
D3 = T - D3;
T = D0;
s_data[i0] = D0 + D1;
s_data[i1] = T - D1;
T = D2;
s_data[i2] = D2 + D3;
s_data[i3] = T - D3;
}
// Do single radix-2 stage for odd power of two
if (log2N & 1) {
cg::sync(cta);
for (int pos = threadIdx.x; pos < N / 2; pos += blockDim.x) {
int i0 = pos << 1;
int i1 = i0 + 1;
float D0 = s_data[i0];
float D1 = s_data[i1];
s_data[i0] = D0 + D1;
s_data[i1] = D0 - D1;
for (int pos = threadIdx.x; pos < N; pos += blockDim.x) {
s_data[pos] = d_Src[pos];
}
}
cg::sync(cta);
// Main radix-4 stages
const int pos = threadIdx.x;
for (int pos = threadIdx.x; pos < N; pos += blockDim.x) {
d_Dst[pos] = s_data[pos];
}
for (int stride = N >> 2; stride > 0; stride >>= 2) {
int lo = pos & (stride - 1);
int i0 = ((pos - lo) << 2) + lo;
int i1 = i0 + stride;
int i2 = i1 + stride;
int i3 = i2 + stride;
cg::sync(cta);
float D0 = s_data[i0];
float D1 = s_data[i1];
float D2 = s_data[i2];
float D3 = s_data[i3];
float T;
T = D0;
D0 = D0 + D2;
D2 = T - D2;
T = D1;
D1 = D1 + D3;
D3 = T - D3;
T = D0;
s_data[i0] = D0 + D1;
s_data[i1] = T - D1;
T = D2;
s_data[i2] = D2 + D3;
s_data[i3] = T - D3;
}
// Do single radix-2 stage for odd power of two
if (log2N & 1) {
cg::sync(cta);
for (int pos = threadIdx.x; pos < N / 2; pos += blockDim.x) {
int i0 = pos << 1;
int i1 = i0 + 1;
float D0 = s_data[i0];
float D1 = s_data[i1];
s_data[i0] = D0 + D1;
s_data[i1] = D0 - D1;
}
}
cg::sync(cta);
for (int pos = threadIdx.x; pos < N; pos += blockDim.x) {
d_Dst[pos] = s_data[pos];
}
}
////////////////////////////////////////////////////////////////////////////////
// Single in-global memory radix-4 Fast Walsh Transform pass
// (for strides exceeding elementary vector size)
////////////////////////////////////////////////////////////////////////////////
__global__ void fwtBatch2Kernel(float *d_Output, float *d_Input, int stride) {
const int pos = blockIdx.x * blockDim.x + threadIdx.x;
const int N = blockDim.x * gridDim.x * 4;
__global__ void fwtBatch2Kernel(float *d_Output, float *d_Input, int stride)
{
const int pos = blockIdx.x * blockDim.x + threadIdx.x;
const int N = blockDim.x * gridDim.x * 4;
float *d_Src = d_Input + blockIdx.y * N;
float *d_Dst = d_Output + blockIdx.y * N;
float *d_Src = d_Input + blockIdx.y * N;
float *d_Dst = d_Output + blockIdx.y * N;
int lo = pos & (stride - 1);
int i0 = ((pos - lo) << 2) + lo;
int i1 = i0 + stride;
int i2 = i1 + stride;
int i3 = i2 + stride;
int lo = pos & (stride - 1);
int i0 = ((pos - lo) << 2) + lo;
int i1 = i0 + stride;
int i2 = i1 + stride;
int i3 = i2 + stride;
float D0 = d_Src[i0];
float D1 = d_Src[i1];
float D2 = d_Src[i2];
float D3 = d_Src[i3];
float D0 = d_Src[i0];
float D1 = d_Src[i1];
float D2 = d_Src[i2];
float D3 = d_Src[i3];
float T;
T = D0;
D0 = D0 + D2;
D2 = T - D2;
T = D1;
D1 = D1 + D3;
D3 = T - D3;
T = D0;
d_Dst[i0] = D0 + D1;
d_Dst[i1] = T - D1;
T = D2;
d_Dst[i2] = D2 + D3;
d_Dst[i3] = T - D3;
float T;
T = D0;
D0 = D0 + D2;
D2 = T - D2;
T = D1;
D1 = D1 + D3;
D3 = T - D3;
T = D0;
d_Dst[i0] = D0 + D1;
d_Dst[i1] = T - D1;
T = D2;
d_Dst[i2] = D2 + D3;
d_Dst[i3] = T - D3;
}
////////////////////////////////////////////////////////////////////////////////
// Put everything together: batched Fast Walsh Transform CPU front-end
////////////////////////////////////////////////////////////////////////////////
void fwtBatchGPU(float *d_Data, int M, int log2N) {
const int THREAD_N = 256;
void fwtBatchGPU(float *d_Data, int M, int log2N)
{
const int THREAD_N = 256;
int N = 1 << log2N;
dim3 grid((1 << log2N) / (4 * THREAD_N), M, 1);
int N = 1 << log2N;
dim3 grid((1 << log2N) / (4 * THREAD_N), M, 1);
for (; log2N > ELEMENTARY_LOG2SIZE; log2N -= 2, N >>= 2, M <<= 2) {
fwtBatch2Kernel<<<grid, THREAD_N>>>(d_Data, d_Data, N / 4);
getLastCudaError("fwtBatch2Kernel() execution failed\n");
}
for (; log2N > ELEMENTARY_LOG2SIZE; log2N -= 2, N >>= 2, M <<= 2) {
fwtBatch2Kernel<<<grid, THREAD_N>>>(d_Data, d_Data, N / 4);
getLastCudaError("fwtBatch2Kernel() execution failed\n");
}
fwtBatch1Kernel<<<M, N / 4, N * sizeof(float)>>>(d_Data, d_Data, log2N);
getLastCudaError("fwtBatch1Kernel() execution failed\n");
fwtBatch1Kernel<<<M, N / 4, N * sizeof(float)>>>(d_Data, d_Data, log2N);
getLastCudaError("fwtBatch1Kernel() execution failed\n");
}
////////////////////////////////////////////////////////////////////////////////
// Modulate two arrays
////////////////////////////////////////////////////////////////////////////////
__global__ void modulateKernel(float *d_A, float *d_B, int N) {
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int numThreads = blockDim.x * gridDim.x;
float rcpN = 1.0f / (float)N;
__global__ void modulateKernel(float *d_A, float *d_B, int N)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int numThreads = blockDim.x * gridDim.x;
float rcpN = 1.0f / (float)N;
for (int pos = tid; pos < N; pos += numThreads) {
d_A[pos] *= d_B[pos] * rcpN;
}
for (int pos = tid; pos < N; pos += numThreads) {
d_A[pos] *= d_B[pos] * rcpN;
}
}
// Interface to modulateKernel()
void modulateGPU(float *d_A, float *d_B, int N) {
modulateKernel<<<128, 256>>>(d_A, d_B, N);
}
void modulateGPU(float *d_A, float *d_B, int N) { modulateKernel<<<128, 256>>>(d_A, d_B, N); }
#endif
#endif

View File

@@ -28,21 +28,20 @@
#ifndef DEFINES_H
#define DEFINES_H
#define DIM 512 // Square size of solver domain
#define DS (DIM * DIM) // Total domain size
#define CPADW (DIM / 2 + 1) // Padded width for real->complex in-place FFT
#define RPADW \
(2 * (DIM / 2 + 1)) // Padded width for real->complex in-place FFT
#define PDS (DIM * CPADW) // Padded total domain size
#define DIM 512 // Square size of solver domain
#define DS (DIM * DIM) // Total domain size
#define CPADW (DIM / 2 + 1) // Padded width for real->complex in-place FFT
#define RPADW (2 * (DIM / 2 + 1)) // Padded width for real->complex in-place FFT
#define PDS (DIM * CPADW) // Padded total domain size
#define DT 0.09f // Delta T for interative solver
#define VIS 0.0025f // Viscosity constant
#define FORCE (5.8f * DIM) // Force scale factor
#define FR 4 // Force update radius
#define DT 0.09f // Delta T for interative solver
#define VIS 0.0025f // Viscosity constant
#define FORCE (5.8f * DIM) // Force scale factor
#define FR 4 // Force update radius
#define TILEX 64 // Tile width
#define TILEY 64 // Tile height
#define TIDSX 64 // Tids in X
#define TIDSY 4 // Tids in Y
#define TILEX 64 // Tile width
#define TILEY 64 // Tile height
#define TIDSX 64 // Tids in X
#define TIDSY 4 // Tids in Y
#endif

View File

@@ -39,21 +39,21 @@
#endif
// Includes
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// CUDA standard includes
#include <cuda_runtime.h>
#include <cuda_gl_interop.h>
#include <cuda_runtime.h>
// CUDA FFT Libraries
#include <cufft.h>
// CUDA helper functions
#include <helper_cuda.h>
#include <helper_functions.h>
#include <rendercheck_gl.h>
#include <helper_cuda.h>
#include "defines.h"
#include "fluidsGL_kernels.h"
@@ -73,34 +73,34 @@ void cleanup(void);
void reshape(int x, int y);
// CUFFT plan handle
cufftHandle planr2c;
cufftHandle planc2r;
cufftHandle planr2c;
cufftHandle planc2r;
static cData *vxfield = NULL;
static cData *vyfield = NULL;
cData *hvfield = NULL;
cData *dvfield = NULL;
static int wWidth = MAX(512, DIM);
cData *hvfield = NULL;
cData *dvfield = NULL;
static int wWidth = MAX(512, DIM);
static int wHeight = MAX(512, DIM);
static int clicked = 0;
static int fpsCount = 0;
static int fpsLimit = 1;
StopWatchInterface *timer = NULL;
static int clicked = 0;
static int fpsCount = 0;
static int fpsLimit = 1;
StopWatchInterface *timer = NULL;
// Particle data
GLuint vbo = 0; // OpenGL vertex buffer object
struct cudaGraphicsResource *cuda_vbo_resource; // handles OpenGL-CUDA exchange
static cData *particles = NULL; // particle positions in host memory
static int lastx = 0, lasty = 0;
GLuint vbo = 0; // OpenGL vertex buffer object
struct cudaGraphicsResource *cuda_vbo_resource; // handles OpenGL-CUDA exchange
static cData *particles = NULL; // particle positions in host memory
static int lastx = 0, lasty = 0;
// Texture pitch
size_t tPitch = 0; // Now this is compatible with gcc in 64-bit
size_t tPitch = 0; // Now this is compatible with gcc in 64-bit
char *ref_file = NULL;
bool g_bQAAddTestForce = true;
int g_iFrameToCompare = 100;
int g_TotalErrors = 0;
char *ref_file = NULL;
bool g_bQAAddTestForce = true;
int g_iFrameToCompare = 100;
int g_TotalErrors = 0;
bool g_bExitESC = false;
@@ -109,389 +109,388 @@ CheckRender *g_CheckRender = NULL;
void autoTest(char **);
extern "C" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx,
float fy, int r);
extern "C" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx,
int dy, float dt);
extern "C" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt,
float visc);
extern "C" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx,
int dy);
extern "C" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r);
extern "C" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt);
extern "C" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt, float visc);
extern "C" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy);
extern "C" void advectParticles(GLuint vbo, cData *v, int dx, int dy, float dt);
void simulateFluids(void) {
// simulate fluid
advectVelocity(dvfield, (float *)vxfield, (float *)vyfield, DIM, RPADW, DIM,
DT);
diffuseProject(vxfield, vyfield, CPADW, DIM, DT, VIS);
updateVelocity(dvfield, (float *)vxfield, (float *)vyfield, DIM, RPADW, DIM);
advectParticles(vbo, dvfield, DIM, DIM, DT);
void simulateFluids(void)
{
// simulate fluid
advectVelocity(dvfield, (float *)vxfield, (float *)vyfield, DIM, RPADW, DIM, DT);
diffuseProject(vxfield, vyfield, CPADW, DIM, DT, VIS);
updateVelocity(dvfield, (float *)vxfield, (float *)vyfield, DIM, RPADW, DIM);
advectParticles(vbo, dvfield, DIM, DIM, DT);
}
void display(void) {
if (!ref_file) {
sdkStartTimer(&timer);
simulateFluids();
}
// render points from vertex buffer
glClear(GL_COLOR_BUFFER_BIT);
glColor4f(0, 1, 0, 0.5f);
glPointSize(1);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexPointer(2, GL_FLOAT, 0, NULL);
glDrawArrays(GL_POINTS, 0, DS);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
if (ref_file) {
return;
}
// Finish timing before swap buffers to avoid refresh sync
sdkStopTimer(&timer);
glutSwapBuffers();
fpsCount++;
if (fpsCount == fpsLimit) {
char fps[256];
float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);
sprintf(fps, "Cuda/GL Stable Fluids (%d x %d): %3.1f fps", DIM, DIM, ifps);
glutSetWindowTitle(fps);
fpsCount = 0;
fpsLimit = (int)MAX(ifps, 1.f);
sdkResetTimer(&timer);
}
glutPostRedisplay();
}
void autoTest(char **argv) {
CFrameBufferObject *fbo =
new CFrameBufferObject(wWidth, wHeight, 4, false, GL_TEXTURE_2D);
g_CheckRender = new CheckFBO(wWidth, wHeight, 4, fbo);
g_CheckRender->setPixelFormat(GL_RGBA);
g_CheckRender->setExecPath(argv[0]);
g_CheckRender->EnableQAReadback(true);
fbo->bindRenderPath();
reshape(wWidth, wHeight);
for (int count = 0; count < g_iFrameToCompare; count++) {
simulateFluids();
// add in a little force so the automated testing is interesting.
if (ref_file) {
int x = wWidth / (count + 1);
int y = wHeight / (count + 1);
float fx = (x / (float)wWidth);
float fy = (y / (float)wHeight);
int nx = (int)(fx * DIM);
int ny = (int)(fy * DIM);
int ddx = 35;
int ddy = 35;
fx = ddx / (float)wWidth;
fy = ddy / (float)wHeight;
int spy = ny - FR;
int spx = nx - FR;
addForces(dvfield, DIM, DIM, spx, spy, FORCE * DT * fx, FORCE * DT * fy,
FR);
lastx = x;
lasty = y;
void display(void)
{
if (!ref_file) {
sdkStartTimer(&timer);
simulateFluids();
}
}
display();
// render points from vertex buffer
glClear(GL_COLOR_BUFFER_BIT);
glColor4f(0, 1, 0, 0.5f);
glPointSize(1);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexPointer(2, GL_FLOAT, 0, NULL);
glDrawArrays(GL_POINTS, 0, DS);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
fbo->unbindRenderPath();
if (ref_file) {
return;
}
// compare to official reference image, printing PASS or FAIL.
printf("> (Frame %d) Readback BackBuffer\n", 100);
g_CheckRender->readback(wWidth, wHeight);
g_CheckRender->savePPM("fluidsGL.ppm", true, NULL);
// Finish timing before swap buffers to avoid refresh sync
sdkStopTimer(&timer);
glutSwapBuffers();
if (!g_CheckRender->PPMvsPPM("fluidsGL.ppm", ref_file, MAX_EPSILON_ERROR,
0.25f)) {
g_TotalErrors++;
}
fpsCount++;
if (fpsCount == fpsLimit) {
char fps[256];
float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);
sprintf(fps, "Cuda/GL Stable Fluids (%d x %d): %3.1f fps", DIM, DIM, ifps);
glutSetWindowTitle(fps);
fpsCount = 0;
fpsLimit = (int)MAX(ifps, 1.f);
sdkResetTimer(&timer);
}
glutPostRedisplay();
}
void autoTest(char **argv)
{
CFrameBufferObject *fbo = new CFrameBufferObject(wWidth, wHeight, 4, false, GL_TEXTURE_2D);
g_CheckRender = new CheckFBO(wWidth, wHeight, 4, fbo);
g_CheckRender->setPixelFormat(GL_RGBA);
g_CheckRender->setExecPath(argv[0]);
g_CheckRender->EnableQAReadback(true);
fbo->bindRenderPath();
reshape(wWidth, wHeight);
for (int count = 0; count < g_iFrameToCompare; count++) {
simulateFluids();
// add in a little force so the automated testing is interesting.
if (ref_file) {
int x = wWidth / (count + 1);
int y = wHeight / (count + 1);
float fx = (x / (float)wWidth);
float fy = (y / (float)wHeight);
int nx = (int)(fx * DIM);
int ny = (int)(fy * DIM);
int ddx = 35;
int ddy = 35;
fx = ddx / (float)wWidth;
fy = ddy / (float)wHeight;
int spy = ny - FR;
int spx = nx - FR;
addForces(dvfield, DIM, DIM, spx, spy, FORCE * DT * fx, FORCE * DT * fy, FR);
lastx = x;
lasty = y;
}
}
display();
fbo->unbindRenderPath();
// compare to official reference image, printing PASS or FAIL.
printf("> (Frame %d) Readback BackBuffer\n", 100);
g_CheckRender->readback(wWidth, wHeight);
g_CheckRender->savePPM("fluidsGL.ppm", true, NULL);
if (!g_CheckRender->PPMvsPPM("fluidsGL.ppm", ref_file, MAX_EPSILON_ERROR, 0.25f)) {
g_TotalErrors++;
}
}
// very simple von neumann middle-square prng. can't use rand() in -qatest
// mode because its implementation varies across platforms which makes testing
// for consistency in the important parts of this program difficult.
float myrand(void) {
static int seed = 72191;
char sq[22];
float myrand(void)
{
static int seed = 72191;
char sq[22];
if (ref_file) {
seed *= seed;
sprintf(sq, "%010d", seed);
// pull the middle 5 digits out of sq
sq[8] = 0;
seed = atoi(&sq[3]);
if (ref_file) {
seed *= seed;
sprintf(sq, "%010d", seed);
// pull the middle 5 digits out of sq
sq[8] = 0;
seed = atoi(&sq[3]);
return seed / 99999.f;
} else {
return rand() / (float)RAND_MAX;
}
}
void initParticles(cData *p, int dx, int dy) {
int i, j;
for (i = 0; i < dy; i++) {
for (j = 0; j < dx; j++) {
p[i * dx + j].x = (j + 0.5f + (myrand() - 0.5f)) / dx;
p[i * dx + j].y = (i + 0.5f + (myrand() - 0.5f)) / dy;
return seed / 99999.f;
}
else {
return rand() / (float)RAND_MAX;
}
}
}
void keyboard(unsigned char key, int x, int y) {
switch (key) {
void initParticles(cData *p, int dx, int dy)
{
int i, j;
for (i = 0; i < dy; i++) {
for (j = 0; j < dx; j++) {
p[i * dx + j].x = (j + 0.5f + (myrand() - 0.5f)) / dx;
p[i * dx + j].y = (i + 0.5f + (myrand() - 0.5f)) / dy;
}
}
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27:
g_bExitESC = true;
g_bExitESC = true;
#if defined(__APPLE__) || defined(MACOSX)
exit(EXIT_SUCCESS);
exit(EXIT_SUCCESS);
#else
glutDestroyWindow(glutGetWindow());
return;
glutDestroyWindow(glutGetWindow());
return;
#endif
break;
break;
case 'r':
memset(hvfield, 0, sizeof(cData) * DS);
cudaMemcpy(dvfield, hvfield, sizeof(cData) * DS, cudaMemcpyHostToDevice);
memset(hvfield, 0, sizeof(cData) * DS);
cudaMemcpy(dvfield, hvfield, sizeof(cData) * DS, cudaMemcpyHostToDevice);
initParticles(particles, DIM, DIM);
initParticles(particles, DIM, DIM);
cudaGraphicsUnregisterResource(cuda_vbo_resource);
cudaGraphicsUnregisterResource(cuda_vbo_resource);
getLastCudaError("cudaGraphicsUnregisterBuffer failed");
getLastCudaError("cudaGraphicsUnregisterBuffer failed");
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(cData) * DS, particles,
GL_DYNAMIC_DRAW_ARB);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(cData) * DS, particles, GL_DYNAMIC_DRAW_ARB);
glBindBuffer(GL_ARRAY_BUFFER, 0);
cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo,
cudaGraphicsMapFlagsNone);
cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsNone);
getLastCudaError("cudaGraphicsGLRegisterBuffer failed");
break;
getLastCudaError("cudaGraphicsGLRegisterBuffer failed");
break;
default:
break;
}
break;
}
}
void click(int button, int updown, int x, int y) {
lastx = x;
lasty = y;
clicked = !clicked;
void click(int button, int updown, int x, int y)
{
lastx = x;
lasty = y;
clicked = !clicked;
}
void motion(int x, int y) {
// Convert motion coordinates to domain
float fx = (lastx / (float)wWidth);
float fy = (lasty / (float)wHeight);
int nx = (int)(fx * DIM);
int ny = (int)(fy * DIM);
void motion(int x, int y)
{
// Convert motion coordinates to domain
float fx = (lastx / (float)wWidth);
float fy = (lasty / (float)wHeight);
int nx = (int)(fx * DIM);
int ny = (int)(fy * DIM);
if (clicked && nx < DIM - FR && nx > FR - 1 && ny < DIM - FR && ny > FR - 1) {
int ddx = x - lastx;
int ddy = y - lasty;
fx = ddx / (float)wWidth;
fy = ddy / (float)wHeight;
int spy = ny - FR;
int spx = nx - FR;
addForces(dvfield, DIM, DIM, spx, spy, FORCE * DT * fx, FORCE * DT * fy,
FR);
lastx = x;
lasty = y;
}
if (clicked && nx < DIM - FR && nx > FR - 1 && ny < DIM - FR && ny > FR - 1) {
int ddx = x - lastx;
int ddy = y - lasty;
fx = ddx / (float)wWidth;
fy = ddy / (float)wHeight;
int spy = ny - FR;
int spx = nx - FR;
addForces(dvfield, DIM, DIM, spx, spy, FORCE * DT * fx, FORCE * DT * fy, FR);
lastx = x;
lasty = y;
}
glutPostRedisplay();
glutPostRedisplay();
}
void reshape(int x, int y) {
wWidth = x;
wHeight = y;
glViewport(0, 0, x, y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 1, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glutPostRedisplay();
void reshape(int x, int y)
{
wWidth = x;
wHeight = y;
glViewport(0, 0, x, y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 1, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glutPostRedisplay();
}
void cleanup(void) {
cudaGraphicsUnregisterResource(cuda_vbo_resource);
void cleanup(void)
{
cudaGraphicsUnregisterResource(cuda_vbo_resource);
deleteTexture();
deleteTexture();
// Free all host and device resources
free(hvfield);
free(particles);
cudaFree(dvfield);
cudaFree(vxfield);
cudaFree(vyfield);
cufftDestroy(planr2c);
cufftDestroy(planc2r);
// Free all host and device resources
free(hvfield);
free(particles);
cudaFree(dvfield);
cudaFree(vxfield);
cudaFree(vyfield);
cufftDestroy(planr2c);
cufftDestroy(planc2r);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vbo);
sdkDeleteTimer(&timer);
sdkDeleteTimer(&timer);
}
int initGL(int *argc, char **argv) {
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(wWidth, wHeight);
glutCreateWindow("Compute Stable Fluids");
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMouseFunc(click);
glutMotionFunc(motion);
glutReshapeFunc(reshape);
int initGL(int *argc, char **argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(wWidth, wHeight);
glutCreateWindow("Compute Stable Fluids");
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMouseFunc(click);
glutMotionFunc(motion);
glutReshapeFunc(reshape);
if (!isGLVersionSupported(1, 5)) {
fprintf(stderr, "ERROR: Support for OpenGL 1.5 is missing");
fflush(stderr);
return false;
}
if (!isGLVersionSupported(1, 5)) {
fprintf(stderr, "ERROR: Support for OpenGL 1.5 is missing");
fflush(stderr);
return false;
}
if (!areGLExtensionsSupported("GL_ARB_vertex_buffer_object")) {
fprintf(stderr, "ERROR: Support for necessary OpenGL extensions missing.");
fflush(stderr);
return false;
}
if (!areGLExtensionsSupported("GL_ARB_vertex_buffer_object")) {
fprintf(stderr, "ERROR: Support for necessary OpenGL extensions missing.");
fflush(stderr);
return false;
}
return true;
return true;
}
int main(int argc, char **argv) {
int devID;
cudaDeviceProp deviceProps;
int main(int argc, char **argv)
{
int devID;
cudaDeviceProp deviceProps;
#if defined(__linux__)
char *Xstatus = getenv("DISPLAY");
if (Xstatus == NULL) {
printf("Waiving execution as X server is not running\n");
exit(EXIT_WAIVED);
}
setenv("DISPLAY", ":0", 0);
char *Xstatus = getenv("DISPLAY");
if (Xstatus == NULL) {
printf("Waiving execution as X server is not running\n");
exit(EXIT_WAIVED);
}
setenv("DISPLAY", ":0", 0);
#endif
printf("%s Starting...\n\n", sSDKname);
printf("%s Starting...\n\n", sSDKname);
printf(
"NOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
printf("NOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
// First initialize OpenGL context, so we can properly set the GL for CUDA.
// This is necessary in order to achieve optimal performance with OpenGL/CUDA
// interop.
if (false == initGL(&argc, argv)) {
exit(EXIT_SUCCESS);
}
// First initialize OpenGL context, so we can properly set the GL for CUDA.
// This is necessary in order to achieve optimal performance with OpenGL/CUDA
// interop.
if (false == initGL(&argc, argv)) {
exit(EXIT_SUCCESS);
}
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
devID = findCudaDevice(argc, (const char **)argv);
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
devID = findCudaDevice(argc, (const char **)argv);
// get number of SMs on this GPU
checkCudaErrors(cudaGetDeviceProperties(&deviceProps, devID));
printf("CUDA device [%s] has %d Multi-Processors\n", deviceProps.name,
deviceProps.multiProcessorCount);
// get number of SMs on this GPU
checkCudaErrors(cudaGetDeviceProperties(&deviceProps, devID));
printf("CUDA device [%s] has %d Multi-Processors\n", deviceProps.name, deviceProps.multiProcessorCount);
// automated build testing harness
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
getCmdLineArgumentString(argc, (const char **)argv, "file", &ref_file);
}
// automated build testing harness
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
getCmdLineArgumentString(argc, (const char **)argv, "file", &ref_file);
}
// Allocate and initialize host data
GLint bsize;
// Allocate and initialize host data
GLint bsize;
sdkCreateTimer(&timer);
sdkResetTimer(&timer);
sdkCreateTimer(&timer);
sdkResetTimer(&timer);
hvfield = (cData *)malloc(sizeof(cData) * DS);
memset(hvfield, 0, sizeof(cData) * DS);
hvfield = (cData *)malloc(sizeof(cData) * DS);
memset(hvfield, 0, sizeof(cData) * DS);
// Allocate and initialize device data
cudaMallocPitch((void **)&dvfield, &tPitch, sizeof(cData) * DIM, DIM);
// Allocate and initialize device data
cudaMallocPitch((void **)&dvfield, &tPitch, sizeof(cData) * DIM, DIM);
cudaMemcpy(dvfield, hvfield, sizeof(cData) * DS, cudaMemcpyHostToDevice);
// Temporary complex velocity field data
cudaMalloc((void **)&vxfield, sizeof(cData) * PDS);
cudaMalloc((void **)&vyfield, sizeof(cData) * PDS);
cudaMemcpy(dvfield, hvfield, sizeof(cData) * DS, cudaMemcpyHostToDevice);
// Temporary complex velocity field data
cudaMalloc((void **)&vxfield, sizeof(cData) * PDS);
cudaMalloc((void **)&vyfield, sizeof(cData) * PDS);
setupTexture(DIM, DIM);
setupTexture(DIM, DIM);
// Create particle array
particles = (cData *)malloc(sizeof(cData) * DS);
memset(particles, 0, sizeof(cData) * DS);
// Create particle array
particles = (cData *)malloc(sizeof(cData) * DS);
memset(particles, 0, sizeof(cData) * DS);
initParticles(particles, DIM, DIM);
initParticles(particles, DIM, DIM);
// Create CUFFT transform plan configuration
checkCudaErrors(cufftPlan2d(&planr2c, DIM, DIM, CUFFT_R2C));
checkCudaErrors(cufftPlan2d(&planc2r, DIM, DIM, CUFFT_C2R));
// Create CUFFT transform plan configuration
checkCudaErrors(cufftPlan2d(&planr2c, DIM, DIM, CUFFT_R2C));
checkCudaErrors(cufftPlan2d(&planc2r, DIM, DIM, CUFFT_C2R));
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(cData) * DS, particles,
GL_DYNAMIC_DRAW_ARB);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(cData) * DS, particles, GL_DYNAMIC_DRAW_ARB);
glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bsize);
glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bsize);
if (bsize != (sizeof(cData) * DS)) goto EXTERR;
if (bsize != (sizeof(cData) * DS))
goto EXTERR;
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo,
cudaGraphicsMapFlagsNone));
getLastCudaError("cudaGraphicsGLRegisterBuffer failed");
checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsNone));
getLastCudaError("cudaGraphicsGLRegisterBuffer failed");
if (ref_file) {
autoTest(argv);
cleanup();
if (ref_file) {
autoTest(argv);
cleanup();
printf("[fluidsGL] - Test Results: %d Failures\n", g_TotalErrors);
exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
} else {
printf("[fluidsGL] - Test Results: %d Failures\n", g_TotalErrors);
exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
else {
#if defined(__APPLE__) || defined(MACOSX)
atexit(cleanup);
atexit(cleanup);
#else
glutCloseFunc(cleanup);
glutCloseFunc(cleanup);
#endif
glutMainLoop();
}
glutMainLoop();
}
if (!ref_file) {
exit(EXIT_SUCCESS);
}
if (!ref_file) {
exit(EXIT_SUCCESS);
}
return 0;
return 0;
EXTERR:
printf("Failed to initialize GL extensions.\n");
printf("Failed to initialize GL extensions.\n");
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}

View File

@@ -25,13 +25,12 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cuda_runtime.h>
#include <cufft.h> // CUDA FFT Libraries
#include <helper_cuda.h> // Helper functions for CUDA Error handling
#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>
#include <cufft.h> // CUDA FFT Libraries
#include <helper_cuda.h> // Helper functions for CUDA Error handling
// OpenGL Graphics includes
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
#include <helper_gl.h>
@@ -41,51 +40,52 @@
// Texture object for reading velocity field
cudaTextureObject_t texObj;
static cudaArray *array = NULL;
static cudaArray *array = NULL;
// Particle data
extern GLuint vbo; // OpenGL vertex buffer object
extern struct cudaGraphicsResource
*cuda_vbo_resource; // handles OpenGL-CUDA exchange
extern GLuint vbo; // OpenGL vertex buffer object
extern struct cudaGraphicsResource *cuda_vbo_resource; // handles OpenGL-CUDA exchange
// Texture pitch
extern size_t tPitch;
extern size_t tPitch;
extern cufftHandle planr2c;
extern cufftHandle planc2r;
cData *vxfield = NULL;
cData *vyfield = NULL;
cData *vxfield = NULL;
cData *vyfield = NULL;
void setupTexture(int x, int y) {
cudaChannelFormatDesc desc = cudaCreateChannelDesc<float2>();
void setupTexture(int x, int y)
{
cudaChannelFormatDesc desc = cudaCreateChannelDesc<float2>();
cudaMallocArray(&array, &desc, y, x);
getLastCudaError("cudaMalloc failed");
cudaMallocArray(&array, &desc, y, x);
getLastCudaError("cudaMalloc failed");
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeArray;
texRes.res.array.array = array;
texRes.resType = cudaResourceTypeArray;
texRes.res.array.array = array;
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
texDescr.normalizedCoords = false;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(cudaCreateTextureObject(&texObj, &texRes, &texDescr, NULL));
checkCudaErrors(cudaCreateTextureObject(&texObj, &texRes, &texDescr, NULL));
}
void updateTexture(cData *data, size_t wib, size_t h, size_t pitch) {
checkCudaErrors(cudaMemcpy2DToArray(array, 0, 0, data, pitch, wib, h,
cudaMemcpyDeviceToDevice));
void updateTexture(cData *data, size_t wib, size_t h, size_t pitch)
{
checkCudaErrors(cudaMemcpy2DToArray(array, 0, 0, data, pitch, wib, h, cudaMemcpyDeviceToDevice));
}
void deleteTexture(void) {
checkCudaErrors(cudaDestroyTextureObject(texObj));
checkCudaErrors(cudaFreeArray(array));
void deleteTexture(void)
{
checkCudaErrors(cudaDestroyTextureObject(texObj));
checkCudaErrors(cudaFreeArray(array));
}
// Note that these kernels are designed to work with arbitrary
@@ -99,54 +99,61 @@ void deleteTexture(void) {
// This method adds constant force vectors to the velocity field
// stored in 'v' according to v(x,t+1) = v(x,t) + dt * f.
__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy,
float fx, float fy, int r, size_t pitch) {
int tx = threadIdx.x;
int ty = threadIdx.y;
cData *fj = (cData *)((char *)v + (ty + spy) * pitch) + tx + spx;
__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r, size_t pitch)
{
int tx = threadIdx.x;
int ty = threadIdx.y;
cData *fj = (cData *)((char *)v + (ty + spy) * pitch) + tx + spx;
cData vterm = *fj;
tx -= r;
ty -= r;
float s = 1.f / (1.f + tx * tx * tx * tx + ty * ty * ty * ty);
vterm.x += s * fx;
vterm.y += s * fy;
*fj = vterm;
cData vterm = *fj;
tx -= r;
ty -= r;
float s = 1.f / (1.f + tx * tx * tx * tx + ty * ty * ty * ty);
vterm.x += s * fx;
vterm.y += s * fy;
*fj = vterm;
}
// This method performs the velocity advection step, where we
// trace velocity vectors back in time to update each grid cell.
// That is, v(x,t+1) = v(p(x,-dt),t). Here we perform bilinear
// interpolation in the velocity space.
__global__ void advectVelocity_k(cData *v, float *vx, float *vy, int dx,
int pdx, int dy, float dt, int lb,
cudaTextureObject_t texObject) {
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
__global__ void advectVelocity_k(cData *v,
float *vx,
float *vy,
int dx,
int pdx,
int dy,
float dt,
int lb,
cudaTextureObject_t texObject)
{
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
cData vterm, ploc;
float vxterm, vyterm;
cData vterm, ploc;
float vxterm, vyterm;
// gtidx is the domain location in x for this thread
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
// gtidx is the domain location in x for this thread
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
if (fi < dy) {
int fj = fi * pdx + gtidx;
vterm = tex2D<cData>(texObject, (float)gtidx, (float)fi);
ploc.x = (gtidx + 0.5f) - (dt * vterm.x * dx);
ploc.y = (fi + 0.5f) - (dt * vterm.y * dy);
vterm = tex2D<cData>(texObject, ploc.x, ploc.y);
vxterm = vterm.x;
vyterm = vterm.y;
vx[fj] = vxterm;
vy[fj] = vyterm;
}
if (fi < dy) {
int fj = fi * pdx + gtidx;
vterm = tex2D<cData>(texObject, (float)gtidx, (float)fi);
ploc.x = (gtidx + 0.5f) - (dt * vterm.x * dx);
ploc.y = (fi + 0.5f) - (dt * vterm.y * dy);
vterm = tex2D<cData>(texObject, ploc.x, ploc.y);
vxterm = vterm.x;
vyterm = vterm.y;
vx[fj] = vxterm;
vy[fj] = vyterm;
}
}
}
}
}
// This method performs velocity diffusion and forces mass conservation
@@ -157,204 +164,197 @@ __global__ void advectVelocity_k(cData *v, float *vx, float *vy, int dx,
// and k is the wavenumber. The projection step forces the Fourier
// velocity vectors to be orthogonal to the vectors for each
// wavenumber: v(k,t) = v(k,t) - ((k dot v(k,t) * k) / k^2.
__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt,
float visc, int lb) {
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt, float visc, int lb)
{
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
cData xterm, yterm;
cData xterm, yterm;
// gtidx is the domain location in x for this thread
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
// gtidx is the domain location in x for this thread
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
if (fi < dy) {
int fj = fi * dx + gtidx;
xterm = vx[fj];
yterm = vy[fj];
if (fi < dy) {
int fj = fi * dx + gtidx;
xterm = vx[fj];
yterm = vy[fj];
// Compute the index of the wavenumber based on the
// data order produced by a standard NN FFT.
int iix = gtidx;
int iiy = (fi > dy / 2) ? (fi - (dy)) : fi;
// Compute the index of the wavenumber based on the
// data order produced by a standard NN FFT.
int iix = gtidx;
int iiy = (fi > dy / 2) ? (fi - (dy)) : fi;
// Velocity diffusion
float kk = (float)(iix * iix + iiy * iiy); // k^2
float diff = 1.f / (1.f + visc * dt * kk);
xterm.x *= diff;
xterm.y *= diff;
yterm.x *= diff;
yterm.y *= diff;
// Velocity diffusion
float kk = (float)(iix * iix + iiy * iiy); // k^2
float diff = 1.f / (1.f + visc * dt * kk);
xterm.x *= diff;
xterm.y *= diff;
yterm.x *= diff;
yterm.y *= diff;
// Velocity projection
if (kk > 0.f) {
float rkk = 1.f / kk;
// Real portion of velocity projection
float rkp = (iix * xterm.x + iiy * yterm.x);
// Imaginary portion of velocity projection
float ikp = (iix * xterm.y + iiy * yterm.y);
xterm.x -= rkk * rkp * iix;
xterm.y -= rkk * ikp * iix;
yterm.x -= rkk * rkp * iiy;
yterm.y -= rkk * ikp * iiy;
// Velocity projection
if (kk > 0.f) {
float rkk = 1.f / kk;
// Real portion of velocity projection
float rkp = (iix * xterm.x + iiy * yterm.x);
// Imaginary portion of velocity projection
float ikp = (iix * xterm.y + iiy * yterm.y);
xterm.x -= rkk * rkp * iix;
xterm.y -= rkk * ikp * iix;
yterm.x -= rkk * rkp * iiy;
yterm.y -= rkk * ikp * iiy;
}
vx[fj] = xterm;
vy[fj] = yterm;
}
}
vx[fj] = xterm;
vy[fj] = yterm;
}
}
}
}
// This method updates the velocity field 'v' using the two complex
// arrays from the previous step: 'vx' and 'vy'. Here we scale the
// real components by 1/(dx*dy) to account for an unnormalized FFT.
__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx,
int pdx, int dy, int lb, size_t pitch) {
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, int lb, size_t pitch)
{
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
float vxterm, vyterm;
cData nvterm;
float vxterm, vyterm;
cData nvterm;
// gtidx is the domain location in x for this thread
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
// gtidx is the domain location in x for this thread
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
if (fi < dy) {
int fjr = fi * pdx + gtidx;
vxterm = vx[fjr];
vyterm = vy[fjr];
if (fi < dy) {
int fjr = fi * pdx + gtidx;
vxterm = vx[fjr];
vyterm = vy[fjr];
// Normalize the result of the inverse FFT
float scale = 1.f / (dx * dy);
nvterm.x = vxterm * scale;
nvterm.y = vyterm * scale;
// Normalize the result of the inverse FFT
float scale = 1.f / (dx * dy);
nvterm.x = vxterm * scale;
nvterm.y = vyterm * scale;
cData *fj = (cData *)((char *)v + fi * pitch) + gtidx;
*fj = nvterm;
}
} // If this thread is inside the domain in Y
} // If this thread is inside the domain in X
cData *fj = (cData *)((char *)v + fi * pitch) + gtidx;
*fj = nvterm;
}
} // If this thread is inside the domain in Y
} // If this thread is inside the domain in X
}
// This method updates the particles by moving particle positions
// according to the velocity field and time step. That is, for each
// particle: p(t+1) = p(t) + dt * v(p(t)).
__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy,
float dt, int lb, size_t pitch) {
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy, float dt, int lb, size_t pitch)
{
int gtidx = blockIdx.x * blockDim.x + threadIdx.x;
int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;
int p;
// gtidx is the domain location in x for this thread
cData pterm, vterm;
// gtidx is the domain location in x for this thread
cData pterm, vterm;
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
if (gtidx < dx) {
for (p = 0; p < lb; p++) {
// fi is the domain location in y for this thread
int fi = gtidy + p;
if (fi < dy) {
int fj = fi * dx + gtidx;
pterm = part[fj];
if (fi < dy) {
int fj = fi * dx + gtidx;
pterm = part[fj];
int xvi = ((int)(pterm.x * dx));
int yvi = ((int)(pterm.y * dy));
vterm = *((cData *)((char *)v + yvi * pitch) + xvi);
int xvi = ((int)(pterm.x * dx));
int yvi = ((int)(pterm.y * dy));
vterm = *((cData *)((char *)v + yvi * pitch) + xvi);
pterm.x += dt * vterm.x;
pterm.x = pterm.x - (int)pterm.x;
pterm.x += 1.f;
pterm.x = pterm.x - (int)pterm.x;
pterm.y += dt * vterm.y;
pterm.y = pterm.y - (int)pterm.y;
pterm.y += 1.f;
pterm.y = pterm.y - (int)pterm.y;
pterm.x += dt * vterm.x;
pterm.x = pterm.x - (int)pterm.x;
pterm.x += 1.f;
pterm.x = pterm.x - (int)pterm.x;
pterm.y += dt * vterm.y;
pterm.y = pterm.y - (int)pterm.y;
pterm.y += 1.f;
pterm.y = pterm.y - (int)pterm.y;
part[fj] = pterm;
}
} // If this thread is inside the domain in Y
} // If this thread is inside the domain in X
part[fj] = pterm;
}
} // If this thread is inside the domain in Y
} // If this thread is inside the domain in X
}
// These are the external function calls necessary for launching fluid
// simulation
extern "C" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx,
float fy, int r) {
dim3 tids(2 * r + 1, 2 * r + 1);
extern "C" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r)
{
dim3 tids(2 * r + 1, 2 * r + 1);
addForces_k<<<1, tids>>>(v, dx, dy, spx, spy, fx, fy, r, tPitch);
getLastCudaError("addForces_k failed.");
addForces_k<<<1, tids>>>(v, dx, dy, spx, spy, fx, fy, r, tPitch);
getLastCudaError("addForces_k failed.");
}
extern "C" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx,
int dy, float dt) {
dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1),
(dy / TILEY) + (!(dy % TILEY) ? 0 : 1));
extern "C" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt)
{
dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));
dim3 tids(TIDSX, TIDSY);
dim3 tids(TIDSX, TIDSY);
updateTexture(v, DIM * sizeof(cData), DIM, tPitch);
advectVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, dt, TILEY / TIDSY,
texObj);
updateTexture(v, DIM * sizeof(cData), DIM, tPitch);
advectVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, dt, TILEY / TIDSY, texObj);
getLastCudaError("advectVelocity_k failed.");
getLastCudaError("advectVelocity_k failed.");
}
extern "C" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt,
float visc) {
// Forward FFT
checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vx, (cufftComplex *)vx));
checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vy, (cufftComplex *)vy));
extern "C" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt, float visc)
{
// Forward FFT
checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vx, (cufftComplex *)vx));
checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vy, (cufftComplex *)vy));
uint3 grid = make_uint3((dx / TILEX) + (!(dx % TILEX) ? 0 : 1),
(dy / TILEY) + (!(dy % TILEY) ? 0 : 1), 1);
uint3 tids = make_uint3(TIDSX, TIDSY, 1);
uint3 grid = make_uint3((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1), 1);
uint3 tids = make_uint3(TIDSX, TIDSY, 1);
diffuseProject_k<<<grid, tids>>>(vx, vy, dx, dy, dt, visc, TILEY / TIDSY);
getLastCudaError("diffuseProject_k failed.");
diffuseProject_k<<<grid, tids>>>(vx, vy, dx, dy, dt, visc, TILEY / TIDSY);
getLastCudaError("diffuseProject_k failed.");
// Inverse FFT
checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vx, (cufftReal *)vx));
checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vy, (cufftReal *)vy));
// Inverse FFT
checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vx, (cufftReal *)vx));
checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vy, (cufftReal *)vy));
}
extern "C" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx,
int dy) {
dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1),
(dy / TILEY) + (!(dy % TILEY) ? 0 : 1));
dim3 tids(TIDSX, TIDSY);
extern "C" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy)
{
dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));
dim3 tids(TIDSX, TIDSY);
updateVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, TILEY / TIDSY,
tPitch);
getLastCudaError("updateVelocity_k failed.");
updateVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, TILEY / TIDSY, tPitch);
getLastCudaError("updateVelocity_k failed.");
}
extern "C" void advectParticles(GLuint vbo, cData *v, int dx, int dy,
float dt) {
dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1),
(dy / TILEY) + (!(dy % TILEY) ? 0 : 1));
dim3 tids(TIDSX, TIDSY);
extern "C" void advectParticles(GLuint vbo, cData *v, int dx, int dy, float dt)
{
dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));
dim3 tids(TIDSX, TIDSY);
cData *p;
cudaGraphicsMapResources(1, &cuda_vbo_resource, 0);
getLastCudaError("cudaGraphicsMapResources failed");
cData *p;
cudaGraphicsMapResources(1, &cuda_vbo_resource, 0);
getLastCudaError("cudaGraphicsMapResources failed");
size_t num_bytes;
cudaGraphicsResourceGetMappedPointer((void **)&p, &num_bytes,
cuda_vbo_resource);
getLastCudaError("cudaGraphicsResourceGetMappedPointer failed");
size_t num_bytes;
cudaGraphicsResourceGetMappedPointer((void **)&p, &num_bytes, cuda_vbo_resource);
getLastCudaError("cudaGraphicsResourceGetMappedPointer failed");
advectParticles_k<<<grid, tids>>>(p, v, dx, dy, dt, TILEY / TIDSY, tPitch);
getLastCudaError("advectParticles_k failed.");
advectParticles_k<<<grid, tids>>>(p, v, dx, dy, dt, TILEY / TIDSY, tPitch);
getLastCudaError("advectParticles_k failed.");
cudaGraphicsUnmapResources(1, &cuda_vbo_resource, 0);
getLastCudaError("cudaGraphicsUnmapResources failed");
cudaGraphicsUnmapResources(1, &cuda_vbo_resource, 0);
getLastCudaError("cudaGraphicsUnmapResources failed");
}

View File

@@ -39,16 +39,14 @@ void deleteTexture(void);
// This method adds constant force vectors to the velocity field
// stored in 'v' according to v(x,t+1) = v(x,t) + dt * f.
__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy,
float fx, float fy, int r, size_t pitch);
__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r, size_t pitch);
// This method performs the velocity advection step, where we
// trace velocity vectors back in time to update each grid cell.
// That is, v(x,t+1) = v(p(x,-dt),t). Here we perform bilinear
// interpolation in the velocity space.
__global__ void advectVelocity_k(cData *v, float *vx, float *vy, int dx,
int pdx, int dy, float dt, int lb,
cudaTextureObject_t tex);
__global__ void
advectVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt, int lb, cudaTextureObject_t tex);
// This method performs velocity diffusion and forces mass conservation
// in the frequency domain. The inputs 'vx' and 'vy' are complex-valued
@@ -58,19 +56,16 @@ __global__ void advectVelocity_k(cData *v, float *vx, float *vy, int dx,
// and k is the wavenumber. The projection step forces the Fourier
// velocity vectors to be orthogonal to the wave wave vectors for each
// wavenumber: v(k,t) = v(k,t) - ((k dot v(k,t) * k) / k^2.
__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt,
float visc, int lb);
__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt, float visc, int lb);
// This method updates the velocity field 'v' using the two complex
// arrays from the previous step: 'vx' and 'vy'. Here we scale the
// real components by 1/(dx*dy) to account for an unnormalized FFT.
__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx,
int pdx, int dy, int lb, size_t pitch);
__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, int lb, size_t pitch);
// This method updates the particles by moving particle positions
// according to the velocity field and time step. That is, for each
// particle: p(t+1) = p(t) + dt * v(p(t)).
__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy,
float dt, int lb, size_t pitch);
__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy, float dt, int lb, size_t pitch);
#endif

View File

@@ -37,16 +37,14 @@ void deleteTexture(void);
// This method adds constant force vectors to the velocity field
// stored in 'v' according to v(x,t+1) = v(x,t) + dt * f.
__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy,
float fx, float fy, int r, size_t pitch);
__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r, size_t pitch);
// This method performs the velocity advection step, where we
// trace velocity vectors back in time to update each grid cell.
// That is, v(x,t+1) = v(p(x,-dt),t). Here we perform bilinear
// interpolation in the velocity space.
__global__ void advectVelocity_k(cData *v, float *vx, float *vy, int dx,
int pdx, int dy, float dt, int lb,
cudaTextureObject_t tex);
__global__ void
advectVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt, int lb, cudaTextureObject_t tex);
// This method performs velocity diffusion and forces mass conservation
// in the frequency domain. The inputs 'vx' and 'vy' are complex-valued
@@ -56,19 +54,16 @@ __global__ void advectVelocity_k(cData *v, float *vx, float *vy, int dx,
// and k is the wavenumber. The projection step forces the Fourier
// velocity vectors to be orthogonal to the wave wave vectors for each
// wavenumber: v(k,t) = v(k,t) - ((k dot v(k,t) * k) / k^2.
__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt,
float visc, int lb);
__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt, float visc, int lb);
// This method updates the velocity field 'v' using the two complex
// arrays from the previous step: 'vx' and 'vy'. Here we scale the
// real components by 1/(dx*dy) to account for an unnormalized FFT.
__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx,
int pdx, int dy, int lb, size_t pitch);
__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, int lb, size_t pitch);
// This method updates the particles by moving particle positions
// according to the velocity field and time step. That is, for each
// particle: p(t+1) = p(t) + dt * v(p(t)).
__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy,
float dt, int lb, size_t pitch);
__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy, float dt, int lb, size_t pitch);
#endif

View File

@@ -34,4 +34,3 @@ Download and install the [CUDA Toolkit 12.5](https://developer.nvidia.com/cuda-d
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
## References (for more details)

Some files were not shown because too many files have changed in this diff Show More