mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-13 00:08:26 +08:00
CUDA 13.2 samples update (#432)
- Added Python samples for CUDA Python 1.0 release - Renamed top-level `Samples` directory to `cpp` to accommodate Python samples.
This commit is contained in:
18
cpp/5_Domain_Specific/BlackScholes/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/BlackScholes/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/BlackScholes/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/BlackScholes/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
246
cpp/5_Domain_Specific/BlackScholes/BlackScholes.cu
Normal file
246
cpp/5_Domain_Specific/BlackScholes/BlackScholes.cu
Normal file
@@ -0,0 +1,246 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This sample evaluates fair call and put prices for a
|
||||
* given set of European options by Black-Scholes formula.
|
||||
* See supplied whitepaper for more explanations.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Process an array of OptN options on GPU
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "BlackScholes_kernel.cuh"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
#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]);
|
||||
|
||||
//'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;
|
||||
|
||||
double delta, ref, sum_delta, sum_ref, max_delta, L1norm, gpuTime;
|
||||
|
||||
StopWatchInterface *hTimer = NULL;
|
||||
int i;
|
||||
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
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("...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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
98
cpp/5_Domain_Specific/BlackScholes/BlackScholes_gold.cpp
Normal file
98
cpp/5_Domain_Specific/BlackScholes/BlackScholes_gold.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <math.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;
|
||||
|
||||
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)))));
|
||||
|
||||
if (d > 0)
|
||||
cnd = 1.0 - 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
119
cpp/5_Domain_Specific/BlackScholes/BlackScholes_kernel.cuh
Normal file
119
cpp/5_Domain_Specific/BlackScholes/BlackScholes_kernel.cuh
Normal file
@@ -0,0 +1,119 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
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)))));
|
||||
|
||||
if (d > 0)
|
||||
cnd = 1.0f - 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;
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
34
cpp/5_Domain_Specific/BlackScholes/CMakeLists.txt
Normal file
34
cpp/5_Domain_Specific/BlackScholes/CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(BlackScholes LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for BlackScholes
|
||||
add_executable(BlackScholes BlackScholes.cu BlackScholes_gold.cpp)
|
||||
|
||||
target_compile_options(BlackScholes PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(BlackScholes PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(BlackScholes PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
34
cpp/5_Domain_Specific/BlackScholes/README.md
Normal file
34
cpp/5_Domain_Specific/BlackScholes/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# BlackScholes - Black-Scholes Option Pricing
|
||||
|
||||
## Description
|
||||
|
||||
This sample evaluates fair call and put prices for a given set of European options by Black-Scholes formula.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Computational Finance
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMalloc, cudaDeviceSynchronize, cudaMemcpy, cudaFree
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[whitepaper](./doc/BlackScholes.pdf)
|
||||
BIN
cpp/5_Domain_Specific/BlackScholes/doc/BlackScholes.doc
Normal file
BIN
cpp/5_Domain_Specific/BlackScholes/doc/BlackScholes.doc
Normal file
Binary file not shown.
BIN
cpp/5_Domain_Specific/BlackScholes/doc/BlackScholes.pdf
Normal file
BIN
cpp/5_Domain_Specific/BlackScholes/doc/BlackScholes.pdf
Normal file
Binary file not shown.
18
cpp/5_Domain_Specific/BlackScholes_nvrtc/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/BlackScholes_nvrtc/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/BlackScholes_nvrtc/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/BlackScholes_nvrtc/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
281
cpp/5_Domain_Specific/BlackScholes_nvrtc/BlackScholes.cpp
Normal file
281
cpp/5_Domain_Specific/BlackScholes_nvrtc/BlackScholes.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This sample evaluates fair call and put prices for a
|
||||
* given set of European options by Black-Scholes formula.
|
||||
* See supplied whitepaper for more explanations.
|
||||
*/
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_functions.h> // helper functions for string parsing
|
||||
#include <nvrtc_helper.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Process an array of OptN options on GPU
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
#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]);
|
||||
|
||||
//'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,
|
||||
|
||||
// GPU instance of input data
|
||||
d_StockPrice, d_OptionStrike, d_OptionYears;
|
||||
|
||||
double delta, ref, sum_delta, sum_ref, max_delta, L1norm, gpuTime;
|
||||
|
||||
StopWatchInterface *hTimer = NULL;
|
||||
int i;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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("...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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
100
cpp/5_Domain_Specific/BlackScholes_nvrtc/BlackScholes_gold.cpp
Normal file
100
cpp/5_Domain_Specific/BlackScholes_nvrtc/BlackScholes_gold.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <math.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;
|
||||
|
||||
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)))));
|
||||
|
||||
if (d > 0)
|
||||
cnd = 1.0 - 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
116
cpp/5_Domain_Specific/BlackScholes_nvrtc/BlackScholes_kernel.cuh
Normal file
116
cpp/5_Domain_Specific/BlackScholes_nvrtc/BlackScholes_kernel.cuh
Normal file
@@ -0,0 +1,116 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
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)))));
|
||||
|
||||
if (d > 0)
|
||||
cnd = 1.0f - 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;
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
45
cpp/5_Domain_Specific/BlackScholes_nvrtc/CMakeLists.txt
Normal file
45
cpp/5_Domain_Specific/BlackScholes_nvrtc/CMakeLists.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(BlackScholes_nvrtc LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for BlackScholes_nvrtc
|
||||
add_executable(BlackScholes_nvrtc BlackScholes.cpp BlackScholes_gold.cpp)
|
||||
|
||||
target_compile_options(BlackScholes_nvrtc PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(BlackScholes_nvrtc PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(BlackScholes_nvrtc PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(BlackScholes_nvrtc PRIVATE
|
||||
CUDA::nvrtc
|
||||
CUDA::cuda_driver
|
||||
)
|
||||
|
||||
# Copy kernel to the output directory
|
||||
add_custom_command(TARGET BlackScholes_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlackScholes_kernel.cuh ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
39
cpp/5_Domain_Specific/BlackScholes_nvrtc/README.md
Normal file
39
cpp/5_Domain_Specific/BlackScholes_nvrtc/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# BlackScholes_nvrtc - Black-Scholes Option Pricing with libNVRTC
|
||||
|
||||
## Description
|
||||
|
||||
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.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Computational Finance, Runtime Compilation
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows, QNX
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuMemcpyDtoH, cuLaunchKernel, cuMemcpyHtoD, cuCtxSynchronize, cuMemAlloc, cuMemFree, cuModuleGetFunction
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaBlockSize, cudaGridSize
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[NVRTC](../../../README.md#nvrtc)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
36
cpp/5_Domain_Specific/CMakeLists.txt
Normal file
36
cpp/5_Domain_Specific/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
add_subdirectory(BlackScholes)
|
||||
add_subdirectory(BlackScholes_nvrtc)
|
||||
add_subdirectory(FDTD3d)
|
||||
add_subdirectory(HSOpticalFlow)
|
||||
add_subdirectory(Mandelbrot)
|
||||
add_subdirectory(MonteCarloMultiGPU)
|
||||
add_subdirectory(NV12toBGRandResize)
|
||||
add_subdirectory(SobelFilter)
|
||||
add_subdirectory(SobolQRNG)
|
||||
add_subdirectory(bicubicTexture)
|
||||
add_subdirectory(bilateralFilter)
|
||||
add_subdirectory(binomialOptions)
|
||||
add_subdirectory(binomialOptions_nvrtc)
|
||||
add_subdirectory(convolutionFFT2D)
|
||||
add_subdirectory(dwtHaar1D)
|
||||
add_subdirectory(dxtc)
|
||||
add_subdirectory(fastWalshTransform)
|
||||
add_subdirectory(fluidsGL)
|
||||
add_subdirectory(marchingCubes)
|
||||
add_subdirectory(nbody)
|
||||
add_subdirectory(p2pBandwidthLatencyTest)
|
||||
add_subdirectory(postProcessGL)
|
||||
add_subdirectory(quasirandomGenerator)
|
||||
add_subdirectory(quasirandomGenerator_nvrtc)
|
||||
add_subdirectory(recursiveGaussian)
|
||||
add_subdirectory(simpleD3D11)
|
||||
add_subdirectory(simpleD3D11Texture)
|
||||
add_subdirectory(simpleD3D12)
|
||||
add_subdirectory(simpleGL)
|
||||
add_subdirectory(simpleVulkan)
|
||||
add_subdirectory(simpleVulkanMMAP)
|
||||
add_subdirectory(smokeParticles)
|
||||
add_subdirectory(stereoDisparity)
|
||||
add_subdirectory(volumeFiltering)
|
||||
add_subdirectory(volumeRender)
|
||||
add_subdirectory(vulkanImageCUDA)
|
||||
18
cpp/5_Domain_Specific/FDTD3d/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/FDTD3d/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/FDTD3d/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/FDTD3d/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
38
cpp/5_Domain_Specific/FDTD3d/CMakeLists.txt
Normal file
38
cpp/5_Domain_Specific/FDTD3d/CMakeLists.txt
Normal file
@@ -0,0 +1,38 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(FDTD3d LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for FDTD3d
|
||||
add_executable(FDTD3d src/FDTD3d.cpp src/FDTD3dGPU.cu src/FDTD3dReference.cpp)
|
||||
|
||||
target_compile_options(FDTD3d PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(FDTD3d PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(FDTD3d PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(FDTD3d PRIVATE
|
||||
inc
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
32
cpp/5_Domain_Specific/FDTD3d/README.md
Normal file
32
cpp/5_Domain_Specific/FDTD3d/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# FDTD3d - CUDA C 3D FDTD
|
||||
|
||||
## Description
|
||||
|
||||
This sample applies a finite differences time domain progression stencil on a 3D surface.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Performance Strategies
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaMalloc, cudaFree, cudaFuncGetAttributes, cudaSetDevice, cudaGetDeviceProperties, cudaDeviceSynchronize, cudaEventRecord, cudaMemcpyToSymbol, cudaEventDestroy, cudaEventElapsedTime, cudaGetDeviceCount, cudaEventCreate
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## References (for more details)
|
||||
54
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3d.h
Normal file
54
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3d.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FDTD3D_H_
|
||||
#define _FDTD3D_H_
|
||||
|
||||
// The values are set to give reasonable runtimes, they can
|
||||
// be changed but note that running very large dimensions can
|
||||
// take a very long time and you should avoid running on your
|
||||
// primary display in this case.
|
||||
#define k_dim_min 96
|
||||
#define k_dim_max 376
|
||||
#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_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_default 5
|
||||
|
||||
#endif
|
||||
56
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3dGPU.h
Normal file
56
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3dGPU.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FDTD3DGPU_H_
|
||||
#define _FDTD3DGPU_H_
|
||||
|
||||
#include <cstddef>
|
||||
#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_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);
|
||||
|
||||
#endif
|
||||
160
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3dGPUKernel.cuh
Normal file
160
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3dGPUKernel.cuh
Normal file
@@ -0,0 +1,160 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
#include "FDTD3dGPU.h"
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
// Note: If you change the RADIUS, you should also change the unrolling below
|
||||
#define RADIUS 4
|
||||
|
||||
__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];
|
||||
|
||||
const int stride_y = dimx + 2 * RADIUS;
|
||||
const int stride_z = stride_y * (dimy + 2 * RADIUS);
|
||||
|
||||
int inputIndex = 0;
|
||||
int outputIndex = 0;
|
||||
|
||||
// Advance inputIndex to start of inner volume
|
||||
inputIndex += RADIUS * stride_y + RADIUS;
|
||||
|
||||
// Advance inputIndex to target element
|
||||
inputIndex += gtidy * stride_y + gtidx;
|
||||
|
||||
float infront[RADIUS];
|
||||
float behind[RADIUS];
|
||||
float current;
|
||||
|
||||
const int tx = ltidx + RADIUS;
|
||||
const int ty = ltidy + RADIUS;
|
||||
|
||||
// Check in bounds
|
||||
if ((gtidx >= dimx + RADIUS) || (gtidy >= dimy + RADIUS))
|
||||
validr = 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];
|
||||
|
||||
inputIndex += stride_z;
|
||||
}
|
||||
|
||||
if (validr)
|
||||
current = input[inputIndex];
|
||||
|
||||
outputIndex = inputIndex;
|
||||
inputIndex += stride_z;
|
||||
|
||||
for (int i = 0; i < RADIUS; i++) {
|
||||
if (validr)
|
||||
infront[i] = input[inputIndex];
|
||||
|
||||
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];
|
||||
|
||||
behind[0] = current;
|
||||
current = infront[0];
|
||||
#pragma unroll 4
|
||||
|
||||
for (int i = 0; i < RADIUS - 1; i++)
|
||||
infront[i] = infront[i + 1];
|
||||
|
||||
if (validr)
|
||||
infront[RADIUS - 1] = input[inputIndex];
|
||||
|
||||
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.
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
tile[ty][tx] = current;
|
||||
cg::sync(cta);
|
||||
|
||||
// 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]);
|
||||
}
|
||||
|
||||
// Store the output value
|
||||
if (validw)
|
||||
output[outputIndex] = value;
|
||||
}
|
||||
}
|
||||
59
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3dReference.h
Normal file
59
cpp/5_Domain_Specific/FDTD3d/inc/FDTD3dReference.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FDTD3DREFERENCE_H_
|
||||
#define _FDTD3DREFERENCE_H_
|
||||
|
||||
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,
|
||||
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);
|
||||
|
||||
#endif
|
||||
232
cpp/5_Domain_Specific/FDTD3d/src/FDTD3d.cpp
Normal file
232
cpp/5_Domain_Specific/FDTD3d/src/FDTD3d.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "FDTD3d.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)))
|
||||
#endif
|
||||
|
||||
//// Name of the log file
|
||||
// const char *printfFile = "FDTD3d.txt";
|
||||
|
||||
// Forward declarations
|
||||
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]);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
// 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));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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, "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, "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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
// 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");
|
||||
|
||||
// Compare the results
|
||||
float tolerance = 0.0001f;
|
||||
printf("\nCompareData (tolerance %f)...\n", tolerance);
|
||||
return compareData(device_output, host_output, dimx, dimy, dimz, radius, tolerance);
|
||||
}
|
||||
260
cpp/5_Domain_Specific/FDTD3d/src/FDTD3dGPU.cu
Normal file
260
cpp/5_Domain_Specific/FDTD3d/src/FDTD3dGPU.cu
Normal file
@@ -0,0 +1,260 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#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;
|
||||
|
||||
// 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);
|
||||
|
||||
// Query target device for maximum memory allocation
|
||||
printf(" cudaGetDeviceProperties\n");
|
||||
struct cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, targetDevice));
|
||||
|
||||
memsize = deviceProp.totalGlobalMem;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Get the number of CUDA enabled GPU devices
|
||||
checkCudaErrors(cudaGetDeviceCount(&deviceCount));
|
||||
|
||||
// Select target device (device 0 by default)
|
||||
targetDevice = findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
checkCudaErrors(cudaSetDevice(targetDevice));
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Check the device limit on the number of threads
|
||||
struct cudaFuncAttributes funcAttrib;
|
||||
checkCudaErrors(cudaFuncGetAttributes(&funcAttrib, FiniteDifferencesKernel));
|
||||
|
||||
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);
|
||||
|
||||
// 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 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)));
|
||||
|
||||
#ifdef GPU_PROFILING
|
||||
|
||||
// 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");
|
||||
|
||||
#ifdef GPU_PROFILING
|
||||
// Enqueue start event
|
||||
checkCudaErrors(cudaEventRecord(profileStart, 0));
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
// 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");
|
||||
|
||||
#ifdef GPU_PROFILING
|
||||
// Enqueue end event
|
||||
checkCudaErrors(cudaEventRecord(profileEnd, 0));
|
||||
#endif
|
||||
|
||||
// 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));
|
||||
|
||||
// Report time
|
||||
#ifdef GPU_PROFILING
|
||||
float elapsedTimeMS = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Cleanup
|
||||
if (bufferIn) {
|
||||
checkCudaErrors(cudaFree(bufferIn));
|
||||
}
|
||||
|
||||
if (bufferOut) {
|
||||
checkCudaErrors(cudaFree(bufferOut));
|
||||
}
|
||||
|
||||
#ifdef GPU_PROFILING
|
||||
|
||||
if (profileStart) {
|
||||
checkCudaErrors(cudaEventDestroy(profileStart));
|
||||
}
|
||||
|
||||
if (profileEnd) {
|
||||
checkCudaErrors(cudaEventDestroy(profileEnd));
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
191
cpp/5_Domain_Specific/FDTD3d/src/FDTD3dReference.cpp
Normal file
191
cpp/5_Domain_Specific/FDTD3d/src/FDTD3dReference.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "FDTD3dReference.h"
|
||||
|
||||
#include <cmath>
|
||||
#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);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
*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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
18
cpp/5_Domain_Specific/HSOpticalFlow/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/HSOpticalFlow/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/HSOpticalFlow/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/HSOpticalFlow/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
44
cpp/5_Domain_Specific/HSOpticalFlow/CMakeLists.txt
Normal file
44
cpp/5_Domain_Specific/HSOpticalFlow/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(HSOpticalFlow LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for HSOpticalFlow
|
||||
add_executable(HSOpticalFlow flowCUDA.cu flowGold.cpp main.cpp)
|
||||
|
||||
target_compile_options(HSOpticalFlow PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(HSOpticalFlow PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(HSOpticalFlow PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(HSOpticalFlow PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_custom_command(TARGET HSOpticalFlow POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||
${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
BIN
cpp/5_Domain_Specific/HSOpticalFlow/FlowCPU.flo
Normal file
BIN
cpp/5_Domain_Specific/HSOpticalFlow/FlowCPU.flo
Normal file
Binary file not shown.
BIN
cpp/5_Domain_Specific/HSOpticalFlow/FlowGPU.flo
Normal file
BIN
cpp/5_Domain_Specific/HSOpticalFlow/FlowGPU.flo
Normal file
Binary file not shown.
34
cpp/5_Domain_Specific/HSOpticalFlow/README.md
Normal file
34
cpp/5_Domain_Specific/HSOpticalFlow/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# HSOpticalFlow - Optical Flow
|
||||
|
||||
## Description
|
||||
|
||||
Variational optical flow estimation example. Uses textures for image operations. Shows how simple PDE solver can be accelerated with CUDA.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Image Processing, Data Parallel Algorithms
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMalloc, cudaMemcpy, cudaMemset, cudaFree
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[whitepaper](./doc/OpticalFlow.pdf)
|
||||
62
cpp/5_Domain_Specific/HSOpticalFlow/addKernel.cuh
Normal file
62
cpp/5_Domain_Specific/HSOpticalFlow/addKernel.cuh
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief add two vectors of size _count_
|
||||
///
|
||||
/// CUDA kernel
|
||||
/// \param[in] op1 term one
|
||||
/// \param[in] op2 term two
|
||||
/// \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;
|
||||
|
||||
if (pos >= count)
|
||||
return;
|
||||
|
||||
sum[pos] = op1[pos] + op2[pos];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief add two vectors of size _count_
|
||||
/// \param[in] op1 term one
|
||||
/// \param[in] op2 term two
|
||||
/// \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));
|
||||
|
||||
AddKernel<<<blocks, threads>>>(op1, op2, count, sum);
|
||||
}
|
||||
76
cpp/5_Domain_Specific/HSOpticalFlow/common.h
Normal file
76
cpp/5_Domain_Specific/HSOpticalFlow/common.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Header for common includes and utility functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Common includes
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <helper_cuda.h>
|
||||
#include <math.h>
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Common constants
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const int StrideAlignment = 32;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Common functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Align up n to the nearest multiple of m
|
||||
inline int iAlignUp(int n, int m = StrideAlignment)
|
||||
{
|
||||
int mod = n % m;
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
1540
cpp/5_Domain_Specific/HSOpticalFlow/data/frame10.ppm
Normal file
1540
cpp/5_Domain_Specific/HSOpticalFlow/data/frame10.ppm
Normal file
File diff suppressed because one or more lines are too long
1464
cpp/5_Domain_Specific/HSOpticalFlow/data/frame11.ppm
Normal file
1464
cpp/5_Domain_Specific/HSOpticalFlow/data/frame11.ppm
Normal file
File diff suppressed because one or more lines are too long
147
cpp/5_Domain_Specific/HSOpticalFlow/derivativesKernel.cuh
Normal file
147
cpp/5_Domain_Specific/HSOpticalFlow/derivativesKernel.cuh
Normal file
@@ -0,0 +1,147 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief compute image derivatives
|
||||
///
|
||||
/// CUDA kernel, relies heavily on texture unit
|
||||
/// \param[in] width image width
|
||||
/// \param[in] height image height
|
||||
/// \param[in] stride image stride
|
||||
/// \param[out] Ix x derivative
|
||||
/// \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,
|
||||
cudaTextureObject_t texSource,
|
||||
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;
|
||||
|
||||
if (ix >= width || iy >= height)
|
||||
return;
|
||||
|
||||
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 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;
|
||||
|
||||
Ix[pos] = (t0 + t1) * 0.5f;
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief compute image derivatives
|
||||
///
|
||||
/// \param[in] I0 source image
|
||||
/// \param[in] I1 tracked image
|
||||
/// \param[in] w image width
|
||||
/// \param[in] h image height
|
||||
/// \param[in] s image stride
|
||||
/// \param[out] Ix x derivative
|
||||
/// \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));
|
||||
|
||||
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);
|
||||
|
||||
cudaTextureDesc texDescr;
|
||||
memset(&texDescr, 0, sizeof(cudaTextureDesc));
|
||||
|
||||
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));
|
||||
|
||||
ComputeDerivativesKernel<<<blocks, threads>>>(w, h, s, Ix, Iy, Iz, texSource, texTarget);
|
||||
}
|
||||
BIN
cpp/5_Domain_Specific/HSOpticalFlow/doc/OpticalFlow.docx
Normal file
BIN
cpp/5_Domain_Specific/HSOpticalFlow/doc/OpticalFlow.docx
Normal file
Binary file not shown.
BIN
cpp/5_Domain_Specific/HSOpticalFlow/doc/OpticalFlow.pdf
Normal file
BIN
cpp/5_Domain_Specific/HSOpticalFlow/doc/OpticalFlow.pdf
Normal file
Binary file not shown.
97
cpp/5_Domain_Specific/HSOpticalFlow/downscaleKernel.cuh
Normal file
97
cpp/5_Domain_Specific/HSOpticalFlow/downscaleKernel.cuh
Normal file
@@ -0,0 +1,97 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief downscale image
|
||||
///
|
||||
/// CUDA kernel, relies heavily on texture unit
|
||||
/// \param[in] width image width
|
||||
/// \param[in] height image height
|
||||
/// \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;
|
||||
|
||||
if (ix >= width || iy >= height) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief downscale image
|
||||
///
|
||||
/// \param[in] src image to downscale
|
||||
/// \param[in] width image width
|
||||
/// \param[in] height image height
|
||||
/// \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));
|
||||
|
||||
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);
|
||||
|
||||
cudaTextureDesc texDescr;
|
||||
memset(&texDescr, 0, sizeof(cudaTextureDesc));
|
||||
|
||||
texDescr.normalizedCoords = true;
|
||||
texDescr.filterMode = cudaFilterModeLinear;
|
||||
texDescr.addressMode[0] = cudaAddressModeMirror;
|
||||
texDescr.addressMode[1] = cudaAddressModeMirror;
|
||||
texDescr.readMode = cudaReadModeElementType;
|
||||
|
||||
checkCudaErrors(cudaCreateTextureObject(&texFine, &texRes, &texDescr, NULL));
|
||||
|
||||
DownscaleKernel<<<blocks, threads>>>(newWidth, newHeight, newStride, out, texFine);
|
||||
}
|
||||
253
cpp/5_Domain_Specific/HSOpticalFlow/flowCUDA.cu
Normal file
253
cpp/5_Domain_Specific/HSOpticalFlow/flowCUDA.cu
Normal file
@@ -0,0 +1,253 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// include kernels
|
||||
#include "addKernel.cuh"
|
||||
#include "derivativesKernel.cuh"
|
||||
#include "downscaleKernel.cuh"
|
||||
#include "solverKernel.cuh"
|
||||
#include "upscaleKernel.cuh"
|
||||
#include "warpingKernel.cuh"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief method logic
|
||||
///
|
||||
/// handles memory allocations, control flow
|
||||
/// \param[in] I0 source image
|
||||
/// \param[in] I1 tracked image
|
||||
/// \param[in] width images width
|
||||
/// \param[in] height images height
|
||||
/// \param[in] stride images stride
|
||||
/// \param[in] alpha degree of displacement field smoothness
|
||||
/// \param[in] nLevels number of levels in a pyramid
|
||||
/// \param[in] nWarpIters number of warping iterations per pyramid level
|
||||
/// \param[in] nSolverIters number of solver iterations (Jacobi iterations)
|
||||
/// \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");
|
||||
|
||||
// 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];
|
||||
|
||||
// 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_u;
|
||||
float *d_v;
|
||||
float *d_nu;
|
||||
float *d_nv;
|
||||
|
||||
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_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));
|
||||
|
||||
// prepare pyramid
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
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(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);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
// 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;
|
||||
|
||||
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));
|
||||
}
|
||||
42
cpp/5_Domain_Specific/HSOpticalFlow/flowCUDA.h
Normal file
42
cpp/5_Domain_Specific/HSOpticalFlow/flowCUDA.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#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
|
||||
#endif
|
||||
523
cpp/5_Domain_Specific/HSOpticalFlow/flowGold.cpp
Normal file
523
cpp/5_Domain_Specific/HSOpticalFlow/flowGold.cpp
Normal file
@@ -0,0 +1,523 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "flowGold.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief host texture fetch
|
||||
///
|
||||
/// read from arbitrary position within image using bilinear interpolation
|
||||
/// out of range coords are mirrored
|
||||
/// \param[in] t texture raw data
|
||||
/// \param[in] w texture width
|
||||
/// \param[in] h texture height
|
||||
/// \param[in] s texture stride
|
||||
/// \param[in] x x coord of the point to fetch value at
|
||||
/// \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;
|
||||
|
||||
// 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;
|
||||
|
||||
// mirror out-of-range position
|
||||
if (ix0 < 0)
|
||||
ix0 = abs(ix0 + 1);
|
||||
|
||||
if (iy0 < 0)
|
||||
iy0 = abs(iy0 + 1);
|
||||
|
||||
if (ix0 >= w)
|
||||
ix0 = w * 2 - ix0 - 1;
|
||||
|
||||
if (iy0 >= h)
|
||||
iy0 = h * 2 - 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 (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;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief host texture fetch
|
||||
///
|
||||
/// read specific texel value
|
||||
/// out of range coords are mirrored
|
||||
/// \param[in] t texture raw data
|
||||
/// \param[in] w texture width
|
||||
/// \param[in] h texture height
|
||||
/// \param[in] s texture stride
|
||||
/// \param[in] x x coord of the point to fetch value at
|
||||
/// \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);
|
||||
|
||||
if (y < 0)
|
||||
y = abs(y + 1);
|
||||
|
||||
if (x >= w)
|
||||
x = w * 2 - x - 1;
|
||||
|
||||
if (y >= h)
|
||||
y = h * 2 - y - 1;
|
||||
|
||||
return src[x + y * s];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief resize image
|
||||
/// \param[in] src image to downscale
|
||||
/// \param[in] width image width
|
||||
/// \param[in] height image height
|
||||
/// \param[in] stride image stride
|
||||
/// \param[in] newWidth image new width
|
||||
/// \param[in] newHeight image new height
|
||||
/// \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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief upscale one component of a displacement field
|
||||
/// \param[in] src field component to upscale
|
||||
/// \param[in] width field current width
|
||||
/// \param[in] height field current height
|
||||
/// \param[in] stride field current stride
|
||||
/// \param[in] newWidth field new width
|
||||
/// \param[in] newHeight field new height
|
||||
/// \param[in] newStride field new 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;
|
||||
|
||||
out[j + i * newStride] = Tex2D(src, width, height, stride, x, y) * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief warp image with provided vector field
|
||||
///
|
||||
/// For each output pixel there is a vector which tells which pixel
|
||||
/// from a source image should be mapped to this particular output
|
||||
/// pixel.
|
||||
/// It is assumed that images and the vector field have the same stride and
|
||||
/// resolution.
|
||||
/// \param[in] src source image
|
||||
/// \param[in] w width
|
||||
/// \param[in] h height
|
||||
/// \param[in] s stride
|
||||
/// \param[in] u horizontal displacement
|
||||
/// \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];
|
||||
|
||||
out[pos] = Tex2D(src, w, h, s, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief computes image derivatives for a pair of images
|
||||
/// \param[in] I0 source image
|
||||
/// \param[in] I1 tracked image
|
||||
/// \param[in] w images width
|
||||
/// \param[in] h images height
|
||||
/// \param[in] s images stride
|
||||
/// \param[out] Ix x derivative
|
||||
/// \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;
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief one iteration of classical Horn-Schunck method
|
||||
///
|
||||
/// It is one iteration of Jacobi method for a corresponding linear system
|
||||
/// \param[in] du0 current horizontal displacement approximation
|
||||
/// \param[in] dv0 current vertical displacement approximation
|
||||
/// \param[in] Ix image x derivative
|
||||
/// \param[in] Iy image y derivative
|
||||
/// \param[in] Iz temporal derivative
|
||||
/// \param[in] w width
|
||||
/// \param[in] h height
|
||||
/// \param[in] s stride
|
||||
/// \param[in] alpha degree of smoothness
|
||||
/// \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;
|
||||
|
||||
// handle borders
|
||||
if (j != 0)
|
||||
left = pos - 1;
|
||||
else
|
||||
left = pos;
|
||||
|
||||
if (j != w - 1)
|
||||
right = pos + 1;
|
||||
else
|
||||
right = pos;
|
||||
|
||||
if (i != 0)
|
||||
down = pos - s;
|
||||
else
|
||||
down = 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief method logic
|
||||
///
|
||||
/// handles memory allocation and control flow
|
||||
/// \param[in] I0 source image
|
||||
/// \param[in] I1 tracked image
|
||||
/// \param[in] width images width
|
||||
/// \param[in] height images height
|
||||
/// \param[in] stride images stride
|
||||
/// \param[in] alpha degree of displacement field smoothness
|
||||
/// \param[in] nLevels number of levels in a pyramid
|
||||
/// \param[in] nWarpIters number of warping iterations per pyramid level
|
||||
/// \param[in] nSolverIters number of solver iterations (Jacobi iterations)
|
||||
/// \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");
|
||||
|
||||
float *u0 = u;
|
||||
float *v0 = v;
|
||||
|
||||
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];
|
||||
|
||||
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];
|
||||
|
||||
// prepare pyramid
|
||||
int currentLevel = nLevels - 1;
|
||||
pI0[currentLevel] = I0;
|
||||
pI1[currentLevel] = I1;
|
||||
|
||||
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];
|
||||
|
||||
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]);
|
||||
|
||||
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);
|
||||
}
|
||||
} // 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;
|
||||
}
|
||||
43
cpp/5_Domain_Specific/HSOpticalFlow/flowGold.h
Normal file
43
cpp/5_Domain_Specific/HSOpticalFlow/flowGold.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
#endif
|
||||
239
cpp/5_Domain_Specific/HSOpticalFlow/main.cpp
Normal file
239
cpp/5_Domain_Specific/HSOpticalFlow/main.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const static char *const sSDKsample = "HSOpticalFlow";
|
||||
|
||||
// CPU-GPU discrepancy threshold for self-test
|
||||
const float THRESHOLD = 0.05f;
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_functions.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "flowCUDA.h"
|
||||
#include "flowGold.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief save optical flow in format described on vision.middlebury.edu/flow
|
||||
/// \param[in] name output file name
|
||||
/// \param[in] w optical flow field width
|
||||
/// \param[in] h optical flow field height
|
||||
/// \param[in] s optical flow field row stride
|
||||
/// \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");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief
|
||||
/// load 4-channel unsigned byte image
|
||||
/// and convert it to single channel FP32 image
|
||||
/// \param[out] img_data pointer to raw image data
|
||||
/// \param[out] img_w image width
|
||||
/// \param[out] img_h image height
|
||||
/// \param[out] img_s image row stride
|
||||
/// \param[in] name image file name
|
||||
/// \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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief compare given flow field with gold (L1 norm)
|
||||
/// \param[in] width optical flow field width
|
||||
/// \param[in] height optical flow field height
|
||||
/// \param[in] stride optical flow field row stride
|
||||
/// \param[in] h_uGold horizontal displacement, gold
|
||||
/// \param[in] h_vGold vertical displacement, gold
|
||||
/// \param[in] h_u horizontal displacement
|
||||
/// \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;
|
||||
|
||||
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);
|
||||
|
||||
printf("L1 error : %.6f\n", error);
|
||||
|
||||
return (error < THRESHOLD);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// application entry point
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// welcome message
|
||||
printf("%s Starting...\n\n", sSDKsample);
|
||||
|
||||
// pick GPU
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
// number of pyramid levels
|
||||
const int nLevels = 5;
|
||||
|
||||
// number of solver iterations on each level
|
||||
const int nSolverIters = 500;
|
||||
|
||||
// number of warping iterations
|
||||
const int nWarpIters = 3;
|
||||
|
||||
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);
|
||||
|
||||
// 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("FlowCPU.flo", width, height, stride, h_uGold, h_vGold);
|
||||
|
||||
// free resources
|
||||
delete[] h_uGold;
|
||||
delete[] h_vGold;
|
||||
|
||||
delete[] h_u;
|
||||
delete[] h_v;
|
||||
|
||||
delete[] h_source;
|
||||
delete[] h_target;
|
||||
|
||||
// report self-test status
|
||||
exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
199
cpp/5_Domain_Specific/HSOpticalFlow/solverKernel.cuh
Normal file
199
cpp/5_Domain_Specific/HSOpticalFlow/solverKernel.cuh
Normal file
@@ -0,0 +1,199 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief one iteration of classical Horn-Schunck method, CUDA kernel.
|
||||
///
|
||||
/// It is one iteration of Jacobi method for a corresponding linear system.
|
||||
/// Template parameters are describe CTA size
|
||||
/// \param[in] du0 current horizontal displacement approximation
|
||||
/// \param[in] dv0 current vertical displacement approximation
|
||||
/// \param[in] Ix image x derivative
|
||||
/// \param[in] Iy image y derivative
|
||||
/// \param[in] Iz temporal derivative
|
||||
/// \param[in] w width
|
||||
/// \param[in] h height
|
||||
/// \param[in] s stride
|
||||
/// \param[in] alpha degree of smoothness
|
||||
/// \param[out] du1 new horizontal displacement approximation
|
||||
/// \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();
|
||||
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
if (ix >= w || iy >= h)
|
||||
return;
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief one iteration of classical Horn-Schunck method, CUDA kernel wrapper.
|
||||
///
|
||||
/// It is one iteration of Jacobi method for a corresponding linear system.
|
||||
/// \param[in] du0 current horizontal displacement approximation
|
||||
/// \param[in] dv0 current vertical displacement approximation
|
||||
/// \param[in] Ix image x derivative
|
||||
/// \param[in] Iy image y derivative
|
||||
/// \param[in] Iz temporal derivative
|
||||
/// \param[in] w width
|
||||
/// \param[in] h height
|
||||
/// \param[in] s stride
|
||||
/// \param[in] alpha degree of smoothness
|
||||
/// \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));
|
||||
|
||||
JacobiIteration<32, 6><<<blocks, threads>>>(du0, dv0, Ix, Iy, Iz, w, h, s, alpha, du1, dv1);
|
||||
}
|
||||
102
cpp/5_Domain_Specific/HSOpticalFlow/upscaleKernel.cuh
Normal file
102
cpp/5_Domain_Specific/HSOpticalFlow/upscaleKernel.cuh
Normal file
@@ -0,0 +1,102 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief upscale one component of a displacement field, CUDA kernel
|
||||
/// \param[in] width field width
|
||||
/// \param[in] height field height
|
||||
/// \param[in] stride field stride
|
||||
/// \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;
|
||||
|
||||
if (ix >= width || iy >= height)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief upscale one component of a displacement field, kernel wrapper
|
||||
/// \param[in] src field component to upscale
|
||||
/// \param[in] width field current width
|
||||
/// \param[in] height field current height
|
||||
/// \param[in] stride field current stride
|
||||
/// \param[in] newWidth field new width
|
||||
/// \param[in] newHeight field new height
|
||||
/// \param[in] newStride field new 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)
|
||||
{
|
||||
dim3 threads(32, 8);
|
||||
dim3 blocks(iDivUp(newWidth, threads.x), iDivUp(newHeight, threads.y));
|
||||
|
||||
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);
|
||||
|
||||
cudaTextureDesc texDescr;
|
||||
memset(&texDescr, 0, sizeof(cudaTextureDesc));
|
||||
|
||||
texDescr.normalizedCoords = true;
|
||||
texDescr.filterMode = cudaFilterModeLinear;
|
||||
texDescr.addressMode[0] = cudaAddressModeMirror;
|
||||
texDescr.addressMode[1] = cudaAddressModeMirror;
|
||||
texDescr.readMode = cudaReadModeElementType;
|
||||
|
||||
checkCudaErrors(cudaCreateTextureObject(&texCoarse, &texRes, &texDescr, NULL));
|
||||
|
||||
UpscaleKernel<<<blocks, threads>>>(newWidth, newHeight, newStride, scale, out, texCoarse);
|
||||
}
|
||||
105
cpp/5_Domain_Specific/HSOpticalFlow/warpingKernel.cuh
Normal file
105
cpp/5_Domain_Specific/HSOpticalFlow/warpingKernel.cuh
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief warp image with a given displacement field, CUDA kernel.
|
||||
/// \param[in] width image width
|
||||
/// \param[in] height image height
|
||||
/// \param[in] stride image stride
|
||||
/// \param[in] u horizontal displacement
|
||||
/// \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;
|
||||
|
||||
const int pos = ix + iy * stride;
|
||||
|
||||
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;
|
||||
|
||||
out[pos] = tex2D<float>(texToWarp, x, y);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief warp image with provided vector field, CUDA kernel wrapper.
|
||||
///
|
||||
/// For each output pixel there is a vector which tells which pixel
|
||||
/// from a source image should be mapped to this particular output
|
||||
/// pixel.
|
||||
/// It is assumed that images and the vector field have the same stride and
|
||||
/// resolution.
|
||||
/// \param[in] src source image
|
||||
/// \param[in] w width
|
||||
/// \param[in] h height
|
||||
/// \param[in] s stride
|
||||
/// \param[in] u horizontal displacement
|
||||
/// \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));
|
||||
|
||||
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);
|
||||
|
||||
cudaTextureDesc texDescr;
|
||||
memset(&texDescr, 0, sizeof(cudaTextureDesc));
|
||||
|
||||
texDescr.normalizedCoords = true;
|
||||
texDescr.filterMode = cudaFilterModeLinear;
|
||||
texDescr.addressMode[0] = cudaAddressModeMirror;
|
||||
texDescr.addressMode[1] = cudaAddressModeMirror;
|
||||
texDescr.readMode = cudaReadModeElementType;
|
||||
|
||||
checkCudaErrors(cudaCreateTextureObject(&texToWarp, &texRes, &texDescr, NULL));
|
||||
|
||||
WarpingKernel<<<blocks, threads>>>(w, h, s, u, v, out, texToWarp);
|
||||
}
|
||||
18
cpp/5_Domain_Specific/Mandelbrot/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/Mandelbrot/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/Mandelbrot/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/Mandelbrot/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
96
cpp/5_Domain_Specific/Mandelbrot/CMakeLists.txt
Normal file
96
cpp/5_Domain_Specific/Mandelbrot/CMakeLists.txt
Normal file
@@ -0,0 +1,96 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(Mandelbrot LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
if(WIN32)
|
||||
set(PC_GLUT_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../Common")
|
||||
set(PC_GLUT_LIBRARY_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../Common/lib/x64")
|
||||
# The GLEW library built on Windows on Arm system is named as glew32.lib/glew32.dll by default.
|
||||
if(EXISTS "${PC_GLUT_LIBRARY_DIRS}/glew32.lib")
|
||||
set(GLEW_LIB_NAME "glew32")
|
||||
else()
|
||||
set(GLEW_LIB_NAME "glew64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(OpenGL)
|
||||
find_package(GLUT)
|
||||
|
||||
# Source file
|
||||
if(${OpenGL_FOUND})
|
||||
if (${GLUT_FOUND})
|
||||
# Add target for Mandelbrot
|
||||
add_executable(Mandelbrot Mandelbrot_cuda.cu Mandelbrot_gold.cpp Mandelbrot.cpp)
|
||||
|
||||
target_compile_options(Mandelbrot PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(Mandelbrot PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(Mandelbrot PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(Mandelbrot PUBLIC
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
${GLUT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(Mandelbrot
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLUT_LIBRARIES}
|
||||
)
|
||||
|
||||
# Copy data files to the output directory
|
||||
add_custom_command(TARGET Mandelbrot POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||
${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(Mandelbrot
|
||||
${PC_GLUT_LIBRARY_DIRS}/freeglut.lib
|
||||
${PC_GLUT_LIBRARY_DIRS}/${GLEW_LIB_NAME}.lib
|
||||
)
|
||||
|
||||
add_custom_command(TARGET Mandelbrot
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/win64/$<CONFIGURATION>/freeglut.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
|
||||
add_custom_command(TARGET Mandelbrot
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/win64/$<CONFIGURATION>/${GLEW_LIB_NAME}.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "GLUT not found - will not build sample 'Mandelbrot'")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "OpenGL not found - will not build sample 'Mandelbrot'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
1353
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot.cpp
Normal file
1353
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot.cpp
Normal file
File diff suppressed because it is too large
Load Diff
529
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_cuda.cu
Normal file
529
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_cuda.cu
Normal file
@@ -0,0 +1,529 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 (iy > 0) {
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 (iy > 0) {
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // 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));
|
||||
|
||||
int numWorkerBlocks = numSMs;
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
int numWorkerBlocks = numSMs;
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
getLastCudaError("Mandelbrot1 kernel execution failed.\n");
|
||||
} // RunMandelbrot1
|
||||
342
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_gold.cpp
Normal file
342
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_gold.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "Mandelbrot_gold.h"
|
||||
|
||||
#define ABS(n) ((n) < 0 ? -(n) : (n))
|
||||
|
||||
/* dfloat class declaration */
|
||||
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]; }
|
||||
};
|
||||
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; }
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
setColor(color, colors, m, animationFrame);
|
||||
|
||||
// Output the pixel
|
||||
int pixel = imageW * iy + ix;
|
||||
|
||||
if (frame == 0) {
|
||||
color.w = 0;
|
||||
dst[pixel] = color;
|
||||
}
|
||||
else
|
||||
updatePixel(dst[pixel], color, frame);
|
||||
}
|
||||
|
||||
} // 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
|
||||
|
||||
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;
|
||||
|
||||
// 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 (iy > 0)
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
setColor(color, colors, m, animationFrame);
|
||||
|
||||
// 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);
|
||||
|
||||
} // 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);
|
||||
|
||||
} // 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 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]);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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 (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));
|
||||
}
|
||||
86
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_gold.h
Normal file
86
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_gold.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _MANDELBROT_GOLD_h_
|
||||
#define _MANDELBROT_GOLD_h_
|
||||
|
||||
#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);
|
||||
|
||||
#endif
|
||||
484
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_kernel.cuh
Normal file
484
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_kernel.cuh
Normal file
@@ -0,0 +1,484 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Mandelbrot_kernel.h"
|
||||
#include "helper_cuda.h"
|
||||
|
||||
// The dimensions of the thread block
|
||||
#define BLOCKDIM_X 16
|
||||
#define BLOCKDIM_Y 16
|
||||
|
||||
#define ABS(n) ((n) < 0 ? -(n) : (n))
|
||||
|
||||
// Double single functions based on DSFUN90 package:
|
||||
// http://crd.lbl.gov/~dhbailey/mpdist/index.html
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 (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
|
||||
|
||||
#else
|
||||
|
||||
// Modified double-single mul function by Norbert Juffa, NVIDIA
|
||||
// uses __fmul_rn() and __fadd_rn() intrinsics which prevent FMAD merging
|
||||
|
||||
/* Based on: Guillaume Da Gra<72>a, David Defour. Implementation of Float-Float
|
||||
* Operators on Graphics Hardware. RNC'7 pp. 23-32, 2006.
|
||||
*/
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 (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
|
||||
|
||||
#endif
|
||||
|
||||
// The core Mandelbrot CUDA GPU calculation function
|
||||
#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;
|
||||
|
||||
T xC, yC;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// Iteration 20
|
||||
i -= 20;
|
||||
|
||||
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
|
||||
|
||||
#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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
66
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_kernel.h
Normal file
66
cpp/5_Domain_Specific/Mandelbrot/Mandelbrot_kernel.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _MANDELBROT_KERNEL_h_
|
||||
#define _MANDELBROT_KERNEL_h_
|
||||
|
||||
#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);
|
||||
|
||||
#endif
|
||||
36
cpp/5_Domain_Specific/Mandelbrot/README.md
Normal file
36
cpp/5_Domain_Specific/Mandelbrot/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Mandelbrot - Mandelbrot
|
||||
|
||||
## Description
|
||||
|
||||
This sample uses CUDA to compute and display the Mandelbrot or Julia sets interactively. It also illustrates the use of "double single" arithmetic to improve precision when zooming a long way into the pattern. This sample uses double precision. Thanks to Mark Granger of NewTek who submitted this code sample.!
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Graphics Interop, Data Parallel Algorithms
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaGLUnmapBufferObject, cudaGraphicsUnmapResources, cudaMemcpy, cudaFree, cudaGLMapBufferObject, cudaGraphicsResourceGetMappedPointer, cudaGraphicsMapResources, cudaGLUnregisterBufferObject, cudaDeviceSynchronize, cudaGLRegisterBufferObject, cudaGraphicsUnregisterResource, cudaMalloc, cudaGraphicsGLRegisterBuffer, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[X11](../../../README.md#x11), [OpenGL](../../../README.md#opengl), [Freeglut](../../../README.md#freeglut), [GLEW](../../../README.md#glew)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
34157
cpp/5_Domain_Specific/Mandelbrot/data/Mandelbrot_fp32.ppm
Normal file
34157
cpp/5_Domain_Specific/Mandelbrot/data/Mandelbrot_fp32.ppm
Normal file
File diff suppressed because it is too large
Load Diff
34200
cpp/5_Domain_Specific/Mandelbrot/data/Mandelbrot_fp64.ppm
Normal file
34200
cpp/5_Domain_Specific/Mandelbrot/data/Mandelbrot_fp64.ppm
Normal file
File diff suppressed because it is too large
Load Diff
1
cpp/5_Domain_Specific/Mandelbrot/data/params.txt
Normal file
1
cpp/5_Domain_Specific/Mandelbrot/data/params.txt
Normal file
@@ -0,0 +1 @@
|
||||
-0.085760 0.007040 3.200000 -0.172400 -0.652693
|
||||
121039
cpp/5_Domain_Specific/Mandelbrot/data/referenceJulia_fp32.ppm
Normal file
121039
cpp/5_Domain_Specific/Mandelbrot/data/referenceJulia_fp32.ppm
Normal file
File diff suppressed because it is too large
Load Diff
121039
cpp/5_Domain_Specific/Mandelbrot/data/referenceJulia_fp64.ppm
Normal file
121039
cpp/5_Domain_Specific/Mandelbrot/data/referenceJulia_fp64.ppm
Normal file
File diff suppressed because it is too large
Load Diff
BIN
cpp/5_Domain_Specific/Mandelbrot/doc/sshot_lg.JPG
Normal file
BIN
cpp/5_Domain_Specific/Mandelbrot/doc/sshot_lg.JPG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
BIN
cpp/5_Domain_Specific/Mandelbrot/doc/sshot_md.JPG
Normal file
BIN
cpp/5_Domain_Specific/Mandelbrot/doc/sshot_md.JPG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
cpp/5_Domain_Specific/Mandelbrot/doc/sshot_sm.JPG
Normal file
BIN
cpp/5_Domain_Specific/Mandelbrot/doc/sshot_sm.JPG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
18
cpp/5_Domain_Specific/MonteCarloMultiGPU/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/MonteCarloMultiGPU/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/MonteCarloMultiGPU/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/MonteCarloMultiGPU/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
42
cpp/5_Domain_Specific/MonteCarloMultiGPU/CMakeLists.txt
Normal file
42
cpp/5_Domain_Specific/MonteCarloMultiGPU/CMakeLists.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(MonteCarloMultiGPU LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for MonteCarloMultiGPU
|
||||
add_executable(MonteCarloMultiGPU MonteCarlo_gold.cpp MonteCarlo_kernel.cu multithreading.cpp MonteCarloMultiGPU.cpp)
|
||||
|
||||
target_compile_options(MonteCarloMultiGPU PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(MonteCarloMultiGPU PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(MonteCarloMultiGPU PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(MonteCarloMultiGPU PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(MonteCarloMultiGPU PRIVATE
|
||||
CUDA::curand
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
483
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarloMultiGPU.cpp
Normal file
483
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarloMultiGPU.cpp
Normal file
@@ -0,0 +1,483 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This sample evaluates fair call price for a
|
||||
* given set of European options using Monte Carlo approach.
|
||||
* See supplied whitepaper for more explanations.
|
||||
*/
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// includes, project
|
||||
#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;
|
||||
char **pArgv = NULL;
|
||||
|
||||
#ifdef WIN32
|
||||
#define strcasecmp _strcmpi
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Common functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
return nOptions;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Black-Scholes formula for call options
|
||||
extern "C" void BlackScholesCall(float &CallResult, TOptionData optionData);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GPU-driving host thread
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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));
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define DO_CPU
|
||||
#undef DO_CPU
|
||||
|
||||
#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");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
printf("main(): waiting for GPU results...\n");
|
||||
cutWaitForThreads(threadID, GPU_N);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
#endif
|
||||
|
||||
printf("Shutting down...\n");
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
101
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarlo_common.h
Normal file
101
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarlo_common.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MONTECARLO_COMMON_H
|
||||
#define MONTECARLO_COMMON_H
|
||||
#include "curand_kernel.h"
|
||||
#include "realtype.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct
|
||||
{
|
||||
float S;
|
||||
float X;
|
||||
float T;
|
||||
float R;
|
||||
float V;
|
||||
} TOptionData;
|
||||
|
||||
typedef struct
|
||||
// #ifdef __CUDACC__
|
||||
//__align__(8)
|
||||
// #endif
|
||||
{
|
||||
float Expected;
|
||||
float Confidence;
|
||||
} TOptionValue;
|
||||
|
||||
// GPU outputs before CPU postprocessing
|
||||
typedef struct
|
||||
{
|
||||
real Expected;
|
||||
real Confidence;
|
||||
} __TOptionValue;
|
||||
|
||||
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;
|
||||
|
||||
// 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-side option values
|
||||
void *d_CallValue;
|
||||
|
||||
// Intermediate device-side buffers
|
||||
void *d_Buffer;
|
||||
|
||||
// random number generator states
|
||||
curandState *rngStates;
|
||||
|
||||
// Pseudorandom samples count
|
||||
int pathN;
|
||||
|
||||
// Time stamp
|
||||
float time;
|
||||
|
||||
int gridSize;
|
||||
} TOptionPlan;
|
||||
|
||||
extern "C" void initMonteCarloGPU(TOptionPlan *plan);
|
||||
extern "C" void MonteCarloGPU(TOptionPlan *plan, cudaStream_t stream = 0);
|
||||
extern "C" void closeMonteCarloGPU(TOptionPlan *plan);
|
||||
|
||||
#endif
|
||||
141
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarlo_gold.cpp
Normal file
141
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarlo_gold.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* 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 "curand_kernel.h"
|
||||
#include "helper_cuda.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Common types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "MonteCarlo_common.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 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 = RSQRT2PI * exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
|
||||
|
||||
if (d > 0)
|
||||
cnd = 1.0 - 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// for(int i=0; i<10; i++) printf("CPU sample = %f\n", samples[i]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (h_Samples == NULL)
|
||||
free(samples);
|
||||
|
||||
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));
|
||||
}
|
||||
224
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarlo_kernel.cu
Normal file
224
cpp/5_Domain_Specific/MonteCarloMultiGPU/MonteCarlo_kernel.cu
Normal file
@@ -0,0 +1,224 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include <cooperative_groups.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
#include <curand_kernel.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
#include "MonteCarlo_common.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper reduction template
|
||||
// Please see the "reduction" CUDA Sample for more information
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "MonteCarlo_reduction.cuh"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Internal GPU-side data structures
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define MAX_OPTIONS (1024 * 1024)
|
||||
|
||||
// Preprocessed input option data
|
||||
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 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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This kernel computes the integral over all paths using a single thread block
|
||||
// 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);
|
||||
|
||||
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;
|
||||
|
||||
// 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};
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS) {
|
||||
printf("MonteCarloGPU(): bad option count.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
__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;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(
|
||||
h_CallValue, plan->d_CallValue, plan->optionCount * sizeof(__TOptionValue), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
// cudaDeviceSynchronize();
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MONTECARLO_REDUCTION_CUH
|
||||
#define MONTECARLO_REDUCTION_CUH
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This function calculates total sum for each of the two input arrays.
|
||||
// SUM_N must be power of two
|
||||
// Unrolling provides a bit of a performance improvement for small
|
||||
// to medium path counts.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
cg::sync(tile32);
|
||||
}
|
||||
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];
|
||||
}
|
||||
__TOptionValue t = {beta, beta2};
|
||||
*d_CallValue = t;
|
||||
}
|
||||
cg::sync(cta);
|
||||
}
|
||||
|
||||
#endif
|
||||
38
cpp/5_Domain_Specific/MonteCarloMultiGPU/README.md
Normal file
38
cpp/5_Domain_Specific/MonteCarloMultiGPU/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# MonteCarloMultiGPU - Monte Carlo Option Pricing with Multi-GPU support
|
||||
|
||||
## Description
|
||||
|
||||
This sample evaluates fair call price for a given set of European options using the Monte Carlo approach, taking advantage of all CUDA-capable GPUs installed in the system. This sample use double precision hardware if a GTX 200 class GPU is present. The sample also takes advantage of CUDA 4.0 capability to supporting using a single CPU thread to control multiple GPUs
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Random Number Generator, Computational Finance, CURAND Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaStreamDestroy, cudaMalloc, cudaFree, cudaMallocHost, cudaSetDevice, cudaEventSynchronize, cudaGetDeviceProperties, cudaDeviceSynchronize, cudaEventRecord, cudaFreeHost, cudaMemset, cudaStreamSynchronize, cudaEventDestroy, cudaMemcpyAsync, cudaStreamCreate, cudaGetDeviceCount, cudaEventCreate
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CURAND](../../../README.md#curand)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[whitepaper](./doc/MonteCarlo.pdf)
|
||||
BIN
cpp/5_Domain_Specific/MonteCarloMultiGPU/doc/MonteCarlo.doc
Normal file
BIN
cpp/5_Domain_Specific/MonteCarloMultiGPU/doc/MonteCarlo.doc
Normal file
Binary file not shown.
BIN
cpp/5_Domain_Specific/MonteCarloMultiGPU/doc/MonteCarlo.pdf
Normal file
BIN
cpp/5_Domain_Specific/MonteCarloMultiGPU/doc/MonteCarlo.pdf
Normal file
Binary file not shown.
74
cpp/5_Domain_Specific/MonteCarloMultiGPU/multithreading.cpp
Normal file
74
cpp/5_Domain_Specific/MonteCarloMultiGPU/multithreading.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <multithreading.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
// Wait for thread to finish
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
72
cpp/5_Domain_Specific/MonteCarloMultiGPU/multithreading.h
Normal file
72
cpp/5_Domain_Specific/MonteCarloMultiGPU/multithreading.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MULTITHREADING_H
|
||||
#define MULTITHREADING_H
|
||||
|
||||
// Simple portable thread library.
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
// Windows threads.
|
||||
#include <windows.h>
|
||||
|
||||
typedef HANDLE CUTThread;
|
||||
typedef unsigned(WINAPI *CUT_THREADROUTINE)(void *);
|
||||
|
||||
#define CUT_THREADPROC unsigned WINAPI
|
||||
#define CUT_THREADEND return 0
|
||||
|
||||
#else
|
||||
// POSIX threads.
|
||||
#include <pthread.h>
|
||||
|
||||
typedef pthread_t CUTThread;
|
||||
typedef void *(*CUT_THREADROUTINE)(void *);
|
||||
|
||||
#define CUT_THREADPROC void
|
||||
#define CUT_THREADEND
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// Create thread.
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE, void *data);
|
||||
|
||||
// Wait for thread to finish.
|
||||
void cutEndThread(CUTThread thread);
|
||||
|
||||
// Wait for multiple threads.
|
||||
void cutWaitForThreads(const CUTThread *threads, int num);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // MULTITHREADING_H
|
||||
39
cpp/5_Domain_Specific/MonteCarloMultiGPU/realtype.h
Normal file
39
cpp/5_Domain_Specific/MonteCarloMultiGPU/realtype.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef REALTYPE_H
|
||||
#define REALTYPE_H
|
||||
|
||||
// #define DOUBLE_PRECISION
|
||||
|
||||
#ifndef DOUBLE_PRECISION
|
||||
typedef float real;
|
||||
#else
|
||||
typedef double real;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
18
cpp/5_Domain_Specific/NV12toBGRandResize/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/NV12toBGRandResize/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/NV12toBGRandResize/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/NV12toBGRandResize/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
44
cpp/5_Domain_Specific/NV12toBGRandResize/CMakeLists.txt
Normal file
44
cpp/5_Domain_Specific/NV12toBGRandResize/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(NV12toBGRandResize LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for NV12toBGRandResize
|
||||
add_executable(NV12toBGRandResize bgr_resize.cu nv12_resize.cu nv12_to_bgr_planar.cu resize_convert_main.cpp utils.cu)
|
||||
|
||||
target_compile_options(NV12toBGRandResize PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(NV12toBGRandResize PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(NV12toBGRandResize PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(NV12toBGRandResize PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_custom_command(TARGET NV12toBGRandResize POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||
${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
32
cpp/5_Domain_Specific/NV12toBGRandResize/README.md
Normal file
32
cpp/5_Domain_Specific/NV12toBGRandResize/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# NV12toBGRandResize - NV12toBGRandResize
|
||||
|
||||
## Description
|
||||
|
||||
This code shows two ways to convert and resize NV12 frames to BGR 3 planars frames using CUDA in batch. Way-1, Convert NV12 Input to BGR @ Input Resolution-1, then Resize to Resolution#2. Way-2, resize NV12 Input to Resolution#2 then convert it to BGR Output. NVIDIA HW Decoder, both dGPU and Tegra, normally outputs NV12 pitch format frames. For the inference using TensorRT, the input frame needs to be BGR planar format with possibly different size. So, conversion and resizing from NV12 to BGR planar is usually required for the inference following decoding. This CUDA code provides a reference implementation for conversion and resizing.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Graphics Interop, Image Processing, Video Processing
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaStreamDestroy, cudaMalloc, cudaFree, cudaMallocManaged, cudaStreamAttachMemAsync, cudaDestroyTextureObject, cudaEventSynchronize, cudaDeviceSynchronize, cudaCreateTextureObject, cudaEventRecord, cudaEventDestroy, cudaEventElapsedTime, cudaStreamCreate, cudaEventCreate
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## References (for more details)
|
||||
174
cpp/5_Domain_Specific/NV12toBGRandResize/bgr_resize.cu
Normal file
174
cpp/5_Domain_Specific/NV12toBGRandResize/bgr_resize.cu
Normal file
@@ -0,0 +1,174 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
// Implements BGR 3 progressive planars frames batch resize
|
||||
|
||||
#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)
|
||||
{
|
||||
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))
|
||||
return;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
h = nSrcHeight * 3 * nBatchSize;
|
||||
while ((h + nTiles - 1) / nTiles > 65536)
|
||||
nTiles++;
|
||||
|
||||
if (nTiles > 2)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
checkCudaErrors(cudaCreateTextureObject(&texSrc[iTile], &resDesc, &texDesc, NULL));
|
||||
float *dpDstNew = dpDst + iTile * (batchTile * 3 * nDstHeight * nDstPitch);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
122
cpp/5_Domain_Specific/NV12toBGRandResize/nv12_resize.cu
Normal file
122
cpp/5_Domain_Specific/NV12toBGRandResize/nv12_resize.cu
Normal file
@@ -0,0 +1,122 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Implements interlace NV12 frames batch resize
|
||||
|
||||
#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;
|
||||
|
||||
int px = x * 2, py = y * 2;
|
||||
|
||||
if ((px + 1) >= nDstWidth || (py + 1) >= nDstHeight)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
cudaTextureDesc texDesc = {};
|
||||
texDesc.filterMode = cudaFilterModePoint;
|
||||
texDesc.readMode = cudaReadModeElementType;
|
||||
|
||||
cudaTextureObject_t texLuma = 0;
|
||||
checkCudaErrors(cudaCreateTextureObject(&texLuma, &resDesc, &texDesc, NULL));
|
||||
|
||||
resDesc.res.pitch2D.desc = cudaCreateChannelDesc<uchar2>();
|
||||
resDesc.res.pitch2D.width /= 2;
|
||||
|
||||
cudaTextureObject_t texChroma = 0;
|
||||
checkCudaErrors(cudaCreateTextureObject(&texChroma, &resDesc, &texDesc, NULL));
|
||||
|
||||
dim3 block(32, 32, 1);
|
||||
|
||||
size_t blockDimZ = nBatchSize;
|
||||
|
||||
// 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);
|
||||
|
||||
checkCudaErrors(cudaDestroyTextureObject(texLuma));
|
||||
checkCudaErrors(cudaDestroyTextureObject(texChroma));
|
||||
}
|
||||
159
cpp/5_Domain_Specific/NV12toBGRandResize/nv12_to_bgr_planar.cu
Normal file
159
cpp/5_Domain_Specific/NV12toBGRandResize/nv12_to_bgr_planar.cu
Normal file
@@ -0,0 +1,159 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
// Implements NV12 to BGR batch conversion
|
||||
|
||||
#include <cuda.h>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "resize_convert.h"
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
__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;
|
||||
|
||||
if ((x << 2) + 1 > nWidth || (y << 1) + 1 > nHeight)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
|
||||
// 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);
|
||||
|
||||
size_t blockDimZ = nBatchSize;
|
||||
|
||||
// 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);
|
||||
}
|
||||
73
cpp/5_Domain_Specific/NV12toBGRandResize/resize_convert.h
Normal file
73
cpp/5_Domain_Specific/NV12toBGRandResize/resize_convert.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __H_RESIZE_CONVERT__
|
||||
#define __H_RESIZE_CONVERT__
|
||||
|
||||
#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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
474
cpp/5_Domain_Specific/NV12toBGRandResize/resize_convert_main.cpp
Normal file
474
cpp/5_Domain_Specific/NV12toBGRandResize/resize_convert_main.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
NVIDIA HW Decoder, both dGPU and Tegra, normally outputs NV12 pitch format
|
||||
frames. For the inference using TensorRT, the input frame needs to be BGR planar
|
||||
format with possibly different size. So, conversion and resizing from NV12 to
|
||||
BGR planar is usually required for the inference following decoding.
|
||||
This CUDA code is to provide a reference implementation for conversion and
|
||||
resizing.
|
||||
|
||||
Limitaion
|
||||
=========
|
||||
NV12resize needs the height to be a even value.
|
||||
|
||||
Note
|
||||
====
|
||||
Resize function needs the pitch of image buffer to be 32 alignment.
|
||||
|
||||
Run
|
||||
====
|
||||
./NV12toBGRandResize
|
||||
OR
|
||||
./NV12toBGRandResize -input=data/test1920x1080.nv12 -width=1920 -height=1080 \
|
||||
-dst_width=640 -dst_height=480 -batch=40 -device=0
|
||||
|
||||
*/
|
||||
|
||||
#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 "resize_convert.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define TEST_LOOP 20
|
||||
|
||||
typedef struct _nv12_to_bgr24_context_t
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int pitch;
|
||||
|
||||
int dst_width;
|
||||
int dst_height;
|
||||
int dst_pitch;
|
||||
|
||||
int batch;
|
||||
int device; // cuda device ID
|
||||
|
||||
char *input_nv12_file;
|
||||
|
||||
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";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int parseCmdLine(int argc, char *argv[])
|
||||
{
|
||||
char **argp = (char **)argv;
|
||||
char *arg = (char *)argv[0];
|
||||
|
||||
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 (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");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
if (!nv12File.is_open()) {
|
||||
std::cerr << "Can't open files\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
frameSize = g_ctx.pitch * g_ctx.ctx_heights;
|
||||
|
||||
#if USE_UVM_MEM
|
||||
pNV12FrameData = d_inputNV12;
|
||||
#else
|
||||
pNV12FrameData = (unsigned char *)malloc(frameSize);
|
||||
if (pNV12FrameData == NULL) {
|
||||
std::cerr << "Failed to malloc pNV12FrameData\n";
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
nv12File.read((char *)pNV12FrameData, frameSize);
|
||||
|
||||
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);
|
||||
#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));
|
||||
|
||||
d_nv12 += g_ctx.ctx_pitch * g_ctx.ctx_heights;
|
||||
}
|
||||
|
||||
#if (USE_UVM_MEM == 0)
|
||||
free(pNV12FrameData);
|
||||
#endif
|
||||
nv12File.close();
|
||||
|
||||
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];
|
||||
|
||||
/* 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));
|
||||
|
||||
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 */
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
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];
|
||||
|
||||
/* 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));
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned char *d_inputNV12;
|
||||
|
||||
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_heights = ceil(g_ctx.height * 3.0f / 2.0f);
|
||||
|
||||
/* 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");
|
||||
#else
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
checkCudaErrors(cudaFree(d_inputNV12));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
148
cpp/5_Domain_Specific/NV12toBGRandResize/utils.cu
Normal file
148
cpp/5_Domain_Specific/NV12toBGRandResize/utils.cu
Normal file
@@ -0,0 +1,148 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* 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 "resize_convert.h"
|
||||
#include "utils.h"
|
||||
|
||||
__global__ void floatToChar(float *src, unsigned char *dst, int height, int width, int batchSize)
|
||||
{
|
||||
int x = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (x >= height * width)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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];
|
||||
#if !defined(_WIN32)
|
||||
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);
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
d_bgr += pitch * height * 3;
|
||||
}
|
||||
|
||||
free(bgr);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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];
|
||||
#if !defined(_WIN32)
|
||||
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);
|
||||
#endif
|
||||
|
||||
int ret = system(mkdir_cmd);
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s.nv12", directory, tag);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cudaMemcpy((void *)nv12Data, (void *)d_nv12, size, cudaMemcpyDeviceToHost);
|
||||
|
||||
nv12File->write((const char *)nv12Data, size);
|
||||
|
||||
free(nv12Data);
|
||||
delete nv12File;
|
||||
}
|
||||
34
cpp/5_Domain_Specific/NV12toBGRandResize/utils.h
Normal file
34
cpp/5_Domain_Specific/NV12toBGRandResize/utils.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#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);
|
||||
#endif
|
||||
118
cpp/5_Domain_Specific/README.md
Normal file
118
cpp/5_Domain_Specific/README.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 5. Domain Specific
|
||||
|
||||
|
||||
### [bicubicTexture](./bicubicTexture)
|
||||
This sample demonstrates how to efficiently implement a Bicubic B-spline interpolation filter with CUDA texture.
|
||||
|
||||
### [bilateralFilter](./bilateralFilter)
|
||||
Bilateral filter is an edge-preserving non-linear smoothing filter that is implemented with CUDA with OpenGL rendering. It can be used in image recovery and denoising. Each pixel is weight by considering both the spatial distance and color distance between its neighbors. Reference:"C. Tomasi, R. Manduchi, Bilateral Filtering for Gray and Color Images, proceeding of the ICCV, 1998, http://users.soe.ucsc.edu/~manduchi/Papers/ICCV98.pdf"
|
||||
|
||||
### [binomialOptions](./binomialOptions)
|
||||
This sample evaluates fair call price for a given set of European options under binomial model.
|
||||
|
||||
### [binomialOptions_nvrtc](./binomialOptions_nvrtc)
|
||||
This sample evaluates fair call price for a given set of European options under binomial model. This sample makes use of NVRTC for Runtime Compilation.
|
||||
|
||||
### [BlackScholes](./BlackScholes)
|
||||
This sample evaluates fair call and put prices for a given set of European options by Black-Scholes formula.
|
||||
|
||||
### [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.
|
||||
|
||||
### [dwtHaar1D](./dwtHaar1D)
|
||||
Discrete Haar wavelet decomposition for 1D signals with a length which is a power of 2.
|
||||
|
||||
### [dxtc](./dxtc)
|
||||
High Quality DXT Compression using CUDA. This example shows how to implement an existing computationally-intensive CPU compression algorithm in parallel on the GPU, and obtain an order of magnitude performance improvement.
|
||||
|
||||
### [fastWalshTransform](./fastWalshTransform)
|
||||
Naturally(Hadamard)-ordered Fast Walsh Transform for batching vectors of arbitrary eligible lengths that are power of two in size.
|
||||
|
||||
### [FDTD3d](./FDTD3d)
|
||||
This sample applies a finite differences time domain progression stencil on a 3D surface.
|
||||
|
||||
### [fluidsGL](./fluidsGL)
|
||||
An example of fluid simulation using CUDA and CUFFT, with OpenGL rendering.
|
||||
|
||||
### [HSOpticalFlow](./HSOpticalFlow)
|
||||
Variational optical flow estimation example. Uses textures for image operations. Shows how simple PDE solver can be accelerated with CUDA.
|
||||
|
||||
### [Mandelbrot](./Mandelbrot)
|
||||
This sample uses CUDA to compute and display the Mandelbrot or Julia sets interactively. It also illustrates the use of "double single" arithmetic to improve precision when zooming a long way into the pattern. This sample uses double precision. Thanks to Mark Granger of NewTek who submitted this code sample.!
|
||||
|
||||
### [marchingCubes](./marchingCubes)
|
||||
This sample extracts a geometric isosurface from a volume dataset using the marching cubes algorithm. It uses the scan (prefix sum) function from the Thrust library to perform stream compaction.
|
||||
|
||||
### [MonteCarloMultiGPU](./MonteCarloMultiGPU)
|
||||
This sample evaluates fair call price for a given set of European options using the Monte Carlo approach, taking advantage of all CUDA-capable GPUs installed in the system. This sample use double precision hardware if a GTX 200 class GPU is present. The sample also takes advantage of CUDA 4.0 capability to supporting using a single CPU thread to control multiple GPUs
|
||||
|
||||
### [nbody](./nbody)
|
||||
This sample demonstrates efficient all-pairs simulation of a gravitational n-body simulation in CUDA. This sample accompanies the GPU Gems 3 chapter "Fast N-Body Simulation with CUDA". With CUDA 5.5, performance on Tesla K20c has increased to over 1.8TFLOP/s single precision. Double Performance has also improved on all Kepler and Fermi GPU architectures as well. Starting in CUDA 4.0, the nBody sample has been updated to take advantage of new features to easily scale the n-body simulation across multiple GPUs in a single PC. Adding "-numbodies=<bodies>" to the command line will allow users to set # of bodies for simulation. Adding "-numdevices=<N>" to the command line option will cause the sample to use N devices (if available) for simulation. In this mode, the position and velocity data for all bodies are read from system memory using "zero copy" rather than from device memory. For a small number of devices (4 or fewer) and a large enough number of bodies, bandwidth is not a bottleneck so we can achieve strong scaling across these devices.
|
||||
|
||||
### [nbody_screen](./nbody_screen)
|
||||
This sample demonstrates efficient all-pairs simulation of a gravitational n-body simulation in CUDA. Unlike the OpenGL nbody sample, there is no user interaction.
|
||||
|
||||
### [NV12toBGRandResize](./NV12toBGRandResize)
|
||||
This code shows two ways to convert and resize NV12 frames to BGR 3 planars frames using CUDA in batch. Way-1, Convert NV12 Input to BGR @ Input Resolution-1, then Resize to Resolution#2. Way-2, resize NV12 Input to Resolution#2 then convert it to BGR Output. NVIDIA HW Decoder, both dGPU and Tegra, normally outputs NV12 pitch format frames. For the inference using TensorRT, the input frame needs to be BGR planar format with possibly different size. So, conversion and resizing from NV12 to BGR planar is usually required for the inference following decoding. This CUDA code provides a reference implementation for conversion and resizing.
|
||||
|
||||
### [p2pBandwidthLatencyTest](./p2pBandwidthLatencyTest)
|
||||
This application demonstrates the CUDA Peer-To-Peer (P2P) data transfers between pairs of GPUs and computes latency and bandwidth. Tests on GPU pairs using P2P and without P2P are tested.
|
||||
|
||||
### [postProcessGL](./postProcessGL)
|
||||
This sample shows how to post-process an image rendered in OpenGL using CUDA.
|
||||
|
||||
### [quasirandomGenerator](./quasirandomGenerator)
|
||||
This sample implements Niederreiter Quasirandom Sequence Generator and Inverse Cumulative Normal Distribution functions for the generation of Standard Normal Distributions.
|
||||
|
||||
### [quasirandomGenerator_nvrtc](./quasirandomGenerator_nvrtc)
|
||||
This sample implements Niederreiter Quasirandom Sequence Generator and Inverse Cumulative Normal Distribution functions for the generation of Standard Normal Distributions, compiling the CUDA kernels involved at runtime using NVRTC.
|
||||
|
||||
### [recursiveGaussian](./recursiveGaussian)
|
||||
This sample implements a Gaussian blur using Deriche's recursive method. The advantage of this method is that the execution time is independent of the filter width.
|
||||
|
||||
### [simpleD3D11](./simpleD3D11)
|
||||
Simple program which demonstrates how to use the CUDA D3D11 External Resource Interoperability APIs to update D3D11 buffers from CUDA and synchronize between D3D11 and CUDA with Keyed Mutexes.
|
||||
|
||||
|
||||
### [simpleD3D11Texture](./simpleD3D11Texture)
|
||||
Simple program which demonstrates Direct3D11 Texture interoperability with CUDA. The program creates a number of D3D11 Textures (2D, 3D, and CubeMap) which are written to from CUDA kernels. Direct3D then renders the results on the screen. A Direct3D Capable device is required.
|
||||
|
||||
### [simpleD3D12](./simpleD3D12)
|
||||
A program which demonstrates Direct3D12 interoperability with CUDA. The program creates a sinewave in DX12 vertex buffer which is created using CUDA kernels. DX12 and CUDA synchronizes using DirectX12 Fences. Direct3D then renders the results on the screen. A DirectX12 Capable NVIDIA GPU is required on Windows10 or higher OS.
|
||||
|
||||
### [simpleGL](./simpleGL)
|
||||
Simple program which demonstrates interoperability between CUDA and OpenGL. The program modifies vertex positions with CUDA and uses OpenGL to render the geometry.
|
||||
|
||||
### [simpleGLES_screen](./simpleGLES_screen)
|
||||
Demonstrates data exchange between CUDA and OpenGL ES (aka Graphics interop). The program modifies vertex positions with CUDA and uses OpenGL ES to render the geometry.
|
||||
|
||||
### [simpleVulkan](./simpleVulkan)
|
||||
This sample demonstrates Vulkan CUDA Interop. CUDA imports the Vulkan vertex buffer and operates on it to create sinewave, 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
|
||||
|
||||
### [simpleVulkanMMAP](./simpleVulkanMMAP)
|
||||
This sample demonstrates Vulkan CUDA Interop via cuMemMap APIs. CUDA exports buffers that Vulkan imports as vertex buffer. CUDA invokes kernels to operate on vertices 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
|
||||
|
||||
### [smokeParticles](./smokeParticles)
|
||||
Smoke simulation with volumetric shadows using half-angle slicing technique. Uses CUDA for procedural simulation, Thrust Library for sorting algorithms, and OpenGL for graphics rendering.
|
||||
|
||||
### [SobelFilter](./SobelFilter)
|
||||
This sample implements the Sobel edge detection filter for 8-bit monochrome images.
|
||||
|
||||
### [SobolQRNG](./SobolQRNG)
|
||||
This sample implements Sobol Quasirandom Sequence Generator.
|
||||
|
||||
### [stereoDisparity](./stereoDisparity)
|
||||
A CUDA program that demonstrates how to compute a stereo disparity map using SIMD SAD (Sum of Absolute Difference) intrinsics. Requires Compute Capability 2.0 or higher.
|
||||
|
||||
### [volumeFiltering](./volumeFiltering)
|
||||
This sample demonstrates 3D Volumetric Filtering using 3D Textures and 3D Surface Writes.
|
||||
|
||||
### [volumeRender](./volumeRender)
|
||||
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
|
||||
18
cpp/5_Domain_Specific/SobelFilter/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/SobelFilter/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/5_Domain_Specific/SobelFilter/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/SobelFilter/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
95
cpp/5_Domain_Specific/SobelFilter/CMakeLists.txt
Normal file
95
cpp/5_Domain_Specific/SobelFilter/CMakeLists.txt
Normal file
@@ -0,0 +1,95 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(SobelFilter LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
if(WIN32)
|
||||
set(PC_GLUT_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../Common")
|
||||
set(PC_GLUT_LIBRARY_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../Common/lib/x64")
|
||||
# The GLEW library built on Windows on Arm system is named as glew32.lib/glew32.dll by default.
|
||||
if(EXISTS "${PC_GLUT_LIBRARY_DIRS}/glew32.lib")
|
||||
set(GLEW_LIB_NAME "glew32")
|
||||
else()
|
||||
set(GLEW_LIB_NAME "glew64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(OpenGL)
|
||||
find_package(GLUT)
|
||||
|
||||
# Source file
|
||||
if(${OpenGL_FOUND})
|
||||
if (${GLUT_FOUND})
|
||||
# Add target for SobelFilter
|
||||
add_executable(SobelFilter SobelFilter.cpp SobelFilter_kernels.cu)
|
||||
|
||||
target_compile_options(SobelFilter PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(SobelFilter PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(SobelFilter PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(SobelFilter PUBLIC
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
${GLUT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(SobelFilter
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLUT_LIBRARIES}
|
||||
)
|
||||
|
||||
add_custom_command(TARGET SobelFilter POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||
${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(SobelFilter
|
||||
${PC_GLUT_LIBRARY_DIRS}/freeglut.lib
|
||||
${PC_GLUT_LIBRARY_DIRS}/${GLEW_LIB_NAME}.lib
|
||||
)
|
||||
|
||||
add_custom_command(TARGET SobelFilter
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/win64/$<CONFIGURATION>/freeglut.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
|
||||
add_custom_command(TARGET SobelFilter
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/win64/$<CONFIGURATION>/${GLEW_LIB_NAME}.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "GLUT not found - will not build sample 'SobelFilter'")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "OpenGL not found - will not build sample 'SobelFilter'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user