Build Generated CUDA Code Using CMake Toolchain
CMake is a third-party, open source tool for build process management. In this example, when generating CUDA® code for a MATLAB® function, you instruct the code generator to also produce a CMakeLists.txt file. This file contains the build instructions for the generated code in a platform-independent CMake language.
After generating CUDA code and the CMakeLists.txt file, you invoke the cmake command to:
Use the selected generator to produce standard build files from the
CMakeLists.txtfile.Run the compiler and other build tools to create a dynamic library.
In this example, the MATLAB entry-point function fog_rectification takes a foggy image as input and produces a defogged image. Your goal is to generate a SIL MEX that links to a generated CUDA dynamic library for this entry-point function. For more information about the algorithm that fog_rectification uses, see Fog Rectification.
Third-Party Prerequisites
Required
CUDA enabled NVIDIA® GPU and compatible driver.
NVIDIA CUDA toolkit.
Environment variables for the compilers and libraries for your specific platform. For more information, see Third-Party Hardware and Setting Up the Prerequisite Products.
Optional
CMake with version greater than 3.18.
Ninja-build
Verify GPU Environment
To verify that the compilers and libraries necessary for running this example are set up correctly, use the coder.checkGpuInstall function.
envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);The fog_rectification Entry-Point Function
The fog_rectification.m entry-point function takes a foggy image as input and returns a defogged image.
type fog_rectificationfunction [out] = fog_rectification(input) %#codegen
%
% Copyright 2017-2023 The MathWorks, Inc.
coder.gpu.kernelfun;
% restoreOut is used to store the output of restoration
restoreOut = zeros(size(input),"double");
% Changing the precision level of input image to double
input = double(input)./255;
%% Dark channel Estimation from input
darkChannel = min(input,[],3);
% diff_im is used as input and output variable for anisotropic
% diffusion
diff_im = 0.9*darkChannel;
num_iter = 3;
% 2D convolution mask for Anisotropic diffusion
hN = [0.0625 0.1250 0.0625; 0.1250 0.2500 0.1250;
0.0625 0.1250 0.0625];
hN = double(hN);
%% Refine dark channel using Anisotropic diffusion.
for t = 1:num_iter
diff_im = conv2(diff_im,hN,"same");
end
%% Reduction with min
diff_im = min(darkChannel,diff_im);
diff_im = 0.6*diff_im ;
%% Parallel element-wise math to compute
% Restoration with inverse Koschmieder's law
factor = 1.0./(1.0-(diff_im));
restoreOut(:,:,1) = (input(:,:,1)-diff_im).*factor;
restoreOut(:,:,2) = (input(:,:,2)-diff_im).*factor;
restoreOut(:,:,3) = (input(:,:,3)-diff_im).*factor;
restoreOut = uint8(255.*restoreOut);
%%
% Stretching performs the histogram stretching of the image.
% im is the input color image and p is cdf limit.
% out is the contrast stretched image and cdf is the cumulative
% prob. density function and T is the stretching function.
% RGB to grayscale conversion
im_gray = im2gray(restoreOut);
[row,col] = size(im_gray);
% histogram calculation
[count,~] = imhist(im_gray);
prob = count'/(row*col);
% cumulative Sum calculation
cdf = cumsum(prob(:));
% Utilize gpucoder.reduce to find less than particular probability.
% This is equal to "i1 = length(find(cdf <= (p/100)));", but is
% more GPU friendly.
% lessThanP is the preprocess function that returns 1 if the input
% value from cdf is less than the defined threshold and returns 0
% otherwise. gpucoder.reduce then sums up the returned values to get
% the final count.
i1 = gpucoder.reduce(cdf,@plus,"preprocess", @lessThanP);
i2 = 255 - gpucoder.reduce(cdf,@plus,"preprocess", @greaterThanP);
o1 = floor(255*.10);
o2 = floor(255*.90);
t1 = (o1/i1)*[0:i1];
t2 = (((o2-o1)/(i2-i1))*[i1+1:i2])-(((o2-o1)/(i2-i1))*i1)+o1;
t3 = (((255-o2)/(255-i2))*[i2+1:255])-(((255-o2)/(255-i2))*i2)+o2;
T = (floor([t1 t2 t3]));
restoreOut(restoreOut == 0) = 1;
u1 = (restoreOut(:,:,1));
u2 = (restoreOut(:,:,2));
u3 = (restoreOut(:,:,3));
% replacing the value from look up table
out1 = T(u1);
out2 = T(u2);
out3 = T(u3);
out = zeros([size(out1),3], "uint8");
out(:,:,1) = uint8(out1);
out(:,:,2) = uint8(out2);
out(:,:,3) = uint8(out3);
end
function out = lessThanP(input)
p = 5/100;
out = uint32(0);
if input <= p
out = uint32(1);
end
end
function out = greaterThanP(input)
p = 5/100;
out = uint32(0);
if input >= 1 - p
out = uint32(1);
end
end
Generate CUDA Code and CMakeLists.txt File
To review the list of toolchains available to you, run this command. This example shows the output of this command on a specific Linux® machine.
coder.make.getToolchains
ans = 7×1 cell
{'CMake' }
{'GNU GCC for NVIDIA Embedded Processors' }
{'GNU gcc/g++ | CMake/gmake (64-bit Linux)'}
{'GNU gcc/g++ | CMake/Ninja (64-bit Linux)'}
{'GNU gcc/g++ | gmake (64-bit Linux)' }
{'NVCC for NVIDIA Embedded Processors' }
{'NVIDIA CUDA | gmake (64-bit Linux)' }
Create a code generation configuration object for a dynamically linked library. Set the VerificationMode property to 'SIL' and the Toolchain property to one of the supported CMake toolchains. See Configure CMake Build Process.
cfg = coder.gpuConfig('dll'); cfg.VerificationMode = 'SIL'; if ispc % Change the CMake Toolchain based on your Microsoft Visual C++ version cfg.Toolchain = 'Microsoft Visual C++ 2022 v17.0 | CMake/nmake (64-bit Windows)'; else cfg.Toolchain = 'GNU gcc/g++ | CMake/gmake (64-bit Linux)'; end
Run the codegen command to generate CUDA code and the CMakeLists.txt file in the code generation folder.
inputImage = imread('foggyInput.png'); codegen fog_rectification.m -config cfg -args {inputImage}
Code generation successful: View report
Build Target from CMakeLists.txt
Navigate to the code generation folder that contains the CMakeLists.txt file, from which you can generate the native build files.
codegenDir = cd('codegen/dll/fog_rectification/'); type CMakeLists.txt
###########################################################################
# CMakeLists.txt generated for component fog_rectification
# Product type: SHARED library
###########################################################################
cmake_minimum_required(VERSION 3.18)
project(fog_rectification)
enable_language(CUDA CXX C)
# Propagate the CMAKE_EXPORT_COMPILE_COMMANDS variable from the
# environment if it is defined as an environment variable, but not as a
# CMake variable. This is to work around a bug in CMake 3.19 when the
# "NMake Makefiles" generator is selected.
if(DEFINED ENV{CMAKE_EXPORT_COMPILE_COMMANDS} AND NOT DEFINED CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_EXPORT_COMPILE_COMMANDS $ENV{CMAKE_EXPORT_COMPILE_COMMANDS})
endif()
# Define common variables that are used within the whole project.
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)
set(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME FALSE)
###########################################################################
## Path variables
###########################################################################
# Derive an absolute path to the code generation anchor folder.
get_filename_component(START_DIR ../../.. ABSOLUTE)
# Special directories defined by using CACHE variables can be overridden
# by setting the variable from the command line, e.g.,
#
# cmake . -DMATLAB_ROOT=/path/to/another/matlab/root
set(MATLAB_ROOT /mathworks/devel/sbs/85/aghosh.sandbox2/matlab CACHE PATH "")
# Additional variables that are defined conditionally.
if(APPLE)
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL x86_64)
list(APPEND MATLAB_ROOT_SYSLIB_PATHS ${MATLAB_ROOT}/bin/maci64)
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL arm64)
list(APPEND MATLAB_ROOT_SYSLIB_PATHS ${MATLAB_ROOT}/bin/maca64)
endif()
elseif(UNIX AND ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL x86_64) AND ("${CMAKE_SYSTEM_NAME}" STREQUAL Linux))
list(APPEND MATLAB_ROOT_SYSLIB_PATHS ${MATLAB_ROOT}/bin/glnxa64)
elseif(WIN32 AND ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL AMD64))
if(MSVC)
list(APPEND MATLAB_ROOT_SYSLIB_PATHS ${MATLAB_ROOT}/extern/lib/win64/microsoft)
elseif(MINGW)
list(APPEND MATLAB_ROOT_SYSLIB_PATHS ${MATLAB_ROOT}/extern/lib/win64/mingw64)
endif()
list(APPEND MATLAB_ROOT_SYSLIB_PATHS ${MATLAB_ROOT}/bin/win64
${MATLAB_ROOT}/lib/win64)
endif()
# Add common link_directories, including any platform-specific paths under
# MATLAB_ROOT.
set(EXTRA_SYSLIB_PATHS ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}/../lib64
${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}/../lib64/stubs
${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}/../lib
${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}/../lib/stubs)
list(APPEND CMAKE_LIBRARY_PATH ${EXTRA_SYSLIB_PATHS})
link_directories(${EXTRA_SYSLIB_PATHS})
if(DEFINED MATLAB_ROOT_SYSLIB_PATHS)
list(APPEND CMAKE_LIBRARY_PATH ${MATLAB_ROOT_SYSLIB_PATHS})
link_directories(${MATLAB_ROOT_SYSLIB_PATHS})
endif()
if(DEFINED MATLAB_ROOT_SYSINCLUDE_PATHS)
list(APPEND CMAKE_INCLUDE_PATH ${MATLAB_ROOT_SYSINCLUDE_PATHS})
endif()
###########################################################################
## System Libraries
###########################################################################
find_library(FOUND_LIBM m NO_SYSTEM_ENVIRONMENT_PATH PATHS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
find_library(LIB_CUBLAS NAMES cublas libcublas REQUIRED)
###########################################################################
## Target definition and commands
###########################################################################
# Definition of target "fog_rectification".
add_library(fog_rectification SHARED ${START_DIR}/codegen/dll/fog_rectification/MWMemoryManager.cpp
${START_DIR}/codegen/dll/fog_rectification/MWLaunchParametersUtilities.cpp
${START_DIR}/codegen/dll/fog_rectification/MWCUBLASUtils.cpp
${START_DIR}/codegen/dll/fog_rectification/fog_rectification_data.cu
${START_DIR}/codegen/dll/fog_rectification/fog_rectification_initialize.cu
${START_DIR}/codegen/dll/fog_rectification/fog_rectification_terminate.cu
${START_DIR}/codegen/dll/fog_rectification/fog_rectification.cu
${START_DIR}/codegen/dll/fog_rectification/fog_rectification_emxutil.cu
${START_DIR}/codegen/dll/fog_rectification/fog_rectification_rtwutil.cu)
# Set properties for target "fog_rectification".
set_target_properties(fog_rectification PROPERTIES PREFIX ""
POSITION_INDEPENDENT_CODE ON
WINDOWS_EXPORT_ALL_SYMBOLS TRUE
CUDA_SEPARABLE_COMPILATION ON
CUDA_ARCHITECTURES OFF
CUDA_RESOLVE_DEVICE_SYMBOLS ON)
# Specify language features required for target "fog_rectification".
target_compile_features(fog_rectification PUBLIC cxx_std_11)
# Specify compiler preprocessor definitions for target
# "fog_rectification".
target_compile_definitions(fog_rectification PRIVATE -DMW_CUDA_ARCH=500
-DMW_GPU_MEMORY_MANAGER
-DBUILDING_FOG_RECTIFICATION
-DMODEL=fog_rectification)
# Specify compiler flags for target "fog_rectification".
target_compile_options(fog_rectification PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:-Wno-deprecated-gpu-targets "SHELL:-diag-suppress 177" "SHELL:-arch sm_50" -fvisibility=hidden >
$<$<COMPILE_LANGUAGE:C>:-fvisibility=hidden -fwrapv >
$<$<COMPILE_LANGUAGE:CXX>:-fvisibility=hidden -fwrapv >)
# Specify include directories for target "fog_rectification".
target_include_directories(fog_rectification PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_include_directories(fog_rectification PUBLIC $<BUILD_INTERFACE:${START_DIR}/codegen/dll/fog_rectification>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/codegen/dll/fog_rectification>
$<BUILD_INTERFACE:${START_DIR}>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>>
$<BUILD_INTERFACE:${MATLAB_ROOT}/extern/include>)
# Specify linker flags for target "fog_rectification".
target_link_options(fog_rectification PRIVATE $<DEVICE_LINK:-Wno-deprecated-gpu-targets "SHELL:-diag-suppress 177" "SHELL:-arch sm_50" >)
# Specify library link dependencies for target "fog_rectification". CMake
# generator expressions are used to create a CMakeLists.txt file that
# supports multiple platforms with differently named system library
# dependencies.
target_link_libraries(fog_rectification PRIVATE cublas
cusolver
cufft
curand
cusparse
cudadevrt
cudart_static)
target_link_libraries(fog_rectification PUBLIC ${LIB_CUBLAS}
$<$<BOOL:${FOUND_LIBM}>:m>)
###########################################################################
## Target install rules
###########################################################################
# Install shared library for target "fog_rectification"
# 'RUNTIME' - for Windows .dll files
# 'LIBRARY' - for shared libs on non DLL platforms
# 'ARCHIVE' - for DLL import libs on Windows
install(TARGETS fog_rectification
EXPORT fog_rectificationTargets
RUNTIME DESTINATION "codegen/dll/fog_rectification"
LIBRARY DESTINATION "codegen/dll/fog_rectification"
ARCHIVE DESTINATION "codegen/dll/fog_rectification")
# Write a rule that generates a wrapper around exported targets to
# preserve tokenization of "special" directories (e.g., MATLAB_ROOT). This
# avoids hard-coding absolute paths in the CMake file with the code used
# to import the targets, and avoids errors when include paths that do not
# exist in the current filesystem are defined, for example, after
# relocating code using PackNGo.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/fog_rectification.cmake" [=[include("${CMAKE_CURRENT_LIST_DIR}/fog_rectificationTargets.cmake")]=] \n)
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/fog_rectification.cmake" [=[set(FOG_RECTIFICATION_TOK_INC_DIRS_ALL]=] \n
[=[ ${MATLAB_ROOT}/extern/include)]=] \n
[=[foreach(TOKDIR_LOOP IN LISTS FOG_RECTIFICATION_TOK_INC_DIRS_ALL)]=] \n
[=[ if(IS_DIRECTORY ${TOKDIR_LOOP})]=] \n
[=[ list(APPEND FOG_RECTIFICATION_TOK_INC_DIRS ${TOKDIR_LOOP})]=] \n
[=[ endif()]=] \n
[=[endforeach()]=] \n
[=[target_include_directories(fog_rectification::fog_rectification INTERFACE ${FOG_RECTIFICATION_TOK_INC_DIRS})]=] \n)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/fog_rectification.cmake"
DESTINATION "codegen/dll/fog_rectification/export")
# Generate and install a file that allows the targets generated from this
# CMakeLists.txt file to be imported into another project.
install(EXPORT fog_rectificationTargets NAMESPACE fog_rectification:: DESTINATION codegen/dll/fog_rectification/export)
###########################################################################
## Build success message
###########################################################################
add_custom_command(TARGET fog_rectification POST_BUILD
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "\\#\\#\\# Created SHARED library: $<TARGET_FILE:fog_rectification>")
###########################################################################
## Call toolchain hook function if defined
###########################################################################
if(COMMAND toolchain_target_hook)
toolchain_target_hook(fog_rectification)
endif()
To build your target, use one of the following two approaches. If necessary, you can also change the build process (for example, by adding or removing a compiler flag).
Approach 1: Run Generated Build Script
The codegen command generates a script to build the target (fog_rectification.sh on Linux platform or fog_rectification.bat on Windows platform). This script contains a sequence of cmake commands. Run this script to build your target.
Note: Before running the following code, delete the > /dev/null command.
if ispc system('fog_rectification.bat'); else system('sh fog_rectification.sh > /dev/null'); end
If necessary, modify the generated CMakeLists.txt file to customize the build process. To rebuild the target after modifying the CMakeLists.txt file, run this script again.
Approach 2: Execute cmake Command
To use this approach, you must have CMake (version greater than 3.18) and Ninja-build on your machine.
The following code changes the generator from gmake to Ninja and then builds the target on a Linux machine.
if isunix % Remove CMake cache files generated from previous run of cmake command rmdir build s % Switch the generator from gmake to ninja and generate the ninja build file system('cmake -G "Ninja" -S . -B build > /dev/null'); % Build the target system('cmake --build build > /dev/null'); end
To run these commands on the Windows platform, you must first configure the environment for Microsoft Visual C++ (MSVC) setup. One option is to run these commands in a Visual Studio Command Prompt.
Run Generated SIL MEX
Navigate to the original working directory that contains the generated SIL MEX fog_rectification_sil.
cd(codegenDir);
Run the generated SIL MEX with the foggy image as input. The SIL MEX returns a defogged image.
[outputImage] = fog_rectification_sil(inputImage);
### Starting SIL execution for 'fog_rectification'
To terminate execution: clear fog_rectification_sil
Plot the foggy image and the defogged image.
p1 = subplot(1, 2, 1); p2 = subplot(1, 2, 2); imshow(inputImage, 'Parent', p1); imshow(outputImage, 'Parent', p2); title(p1, 'Foggy Input Image'); title(p2, 'Defogged Output Image');
