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,95 @@
cmake_minimum_required(VERSION 3.20)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
project(recursiveGaussian 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 recursiveGaussian
add_executable(recursiveGaussian recursiveGaussian_cuda.cu recursiveGaussian.cpp)
target_compile_options(recursiveGaussian PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
target_compile_features(recursiveGaussian PRIVATE cxx_std_17 cuda_std_17)
set_target_properties(recursiveGaussian PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_include_directories(recursiveGaussian PUBLIC
${OPENGL_INCLUDE_DIR}
${CUDAToolkit_INCLUDE_DIRS}
${GLUT_INCLUDE_DIRS}
)
target_link_libraries(recursiveGaussian
${OPENGL_LIBRARIES}
${GLUT_LIBRARIES}
)
add_custom_command(TARGET recursiveGaussian POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/data
${CMAKE_CURRENT_BINARY_DIR}/data
)
if(WIN32)
target_link_libraries(recursiveGaussian
${PC_GLUT_LIBRARY_DIRS}/freeglut.lib
${PC_GLUT_LIBRARY_DIRS}/${GLEW_LIB_NAME}.lib
)
add_custom_command(TARGET recursiveGaussian
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 recursiveGaussian
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 'recursiveGaussian'")
endif()
else()
message(STATUS "OpenGL not found - will not build sample 'recursiveGaussian'")
endif()
# Include installation configuration
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
setup_samples_install()

View File

@@ -0,0 +1,36 @@
# recursiveGaussian - Recursive Gaussian Filter
## Description
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.
## Key Concepts
Graphics Interop, Image 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, armv7l
## CUDA APIs involved
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
cudaGraphicsUnmapResources, cudaMemcpy, cudaFree, cudaGraphicsResourceGetMappedPointer, cudaGraphicsMapResources, cudaDeviceSynchronize, cudaGetDevice, 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)

View File

@@ -0,0 +1,530 @@
/* 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.
*/
/*
Recursive Gaussian filter
sgreen 8/1/08
This code sample implements a Gaussian blur using Deriche's recursive method:
http://citeseer.ist.psu.edu/deriche93recursively.html
This is similar to the box filter sample in the SDK, but it uses the previous
outputs of the filter as well as the previous inputs. This is also known as an
IIR (infinite impulse response) filter, since its response to an input impulse
can last forever.
The main advantage of this method is that the execution time is independent of
the filter width.
The GPU processes columns of the image in parallel. To avoid uncoalesced reads
for the row pass we transpose the image and then transpose it back again
afterwards.
The implementation is based on code from the CImg library:
http://cimg.sourceforge.net/
Thanks to David Tschumperl<72> and all the CImg contributors!
*/
#pragma warning(disable : 4819)
// OpenGL Graphics includes
#include <helper_gl.h>
#if defined(__APPLE__) || defined(MACOSX)
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include <GLUT/glut.h>
#ifndef glutCloseFunc
#define glutCloseFunc glutWMCloseFunc
#endif
#else
#include <GL/freeglut.h>
#endif
// CUDA includes and interop headers
#include <cuda_gl_interop.h>
#include <cuda_runtime.h>
// CUDA utilities and system includes
#include <helper_cuda.h> // includes cuda.h and cuda_runtime_api.h
#include <helper_functions.h>
// Includes
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX(a, b) ((a > b) ? a : b)
#define USE_SIMPLE_FILTER 0
#define MAX_EPSILON_ERROR 5.0f
#define THRESHOLD 0.15f
// Define the files that are to be save and the reference images for validation
const char *sOriginal[] = {"teapot512_10.ppm", "teapot512_14.ppm", "teapot512_18.ppm", "teapot512_22.ppm", NULL};
const char *sReference[] = {"ref_10.ppm", "ref_14.ppm", "ref_18.ppm", "ref_22.ppm", NULL};
const char *image_filename = "teapot512.ppm";
float sigma = 10.0f;
int order = 0;
int nthreads = 64; // number of threads per block
unsigned int width, height;
unsigned int *h_img = NULL;
unsigned int *d_img = NULL;
unsigned int *d_temp = NULL;
GLuint pbo = 0; // OpenGL pixel buffer object
GLuint texid = 0; // texture
cudaGraphicsResource_t cuda_vbo_resource;
StopWatchInterface *timer = 0;
// Auto-Verification Code
const int frameCheckNumber = 4;
int fpsCount = 0; // FPS count for averaging
int fpsLimit = 1; // FPS limit for sampling
unsigned int frameCount = 0;
int *pArgc = NULL;
char **pArgv = NULL;
bool runBenchmark = false;
const char *sSDKsample = "CUDA Recursive Gaussian";
extern "C" void transpose(unsigned int *d_src, unsigned int *d_dest, unsigned int width, int height);
extern "C" void gaussianFilterRGBA(unsigned int *d_src,
unsigned int *d_dest,
unsigned int *d_temp,
int width,
int height,
float sigma,
int order,
int nthreads);
void cleanup();
void computeFPS()
{
frameCount++;
fpsCount++;
if (fpsCount == fpsLimit) {
char fps[256];
float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);
sprintf(fps, "%s (sigma=%4.2f): %3.1f fps", sSDKsample, sigma, ifps);
glutSetWindowTitle(fps);
fpsCount = 0;
fpsLimit = ftoi(MAX(ifps, 1.f));
sdkResetTimer(&timer);
}
}
// display results using OpenGL
void display()
{
sdkStartTimer(&timer);
// execute filter, writing results to pbo
unsigned int *d_result;
checkCudaErrors(cudaGraphicsMapResources(1, &cuda_vbo_resource, 0));
size_t num_bytes;
checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&d_result, &num_bytes, cuda_vbo_resource));
gaussianFilterRGBA(d_img, d_result, d_temp, width, height, sigma, order, nthreads);
// unmap buffer object
checkCudaErrors(cudaGraphicsUnmapResources(1, &cuda_vbo_resource, 0));
// load texture from pbo
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
glBindTexture(GL_TEXTURE_2D, texid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
// display results
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glBegin(GL_QUADS);
glTexCoord2f(0, 1);
glVertex2f(0, 0);
glTexCoord2f(1, 1);
glVertex2f(1, 0);
glTexCoord2f(1, 0);
glVertex2f(1, 1);
glTexCoord2f(0, 0);
glVertex2f(0, 1);
glEnd();
glDisable(GL_TEXTURE_2D);
glutSwapBuffers();
sdkStopTimer(&timer);
computeFPS();
}
void idle() { glutPostRedisplay(); }
void cleanup()
{
sdkDeleteTimer(&timer);
checkCudaErrors(cudaFree(d_img));
checkCudaErrors(cudaFree(d_temp));
if (!runBenchmark) {
if (pbo) {
// unregister this buffer object with CUDA
checkCudaErrors(cudaGraphicsUnregisterResource(cuda_vbo_resource));
glDeleteBuffers(1, &pbo);
}
if (texid) {
glDeleteTextures(1, &texid);
}
}
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27:
#if defined(__APPLE__) || defined(MACOSX)
exit(EXIT_SUCCESS);
#else
glutDestroyWindow(glutGetWindow());
return;
#endif
break;
case '=':
sigma += 0.1f;
break;
case '-':
sigma -= 0.1f;
if (sigma < 0.0) {
sigma = 0.0f;
}
break;
case '+':
sigma += 1.0f;
break;
case '_':
sigma -= 1.0f;
if (sigma < 0.0) {
sigma = 0.0f;
}
break;
case '0':
order = 0;
break;
case '1':
order = 1;
sigma = 0.5f;
break;
case '2':
order = 2;
sigma = 0.5f;
break;
default:
break;
}
printf("sigma = %f\n", sigma);
glutPostRedisplay();
}
void reshape(int x, int y)
{
glViewport(0, 0, x, y);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
}
void initCudaBuffers()
{
unsigned int size = width * height * sizeof(unsigned int);
// allocate device memory
checkCudaErrors(cudaMalloc((void **)&d_img, size));
checkCudaErrors(cudaMalloc((void **)&d_temp, size));
checkCudaErrors(cudaMemcpy(d_img, h_img, size, cudaMemcpyHostToDevice));
sdkCreateTimer(&timer);
}
void initGLBuffers()
{
// create pixel buffer object to store final image
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width * height * sizeof(GLubyte) * 4, h_img, GL_STREAM_DRAW_ARB);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, pbo, cudaGraphicsRegisterFlagsWriteDiscard));
// create texture for display
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
}
void initGL(int *argc, char **argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowSize(width, height);
glutCreateWindow(sSDKsample);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutReshapeFunc(reshape);
glutIdleFunc(idle);
#if defined(__APPLE__) || defined(MACOSX)
atexit(cleanup);
#else
glutCloseFunc(cleanup);
#endif
printf("Press '+' and '-' to change filter width\n");
printf("0, 1, 2 - change filter order\n");
if (!isGLVersionSupported(2, 0)
|| !areGLExtensionsSupported("GL_ARB_vertex_buffer_object GL_ARB_pixel_buffer_object")) {
fprintf(stderr, "Required OpenGL extensions missing.");
exit(EXIT_FAILURE);
}
}
void benchmark(int iterations)
{
// allocate memory for result
unsigned int *d_result;
unsigned int size = width * height * sizeof(unsigned int);
checkCudaErrors(cudaMalloc((void **)&d_result, size));
// warm-up
gaussianFilterRGBA(d_img, d_result, d_temp, width, height, sigma, order, nthreads);
checkCudaErrors(cudaDeviceSynchronize());
sdkStartTimer(&timer);
// execute the kernel
for (int i = 0; i < iterations; i++) {
gaussianFilterRGBA(d_img, d_result, d_temp, width, height, sigma, order, nthreads);
}
checkCudaErrors(cudaDeviceSynchronize());
sdkStopTimer(&timer);
// check if kernel execution generated an error
getLastCudaError("Kernel execution failed");
printf("Processing time: %f (ms)\n", sdkGetTimerValue(&timer));
printf("%.2f Mpixels/sec\n", (width * height * iterations / (sdkGetTimerValue(&timer) / 1000.0f)) / 1e6);
checkCudaErrors(cudaFree(d_result));
}
bool runSingleTest(const char *ref_file, const char *exec_path)
{
// allocate memory for result
int nTotalErrors = 0;
unsigned int *d_result;
unsigned int size = width * height * sizeof(unsigned int);
checkCudaErrors(cudaMalloc((void **)&d_result, size));
// warm-up
gaussianFilterRGBA(d_img, d_result, d_temp, width, height, sigma, order, nthreads);
checkCudaErrors(cudaDeviceSynchronize());
sdkStartTimer(&timer);
gaussianFilterRGBA(d_img, d_result, d_temp, width, height, sigma, order, nthreads);
checkCudaErrors(cudaDeviceSynchronize());
getLastCudaError("Kernel execution failed");
sdkStopTimer(&timer);
unsigned char *h_result = (unsigned char *)malloc(width * height * 4);
checkCudaErrors(cudaMemcpy(h_result, d_result, width * height * 4, cudaMemcpyDeviceToHost));
char dump_file[1024];
sprintf(dump_file, "teapot512_%02d.ppm", (int)sigma);
sdkSavePPM4ub(dump_file, h_result, width, height);
if (!sdkComparePPM(dump_file, sdkFindFilePath(ref_file, exec_path), MAX_EPSILON_ERROR, THRESHOLD, false)) {
nTotalErrors++;
}
printf("Processing time: %f (ms)\n", sdkGetTimerValue(&timer));
printf("%.2f Mpixels/sec\n", (width * height / (sdkGetTimerValue(&timer) / 1000.0f)) / 1e6);
checkCudaErrors(cudaFree(d_result));
free(h_result);
printf("Summary: %d errors!\n", nTotalErrors);
printf(nTotalErrors == 0 ? "Test passed\n" : "Test failed!\n");
return (nTotalErrors == 0);
}
////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
pArgc = &argc;
pArgv = argv;
char *ref_file = NULL;
#if defined(__linux__)
setenv("DISPLAY", ":0", 0);
#endif
printf("%s Starting...\n\n", sSDKsample);
printf("NOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
// use command-line specified CUDA device, otherwise use device with highest
// Gflops/s
if (argc > 1) {
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
getCmdLineArgumentString(argc, (const char **)argv, "file", &ref_file);
fpsLimit = frameCheckNumber;
}
}
// Get the path of the filename
char *filename;
if (getCmdLineArgumentString(argc, (const char **)argv, "image", &filename)) {
image_filename = filename;
}
// load image
char *image_path = sdkFindFilePath(image_filename, argv[0]);
if (image_path == NULL) {
fprintf(stderr, "Error unable to find and load image file: '%s'\n", image_filename);
exit(EXIT_FAILURE);
}
sdkLoadPPM4ub(image_path, (unsigned char **)&h_img, &width, &height);
if (!h_img) {
printf("Error unable to load PPM file: '%s'\n", image_path);
exit(EXIT_FAILURE);
}
printf("Loaded '%s', %d x %d pixels\n", image_path, width, height);
if (checkCmdLineFlag(argc, (const char **)argv, "threads")) {
nthreads = getCmdLineArgumentInt(argc, (const char **)argv, "threads");
}
if (checkCmdLineFlag(argc, (const char **)argv, "sigma")) {
sigma = getCmdLineArgumentFloat(argc, (const char **)argv, "sigma");
}
runBenchmark = checkCmdLineFlag(argc, (const char **)argv, "benchmark");
int device;
struct cudaDeviceProp prop;
cudaGetDevice(&device);
cudaGetDeviceProperties(&prop, device);
if (!strncmp("Tesla", prop.name, 5)) {
printf("Tesla card detected, running the test in benchmark mode (no OpenGL "
"display)\n");
// runBenchmark = true;
runBenchmark = true;
}
// Benchmark or AutoTest mode detected, no OpenGL
if (runBenchmark == true || ref_file != NULL) {
findCudaDevice(argc, (const char **)argv);
}
else {
// First initialize OpenGL context, and then select CUDA device.
initGL(&argc, argv);
findCudaDevice(argc, (const char **)argv);
}
initCudaBuffers();
if (ref_file) {
printf("(Automated Testing)\n");
bool testPassed = runSingleTest(ref_file, argv[0]);
cleanup();
exit(testPassed ? EXIT_SUCCESS : EXIT_FAILURE);
}
if (runBenchmark) {
printf("(Run Benchmark)\n");
benchmark(100);
cleanup();
exit(EXIT_SUCCESS);
}
initGLBuffers();
glutMainLoop();
exit(EXIT_SUCCESS);
}

