Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-device C/C++ sample #4

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ project(OpenCL-SDK
)

include(CMakeDependentOption)
option(OPENCL_SDK_BUILD_SAMPLES "Build sample code" ON)
option(OPENCL_SDK_BUILD_UTILITY_LIBRARIES "Build utility libraries" ON)
cmake_dependent_option(OPENCL_SDK_BUILD_SAMPLES "Build sample code" ON OPENCL_SDK_BUILD_UTILITY_LIBRARIES OFF)
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)

Expand All @@ -47,8 +48,9 @@ list(APPEND CMAKE_MODULE_PATH
${PROJECT_SOURCE_DIR}/cmake/Modules
)
include(Dependencies)

add_subdirectory(lib)
if(OPENCL_SDK_BUILD_UTILITY_LIBRARIES)
add_subdirectory(lib)
endif()
if(OPENCL_SDK_BUILD_SAMPLES)
add_subdirectory(samples)
endif()
Expand Down
7 changes: 7 additions & 0 deletions cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
if(OPENCL_SDK_BUILD_UTILITY_LIBRARIES)
foreach(DEP IN ITEMS whereami)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Dependencies/${DEP}")
include(${DEP})
endforeach()
endif()

if(OPENCL_SDK_BUILD_SAMPLES)
foreach(DEP IN ITEMS cargs TCLAP Stb)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Dependencies/${DEP}")
Expand Down
15 changes: 15 additions & 0 deletions cmake/Dependencies/whereami/whereami.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.11)
include(FetchContent)
FetchContent_Declare(
whereami-external
GIT_REPOSITORY https://github.com/gpakosz/whereami.git
GIT_TAG ba364cd54fd431c76c045393b6522b4bff547f50 # master @ 2023.04.20.
)
FetchContent_MakeAvailable(whereami-external)
add_library(whereami IMPORTED INTERFACE)
target_include_directories(whereami
INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/_deps/whereami-external-src/src"
)
target_sources(whereami
INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/_deps/whereami-external-src/src/whereami.c"
)
2 changes: 2 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ foreach(UTIL_LIB_NAME IN ITEMS Utils UtilsCpp)
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)
target_link_libraries(${UTIL_LIB_TARGET}
PRIVATE
whereami
PUBLIC
${UTIL_LIB_DEPS}
OpenCL::OpenCL
Expand Down
95 changes: 95 additions & 0 deletions lib/Utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The OpenCL Utility Library provides both C and C++ bindings with near feature pa
- [Context](#context-utilities)
- [Event](#event-utilities)
- [Error](#error-handling-utilities)
- [File](#file-utilities)

### Platform utilities

Expand Down Expand Up @@ -119,3 +120,97 @@ public:
};
```
This type is used as the exception type thrown by utilities when an error occurs and the compiling code defines `CL_HPP_ENABLE_EXCEPTIONS`

### File utilities

```c
char* cl_util_read_text_file(
const char* const filename,
size_t* const length,
cl_int* const error);
```

```c++
std::string cl::util::read_text_file(
const char* const filename,
cl_int* const error = nullptr);
```

These functions read a text file into memory, where `filename` is evaluated relative to the current working directory. The C-version contains a terminating null and takes an optional pointer to `length` by which the length my be returned, potentially saving a subsequent call to `strlen`. The function hands ownership of the allocated storage to the caller.

```c
unsigned char* cl_util_read_binary_file(
const char* const filename,
size_t* const length,
cl_int* const error);
```

```c++
std::vector<unsigned char> read_binary_file(
const char* const filename,
cl_int* const error = nullptr);
```

These functions read a binary file into memory, where `filename` is evaluated relative to the current working directory. The C-version takes an optional pointer to `length` by which the length my be returned. Because it's binary data, it is _not_ null-terminated, therefore highly recommended to take it's size. The returned types align with OpenCL APIs taking binaries as input. The function hands ownership of the allocated storage to the caller.

```c
cl_program cl_util_read_binaries(
const cl_context context,
const cl_device_id* const devices,
const cl_uint num_devices,
const char* const program_file_name,
cl_int* const error
);
```

```c++
Program::Binaries read_binary_files(
const std::vector<cl::Device>& devices,
const char* const program_file_name,
cl_int* const error = nullptr);
```

These functions read a set of binary files into memory. `program_file_name` is a pattern that will be completed for every input device using the `"(program_file_name)_(name of device).bin"` pattern. If any of the files are not found, the function fails.

```c
cl_int cl_util_write_binaries(
const cl_program program,
const char* const program_file_name);
```

```c++
write_binaries(
const cl::Program::Binaries& binaries,
const std::vector<cl::Device>& devices,
const char* const program_file_name);
```

These functions will write all device binaries of a program to persistent storage. `program_file_name` is a pattern that will be completed for every input device using the `"(program_file_name)_(name of device).bin"` pattern.

```c
cl_int cl_util_executable_folder(
char* filename,
size_t* const length);
```

```c++
std::string executable_folder(
cl_int* const error = nullptr);
```

These functions return the path to the folder containing the currently running executable. It is typically useful to find assets which are stored uniformly in a program's build and install tree. The C-version contains a terminating null and takes an optional pointer to `length` by which the length my be returned, potentially saving a subsequent call to `strlen`.

```c
char* cl_util_read_exe_relative_text_file(
const char* const rel_path,
size_t* const length,
cl_int* const error);
```

```c++
std::string read_exe_relative_text_file(
const char* const filename,
cl_int* const error = nullptr);
```

These functions read a text file into memory, where `filename` is evaluated relative to the executable currently running. The C-version contains a terminating null and takes an optional pointer to `length` by which the length will be returned, potentially saving a subsequent call to `strlen`. The function hands ownership of the allocated storage to the caller.
7 changes: 6 additions & 1 deletion lib/include/CL/SDK/InteropWindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@
#include <SFML/Window.hpp>
#include <SFML/OpenGL.hpp>

// STL includes
#include <type_traits>

namespace cl {
namespace sdk {
class SDKCPP_EXPORT InteropWindow : public sf::Window {
public:
using Style = std::underlying_type_t<decltype(sf::Style::Default)>;

explicit InteropWindow(
sf::VideoMode mode, const sf::String& title,
sf::Uint32 style = sf::Style::Default,
Style style = sf::Style::Default,
const sf::ContextSettings& settings = sf::ContextSettings{},
cl_uint platform_id = 0, cl_uint device_id = 0,
cl_bitfield device_type = CL_DEVICE_TYPE_DEFAULT);
Expand Down
2 changes: 2 additions & 0 deletions lib/include/CL/Utils/Context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ namespace util {
Context UTILSCPP_EXPORT get_context(cl_uint plat_id, cl_uint dev_id,
cl_device_type type,
cl_int* error = nullptr);

void UTILSCPP_EXPORT print_device_info(const cl::Device& device);
}
}
13 changes: 13 additions & 0 deletions lib/include/CL/Utils/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,16 @@ cl_program cl_util_read_binaries(const cl_context context,
const cl_uint num_devices,
const char* const program_file_name,
cl_int* const error);

// returns the folder containing the running executable
UTILS_EXPORT
cl_int cl_util_executable_folder(char* filename, size_t* const length);

// read all the text file contents securely in ANSI C89
// return pointer to C-string with file contents
// interprets filename relative to the folder containing
// the running executable
UTILS_EXPORT
char* cl_util_read_exe_relative_text_file(const char* const rel_path,
size_t* const length,
cl_int* const error);
16 changes: 11 additions & 5 deletions lib/include/CL/Utils/File.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,24 @@ namespace cl {
namespace util {

std::string UTILSCPP_EXPORT read_text_file(const char* const filename,
cl_int* const error);
cl_int* const error = nullptr);

std::vector<unsigned char> UTILSCPP_EXPORT
read_binary_file(const char* const filename, cl_int* const error);
read_binary_file(const char* const filename, cl_int* const error = nullptr);

Program::Binaries UTILSCPP_EXPORT
read_binary_files(const std::vector<cl::Device>& devices,
const char* const program_file_name, cl_int* const error);
Program::Binaries UTILSCPP_EXPORT read_binary_files(
const std::vector<cl::Device>& devices,
const char* const program_file_name, cl_int* const error = nullptr);

cl_int UTILSCPP_EXPORT
write_binaries(const cl::Program::Binaries& binaries,
const std::vector<cl::Device>& devices,
const char* const program_file_name);

std::string UTILSCPP_EXPORT
executable_folder(cl_int* const error = nullptr);

std::string UTILSCPP_EXPORT read_exe_relative_text_file(
const char* const filename, cl_int* const error = nullptr);
}
}
2 changes: 1 addition & 1 deletion lib/src/SDK/Image.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ cl_sdk_image cl_sdk_read_image(const char* const file_name, cl_int* const error)

static char* to_lowercase(const char* const s, char* const d, const size_t n)
{
for (size_t i = 0; i < n; ++i) d[i] = tolower(s[i]);
for (size_t i = 0; i < n; ++i) d[i] = (char)tolower(s[i]);
return d;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/src/SDK/InteropWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#include <CL/SDK/InteropContext.hpp>

cl::sdk::InteropWindow::InteropWindow(sf::VideoMode mode,
const sf::String& title, sf::Uint32 style,
const sf::String& title, Style style,
const sf::ContextSettings& settings,
cl_uint platform_id, cl_uint device_id,
cl_bitfield device_type)
: sf::Window{ mode, title, style, settings }, plat_id{ platform_id },
dev_id{ device_id }, dev_type{ device_type }
: sf::Window{ mode, title, static_cast<sf::Uint32>(style), settings },
plat_id{ platform_id }, dev_id{ device_id }, dev_type{ device_type }
{}

void cl::sdk::InteropWindow::run()
Expand Down
16 changes: 16 additions & 0 deletions lib/src/Utils/Context.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// OpenCL SDK includes
#include <CL/Utils/Context.hpp>

#include <iostream>
#include <string>

cl::Context cl::util::get_context(cl_uint plat_id, cl_uint dev_id,
cl_device_type type, cl_int* error)
{
Expand Down Expand Up @@ -40,3 +43,16 @@ cl::Context cl::util::get_context(cl_uint plat_id, cl_uint dev_id,

return cl::Context{};
}

void cl::util::print_device_info(const cl::Device& device)
{
const cl::Platform platform(device.getInfo<CL_DEVICE_PLATFORM>());
const std::string platform_vendor = platform.getInfo<CL_PLATFORM_VENDOR>();
const std::string device_name = device.getInfo<CL_DEVICE_NAME>();
const std::string device_opencl_c_version =
device.getInfo<CL_DEVICE_OPENCL_C_VERSION>();
std::cout << "Selected platform by " << platform_vendor
<< "\nSelected device: " << device_name << '\n'
<< device_opencl_c_version << '\n'
<< std::endl;
}
76 changes: 76 additions & 0 deletions lib/src/Utils/File.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include <stdio.h> // fopen, ferror, fread, fclose
#include <string.h> // memset

// whereami includes
#include <whereami.h>

// read all the text file contents securely in ANSI C89
// return pointer to C-string with file contents
// can handle streams with no known size and no support for fseek
Expand Down Expand Up @@ -337,3 +340,76 @@ cl_program cl_util_read_binaries(const cl_context context,
if (error != NULL) *error = err;
return program;
}

UTILS_EXPORT
cl_int cl_util_executable_folder(char *const filename, size_t *const length)
{
cl_int err = CL_SUCCESS;
int wai_length, wai_dirname_length = 0;
char *wai_filename = NULL;

#define IF_ERR(func, error_type, label) \
do \
{ \
if (func) \
{ \
err = error_type; \
goto label; \
} \
} while (0)

wai_length = wai_getExecutablePath(NULL, 0, NULL);
IF_ERR(wai_length == -1, CL_UTIL_FILE_OPERATION_ERROR, end);
MEM_CHECK(wai_filename = (char *)malloc(wai_length), err, end);
IF_ERR(wai_getExecutablePath(wai_filename, wai_length, &wai_dirname_length)
== -1,
CL_UTIL_FILE_OPERATION_ERROR, end);

if (length != NULL)
{
*length = (int)wai_dirname_length + 1;
}

if (filename != NULL)
{
memmove(filename, wai_filename, wai_dirname_length);
filename[wai_dirname_length] = '\0';
}

end:
free(wai_filename);

return err;
}

// read all the text file contents securely in ANSI C89
// return pointer to C-string with file contents
// interprets filename relative to the folder containing
// the running executable
UTILS_EXPORT
char *cl_util_read_exe_relative_text_file(const char *const rel_path,
size_t *const length,
cl_int *const error)
{
char *result = NULL;
size_t result_size = 0;
char *abs_path = NULL;
cl_int err = CL_SUCCESS;
size_t exe_folder_length;
OCLERROR_RET(cl_util_executable_folder(NULL, &exe_folder_length), err, end);
MEM_CHECK(abs_path =
(char *)malloc(exe_folder_length + strlen(rel_path) + 1),
err, end);
OCLERROR_RET(cl_util_executable_folder(abs_path, NULL), err, end);
strcat(strcat(abs_path, "/"), rel_path);
OCLERROR_PAR(result = cl_util_read_text_file(abs_path, &result_size, &err),
err, end);

if (length != NULL) *length = result_size;

if (error != NULL) *error = err;
end:
free(abs_path);

return result;
}
Loading
Loading