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.txt
file.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_rectification
function [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');