View File

@@ -0,0 +1,155 @@
/* 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.
*/
/*
Recursive Gaussian filter
sgreen 8/1/08
This code sample implements a Gaussian blur using Deriche's recursive method:
http://citeseer.ist.psu.edu/deriche93recursively.html
This is similar to the box filter sample in the SDK, but it uses the previous
outputs of the filter as well as the previous inputs. This is also known as an
IIR (infinite impulse response) filter, since its response to an input impulse
can last forever.
The main advantage of this method is that the execution time is independent of
the filter width.
The GPU processes columns of the image in parallel. To avoid uncoalesced reads
for the row pass we transpose the image and then transpose it back again
afterwards.
The implementation is based on code from the CImg library:
http://cimg.sourceforge.net/
Thanks to David Tschumperl<72> and all the CImg contributors!
*/
#include <cuda_runtime.h>
#include <helper_cuda.h>
#include <helper_math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "recursiveGaussian_kernel.cuh"
#define USE_SIMPLE_FILTER 0
// Round a / b to nearest higher integer value
int iDivUp(int a, int b) { return (a % b != 0) ? (a / b + 1) : (a / b); }
/*
Transpose a 2D array (see SDK transpose example)
*/
extern "C" void transpose(uint *d_src, uint *d_dest, uint width, int height)
{
dim3 grid(iDivUp(width, BLOCK_DIM), iDivUp(height, BLOCK_DIM), 1);
dim3 threads(BLOCK_DIM, BLOCK_DIM, 1);
d_transpose<<<grid, threads>>>(d_dest, d_src, width, height);
getLastCudaError("Kernel execution failed");
}
/*
Perform Gaussian filter on a 2D image using CUDA
Parameters:
d_src - pointer to input image in device memory
d_dest - pointer to destination image in device memory
d_temp - pointer to temporary storage in device memory
width - image width
height - image height
sigma - sigma of Gaussian
order - filter order (0, 1 or 2)
*/
// 8-bit RGBA version
extern "C" void
gaussianFilterRGBA(uint *d_src, uint *d_dest, uint *d_temp, int width, int height, float sigma, int order, int nthreads)
{
// compute filter coefficients
const float nsigma = sigma < 0.1f ? 0.1f : sigma, alpha = 1.695f / nsigma, ema = (float)std::exp(-alpha),
ema2 = (float)std::exp(-2 * alpha), b1 = -2 * ema, b2 = ema2;
float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
switch (order) {
case 0: {
const float k = (1 - ema) * (1 - ema) / (1 + 2 * alpha * ema - ema2);
a0 = k;
a1 = k * (alpha - 1) * ema;
a2 = k * (alpha + 1) * ema;
a3 = -k * ema2;
} break;
case 1: {
const float k = (1 - ema) * (1 - ema) / ema;
a0 = k * ema;
a1 = a3 = 0;
a2 = -a0;
} break;
case 2: {
const float ea = (float)std::exp(-alpha), k = -(ema2 - 1) / (2 * alpha * ema),
kn = (-2 * (-1 + 3 * ea - 3 * ea * ea + ea * ea * ea) / (3 * ea + 1 + 3 * ea * ea + ea * ea * ea));
a0 = kn;
a1 = -kn * (1 + k * alpha) * ema;
a2 = kn * (1 - k * alpha) * ema;
a3 = -kn * ema2;
} break;
default:
fprintf(stderr, "gaussianFilter: invalid order parameter!\n");
return;
}
coefp = (a0 + a1) / (1 + b1 + b2);
coefn = (a2 + a3) / (1 + b1 + b2);
// process columns
#if USE_SIMPLE_FILTER
d_simpleRecursive_rgba<<<iDivUp(width, nthreads), nthreads>>>(d_src, d_temp, width, height, ema);
#else
d_recursiveGaussian_rgba<<<iDivUp(width, nthreads), nthreads>>>(
d_src, d_temp, width, height, a0, a1, a2, a3, b1, b2, coefp, coefn);
#endif
getLastCudaError("Kernel execution failed");
transpose(d_temp, d_dest, width, height);
getLastCudaError("transpose: Kernel execution failed");
// process rows
#if USE_SIMPLE_FILTER
d_simpleRecursive_rgba<<<iDivUp(height, nthreads), nthreads>>>(d_dest, d_temp, height, width, ema);
#else
d_recursiveGaussian_rgba<<<iDivUp(height, nthreads), nthreads>>>(
d_dest, d_temp, height, width, a0, a1, a2, a3, b1, b2, coefp, coefn);
#endif
getLastCudaError("Kernel execution failed");
transpose(d_temp, d_dest, height, width);
}

