diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index ef82b6ec..1e9a4d31 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -28,8 +28,16 @@ jobs:
submodules: recursive
- name: Build
run: |
- mkdir build
- cd build
+ if [[ "${{ matrix.os }}" == "ubuntu-20.04" ]]; then
+ sudo apt update
+ sudo apt install -y libtclap-dev libglm-dev libglew-dev libsfml-dev libstb-dev libidn11 libx11-dev libxrandr-dev libxi-dev mesa-common-dev libgl1-mesa-dev libglu1-mesa-dev libudev-dev
+ elif [[ "${{ matrix.os }}" == "macos-latest" ]]; then
+ brew install tclap glm glew sfml mesa-glu
+ git clone https://github.com/Microsoft/vcpkg.git
+ ./vcpkg/bootstrap-vcpkg.sh
+ ./vcpkg/vcpkg install stb
+ TOOLCHAIN_ARG="-D CMAKE_TOOLCHAIN_FILE=./vcpkg/scripts/buildsystems/vcpkg.cmake"
+ fi
if [[ "${{ matrix.compiler }}" == "gcc" ]]; then
CC=gcc
CXX=g++
@@ -37,8 +45,8 @@ jobs:
CC=clang
CXX=clang++
fi
- cmake -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX ../
- make -j2 VERBOSE=1
+ cmake -D CMAKE_C_COMPILER=$CC -D CMAKE_CXX_COMPILER=$CXX $TOOLCHAIN_ARG -S . -B build
+ cmake --build ./build --verbose --parallel 2
buildwin:
needs: format
name: Build Windows
@@ -50,10 +58,11 @@ jobs:
submodules: recursive
- name: Build
run: |
- mkdir build
- cd build
- cmake ../
- cmake --build .
+ git clone https://github.com/Microsoft/vcpkg.git
+ .\vcpkg\bootstrap-vcpkg.bat
+ .\vcpkg\vcpkg.exe --triplet=x64-windows install sfml tclap glm glew stb
+ cmake -D CMAKE_TOOLCHAIN_FILE=.\vcpkg\scripts\buildsystems\vcpkg.cmake -S . -B build
+ cmake --build ./build -- /verbosity:minimal /maxCpuCount /noLogo
python:
name: Exercise Python examples on ${{matrix.os}}
diff --git a/.gitignore b/.gitignore
index 8598c422..adacb9e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ install/
# vim
*~
.*.sw[op]
+.vscode
diff --git a/.gitlab/ubuntu-18.04-rocm.Dockerfile b/.gitlab/ubuntu-18.04-rocm.Dockerfile
new file mode 100644
index 00000000..d17ef498
--- /dev/null
+++ b/.gitlab/ubuntu-18.04-rocm.Dockerfile
@@ -0,0 +1,10 @@
+FROM streamhpc/opencl-sdk-base:ubuntu-18.04-20211119
+RUN set -ex; \
+ export DEBIAN_FRONTEND=noninteractive ; \
+# Register ROCm APT repo
+ wget --quiet --recursive --no-directories --no-parent "https://repo.radeon.com/amdgpu-install/latest/ubuntu/bionic/" --accept "amdgpu-install-*_all.deb" ; \
+ apt install -y -qq ./amdgpu-install-*_all.deb libnuma-dev initramfs-tools ; \
+ apt update -qq; \
+ rm ./amdgpu-install-*_all.deb ; \
+# Install OpenCL package only (and dependency)
+ amdgpu-install -y --usecase=opencl
\ No newline at end of file
diff --git a/.gitlab/ubuntu-18.04.Dockerfile b/.gitlab/ubuntu-18.04.Dockerfile
new file mode 100644
index 00000000..04869a8a
--- /dev/null
+++ b/.gitlab/ubuntu-18.04.Dockerfile
@@ -0,0 +1,45 @@
+FROM ubuntu:18.04 AS apt-installs
+RUN set -ex; \
+ export DEBIAN_FRONTEND=noninteractive ; \
+ apt update -qq; \
+# install wget to download repository keys and CMake tarballs
+# install software-properties-common for the apt-add-repository command
+ apt install -y -qq wget software-properties-common ; \
+# Canonical hosts recent GCC compilers in ubuntu-toolchain-r/test
+ apt-add-repository -y ppa:ubuntu-toolchain-r/test ; \
+# LLVM hosts most toolchain in separate repos. We only register those absent from ubuntu-toolchain-r/test
+ wget -q -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - ; \
+ apt-add-repository -y 'deb [arch=amd64] https://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' ; \
+ apt-add-repository -y 'deb [arch=amd64] https://apt.llvm.org/bionic/ llvm-toolchain-bionic-12 main' ; \
+ apt-add-repository -y 'deb [arch=amd64] https://apt.llvm.org/bionic/ llvm-toolchain-bionic-13 main' ; \
+# install ninja, GCC 7-10, LLVM 8-13 and build-essential to get linkers, etc.
+# install git to download dependencies
+# install ruby to run CMock
+# install libidn11 which CMake 3.1.3 only depends on
+# install ca-certificates to `git clone` via HTTPS
+# install SFML dependencies
+# libx11-dev libxrandr-dev libxi-dev
+# mesa-common-dev for gl.h
+# libgl1-mesa-dev for libGL.so
+# libglu1-mesa-dev for glu.h
+# libudev-dev
+# install Vcpkg dependencies
+# curl zip unzip tar
+ apt install -y -qq build-essential g++-7 g++-8 g++-9 g++-10 clang-8 clang-9 clang-10 clang-11 clang-12 clang-13 ninja-build git ruby libidn11 ca-certificates libx11-dev libxrandr-dev libxi-dev mesa-common-dev libgl1-mesa-dev libglu1-mesa-dev libudev-dev curl zip unzip tar
+
+# install CMake minimum (3.0.2 (Headers, ICD Loader), 3.1.3 (CLHPP), 3.10.3 (SDK)) and latest (3.21.2)
+RUN mkdir -p /opt/Kitware/CMake ; \
+ wget -c https://github.com/Kitware/CMake/releases/download/v3.0.2/cmake-3.0.2-Linux-i386.tar.gz -O - | tar -xz --directory /opt/Kitware/CMake ; \
+ mv /opt/Kitware/CMake/cmake-3.0.2-Linux-i386 /opt/Kitware/CMake/3.0.2 ; \
+ wget -c https://github.com/Kitware/CMake/releases/download/v3.1.3/cmake-3.1.3-Linux-x86_64.tar.gz -O - | tar -xz --directory /opt/Kitware/CMake ; \
+ mv /opt/Kitware/CMake/cmake-3.1.3-Linux-x86_64 /opt/Kitware/CMake/3.1.3 ; \
+ wget -c https://github.com/Kitware/CMake/releases/download/v3.10.3/cmake-3.10.3-Linux-x86_64.tar.gz -O - | tar -xz --directory /opt/Kitware/CMake ; \
+ mv /opt/Kitware/CMake/cmake-3.10.3-Linux-x86_64 /opt/Kitware/CMake/3.10.3 ; \
+ wget -c https://github.com/Kitware/CMake/releases/download/v3.21.2/cmake-3.21.2-linux-x86_64.tar.gz -O - | tar -xz --directory /opt/Kitware/CMake ; \
+ mv /opt/Kitware/CMake/cmake-3.21.2-linux-x86_64 /opt/Kitware/CMake/3.21.2
+
+# install Vcpkg
+RUN git clone --depth 1 https://github.com/Microsoft/vcpkg.git /opt/Microsoft/vcpkg ; \
+ /opt/Microsoft/vcpkg/bootstrap-vcpkg.sh ; \
+# install SFML, TCLAP, GLM
+ /opt/Microsoft/vcpkg/vcpkg install sfml tclap glm glew
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8e765948..2aca2aad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,29 +14,91 @@
cmake_minimum_required(VERSION 3.0)
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 14)
-project(OpenCL-SDK)
+project(OpenCL-SDK
+ VERSION 1.0
+ LANGUAGES
+ C CXX
+)
+
+include(CMakeDependentOption)
+option(OPENCL_SDK_BUILD_SAMPLES "Build sample code" ON)
+cmake_dependent_option(OPENCL_SDK_BUILD_OPENGL_SAMPLES "Build OpenCL-OpenGL interop sample code" ON OPENCL_SDK_BUILD_SAMPLES OFF)
+cmake_dependent_option(OPENCL_SDK_TEST_SAMPLES "Add CTest to samples (where applicable)" ON OPENCL_SDK_BUILD_SAMPLES OFF)
+
+include(CTest)
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE "Release" CACHE PATH "Build Type" FORCE)
endif()
-set(OPENCL_ICD_LOADER_HEADERS_DIR
- "${PROJECT_SOURCE_DIR}/external/OpenCL-Headers" CACHE PATH "Path to OpenCL
- Headers")
-
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/install" CACHE PATH "Install Path" FORCE)
endif()
-set(OPENCL_SDK_INCLUDE_DIRS
- "${PROJECT_SOURCE_DIR}/external/OpenCL-Headers"
- "${PROJECT_SOURCE_DIR}/external/OpenCL-CLHPP/include")
+add_subdirectory(external/OpenCL-Headers)
+add_subdirectory(external/OpenCL-ICD-Loader)
+add_subdirectory(external/OpenCL-CLHPP)
+
+if(OPENCL_SDK_BUILD_SAMPLES)
+ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
+ add_subdirectory(third_party/cargs)
+ find_package(TCLAP REQUIRED)
+ find_package(Stb REQUIRED)
+ if(OPENCL_SDK_BUILD_OPENGL_SAMPLES)
+ cmake_minimum_required(VERSION 3.10) # SFML 2 won't find Freetype::Freetype under 3.10
+ find_package(OpenGL REQUIRED)
+ if(CMAKE_SYSTEM_NAME MATCHES Linux) # TODO: Add EGL support
+ # OpenGL doesn't explicitly depend on X11 (as of CMake v3.2) but we'll need it
+ find_package(X11 REQUIRED)
+ endif()
+ find_package(GLEW REQUIRED)
+ if(NOT TARGET OpenGL::GLU)
+ # GLU is a dependency of GLEW but it's not advertized as an OpenGL COMPONENT
+ message(FATAL_ERROR "GLEW depends on GLU but was not found.")
+ endif()
+ find_package(SFML 2
+ REQUIRED
+ COMPONENTS window graphics
+ )
+ find_package(GLEW REQUIRED)
+ find_package(glm CONFIG REQUIRED)
+ endif(OPENCL_SDK_BUILD_OPENGL_SAMPLES)
+endif(OPENCL_SDK_BUILD_SAMPLES)
+
+add_subdirectory(lib)
+if(OPENCL_SDK_BUILD_SAMPLES)
+ add_subdirectory(samples)
+endif()
-add_subdirectory(${PROJECT_SOURCE_DIR}/external/OpenCL-ICD-Loader)
-add_subdirectory(${PROJECT_SOURCE_DIR}/samples)
+include(GNUInstallDirs)
+file(
+ WRITE ${PROJECT_BINARY_DIR}/OpenCL/OpenCLConfig.cmake
+ [[
+get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH)
+include("${PARENT_DIR}/OpenCLHeaders/OpenCLHeadersConfig.cmake")
+include("${PARENT_DIR}/OpenCLICDLoader/OpenCLICDLoaderConfig.cmake")
+include("${PARENT_DIR}/OpenCLHeadersCpp/OpenCLHeadersCppConfig.cmake")
+include("${PARENT_DIR}/OpenCLUtils/OpenCLUtilsConfig.cmake")
+include("${PARENT_DIR}/OpenCLUtilsCpp/OpenCLUtilsCppConfig.cmake")
+ ]]
+)
+set(config_package_location ${CMAKE_INSTALL_DATADIR}/cmake/OpenCL)
+install(
+ FILES ${PROJECT_BINARY_DIR}/OpenCL/OpenCLConfig.cmake
+ DESTINATION ${config_package_location}
+)
-set_target_properties(OpenCL PROPERTIES LIBRARY_OUTPUT_DIRECTORY
- ${CMAKE_CURRENT_BINARY_DIR})
+unset(CMAKE_SIZEOF_VOID_P)
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/OpenCL/OpenCLConfigVersion.cmake
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY AnyNewerVersion
+)
+install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/OpenCL/OpenCLConfigVersion.cmake
+ DESTINATION ${config_package_location}
+)
diff --git a/README.md b/README.md
index 4aead069..663bcd8c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# OpenCL-SDK (in development)
+# OpenCLTM SDK (in development)
This is the Khronos OpenCL SDK. It brings together all the components needed to
develop OpenCL applications:
@@ -13,40 +13,57 @@ It also contains resources useful to OpenCL developers:
- Code samples (`samples/`)
- Documentation (`docs/`)
-## Setting Up the SDK
+## Build Instructions
-This repository uses sub-modules for the OpenCL Headers, OpenCL C++ bindings, and OpenCL ICD Loader.
+### Dependencies
-To clone a new repository with all sub-modules included, use the `--recursive` option.
-Note that this option clones all sub-modules and their dependencies, which are not required for the OpenCL SDK:
+- This repository uses sub-modules for the OpenCL Headers, OpenCL C++ bindings, and OpenCL ICD Loader and some of their transitive dependencies.
-```sh
-$ git clone --recursive https://github.com/KhronosGroup/OpenCL-SDK.git
-```
+ - To clone a new repository with all sub-modules included, use the `--recursive` option. Note that this option clones all sub-modules and their dependencies, which are not strictly required for the OpenCL SDK:
-Alternatively, to clone only the sub-modules for the OpenCL SDK, first clone this repository without sub-modules included:
+ git clone --recursive https://github.com/KhronosGroup/OpenCL-SDK.git
-```sh
-$ git clone https://github.com/KhronosGroup/OpenCL-SDK.git
-```
+ - Alternatively, to clone only the sub-modules for the OpenCL SDK, first clone this repository without sub-modules included then setup submodules non-recursively:
-Then setup sub-modules:
+ git clone https://github.com/KhronosGroup/OpenCL-SDK.git
+ git submodule init
+ git submodule update
-```sh
-$ git submodule init
-$ git submodule update
-```
+- The SDK uses CMake for its build system.
+If CMake is not provided by your build system or OS package manager, please consult the [CMake website](https://cmake.org).
-## Building the Samples
+- The SDK samples depend on
-This repository uses CMake as its build system.
-The suggested build directory is `build`.
+ - [Templatized C++ Command Line Parser Library](http://tclap.sourceforge.net/) (aka. TCLAP)
+ - [Simple and Fast Multimedia Library](https://www.sfml-dev.org/) (aka. SFML)
+ - [OpenGL Mathematics](https://glm.g-truc.net/0.9.9/index.html) (aka. GLM)
-To generate build files, use for example:
+### Example Build
-```sh
-$ mkdir build && cd build
-$ cmake ..
-```
+> The example build guide uses [Vcpkg](https://vcpkg.io/en/index.html) to fetch all dependencies. Note that Vcpkg is _not_ a requirement and is only used for convenience. One may provide dependencies through any other CMake mechanism. For details on how to install Vcpkg, refer to it's [Getting Started Guide](https://vcpkg.io/en/getting-started.html). The example build assumes targeting 64-bit Windows.
-Then build with the generated build files.
+1. Clone this repo with the rest of the OpenCL SDK components:
+
+ git clone https://github.com/KhronosGroup/OpenCL-SDK.git
+ git submodule init
+ git submodule update
+
+1. Install dependencies
+
+ vcpkg --triplet x64-windows install sfml tclap glm
+
+1. Build and install SDK with samples and no downstream unit tests
+
+ cmake -A x64 `
+ -D BUILD_TESTING=OFF `
+ -D BUILD_DOCS=OFF `
+ -D BUILD_EXAMPLES=OFF `
+ -D BUILD_TESTS=OFF `
+ -D OPENCL_SDK_BUILD_SAMPLES=ON `
+ -D OPENCL_SDK_TEST_SAMPLES=OFF `
+ -D CMAKE_TOOLCHAIN_FILE=/vcpkg/install/root/scripts/buildsystems/vcpkg.cmake `
+ -D VCPKG_TARGET_TRIPLET=x64-windows
+ -B ./OpenCL-SDK/build -S ./OpenCL-SDK
+ cmake --build ./OpenCL-SDK/build --target install
+
+_(Note: on Linux paths to dependent libraries are automatically handled by RPATH in both the build and install tree. On Windows all DLLs have to be on the `PATH`. Vcpkg copies dependent DLLs to the build tree, but in order to do the same in the install tree, sufficiently new CMake version is required. CMake 3.21 instroduces `install(IMPORTED_RUNTIME_ARTIFACTS)`.)_
\ No newline at end of file
diff --git a/cmake/Modules/FindStb.cmake b/cmake/Modules/FindStb.cmake
new file mode 100644
index 00000000..0a4886e5
--- /dev/null
+++ b/cmake/Modules/FindStb.cmake
@@ -0,0 +1,30 @@
+# - Find Stb
+# Find the Stb headers
+#
+# Stb_INCLUDE_DIR - where to find the TCLAP headers
+# Stb_FOUND - True if TCLAP is found
+
+if (Stb_INCLUDE_DIR)
+ # already in cache, be silent
+ set (Stb_FIND_QUIETLY TRUE)
+endif (Stb_INCLUDE_DIR)
+
+# find the headers
+find_path (Stb_INCLUDE_PATH stb_image.h
+ PATHS
+ ${Stb_DIR}
+ PATH_SUFFIXES
+ include
+ include/stb
+ )
+
+# handle the QUIETLY and REQUIRED arguments and set Stb_FOUND to
+# TRUE if all listed variables are TRUE
+include (FindPackageHandleStandardArgs)
+find_package_handle_standard_args (Stb "Stb (https://github.com/nothings/stb) could not be found. Set Stb_INCLUDE_PATH to point to the headers adding '-D Stb_INCLUDE_PATH=/path/to/stb' to the cmake command." Stb_INCLUDE_PATH)
+
+if (Stb_FOUND)
+ set (Stb_INCLUDE_DIR ${Stb_INCLUDE_PATH})
+endif (Stb_FOUND)
+
+mark_as_advanced(Stb_INCLUDE_PATH)
diff --git a/cmake/Modules/FindTCLAP.cmake b/cmake/Modules/FindTCLAP.cmake
new file mode 100644
index 00000000..781450a2
--- /dev/null
+++ b/cmake/Modules/FindTCLAP.cmake
@@ -0,0 +1,28 @@
+# - Find TCLAP
+# Find the TCLAP headers
+#
+# TCLAP_INCLUDE_DIR - where to find the TCLAP headers
+# TCLAP_FOUND - True if TCLAP is found
+
+if (TCLAP_INCLUDE_DIR)
+ # already in cache, be silent
+ set (TCLAP_FIND_QUIETLY TRUE)
+endif (TCLAP_INCLUDE_DIR)
+
+# find the headers
+find_path (TCLAP_INCLUDE_PATH tclap/CmdLine.h
+ PATHS
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+# handle the QUIETLY and REQUIRED arguments and set TCLAP_FOUND to
+# TRUE if all listed variables are TRUE
+include (FindPackageHandleStandardArgs)
+find_package_handle_standard_args (TCLAP "TCLAP (http://tclap.sourceforge.net/) could not be found. Set TCLAP_INCLUDE_PATH to point to the headers adding '-DTCLAP_INCLUDE_PATH=/path/to/tclap' to the cmake command." TCLAP_INCLUDE_PATH)
+
+if (TCLAP_FOUND)
+ set (TCLAP_INCLUDE_DIR ${TCLAP_INCLUDE_PATH})
+endif (TCLAP_FOUND)
+
+mark_as_advanced(TCLAP_INCLUDE_PATH)
diff --git a/external/OpenCL-CLHPP b/external/OpenCL-CLHPP
index 1df82b97..62d36e41 160000
--- a/external/OpenCL-CLHPP
+++ b/external/OpenCL-CLHPP
@@ -1 +1 @@
-Subproject commit 1df82b9749739f2681081092ae163bb0f0d40f66
+Subproject commit 62d36e41275d12ec1220f68abb8fe66d12306c45
diff --git a/external/OpenCL-Headers b/external/OpenCL-Headers
index 59ac4dc2..80c10b1f 160000
--- a/external/OpenCL-Headers
+++ b/external/OpenCL-Headers
@@ -1 +1 @@
-Subproject commit 59ac4dc2f282286d8db83143686cfe37ec658b84
+Subproject commit 80c10b1f65b932894b830da7cd37bc56c541bae4
diff --git a/external/OpenCL-ICD-Loader b/external/OpenCL-ICD-Loader
index 169f05d0..b7a648b2 160000
--- a/external/OpenCL-ICD-Loader
+++ b/external/OpenCL-ICD-Loader
@@ -1 +1 @@
-Subproject commit 169f05d026e65948b30cfe2200595fda92198cf7
+Subproject commit b7a648b2702e5484725163dcb99a3960370807b5
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 00000000..8a52459f
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,214 @@
+include(GenerateExportHeader)
+include(GNUInstallDirs)
+
+foreach(UTIL_LIB_NAME IN ITEMS Utils UtilsCpp)
+ if(UTIL_LIB_NAME STREQUAL Utils)
+ set(UTIL_LIB_SOURCES src/Utils/Utils.c)
+ set(UTIL_LIB_DEPS
+ OpenCL::Headers
+ $<$:m>
+ )
+ set(UTIL_CL_VERSION_MACRO_NAME CL_TARGET_OPENCL_VERSION)
+ elseif(UTIL_LIB_NAME STREQUAL UtilsCpp)
+ set(UTIL_LIB_SOURCES src/Utils/Utils.cpp)
+ set(UTIL_LIB_DEPS
+ OpenCL::HeadersCpp
+ OpenCL::Utils
+ )
+ set(UTIL_CL_VERSION_MACRO_NAME CL_HPP_TARGET_OPENCL_VERSION)
+ else()
+ message(FATAL_ERROR "Unkown Util flavor")
+ endif()
+ set(UTIL_LIB_TARGET OpenCL${UTIL_LIB_NAME})
+ add_library(${UTIL_LIB_TARGET} ${UTIL_LIB_SOURCES})
+ add_library(OpenCL::${UTIL_LIB_NAME} ALIAS ${UTIL_LIB_TARGET})
+
+ string(TOUPPER ${UTIL_LIB_NAME} UPPER_UTIL_LIB_NAME)
+ generate_export_header(${UTIL_LIB_TARGET}
+ EXPORT_MACRO_NAME ${UPPER_UTIL_LIB_NAME}_EXPORT
+ EXPORT_FILE_NAME OpenCL${UTIL_LIB_NAME}_Export.h
+ )
+
+ target_include_directories(${UTIL_LIB_TARGET}
+ PUBLIC
+ $
+ $
+ $
+ )
+ target_link_libraries(${UTIL_LIB_TARGET}
+ PUBLIC
+ ${UTIL_LIB_DEPS}
+ OpenCL::OpenCL
+ )
+ target_compile_definitions(${UTIL_LIB_TARGET}
+ PRIVATE
+ ${UTIL_CL_VERSION_MACRO_NAME}=300
+ PUBLIC
+ CL_HPP_ENABLE_EXCEPTIONS
+ )
+
+ set_target_properties(${UTIL_LIB_TARGET}
+ PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}
+ LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}
+ RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}
+ INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
+ FOLDER "Libraries/${UTIL_LIB_NAME}"
+ )
+
+ install(
+ TARGETS ${UTIL_LIB_TARGET}
+ EXPORT OpenCL${UTIL_LIB_NAME}Targets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ )
+ install(
+ DIRECTORY include/CL/Utils
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CL
+ )
+ export(
+ EXPORT OpenCL${UTIL_LIB_NAME}Targets
+ FILE ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/cmake/OpenCL${UTIL_LIB_NAME}/OpenCL${UTIL_LIB_NAME}Targets.cmake
+ NAMESPACE OpenCL::
+ )
+ file(
+ WRITE ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/cmake/OpenCL${UTIL_LIB_NAME}/OpenCL${UTIL_LIB_NAME}Config.cmake
+ "include(\"\${CMAKE_CURRENT_LIST_DIR}/OpenCL${UTIL_LIB_NAME}Targets.cmake\")"
+ )
+
+ set(config_package_location ${CMAKE_INSTALL_DATADIR}/cmake/OpenCL${UTIL_LIB_NAME})
+ install(
+ EXPORT OpenCL${UTIL_LIB_NAME}Targets
+ FILE OpenCL${UTIL_LIB_NAME}Targets.cmake
+ NAMESPACE OpenCL::
+ DESTINATION ${config_package_location}
+ )
+ install(
+ FILES ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/cmake/OpenCL${UTIL_LIB_NAME}/OpenCL${UTIL_LIB_NAME}Config.cmake
+ DESTINATION ${config_package_location}
+ )
+
+ unset(CMAKE_SIZEOF_VOID_P)
+ include(CMakePackageConfigHelpers)
+ write_basic_package_version_file(
+ ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/cmake/OpenCL${UTIL_LIB_NAME}/OpenCL${UTIL_LIB_NAME}ConfigVersion.cmake
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY AnyNewerVersion
+ )
+ install(
+ FILES ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/cmake/OpenCL${UTIL_LIB_NAME}/OpenCL${UTIL_LIB_NAME}ConfigVersion.cmake
+ DESTINATION ${config_package_location}
+ )
+endforeach()
+
+if(OPENCL_SDK_BUILD_SAMPLES)
+ foreach(SDK_LIB_NAME IN ITEMS SDK SDKCpp)
+ if(SDK_LIB_NAME STREQUAL SDK)
+ set(SDK_LIB_SOURCES src/SDK/SDK.c)
+ set(SDK_LIB_DEPS
+ OpenCL::Headers
+ OpenCL::Utils
+ cargs
+ )
+ set(SDK_LIB_INCLUDES
+ ${Stb_INCLUDE_DIR}
+ )
+ set(SDK_CL_VERSION_MACRO_NAME CL_TARGET_OPENCL_VERSION)
+ elseif(SDK_LIB_NAME STREQUAL SDKCpp)
+ set(SDK_LIB_SOURCES
+ src/SDK/CLI.cpp
+ src/SDK/Image.cpp
+ $<$:src/SDK/InteropContext.cpp>
+ $<$:src/SDK/InteropWindow.cpp>
+ )
+ set(SDK_LIB_DEPS
+ OpenCL::HeadersCpp
+ OpenCL::UtilsCpp
+ $<$:OpenGL::GL>
+ $<$:GLEW::GLEW>
+ $<$,$>:OpenGL::GLU>
+ $<$:sfml-system>
+ $<$:sfml-window>
+ $<$:sfml-graphics>
+ )
+ set(SDK_LIB_INCLUDES
+ ${TCLAP_INCLUDE_DIR}
+ ${Stb_INCLUDE_DIR}
+ )
+ set(SDK_CL_VERSION_MACRO_NAME CL_HPP_TARGET_OPENCL_VERSION)
+ else()
+ message(FATAL_ERROR "Unkown SDK flavor")
+ endif()
+
+ set(SDK_LIB_TARGET OpenCL${SDK_LIB_NAME})
+ add_library(${SDK_LIB_TARGET} ${SDK_LIB_SOURCES})
+ add_library(OpenCL::${SDK_LIB_NAME} ALIAS ${SDK_LIB_TARGET})
+
+ string(TOUPPER ${SDK_LIB_NAME} UPPER_SDK_LIB_NAME)
+ generate_export_header(${SDK_LIB_TARGET}
+ EXPORT_MACRO_NAME ${UPPER_SDK_LIB_NAME}_EXPORT
+ EXPORT_FILE_NAME OpenCL${SDK_LIB_NAME}_Export.h
+ )
+ configure_file(
+ include/CL/SDK/OpenCLSDK_Config.in.h
+ ${CMAKE_CURRENT_BINARY_DIR}/OpenCLSDK_Config.h
+ )
+
+ target_include_directories(${SDK_LIB_TARGET}
+ PUBLIC
+ ${SDK_LIB_INCLUDES}
+ $
+ $
+ $
+ )
+ target_link_libraries(${SDK_LIB_TARGET}
+ PUBLIC
+ ${SDK_LIB_DEPS}
+ OpenCL::OpenCL
+ )
+ target_compile_definitions(${SDK_LIB_TARGET}
+ PRIVATE
+ ${SDK_CL_VERSION_MACRO_NAME}=300
+ PUBLIC
+ CL_HPP_ENABLE_EXCEPTIONS
+ )
+
+ set_target_properties(${SDK_LIB_TARGET}
+ PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}
+ LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}
+ RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}
+ INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
+ FOLDER "Libraries/${SDK_LIB_TARGET}"
+ )
+ install(
+ TARGETS ${SDK_LIB_TARGET}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ )
+ # Copying DLLs the samples depend on only makes sense on Wnidows. On *nix
+ # OSes we rely on RPATH. We can only do this with sufficiently new CMake.
+ if(
+ OPENCL_SDK_BUILD_OPENGL_SAMPLES AND
+ CMAKE_VERSION VERSION_GREATER_EQUAL 3.21 AND
+ CMAKE_SYSTEM_NAME MATCHES Windows
+ )
+ foreach(DEP IN ITEMS
+ GLEW::GLEW
+ sfml-window
+ sfml-graphics
+ sfml-system
+ )
+ get_target_property(DEP_TYPE ${DEP} TYPE)
+ if(${DEP_TYPE} STREQUAL SHARED_LIBRARY)
+ install(
+ IMPORTED_RUNTIME_ARTIFACTS
+ ${DEP}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ )
+ endif()
+ endforeach()
+ endif()
+ endforeach()
+endif(OPENCL_SDK_BUILD_SAMPLES)
diff --git a/lib/README.md b/lib/README.md
new file mode 100644
index 00000000..8fb015da
--- /dev/null
+++ b/lib/README.md
@@ -0,0 +1,15 @@
+# OpenCL Utility and OpenCL SDK Library Documentation
+
+There are two libraries in the OpenCL SDK which all samples utilize to different extents. One such library is the OpenCL Utility Library which is an exported library and is meant to ease the use of OpenCL, while the OpenCL SDK Library builds on top of it but is not exported when installing the SDK. The OpenCL SDK lib extends the Utility library in ways which likely don't make sense outside the context of the SDK samples.
+
+## OpenCL Utility Library
+
+One may think of this library as analogous to GLU and GLUT in the domain of OpenGL. A set of utilities which condense common tasks into singular functions or add missing functionality of the API which otherwise couldn't be added as a non-API-breaking change.
+
+For a complete list utilities provided by this library, refer to the [OpenCL Utility Library docs](./Utils.md).
+
+## OpenCL SDK Library
+
+The SDK library extends the Utility library by deduplicating common tasks like command-line argument parsing, selecting devices, logging, and other contentious tasks which your application likely does differently, hence the value in shipping it for external use, moreover promise forward and/or backward compatibility is low.
+
+For a complete list functionality provided by this library, refer to the [OpenCL SDK Library docs](./SDK.md).
\ No newline at end of file
diff --git a/lib/SDK.md b/lib/SDK.md
new file mode 100644
index 00000000..e9a55596
--- /dev/null
+++ b/lib/SDK.md
@@ -0,0 +1,278 @@
+# OpenCL SDK Library
+
+The OpenCL SDK Library hosts both C and C++ utilities which are generally useful for writing OpenCL applications but are either dependency-heavy or contentious. Because these utilities aren't the subject of universal interest, these utilities are _not_ exported, meaning SDK installations won't install their headers nor their libraries. Doing so the [OpenCL Utility Library](./Utils.md) can be kept dependency-free.
+
+The utilities are broken into to libraries, `OpenCLSDK` and `OpenCLSDKCpp`. Samples include ``/`` and link to their libraries respectively.
+
+## List of utilities
+
+- [Command-line Interface](#command-line-interface-utilities)
+- [Pseudo Random Number Generation utilities](#pseudo-random-number-generation-utilities)
+- [Image utilities](#image-utilities)
+- [OpenCL-OpenGL interop utilities](#openCL-openGL-interop-utilities)
+
+### Command-line interface utilities
+
+#### C
+```c
+struct cl_sdk_options_DeviceTriplet
+{
+ int plat_index;
+ int dev_index;
+ cl_device_type dev_type;
+};
+
+struct cl_sdk_options_Diagnostic
+{
+ bool verbose;
+ bool quiet;
+};
+
+struct cl_sdk_options_SingleDevice
+{
+ struct cl_sdk_options_DeviceTriplet triplet;
+};
+
+struct cl_sdk_options_MultiDevice
+{
+ struct cl_sdk_options_DeviceTriplet * triplets;
+ size_t number;
+};
+```
+#### C++
+```c++
+struct cl::sdk::DeviceTriplet
+{
+ int plat_index;
+ int dev_index;
+ cl_device_type dev_type;
+};
+struct cl::sdk::Diagnostic
+{
+ bool verbose,
+ quiet;
+};
+struct cl::sdk::SingleDevice
+{
+ DeviceTriplet triplet;
+};
+struct cl::sdk::MultiDevice
+{
+ cl::vector triplets;
+};
+struct cl::sdk::Window
+{
+ int width;
+ int height;
+ bool fullscreen;
+};
+```
+
+The SDK Library deduplicates the storage the result of common CLI argument parsing. These types are used throughout the SDK samples.
+
+#### C++
+```c++
+template
+auto cl::sdk::parse();
+
+template
+Option cl::sdk::comprehend(Parsers... parsers);
+
+template
+std::tuple cl::sdk::parse_cli(int argc, char* argv[], std::string banner = "OpenCL SDK sample template")
+```
+
+These functions reduce the boilerplate needed for each sample to introduce their own set of command-line arguments and helps deduplicate the set of common options which most samples inherit.
+
+The first two are the customizable parts where each sample (and the the pre-defined options) specify how CLI arguments should be processed. `cl::sdk::parse()` specifies what the option names are and should return an instance of tuple-like type (`std::get(const type&)` be valid) containing smart-pointer-like types (`ptr.get()` be valid) holding TCLAP arguments and switches. `cl::sdk::comprehend()` takes this tuple already expanded as arguments with names and "makes sense" of the strings of args already validated during parsing and returns a singular struct of the options at hand.
+
+> The types returned by `cl::sdk::parse()` must exactly match the `Parsers...` of `cl::sdk::comprehend()`.
+
+`cl::sdk::parse_cli()` is a simple metaprogram that invokes the `parse` and `comprehend` functions of all the `Options` types it is given in the correct order. First it registers all the command-line arguments and switches, then invokes the parse method of the underlying CLI parser and then invokes the comprehend method of the
+
+Simplest example is the C++ SAXPY sample adding a single option to control the length of the vector operands. The sample uses a single OpenCL device and respects diagnostic level options as well.
+
+#### C++
+```c++
+// Sample-specific option
+struct SaxpyOptions { size_t length; };
+
+// Add option to CLI parsing SDK utility
+template <> auto cl::sdk::parse(){
+ return std::make_tuple(
+ std::make_shared>("l", "length", "Length of input", false, 1'048'576, "positive integral")
+ );
+}
+template <> SaxpyOptions cl::sdk::comprehend(
+ std::shared_ptr> length_arg){
+ return SaxpyOptions{
+ length_arg->getValue()
+ };
+}
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ // Parse command-line options
+ auto opts = cl::sdk::parse_cli<
+ cl::sdk::options::Diagnostic,
+ cl::sdk::options::SingleDevice,
+ SaxpyOptions>(argc, argv);
+ const auto& diag_opts = std::get<0>(opts);
+ const auto& dev_opts = std::get<1>(opts);
+ const auto& saxpy_opts = std::get<2>(opts);
+ ...
+ }
+ // catch clauses
+}
+```
+
+_(Note: C++17 structured-bindings allows cleaner binding of names to the members of the tuple returned by `cl::sdk::parse_cli`.)_
+
+### Pseudo Random Number Generation utilities
+
+#### C
+```c
+void cl_sdk_fill_with_random_floats(pcg32_random_t * rng, cl_float * arr, const size_t len);
+```
+Fills an array with random floats in the range [0, 1).
+- `rng` must point to a valid PRNG instance.
+- `arr` must be allocated storage long enough to store the results.
+- `len` is the count of elements that will be written to `arr`.
+
+```c
+void cl_sdk_fill_with_random_floats_range(pcg32_random_t * rng,
+ cl_float * arr, const size_t len, const cl_float low, const cl_float hi);
+```
+Fills an array with random floats in the range [`low`, `hi`).
+- `rng` must point to a valid PRNG instance.
+- `arr` must be allocated storage long enough to store the results.
+- `len` is the count of elements that will be written to `arr`.
+- `low` is the low-end of the range.
+- `hi` is the high-end of the range.
+
+```c
+void cl_sdk_fill_with_random_ints_range(pcg32_random_t * rng,
+ cl_int * arr, const size_t len, const cl_int low, const cl_int hi);
+```
+Fills an array with uniformly distributed numbers in the range [`low`, `hi`]. Uses rejection sampling from uniform bit distribution.
+- `rng` must point to a valid PRNG instance.
+- `arr` must be allocated storage long enough to store the results.
+- `len` is the count of elements that will be written to `arr`.
+- `low` is the low-end of the range.
+- `hi` is the high-end of the range.
+
+#### C++
+```c++
+template
+void fill_with_random(PRNG&& prng, Containers&&... containers);
+```
+Fills containers with random numbers using a user-provided PRNG.
+- `prng` must be a PRNG, a callable type with `T(void)` signature where `T` is implicitly convertible to the `value_type` of `containers`.
+- `containers` must be a container providing a [LegacyOutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator).
+
+### Image utilities
+#### C
+```c
+typedef struct cl_sdk_image
+{
+ int width, height, pixel_size;
+ unsigned char* pixels;
+}
+cl_sdk_image;
+```
+#### C++
+```c++
+struct cl::sdk::Image
+{
+ int width, height, pixel_size;
+ cl::vector pixels;
+};
+```
+Used to store pixel information of images read/written to/from storage.
+- `width` and `height` store the number of pixels the image has in x-y directions respectively.
+- `pixel_size` stores the number of bytes used to store a single pixel.
+- `pixels` stores the actual pixel data.
+
+#### C
+```c
+cl_sdk_image cl_sdk_read_image(const char* file_name, cl_int* err);
+```
+#### C++
+```c++
+Image cl::sdk::read_image(const char* file_name, cl_int* err);
+```
+Reads a BMP/JPEG/PNG image from disk.
+- `file_name` specifies the absolute or relative path (to the current working-directory) to the image.
+- `err` is an optional pointer used to capture error conditions.
+
+Returns an `cl_sdk_image`/`cl::sdk::Image` instance. If an error occurs, the returned image is in an invalid state.
+
+#### C
+```c
+void cl_sdk_write_image(const char * file_name, const cl_sdk_image * im, cl_int * err);
+```
+#### C++
+```c++
+void cl::sdk::write_image(const char* file_name, const Image& image, cl_int* err);
+```
+Writes a BMP/JPEG/PNG image from disk.
+- `file_name` specifies the absolute or relative path (to the current working-directory) to the image.
+- `image` is the source of pixel information.
+- `err` is an optional pointer used to capture error conditions.
+### OpenCL-OpenGL interop utilities
+
+#### C++
+```c++
+cl::vector cl::sdk::get_interop_context_properties(const cl::Device& plat, cl_int* error = nullptr);
+```
+
+This function returns a null-terminated list of context properties required to setup an OpenCL-OpenGL interop context with the currently active OpenGL context.
+
+If `error` is non-null or if `CL_HPP_ENABLE_EXCEPTIONS` is used, ordinary OpenCL error codes may be returned and the following library-specific error codes:
+
+- `CL_UTIL_OS_GL_QUERY_ERROR` if platform-specific errors occur when trying to query for the currently active OpenGL context.
+
+#### C++
+```c++
+cl::Context cl::sdk::get_interop_context(int plat_id, int dev_id, cl_device_type type, cl_int* error = nullptr);
+```
+
+This function creates an interop context on the platform with id `plat_id` with a single device of type `type` and id `dev_id` which is able to share resources with the currently active OpenGL context.
+
+If `error` is non-null or if `CL_HPP_ENABLE_EXCEPTIONS` is used, ordinary OpenCL error codes may be returned and the following library-specific error codes:
+
+- `CL_UTIL_INDEX_OUT_OF_RANGE` if the requested platform or device id is outside the range of available platforms or devices of the selected `type` on the platform.
+- `CL_UTIL_OS_GL_QUERY_ERROR` if platform-specific errors occur when trying to query for the currently active OpenGL context.
+
+#### C++
+```c++
+class cl::sdk::InteropWindow : public sf::Window
+{
+public:
+ explicit InteropWindow(
+ sf::VideoMode mode,
+ const sf::String& title,
+ sf::Uint32 style = sf::Style::Default,
+ const sf::ContextSettings& settings = sf::ContextSettings{},
+ int platform_id = 0,
+ int device_id = 0,
+ cl_bitfield device_type = CL_DEVICE_TYPE_DEFAULT
+ );
+
+ void run();
+
+protected:
+ // Core functionality to be overriden
+ virtual void initializeGL() = 0; // Function that initializes all OpenGL assets needed to draw a scene
+ virtual void initializeCL() = 0; // Function that initializes all OpenCL assets needed to draw a scene
+ virtual void updateScene() = 0; // Function that holds scene update guaranteed not to conflict with drawing
+ virtual void render() = 0; // Function that does the native rendering
+ virtual void event(const sf::Event& e) = 0; // Function that handles render area resize
+
+ cl::Context opencl_context;
+ bool cl_khr_gl_event_supported;
+};
+```
+This class encapsulates an interactive window with the content being one OpenGL canvas. It provides a set of functions for the user to override in derived classes.
\ No newline at end of file
diff --git a/lib/Utils.md b/lib/Utils.md
new file mode 100644
index 00000000..61ec0138
--- /dev/null
+++ b/lib/Utils.md
@@ -0,0 +1,121 @@
+# OpenCL Utility Library
+
+The OpenCL Utility Library provides both C and C++ bindings with near feature parity. The utilities are broken into to libraries, `OpenCLUtils` and `OpenCLUtilsCpp`. To include them in your project, include ``/`` and link to their libraries respectively.
+
+## List of utilities
+
+- [Platform](#platform-utilities)
+- [Device](#device-utilities)
+- [Context](#context-utilities)
+- [Event](#event-utilities)
+- [Error](#error-handling-utilities)
+
+### Platform utilities
+
+```c++
+bool cl::util::supports_extension(const cl::Platform& platform, const cl::string& extension);
+```
+Tells whether a platform supports a given extension.
+- `platform` is the platform to query.
+- `extension` is the extension string being searched for.
+
+```c++
+bool cl::util::platform_version_contains(const cl::Platform& platform, const cl::string& version_fragment);
+```
+Tells whether the platform version string contains a specific fragment.
+- `platform` is the platform to query.
+- `version_fragment` is the version string fragment to search for.
+
+### Device utilities
+
+```c++
+bool cl::util::opencl_c_version_contains(const cl::Device& device, const cl::string& version_fragment);
+```
+Tells whether the device OpenCL C version string contains a specific fragment.
+- `device` is the device to query.
+- `version_fragment` is the version string fragment to search for.
+
+```c++
+bool cl::util::supports_extension(const cl::Device& device, const cl::string& extension);
+```
+Tells whether a device supports a given extension.
+- `device` is the device to query.
+- `extension` is the extension string being searched for.
+
+```c++
+bool cl::util::supports_feature(const cl::Device& device, const cl::string& feature_name);
+```
+Tells whether a device supports any version of a feature.
+- `device` is the device to query.
+- `feature_name` is the feature name string being searched for.
+
+_(Note: this function is only available when both the Utility library and the using code defines minimally `CL_VERSION_3_0`.)_
+### Context utilities
+
+```c
+cl_context cl_util_get_context(int plat_id, int dev_id, cl_device_type type, cl_int* error);
+```
+```c++
+cl::Context cl::util::get_context(int plat_id, int dev_id, cl_device_type type, cl_int* error = nullptr);
+```
+
+These functions create a context on the platform with id `plat_id` with a single device of type `type` and id `dev_id`.
+
+If `error` is non-null or if `CL_HPP_ENABLE_EXCEPTIONS` is used, ordinary OpenCL error codes may be returned and the following library-specific error codes:
+
+- `CL_UTIL_INDEX_OUT_OF_RANGE` if the requested platform or device id is outside the range of available platforms or devices of the selected `type` on the platform.
+
+### Event utilities
+
+```c++
+template
+auto cl::util::get_duration(cl::Event& ev);
+```
+
+This function template can be used to query an event for the duration of time measured in user-provided units between two state transitions. By default the return type is `std::chrono::nanoseconds` as that is the unit of measure of the OpenCL API.
+
+### Error handling utilities
+
+```c++
+class Error : public std::exception
+{
+private:
+ int err_;
+ const char * errStr_;
+public:
+ /*! \brief Create a new SDK error exception for a given error code
+ * and corresponding message.
+ *
+ * \param err error code value.
+ *
+ * \param errStr a descriptive string that must remain in scope until
+ * handling of the exception has concluded. If set, it
+ * will be returned by what().
+ */
+ Error(cl_int err, const char * errStr = NULL) : err_(err), errStr_(errStr)
+ {}
+
+ ~Error() throw() {}
+
+ /*! \brief Get error string associated with exception
+ *
+ * \return A memory pointer to the error message string.
+ */
+ virtual const char * what() const throw ()
+ {
+ if (errStr_ == NULL) {
+ return "empty";
+ }
+ else {
+ return errStr_;
+ }
+ }
+
+ /*! \brief Get error code associated with exception
+ *
+ * \return The error code.
+ */
+ cl_int err(void) const { return err_; }
+};
+```
+This type is used as the exception type thrown by utilities when an error occurs and the compiling code defines `CL_HPP_ENABLE_EXCEPTIONS`
diff --git a/lib/include/CL/SDK/CLI.h b/lib/include/CL/SDK/CLI.h
new file mode 100644
index 00000000..f04410b5
--- /dev/null
+++ b/lib/include/CL/SDK/CLI.h
@@ -0,0 +1,147 @@
+#pragma once
+
+// OpenCL SDK includes
+#include
+
+// STL includes
+#include // realloc
+#include // bool
+#include // memcpy, strcmp, strtoul
+
+// cargs includes
+#include
+
+typedef struct cag_option cag_option;
+
+cag_option *add_CLI_options(cag_option *opts, size_t *const num_opts,
+ cag_option *add_opts, size_t add_num_opts)
+{
+ cag_option *tmp = NULL;
+
+ tmp = (cag_option *)realloc(
+ opts, sizeof(cag_option) * (*num_opts + add_num_opts));
+ if (tmp)
+ {
+ memcpy(tmp + *num_opts, add_opts, sizeof(cag_option) * add_num_opts);
+ *num_opts += add_num_opts;
+ }
+
+ return tmp;
+}
+
+enum ParseState
+{
+ ParsedOK,
+ NotParsed,
+ ParseError
+};
+
+typedef enum ParseState ParseState;
+
+cag_option DiagnosticOptions[] = {
+ { .identifier = 'h',
+ .access_letters = "h",
+ .access_name = "help",
+ .description = "Show this help" },
+
+ { .identifier = 'q',
+ .access_letters = "q",
+ .access_name = "quiet",
+ .description = "Suppress standard output" },
+
+ { .identifier = 'v',
+ .access_letters = "v",
+ .access_name = "verbose",
+ .description = "Extra informational output" }
+};
+
+ParseState parse_DiagnosticOptions(const char identifier,
+ struct cl_sdk_options_Diagnostic *diag_opts)
+{
+ switch (identifier)
+ {
+ case 'q': diag_opts->quiet = true; return ParsedOK;
+ case 'v': diag_opts->verbose = true; return ParsedOK;
+ }
+ return NotParsed;
+}
+
+cag_option SingleDeviceOptions[] = {
+ { .identifier = 'p',
+ .access_letters = "p",
+ .access_name = "platform",
+ .value_name = "(positive integer)",
+ .description = "Index of platform to use" },
+
+ { .identifier = 'd',
+ .access_letters = "d",
+ .access_name = "device",
+ .value_name = "(positive integer)",
+ .description = "Index of device to use" },
+
+ { .identifier = 't',
+ .access_letters = "t",
+ .access_name = "type",
+ .value_name = "(all|cpu|gpu|acc|def|cus)",
+ .description = "Type of device to use" }
+};
+
+// TODO: error handling
+cl_device_type get_dev_type(const char *in)
+{
+ if (!strcmp(in, "all"))
+ return CL_DEVICE_TYPE_ALL;
+ else if (!strcmp(in, "cpu"))
+ return CL_DEVICE_TYPE_CPU;
+ else if (!strcmp(in, "gpu"))
+ return CL_DEVICE_TYPE_GPU;
+ else if (!strcmp(in, "acc"))
+ return CL_DEVICE_TYPE_ACCELERATOR;
+ else if (!strcmp(in, "def"))
+ return CL_DEVICE_TYPE_DEFAULT;
+ else if (!strcmp(in, "cus"))
+ return CL_DEVICE_TYPE_CUSTOM;
+ else
+ return CL_DEVICE_TYPE_ALL; // CL_INVALID_DEVICE_TYPE;// "Unkown device
+ // type after cli parse. Should not have
+ // happened."
+}
+
+ParseState
+parse_SingleDeviceOptions(const char identifier,
+ cag_option_context *cag_context,
+ struct cl_sdk_options_SingleDevice *dev_opts)
+{
+ const char *value;
+
+#define IF_ERR(op) \
+ if ((value = cag_option_get_value(cag_context))) \
+ { \
+ op; \
+ return ParsedOK; \
+ } \
+ else \
+ return ParseError;
+
+ switch (identifier)
+ {
+ case 'p': IF_ERR(dev_opts->triplet.plat_index = strtoul(value, NULL, 0))
+ case 'd': IF_ERR(dev_opts->triplet.dev_index = strtoul(value, NULL, 0))
+ case 't': IF_ERR(dev_opts->triplet.dev_type = get_dev_type(value))
+ }
+ return NotParsed;
+
+#undef IF_ERR
+}
+
+#define PARS_OPTIONS(parser, state) \
+ do \
+ { \
+ if (state == NotParsed) state = parser; \
+ if (state == ParseError) \
+ { \
+ fprintf(stderr, "Parse error\n"); \
+ identifier = 'h'; \
+ state = ParsedOK; \
+ } \
+ } while (0)
diff --git a/lib/include/CL/SDK/CLI.hpp b/lib/include/CL/SDK/CLI.hpp
new file mode 100644
index 00000000..6937c2cf
--- /dev/null
+++ b/lib/include/CL/SDK/CLI.hpp
@@ -0,0 +1,147 @@
+#pragma once
+
+// OpenCL SDK includes
+#include "OpenCLSDKCpp_Export.h"
+#include
+
+// OpenCL Utils includes
+#include
+
+// TCLAP includes
+#include
+
+// STL includes
+#include // std::shared_ptr, std::make_shared
+#include // std::make_tuple
+#include // std::vector
+
+namespace cl {
+namespace sdk {
+ template auto parse();
+
+ template
+ Option comprehend(Parsers... parsers);
+
+ namespace detail {
+ template
+ auto comprehend_helper(std::tuple parser)
+ {
+ return util::detail::apply(comprehend