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:
Dheemanth
2026-05-13 15:13:18 -07:00
committed by GitHub
parent a4526d5229
commit aeab82ff30
2072 changed files with 15077 additions and 1970 deletions

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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);
}

View 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);
}

View 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);
}
}

View 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()

View 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)

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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);
}

View 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);
}

View 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);
}
}

View 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()

View 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)

View 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)

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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()

View 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)

View 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

View 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

View 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;
}
}

View 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

View 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);
}

View 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;
}

View 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;
}

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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()

Binary file not shown.

Binary file not shown.

View 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)

View 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);
}

View 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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);
}

View 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);
}

View 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));
}

View 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

View 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;
}

View 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

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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()

File diff suppressed because it is too large Load Diff

View 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

View 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));
}

View 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

View 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

View 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

View 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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
-0.085760 0.007040 3.200000 -0.172400 -0.652693

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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()

View 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);
}

View 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

View 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));
}

View 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();
}

View File

@@ -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

View 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)

View 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

View 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

View 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

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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()

View 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)

View 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

View 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));
}

View 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);
}

View 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

View 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;
}

View 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;
}

View 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

View 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

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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