Main Content

Build Generated CUDA Code Using CMake Toolchain

Since R2024a

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

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');

Figure contains 2 axes objects. Axes object 1 with title Foggy Input Image contains an object of type image. Axes object 2 with title Defogged Output Image contains an object of type image.

Related Topics

External Websites