mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-25 22:28:17 +08:00
Apply consistent code formatting across the repo. Add clang-format and pre-commit hooks.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#ifndef REALTYPE_H
|
||||
#define REALTYPE_H
|
||||
|
||||
//#define DOUBLE_PRECISION
|
||||
// #define DOUBLE_PRECISION
|
||||
|
||||
#ifndef DOUBLE_PRECISION
|
||||
typedef float real;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}},
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#ifndef REALTYPE_H
|
||||
#define REALTYPE_H
|
||||
|
||||
//#define DOUBLE_PRECISION
|
||||
// #define DOUBLE_PRECISION
|
||||
|
||||
#ifndef DOUBLE_PRECISION
|
||||
typedef float real;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 ÷nd, uint divisor, uint &rem) {
|
||||
inline __device__ void udivmod(uint ÷nd, 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user