View File

@@ -0,0 +1,235 @@
/* 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.
*/
/*
Recursive Gaussian filter
*/
#ifndef _RECURSIVEGAUSSIAN_KERNEL_CU_
#define _RECURSIVEGAUSSIAN_KERNEL_CU_
#include <cooperative_groups.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace cg = cooperative_groups;
#include <helper_cuda.h>
#include <helper_math.h>
#define BLOCK_DIM 16
#define CLAMP_TO_EDGE 1
// Transpose kernel (see transpose CUDA Sample for details)
__global__ void d_transpose(uint *odata, uint *idata, int width, int height)
{
// Handle to thread block group
cg::thread_block cta = cg::this_thread_block();
__shared__ uint block[BLOCK_DIM][BLOCK_DIM + 1];
// read the matrix tile into shared memory
unsigned int xIndex = blockIdx.x * BLOCK_DIM + threadIdx.x;
unsigned int yIndex = blockIdx.y * BLOCK_DIM + threadIdx.y;
if ((xIndex < width) && (yIndex < height)) {
unsigned int index_in = yIndex * width + xIndex;
block[threadIdx.y][threadIdx.x] = idata[index_in];
}
cg::sync(cta);
// write the transposed matrix tile to global memory
xIndex = blockIdx.y * BLOCK_DIM + threadIdx.x;
yIndex = blockIdx.x * BLOCK_DIM + threadIdx.y;
if ((xIndex < height) && (yIndex < width)) {
unsigned int index_out = yIndex * height + xIndex;
odata[index_out] = block[threadIdx.x][threadIdx.y];
}
}
// RGBA version
// reads from 32-bit uint array holding 8-bit RGBA
// convert floating point rgba color to 32-bit integer
__device__ uint rgbaFloatToInt(float4 rgba)
{
rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]
rgba.y = __saturatef(rgba.y);
rgba.z = __saturatef(rgba.z);
rgba.w = __saturatef(rgba.w);
return (uint(rgba.w * 255) << 24) | (uint(rgba.z * 255) << 16) | (uint(rgba.y * 255) << 8) | uint(rgba.x * 255);
}
// convert from 32-bit int to float4
__device__ float4 rgbaIntToFloat(uint c)
{
float4 rgba;
rgba.x = (c & 0xff) / 255.0f;
rgba.y = ((c >> 8) & 0xff) / 255.0f;
rgba.z = ((c >> 16) & 0xff) / 255.0f;
rgba.w = ((c >> 24) & 0xff) / 255.0f;
return rgba;
}
/*
simple 1st order recursive filter
- processes one image column per thread
parameters:
id - pointer to input data (RGBA image packed into 32-bit integers)
od - pointer to output data
w - image width
h - image height
a - blur parameter
*/
__global__ void d_simpleRecursive_rgba(uint *id, uint *od, int w, int h, float a)
{
unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
if (x >= w)
return;
id += x; // advance pointers to correct column
od += x;
// forward pass
float4 yp = rgbaIntToFloat(*id); // previous output
for (int y = 0; y < h; y++) {
float4 xc = rgbaIntToFloat(*id);
float4 yc = xc + a * (yp - xc); // simple lerp between current and previous value
*od = rgbaFloatToInt(yc);
id += w;
od += w; // move to next row
yp = yc;
}
// reset pointers to point to last element in column
id -= w;
od -= w;
// reverse pass
// ensures response is symmetrical
yp = rgbaIntToFloat(*id);
for (int y = h - 1; y >= 0; y--) {
float4 xc = rgbaIntToFloat(*id);
float4 yc = xc + a * (yp - xc);
*od = rgbaFloatToInt((rgbaIntToFloat(*od) + yc) * 0.5f);
id -= w;
od -= w; // move to previous row
yp = yc;
}
}
/*
recursive Gaussian filter
parameters:
id - pointer to input data (RGBA image packed into 32-bit integers)
od - pointer to output data
w - image width
h - image height
a0-a3, b1, b2, coefp, coefn - filter parameters
*/
__global__ void d_recursiveGaussian_rgba(uint *id,
uint *od,
int w,
int h,
float a0,
float a1,
float a2,
float a3,
float b1,
float b2,
float coefp,
float coefn)
{
unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
if (x >= w)
return;
id += x; // advance pointers to correct column
od += x;
// forward pass
float4 xp = make_float4(0.0f); // previous input
float4 yp = make_float4(0.0f); // previous output
float4 yb = make_float4(0.0f); // previous output by 2
#if CLAMP_TO_EDGE
xp = rgbaIntToFloat(*id);
yb = coefp * xp;
yp = yb;
#endif
for (int y = 0; y < h; y++) {
float4 xc = rgbaIntToFloat(*id);
float4 yc = a0 * xc + a1 * xp - b1 * yp - b2 * yb;
*od = rgbaFloatToInt(yc);
id += w;
od += w; // move to next row
xp = xc;
yb = yp;
yp = yc;
}
// reset pointers to point to last element in column
id -= w;
od -= w;
// reverse pass
// ensures response is symmetrical
float4 xn = make_float4(0.0f);
float4 xa = make_float4(0.0f);
float4 yn = make_float4(0.0f);
float4 ya = make_float4(0.0f);
#if CLAMP_TO_EDGE
xn = xa = rgbaIntToFloat(*id);
yn = coefn * xn;
ya = yn;
#endif
for (int y = h - 1; y >= 0; y--) {
float4 xc = rgbaIntToFloat(*id);
float4 yc = a2 * xn + a3 * xa - b1 * yn - b2 * ya;
xa = xn;
xn = xc;
ya = yn;
yn = yc;
*od = rgbaFloatToInt(rgbaIntToFloat(*od) + yc);
id -= w;
od -= w; // move to previous row
}
}
#endif // #ifndef _GAUSSIAN_KERNEL_H_