diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7de163ce..a778ff1a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -144,11 +144,13 @@ The build command will create the following files: - `./install/lib/libcucim*` - `./python/install/lib/_cucim.cpython-*-x86_64-linux-gnu.so` - `./cpp/plugins/cucim.kit.cuslide/install/lib/cucim.kit.cuslide@*.so` +- `./cpp/plugins/cucim.kit.cumed/install/lib/cucim.kit.cumed@*.so` And, it will copy the built library files to `python/cucim/src/cucim/clara/` folder: - `libcucim.so.*` - `_cucim.cpython-*-x86_64-linux-gnu.so` - `cucim.kit.cuslide@*.so` +- `cucim.kit.cumed@*.so` **Building `cucim`(python bindings)** @@ -173,14 +175,14 @@ Once it is built, the subsequent build doesn't take much time. However, if a build option or dependent packages are updated, the build can be failed (due to CMakeCache.txt or existing build files). In that case, you can remove use the following commands to remove CMakeCache.txt or build folder, then build it again. -1) Remove CMakeCache.txt for libcucim, cuslide plugin, and the python wrapper (pybind11). +1) Remove CMakeCache.txt for libcucim, cuslide/cumed plugin, and the python wrapper (pybind11). ```bash # this command wouldn't remove already downloaded dependency so faster than `clean` subcommand ./run build_local clean_cache ``` -2) Remove `build-*` and `install` folder for libcucim, cuslide plugin, and the python wrapper (pybind11). +2) Remove `build-*` and `install` folder for libcucim, cuslide/cumed plugin, and the python wrapper (pybind11). ```bash # this command is for clean build diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index 4cde16922..fad673124 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -37,6 +37,7 @@ sed_runner 's/release = .*/release = '"'${NEXT_FULL_TAG}'"'/g' docs/source/conf. sed_runner "s/${CURRENT_LONG_TAG}/${NEXT_FULL_TAG}/g" VERSION sed_runner "s/${CURRENT_LONG_TAG}/${NEXT_FULL_TAG}/g" python/cucim/VERSION sed_runner "s/${CURRENT_LONG_TAG}/${NEXT_FULL_TAG}/g" cpp/plugins/cucim.kit.cuslide/VERSION +sed_runner "s/${CURRENT_LONG_TAG}/${NEXT_FULL_TAG}/g" cpp/plugins/cucim.kit.cumed/VERSION sed_runner "s#\[Version ${CURRENT_LONG_TAG}\](release_notes/v${CURRENT_LONG_TAG}.md)#\[Version ${NEXT_FULL_TAG}\](release_notes/v${NEXT_FULL_TAG}.md)#g" python/cucim/docs/index.md sed_runner "s/v${CURRENT_LONG_TAG}/v${NEXT_FULL_TAG}/g" python/cucim/docs/getting_started/index.md sed_runner "s#cucim.kit.cuslide@${CURRENT_LONG_TAG}.so#cucim.kit.cuslide@${NEXT_FULL_TAG}.so#g" python/cucim/docs/getting_started/index.md diff --git a/conda/recipes/libcucim/build.sh b/conda/recipes/libcucim/build.sh index 2c0b6f045..364bb9533 100644 --- a/conda/recipes/libcucim/build.sh +++ b/conda/recipes/libcucim/build.sh @@ -28,9 +28,11 @@ cp -P -r install/bin/* $PREFIX/bin/ || true cp -P -r install/lib/* $PREFIX/lib/ || true cp -P -r install/include/* $PREFIX/include/ || true -# Build libcucim.kit.cuslide plugin -./run build_local cuslide ${CUCIM_BUILD_TYPE} ${PREFIX} - -mkdir -p $PREFIX/bin $PREFIX/lib $PREFIX/include -cp -P -r cpp/plugins/cucim.kit.cuslide/install/bin/* $PREFIX/bin/ || true -cp -P -r cpp/plugins/cucim.kit.cuslide/install/lib/* $PREFIX/lib/ || true +# Build plugins +for plugin_name in cuslide cumed; do + echo "Building cucim.kit.${plugin_name} ..." + ./run build_local ${plugin_name} ${CUCIM_BUILD_TYPE} ${PREFIX} + mkdir -p $PREFIX/bin $PREFIX/lib $PREFIX/include + cp -P -r cpp/plugins/cucim.kit.${plugin_name}/install/bin/* $PREFIX/bin/ || true + cp -P -r cpp/plugins/cucim.kit.${plugin_name}/install/lib/* $PREFIX/lib/ || true +done diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 804ee3368..2f86a9c4a 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -63,6 +63,8 @@ add_library(${CUCIM_PACKAGE_NAME} include/cucim/macros/defines.h include/cucim/memory/dlpack.h include/cucim/memory/memory_manager.h + include/cucim/plugin/image_format.h + include/cucim/plugin/plugin_config.h include/cucim/util/cuda.h include/cucim/util/file.h include/cucim/util/platform.h @@ -96,6 +98,8 @@ add_library(${CUCIM_PACKAGE_NAME} src/logger/logger.cpp src/logger/timer.cpp src/memory/memory_manager.cu + src/plugin/image_format.cpp + src/plugin/plugin_config.cpp src/util/file.cpp src/util/platform.cpp) diff --git a/cpp/include/cucim/config/config.h b/cpp/include/cucim/config/config.h index bc15aae18..1ec1200fc 100644 --- a/cpp/include/cucim/config/config.h +++ b/cpp/include/cucim/config/config.h @@ -20,6 +20,7 @@ #include "cucim/macros/api_header.h" #include "cucim/cache/cache_type.h" #include "cucim/cache/image_cache_config.h" +#include "cucim/plugin/plugin_config.h" #include #include @@ -38,6 +39,7 @@ class EXPORT_VISIBLE Config Config(); cucim::cache::ImageCacheConfig& cache(); + cucim::plugin::PluginConfig& plugin(); std::string shm_name() const; pid_t pid() const; @@ -52,6 +54,7 @@ class EXPORT_VISIBLE Config std::string source_path_; cucim::cache::ImageCacheConfig cache_; + cucim::plugin::PluginConfig plugin_; }; } // namespace cucim::config diff --git a/cpp/include/cucim/core/framework.h b/cpp/include/cucim/core/framework.h index 9534e6d49..29e02b099 100644 --- a/cpp/include/cucim/core/framework.h +++ b/cpp/include/cucim/core/framework.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,7 +63,8 @@ struct PluginLoadingDesc struct Framework { - void load_plugins(const PluginLoadingDesc& desc = PluginLoadingDesc::get_default()); + // TODO: need to update for better plugin support - https://github.com/rapidsai/cucim/issues/134 + // void load_plugins(const PluginLoadingDesc& desc = PluginLoadingDesc::get_default()); bool(CUCIM_ABI* register_plugin)(const char* client_name, const PluginRegistrationDesc& desc); void*(CUCIM_ABI* acquire_interface_from_library_with_client)(const char* client_name, InterfaceDesc desc, @@ -74,6 +75,7 @@ struct Framework T* acquire_interface_from_library(const char* library_path); // cuCIM-specific methods + void(CUCIM_ABI* load_plugin)(const char* library_path); const char*(CUCIM_ABI* get_plugin_root)(); void(CUCIM_ABI* set_plugin_root)(const char* path); }; diff --git a/cpp/include/cucim/cuimage.h b/cpp/include/cucim/cuimage.h index 18efc028b..75c49dee2 100644 --- a/cpp/include/cucim/cuimage.h +++ b/cpp/include/cucim/cuimage.h @@ -24,6 +24,7 @@ #include "cucim/io/device.h" #include "cucim/io/format/image_format.h" #include "cucim/memory/dlpack.h" +#include "cucim/plugin/image_format.h" #include #include @@ -106,7 +107,7 @@ class EXPORT_VISIBLE CuImage : public std::enable_shared_from_this operator bool() const { - return !!image_formats_ && !is_loaded_; + return !!image_format_ && !is_loaded_; } static Framework* get_framework(); @@ -181,9 +182,10 @@ class EXPORT_VISIBLE CuImage : public std::enable_shared_from_this // Note: config_ should be placed before cache_manager_ (cache_manager_ depends on config_) static std::unique_ptr config_; static std::unique_ptr cache_manager_; + static std::unique_ptr image_format_plugins_; mutable Mutex mutex_; - cucim::io::format::IImageFormat* image_formats_ = nullptr; + cucim::io::format::ImageFormatDesc* image_format_ = nullptr; CuCIMFileHandle file_handle_{}; io::format::ImageMetadataDesc* image_metadata_ = nullptr; io::format::ImageDataDesc* image_data_ = nullptr; diff --git a/cpp/include/cucim/io/format/image_format.h b/cpp/include/cucim/io/format/image_format.h index ba4dc5ba0..091ea098b 100644 --- a/cpp/include/cucim/io/format/image_format.h +++ b/cpp/include/cucim/io/format/image_format.h @@ -170,9 +170,10 @@ struct ImageCheckerDesc * Returns true if the given file is valid for the format * @param file_name * @param buf + * @param size * @return */ - bool(CUCIM_ABI* is_valid)(const char* file_name, const char* buf); + bool(CUCIM_ABI* is_valid)(const char* file_name, const char* buf, size_t size); }; struct ImageParserDesc diff --git a/cpp/include/cucim/plugin/image_format.h b/cpp/include/cucim/plugin/image_format.h new file mode 100644 index 000000000..79229a701 --- /dev/null +++ b/cpp/include/cucim/plugin/image_format.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CUCIM_PLUGIN_IMAGE_FORMAT_H +#define CUCIM_PLUGIN_IMAGE_FORMAT_H + +#include "cucim/filesystem/file_path.h" +#include "cucim/io/format/image_format.h" + + +namespace cucim::plugin +{ + +class ImageFormat +{ +public: + ImageFormat() = default; + ~ImageFormat() = default; + + bool add_interfaces(const cucim::io::format::IImageFormat* image_formats); + cucim::io::format::ImageFormatDesc* detect_image_format(const filesystem::Path& path); + + operator bool() const + { + return !image_formats_.empty(); + } + +private: + std::vector image_formats_; +}; + +} // namespace cucim::plugin + +#endif // CUCIM_PLUGIN_IMAGE_FORMAT_H diff --git a/cpp/include/cucim/plugin/plugin_config.h b/cpp/include/cucim/plugin/plugin_config.h new file mode 100644 index 000000000..047f21146 --- /dev/null +++ b/cpp/include/cucim/plugin/plugin_config.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CUCIM_PLUGIN_PLUGIN_CONFIG_H +#define CUCIM_PLUGIN_PLUGIN_CONFIG_H + +#include "cucim/core/framework.h" + +#include +#include + +namespace cucim::plugin +{ + +#define XSTR(x) STR(x) +#define STR(x) #x + +struct EXPORT_VISIBLE PluginConfig +{ + void load_config(const void* json_obj); + + std::vector plugin_names{ std::string("cucim.kit.cuslide@" XSTR(CUCIM_VERSION) ".so"), + std::string("cucim.kit.cumed@" XSTR(CUCIM_VERSION) ".so") }; +}; + +#undef STR +#undef XSTR + +} // namespace cucim::plugin + +#endif // CUCIM_PLUGIN_PLUGIN_CONFIG_H diff --git a/cpp/plugins/cucim.kit.cumed/.clang-format b/cpp/plugins/cucim.kit.cumed/.clang-format new file mode 100644 index 000000000..8c5dc7828 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.clang-format @@ -0,0 +1,84 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine : false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: false +BreakBeforeBinaryOperators: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace : true +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakStringLiterals: false +ColumnLimit: 120 +CommentPragmas: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerBinding: false +FixNamespaceComments: true +IndentCaseLabels: false +IndentPPDirectives: AfterHash +IndentFunctionDeclarationAfterType: false +IndentWidth: 4 +SortIncludes: false +IncludeCategories: + - Regex: '[<"](.*\/)?Defines.h[>"]' + Priority: 1 + - Regex: '<[[:alnum:]_.]+>' + Priority: 5 + - Regex: '<[[:alnum:]_.\/]+>' + Priority: 4 + - Regex: '".*"' + Priority: 2 +IncludeBlocks: Regroup +Language: Cpp +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 0 +PenaltyBreakComment: 1 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 1 +PenaltyExcessCharacter: 10 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerAlignment: Left +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +Standard: Cpp11 +ReflowComments: true +TabWidth: 4 +UseTab: Never diff --git a/cpp/plugins/cucim.kit.cumed/.editorconfig b/cpp/plugins/cucim.kit.cumed/.editorconfig new file mode 100644 index 000000000..c69a96fa2 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.editorconfig @@ -0,0 +1,7 @@ +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +max_line_length = 120 +insert_final_newline = true diff --git a/cpp/plugins/cucim.kit.cumed/.gitignore b/cpp/plugins/cucim.kit.cumed/.gitignore new file mode 100644 index 000000000..f593ea4f4 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.gitignore @@ -0,0 +1,3 @@ +cmake-build* +install + diff --git a/cpp/plugins/cucim.kit.cumed/.idea/.gitignore b/cpp/plugins/cucim.kit.cumed/.idea/.gitignore new file mode 100644 index 000000000..73f69e095 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/cpp/plugins/cucim.kit.cumed/.idea/.name b/cpp/plugins/cucim.kit.cumed/.idea/.name new file mode 100644 index 000000000..93c67f482 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/.name @@ -0,0 +1 @@ +cumed diff --git a/cpp/plugins/cucim.kit.cumed/.idea/codeStyles/Project.xml b/cpp/plugins/cucim.kit.cumed/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..c8f84c353 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/codeStyles/Project.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/cpp/plugins/cucim.kit.cumed/.idea/codeStyles/codeStyleConfig.xml b/cpp/plugins/cucim.kit.cumed/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..0f7bc519d --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + diff --git a/cpp/plugins/cucim.kit.cumed/.idea/cucim.kit.cumed.iml b/cpp/plugins/cucim.kit.cumed/.idea/cucim.kit.cumed.iml new file mode 100644 index 000000000..8afe22e01 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/cucim.kit.cumed.iml @@ -0,0 +1,2 @@ + + diff --git a/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/includes/NVIDIA_CMAKE_HEADER.cmake b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/includes/NVIDIA_CMAKE_HEADER.cmake new file mode 100644 index 000000000..7272e0dec --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/includes/NVIDIA_CMAKE_HEADER.cmake @@ -0,0 +1,14 @@ +# +# Copyright (c) $YEAR, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/includes/NVIDIA_C_HEADER.h b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/includes/NVIDIA_C_HEADER.h new file mode 100644 index 000000000..cf0461d4c --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/includes/NVIDIA_C_HEADER.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) $YEAR, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C Header File.h b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C Header File.h new file mode 100644 index 000000000..9cb1d09e2 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C Header File.h @@ -0,0 +1,5 @@ +#parse("NVIDIA_C_HEADER.h") +#[[#ifndef]]# ${INCLUDE_GUARD} +#[[#define]]# ${INCLUDE_GUARD} + +#[[#endif]]# //${INCLUDE_GUARD} diff --git a/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C Source File.c b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C Source File.c new file mode 100644 index 000000000..b04dd6c62 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C Source File.c @@ -0,0 +1,4 @@ +#parse("NVIDIA_C_HEADER.h") +#if (${HEADER_FILENAME}) +#[[#include]]# "${HEADER_FILENAME}" +#end diff --git a/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C++ Class Header.h b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C++ Class Header.h new file mode 100644 index 000000000..f521fa555 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C++ Class Header.h @@ -0,0 +1,13 @@ +#parse("NVIDIA_C_HEADER.h") +#[[#ifndef]]# ${INCLUDE_GUARD} +#[[#define]]# ${INCLUDE_GUARD} + +${NAMESPACES_OPEN} + +class ${NAME} { + +}; + +${NAMESPACES_CLOSE} + +#[[#endif]]# //${INCLUDE_GUARD} diff --git a/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C++ Class.cc b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C++ Class.cc new file mode 100644 index 000000000..42f43ccf4 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/C++ Class.cc @@ -0,0 +1,2 @@ +#parse("NVIDIA_C_HEADER.h") +#[[#include]]# "${HEADER_FILENAME}" diff --git a/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/CMakeLists.txt.cmake b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/CMakeLists.txt.cmake new file mode 100644 index 000000000..846356219 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/fileTemplates/internal/CMakeLists.txt.cmake @@ -0,0 +1 @@ +#parse("NVIDIA_CMAKE_HEADER.cmake") diff --git a/cpp/plugins/cucim.kit.cumed/.idea/misc.xml b/cpp/plugins/cucim.kit.cumed/.idea/misc.xml new file mode 100644 index 000000000..2019083a1 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/cpp/plugins/cucim.kit.cumed/.idea/vcs.xml b/cpp/plugins/cucim.kit.cumed/.idea/vcs.xml new file mode 100644 index 000000000..fbbc5665e --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/cpp/plugins/cucim.kit.cumed/CMakeLists.txt b/cpp/plugins/cucim.kit.cumed/CMakeLists.txt new file mode 100644 index 000000000..30f162a91 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/CMakeLists.txt @@ -0,0 +1,299 @@ +# Apache License, Version 2.0 +# Copyright 2021 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# CUDA_STANDARD 17 is supported from CMAKE 3.18 +# : https://cmake.org/cmake/help/v3.18/prop_tgt/CUDA_STANDARD.html +cmake_minimum_required(VERSION 3.18) + +################################################################################ +# Prerequisite statements +################################################################################ + +# Set VERSION +unset(VERSION CACHE) +file(STRINGS ${CMAKE_CURRENT_LIST_DIR}/VERSION VERSION) + +# Append local cmake module path +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules") + +project(cumed VERSION ${VERSION} DESCRIPTION "cumed" LANGUAGES CXX CUDA) +set(CUCIM_PLUGIN_NAME "cucim.kit.cumed") + +################################################################################ +# Include utilities +################################################################################ +include(SuperBuildUtils) +include(CuCIMUtils) + +################################################################################ +# Set cmake policy +################################################################################ +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19") + cmake_policy(SET CMP0110 NEW) # For add_test() to support arbitrary characters in test name +endif() + +################################################################################ +# Basic setup +################################################################################ + +# Set default build type +set(DEFAULT_BUILD_TYPE "Release") +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") + set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif () + +# Set default output directories +if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +endif() +if (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +endif() +if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") +endif() + +# Find CUDAToolkit as rmm depends on it +find_package(CUDAToolkit REQUIRED) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CUDA_STANDARD 17) # Clion issue: https://youtrack.jetbrains.com/issue/CPP-19165 (fixed) +set(CMAKE_CUDA_STANDARD_REQUIRED YES) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +cucim_define_cuda_architectures(60;70;75;80;86) +# https://github.com/Kitware/CMake/blob/master/Modules/Compiler/NVIDIA-CUDA.cmake#L11 +# https://gitlab.kitware.com/cmake/cmake/-/issues/19017 +# For CUDA >= 10.2, we cannot use --compiler-options as '-forward-unknown-to-host-compiler' would be added by default to nvcc options. +# For the reason, we add "${CMAKE_CXX_FLAGS}" instead of "--compiler-options ${CMAKE_CXX_FLAGS}" here. +# ==> We changed to use "${CMAKE_CUDA_FLAGS}" instead of "${CMAKE_CXX_FLAGS}" ${CMAKE_CXX_FLAGS} can have wrong options such as '-march=nocona' +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -use_fast_math -Xptxas=-v") +set(CMAKE_CUDA_FLAGS_DEBUG "${CMAKE_CUDA_FLAGS_DEBUG} -G") +set(CMAKE_CUDA_FLAGS_RELEASE "${CMAKE_CUDA_FLAGS_RELEASE} -lineinfo") +set(CMAKE_CUDA_FLAGS_RELWITHDEBINFO "${CMAKE_CUDA_FLAGS_RELWITHDEBINFO} -lineinfo") + +# Include CUDA headers explicitly for VSCode intelli-sense +include_directories(AFTER SYSTEM ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) + +# Disable visibility to not expose unnecessary symbols +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) + +# Set RPATH +if (NOT APPLE) + set(CMAKE_INSTALL_RPATH $ORIGIN) +endif() + +# Set Installation setup +if (NOT CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_LIST_DIR}/install) # CACHE PATH "install here" FORCE) +endif () + +include(GNUInstallDirs) +# Force to set CMAKE_INSTALL_LIBDIR to lib as the library can be built with Cent OS ('lib64' is set) and +# /usr/local/lib64 or /usr/local/lib is not part of ld.so.conf* (`cat /etc/ld.so.conf.d/* | grep lib64`) +# https://gitlab.kitware.com/cmake/cmake/-/issues/20565 +set(CMAKE_INSTALL_LIBDIR lib) + +include(ExternalProject) + +################################################################################ +# Options +################################################################################ + +# Setup CXX11 ABI +# : Adds CXX11 ABI definition to the compiler command line for targets in the current directory, +# whether added before or after this command is invoked, and for the ones in sub-directories added after. +add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) # TODO: create two library, one with CXX11 ABI and one without it. + +################################################################################ +# Define dependencies +################################################################################ +superbuild_depend(fmt) +superbuild_depend(catch2) +superbuild_depend(googletest) +superbuild_depend(googlebenchmark) +superbuild_depend(cli11) + +################################################################################ +# Find cucim package +################################################################################ +if (NOT CUCIM_SDK_PATH) + get_filename_component(CUCIM_SDK_PATH "${CMAKE_SOURCE_DIR}/../../.." ABSOLUTE) + message("CUCIM_SDK_PATH is not set. Using '${CUCIM_SDK_PATH}'") +else() + message("CUCIM_SDK_PATH is set to ${CUCIM_SDK_PATH}") +endif() + +find_package(cucim CONFIG REQUIRED + HINTS ${CUCIM_SDK_PATH}/install/${CMAKE_INSTALL_LIBDIR}/cmake/cucim + $ENV{PREFIX}/include/cmake/cucim # In case conda build is used + ) + + +################################################################################ +# Define compile options +################################################################################ + +if(NOT BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) +endif() + +################################################################################ +# Add library: cucim +################################################################################ + +# Add library +add_library(${CUCIM_PLUGIN_NAME} + src/cumed/cumed.cpp + src/cumed/cumed.h + ) + +# At least one file needs to be compiled with nvcc. +# Otherwise, it will cause `/usr/bin/ld: cannot find -lcudart` error message. +set_source_files_properties(src/cumed/cumed.cpp PROPERTIES LANGUAGE CUDA) + +# Compile options +set_target_properties(${CUCIM_PLUGIN_NAME} + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO + CUDA_STANDARD 17 + CUDA_STANDARD_REQUIRED YES + CUDA_EXTENSIONS NO + CUDA_SEPARABLE_COMPILATION ON + CUDA_RUNTIME_LIBRARY Shared + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} +) +target_compile_features(${CUCIM_PLUGIN_NAME} PRIVATE cxx_std_17) +# Use generator expression to avoid `nvcc fatal : Value '-std=c++17' is not defined for option 'Werror'` +target_compile_options(${CUCIM_PLUGIN_NAME} PRIVATE $<$:-Werror -Wall -Wextra>) + +# Link libraries +target_link_libraries(${CUCIM_PLUGIN_NAME} + PRIVATE + deps::fmt + cucim::cucim + ) + +target_include_directories(${CUCIM_PLUGIN_NAME} + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src + # turbojpeg.h is not included in 'turbojpeg-static' so manually include + ${deps-libjpeg-turbo_SOURCE_DIR} + ) + +# Do not generate SONAME as this would be used as plugin +# Need to use IMPORTED_NO_SONAME when using this .so file. +set_target_properties(${CUCIM_PLUGIN_NAME} PROPERTIES NO_SONAME 1) +# Prevent relative path problem of .so with no DT_SONAME. +# : https://stackoverflow.com/questions/27261288/cmake-linking-shared-c-object-from-externalproject-produces-binaries-with-rel +target_link_options(${CUCIM_PLUGIN_NAME} PRIVATE "LINKER:-soname=${CUCIM_PLUGIN_NAME}@${PROJECT_VERSION}.so") + +# Do not add 'lib' prefix for the library +set_target_properties(${CUCIM_PLUGIN_NAME} PROPERTIES PREFIX "") +# Postfix version +set_target_properties(${CUCIM_PLUGIN_NAME} PROPERTIES OUTPUT_NAME "${CUCIM_PLUGIN_NAME}@${PROJECT_VERSION}") + +################################################################################ +# Add tests +#########################################################std####################### +add_subdirectory(tests) +add_subdirectory(benchmarks) + +################################################################################ +# Install +################################################################################ +set(INSTALL_TARGETS + ${CUCIM_PLUGIN_NAME} + cumed_tests + cumed_benchmarks + ) + +install(TARGETS ${INSTALL_TARGETS} + EXPORT ${CUCIM_PLUGIN_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT ${CUCIM_PLUGIN_NAME}_Runtime + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT ${CUCIM_PLUGIN_NAME}_Runtime + NAMELINK_COMPONENT ${CUCIM_PLUGIN_NAME}_Development + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT ${CUCIM_PLUGIN_NAME}_Development + ) + +# Currently cumed plugin doesn't have include path so comment out +# install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(EXPORT ${CUCIM_PLUGIN_NAME}-targets + FILE + ${CUCIM_PLUGIN_NAME}-targets.cmake + NAMESPACE + ${PROJECT_NAME}:: + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/${CUCIM_PLUGIN_NAME}) + +# Write package configs +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${CUCIM_PLUGIN_NAME} +) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config-version.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${CUCIM_PLUGIN_NAME} +) + + +set(CMAKE_EXPORT_PACKAGE_REGISTRY ON) +export(PACKAGE ${CUCIM_PLUGIN_NAME}) + + +# Write package configs +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${CUCIM_PLUGIN_NAME} +) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config-version.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CUCIM_PLUGIN_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${CUCIM_PLUGIN_NAME} +) + +set(CMAKE_EXPORT_PACKAGE_REGISTRY ON) # TODO: duplicate? +export(PACKAGE ${CUCIM_PLUGIN_NAME}) + +unset(BUILD_SHARED_LIBS CACHE) diff --git a/cpp/plugins/cucim.kit.cumed/VERSION b/cpp/plugins/cucim.kit.cumed/VERSION new file mode 100644 index 000000000..2cfbd895a --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/VERSION @@ -0,0 +1 @@ +21.12.00 diff --git a/cpp/plugins/cucim.kit.cumed/benchmarks/CMakeLists.txt b/cpp/plugins/cucim.kit.cumed/benchmarks/CMakeLists.txt new file mode 100644 index 000000000..43044a9b5 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/benchmarks/CMakeLists.txt @@ -0,0 +1,49 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +################################################################################ +# Add executable: cumed_benchmarks +################################################################################ +add_executable(cumed_benchmarks main.cpp config.h) +#set_source_files_properties(main.cpp PROPERTIES LANGUAGE CUDA) # failed with CLI11 library + +set_target_properties(cumed_benchmarks + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO + CUDA_STANDARD 17 + CUDA_STANDARD_REQUIRED YES + CUDA_EXTENSIONS NO + CUDA_SEPARABLE_COMPILATION ON + CUDA_RUNTIME_LIBRARY Shared +) +target_compile_features(cumed_benchmarks PRIVATE ${CUCIM_REQUIRED_FEATURES}) +# Use generator expression to avoid `nvcc fatal : Value '-std=c++17' is not defined for option 'Werror'` +target_compile_options(cumed_benchmarks PRIVATE $<$:-Werror -Wall -Wextra>) +target_compile_definitions(cumed_benchmarks + PUBLIC + CUMED_VERSION=${PROJECT_VERSION} + CUMED_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} + CUMED_VERSION_MINOR=${PROJECT_VERSION_MINOR} + CUMED_VERSION_PATCH=${PROJECT_VERSION_PATCH} + CUMED_VERSION_BUILD=${PROJECT_VERSION_BUILD} +) +target_link_libraries(cumed_benchmarks + PRIVATE + cucim::cucim + deps::googlebenchmark + deps::cli11 + ) diff --git a/cpp/plugins/cucim.kit.cumed/benchmarks/config.h b/cpp/plugins/cucim.kit.cumed/benchmarks/config.h new file mode 100644 index 000000000..bbd0278ed --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/benchmarks/config.h @@ -0,0 +1,47 @@ +/* + * Apache License, Version 2.0 + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CUMED_CONFIG_H +#define CUMED_CONFIG_H + +#include + +struct AppConfig +{ + std::string input_file = "test_data/private/generic_tiff_000.tif"; + bool discard_cache = false; + int random_seed = 0; + bool random_start_location = false; + + int64_t image_width = 0; + int64_t image_height = 0; + + // Pseudo configurations for google benchmark + bool benchmark_list_tests = false; + std::string benchmark_filter; // + int benchmark_min_time = 0; // + int benchmark_repetitions = 0; // + bool benchmark_report_aggregates_only = false; + bool benchmark_display_aggregates_only = false; + std::string benchmark_format; // + std::string benchmark_out; // + std::string benchmark_out_format; // + std::string benchmark_color; // {auto|true|false} + std::string benchmark_counters_tabular; + std::string v; // +}; + +#endif // CUMED_CONFIG_H diff --git a/cpp/plugins/cucim.kit.cumed/benchmarks/main.cpp b/cpp/plugins/cucim.kit.cumed/benchmarks/main.cpp new file mode 100644 index 000000000..358d7fd3e --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/benchmarks/main.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include +#include +#include + +#include "cucim/core/framework.h" +#include "cucim/io/format/image_format.h" +#include "cucim/memory/memory_manager.h" + +#define XSTR(x) STR(x) +#define STR(x) #x + +//#include + +CUCIM_FRAMEWORK_GLOBALS("cumed.app") + +static AppConfig g_config; + + +static void test_basic(benchmark::State& state) +{ + for (auto _ : state) + { + // Implement + // auto item = state.range(0); + } +} + + +BENCHMARK(test_basic)->Unit(benchmark::kMicrosecond)->RangeMultiplier(2)->Range(1, 4096); + +static bool remove_help_option(int* argc, char** argv) +{ + for (int i = 1; argc && i < *argc; ++i) + { + if (strncmp(argv[i], "-h", 3) == 0 || strncmp(argv[i], "--help", 7) == 0) + { + for (int j = i + 1; argc && j < *argc; ++j) + { + argv[j - 1] = argv[j]; + } + --(*argc); + argv[*argc] = nullptr; + return true; + } + } + return false; +} + +static bool setup_configuration() +{ + + return true; +} + +// BENCHMARK_MAIN(); +int main(int argc, char** argv) +{ + // Skip processing help option + bool has_help_option = remove_help_option(&argc, argv); + + ::benchmark::Initialize(&argc, argv); + CLI::App app{ "benchmark: cuMed" }; + app.add_option("--test_file", g_config.input_file, "An input file path"); + + // Pseudo benchmark options + app.add_option("--benchmark_list_tests", g_config.benchmark_list_tests, "{true|false}"); + app.add_option("--benchmark_filter", g_config.benchmark_filter, ""); + app.add_option("--benchmark_min_time", g_config.benchmark_min_time, ""); + app.add_option("--benchmark_repetitions", g_config.benchmark_repetitions, ""); + app.add_option("--benchmark_report_aggregates_only", g_config.benchmark_report_aggregates_only, "{true|false}"); + app.add_option("--benchmark_display_aggregates_only", g_config.benchmark_display_aggregates_only, "{true|false}"); + app.add_option("--benchmark_format", g_config.benchmark_format, ""); + app.add_option("--benchmark_out", g_config.benchmark_out, ""); + app.add_option("--benchmark_out_format", g_config.benchmark_out_format, ""); + app.add_option("--benchmark_color", g_config.benchmark_color, "{auto|true|false}"); + app.add_option("--benchmark_counters_tabular", g_config.benchmark_counters_tabular, "{true|false}"); + app.add_option("--v", g_config.v, ""); + + // Append help option if exists + if (has_help_option) + { + argv[argc] = const_cast("--help"); + ++argc; + // https://github.com/matepek/vscode-catch2-test-adapter detects google benchmark binaries by the following + // text: + printf("benchmark [--benchmark_list_tests={true|false}]\n"); + } + CLI11_PARSE(app, argc, argv); + + if (!setup_configuration()) + { + return 1; + } + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/cpp/plugins/cucim.kit.cumed/cmake/cucim.kit.cumed-config.cmake.in b/cpp/plugins/cucim.kit.cumed/cmake/cucim.kit.cumed-config.cmake.in new file mode 100644 index 000000000..efb735cb9 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/cucim.kit.cumed-config.cmake.in @@ -0,0 +1,25 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +@PACKAGE_INIT@ + +# Find dependent libraries +# ... +include(CMakeFindDependencyMacro) +#find_dependency(Boost x.x.x REQUIRED) + +if(NOT TARGET cumed::cumed) + include(${CMAKE_CURRENT_LIST_DIR}/cucim.kit.cumed-targets.cmake) +endif() diff --git a/cpp/plugins/cucim.kit.cumed/cmake/deps/catch2.cmake b/cpp/plugins/cucim.kit.cumed/cmake/deps/catch2.cmake new file mode 100644 index 000000000..0872d67bb --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/deps/catch2.cmake @@ -0,0 +1,40 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if (NOT TARGET deps::catch2) + FetchContent_Declare( + deps-catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v2.13.1 + GIT_SHALLOW TRUE + ) + FetchContent_GetProperties(deps-catch2) + if (NOT deps-catch2_POPULATED) + message(STATUS "Fetching catch2 sources") + FetchContent_Populate(deps-catch2) + message(STATUS "Fetching catch2 sources - done") + endif () + + add_subdirectory(${deps-catch2_SOURCE_DIR} ${deps-catch2_BINARY_DIR} EXCLUDE_FROM_ALL) + + # Include Append catch2's cmake module path so that we can use `include(ParseAndAddCatchTests)`. + list(APPEND CMAKE_MODULE_PATH "${deps-catch2_SOURCE_DIR}/contrib") + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} PARENT_SCOPE) + + add_library(deps::catch2 INTERFACE IMPORTED GLOBAL) + target_link_libraries(deps::catch2 INTERFACE Catch2::Catch2) + set(deps-catch2_SOURCE_DIR ${deps-catch2_SOURCE_DIR} CACHE INTERNAL "" FORCE) + mark_as_advanced(deps-catch2_SOURCE_DIR) +endif () diff --git a/cpp/plugins/cucim.kit.cumed/cmake/deps/cli11.cmake b/cpp/plugins/cucim.kit.cumed/cmake/deps/cli11.cmake new file mode 100644 index 000000000..2afb77841 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/deps/cli11.cmake @@ -0,0 +1,41 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if (NOT TARGET deps::cli11) + FetchContent_Declare( + deps-cli11 + GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git + GIT_TAG v1.9.1 + GIT_SHALLOW TRUE + ) + FetchContent_GetProperties(deps-cli11) + if (NOT deps-cli11_POPULATED) + message(STATUS "Fetching cli11 sources") + FetchContent_Populate(deps-cli11) + message(STATUS "Fetching cli11 sources - done") + endif () + + add_subdirectory(${deps-cli11_SOURCE_DIR} ${deps-cli11_BINARY_DIR} EXCLUDE_FROM_ALL) + + add_library(deps::cli11 INTERFACE IMPORTED GLOBAL) + target_link_libraries(deps::cli11 INTERFACE CLI11::CLI11) + set(deps-cli11_SOURCE_DIR ${deps-cli11_SOURCE_DIR} CACHE INTERNAL "" FORCE) + mark_as_advanced(deps-cli11_SOURCE_DIR) +endif () + +# Note that library had a failure with nvcc compiler and gcc 9.x headers +# ...c++/9/tuple(553): error: pack "_UElements" does not have the same number of elements as "_Elements" +# __and_...>::value; +# Not using nvcc for main code that uses cli11 solved the issue. diff --git a/cpp/plugins/cucim.kit.cumed/cmake/deps/fmt.cmake b/cpp/plugins/cucim.kit.cumed/cmake/deps/fmt.cmake new file mode 100644 index 000000000..cdf40f061 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/deps/fmt.cmake @@ -0,0 +1,42 @@ +# Apache License, Version 2.0 +# Copyright 2021 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if (NOT TARGET deps::fmt) + FetchContent_Declare( + deps-fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 7.0.1 + GIT_SHALLOW TRUE + ) + FetchContent_GetProperties(deps-fmt) + if (NOT deps-fmt_POPULATED) + message(STATUS "Fetching fmt sources") + FetchContent_Populate(deps-fmt) + message(STATUS "Fetching fmt sources - done") + endif () + + # Create static library + cucim_set_build_shared_libs(OFF) + add_subdirectory(${deps-fmt_SOURCE_DIR} ${deps-fmt_BINARY_DIR} EXCLUDE_FROM_ALL) + # Set PIC to prevent the following error message + # : /usr/bin/ld: ../lib/libfmtd.a(format.cc.o): relocation R_X86_64_PC32 against symbol `stderr@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC + set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON) + cucim_restore_build_shared_libs() + + add_library(deps::fmt INTERFACE IMPORTED GLOBAL) + target_link_libraries(deps::fmt INTERFACE fmt::fmt-header-only) + set(deps-fmt_SOURCE_DIR ${deps-fmt_SOURCE_DIR} CACHE INTERNAL "" FORCE) + mark_as_advanced(deps-fmt_SOURCE_DIR) +endif () diff --git a/cpp/plugins/cucim.kit.cumed/cmake/deps/googlebenchmark.cmake b/cpp/plugins/cucim.kit.cumed/cmake/deps/googlebenchmark.cmake new file mode 100644 index 000000000..05dc2d696 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/deps/googlebenchmark.cmake @@ -0,0 +1,40 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if (NOT TARGET deps::googlebenchmark) + FetchContent_Declare( + deps-googlebenchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v1.5.1 + GIT_SHALLOW TRUE + ) + FetchContent_GetProperties(deps-googlebenchmark) + if (NOT deps-googlebenchmark_POPULATED) + message(STATUS "Fetching googlebenchmark sources") + FetchContent_Populate(deps-googlebenchmark) + message(STATUS "Fetching googlebenchmark sources - done") + endif () + + # Create static library + cucim_set_build_shared_libs(OFF) + set(BENCHMARK_ENABLE_GTEST_TESTS OFF) + add_subdirectory(${deps-googlebenchmark_SOURCE_DIR} ${deps-googlebenchmark_BINARY_DIR} EXCLUDE_FROM_ALL) + cucim_restore_build_shared_libs() + + add_library(deps::googlebenchmark INTERFACE IMPORTED GLOBAL) + target_link_libraries(deps::googlebenchmark INTERFACE benchmark::benchmark) + set(deps-googlebenchmark_SOURCE_DIR ${deps-googlebenchmark_SOURCE_DIR} CACHE INTERNAL "" FORCE) + mark_as_advanced(deps-googlebenchmark_SOURCE_DIR) +endif () diff --git a/cpp/plugins/cucim.kit.cumed/cmake/deps/googletest.cmake b/cpp/plugins/cucim.kit.cumed/cmake/deps/googletest.cmake new file mode 100644 index 000000000..a9e878601 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/deps/googletest.cmake @@ -0,0 +1,45 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if (NOT TARGET deps::googletest) + FetchContent_Declare( + deps-googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.10.0 + GIT_SHALLOW TRUE + ) + FetchContent_GetProperties(deps-googletest) + if (NOT deps-googletest_POPULATED) + message(STATUS "Fetching googletest sources") + FetchContent_Populate(deps-googletest) + message(STATUS "Fetching googletest sources - done") + endif () + + # Create static library + cucim_set_build_shared_libs(OFF) + add_subdirectory(${deps-googletest_SOURCE_DIR} ${deps-googletest_BINARY_DIR} EXCLUDE_FROM_ALL) + cucim_restore_build_shared_libs() + + add_library(deps::googletest INTERFACE IMPORTED GLOBAL) + target_link_libraries(deps::googletest INTERFACE googletest) + set(deps-googletest_SOURCE_DIR ${deps-googletest_SOURCE_DIR} CACHE INTERNAL "" FORCE) + mark_as_advanced(deps-googletest_SOURCE_DIR) +endif () + +#CMake Warning (dev) in cmake-build-debug/_deps/deps-googletest-src/googlemock/CMakeLists.txt: +# Policy CMP0082 is not set: Install rules from add_subdirectory() are +# interleaved with those in caller. Run "cmake --help-policy CMP0082" for +# policy details. Use the cmake_policy command to set the policy and +# suppress this warning. diff --git a/cpp/plugins/cucim.kit.cumed/cmake/modules/CuCIMUtils.cmake b/cpp/plugins/cucim.kit.cumed/cmake/modules/CuCIMUtils.cmake new file mode 100644 index 000000000..cfe0b1495 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/modules/CuCIMUtils.cmake @@ -0,0 +1,60 @@ +# +# Copyright (c) 2020-2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Store current BUILD_SHARED_LIBS setting in CUCIM_OLD_BUILD_SHARED_LIBS +if(NOT COMMAND cucim_set_build_shared_libs) + macro(cucim_set_build_shared_libs new_value) + set(CUCIM_OLD_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}}) + if (DEFINED CACHE{BUILD_SHARED_LIBS}) + set(CUCIM_OLD_BUILD_SHARED_LIBS_CACHED TRUE) + else() + set(CUCIM_OLD_BUILD_SHARED_LIBS_CACHED FALSE) + endif() + set(BUILD_SHARED_LIBS ${new_value} CACHE BOOL "" FORCE) + endmacro() +endif() + +# Restore BUILD_SHARED_LIBS setting from CUCIM_OLD_BUILD_SHARED_LIBS +if(NOT COMMAND cucim_restore_build_shared_libs) + macro(cucim_restore_build_shared_libs) + if (CUCIM_OLD_BUILD_SHARED_LIBS_CACHED) + set(BUILD_SHARED_LIBS ${CUCIM_OLD_BUILD_SHARED_LIBS} CACHE BOOL "" FORCE) + else() + unset(BUILD_SHARED_LIBS CACHE) + set(BUILD_SHARED_LIBS ${CUCIM_OLD_BUILD_SHARED_LIBS}) + endif() + endmacro() +endif() + +# Define CMAKE_CUDA_ARCHITECTURES for the given architecture values +# +# Params: +# arch_list - architecture value list (e.g., '60;70;75;80;86') +if(NOT COMMAND cucim_define_cuda_architectures) + function(cucim_define_cuda_architectures arch_list) + set(arch_string "") + # Create SASS for all architectures in the list + foreach(arch IN LISTS arch_list) + set(arch_string "${arch_string}" "${arch}-real") + endforeach(arch) + + # Create PTX for the latest architecture for forward-compatibility. + list(GET arch_list -1 latest_arch) + foreach(arch IN LISTS arch_list) + set(arch_string "${arch_string}" "${latest_arch}-virtual") + endforeach(arch) + set(CMAKE_CUDA_ARCHITECTURES ${arch_string} PARENT_SCOPE) + endfunction() +endif() diff --git a/cpp/plugins/cucim.kit.cumed/cmake/modules/SuperBuildUtils.cmake b/cpp/plugins/cucim.kit.cumed/cmake/modules/SuperBuildUtils.cmake new file mode 100644 index 000000000..c9b40946d --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/cmake/modules/SuperBuildUtils.cmake @@ -0,0 +1,24 @@ +# Apache License, Version 2.0 +# Copyright 2021 NVIDIA Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(FetchContent) + +set(CMAKE_SUPERBUILD_DEPS_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/..") + +if(NOT COMMAND superbuild_depend) + function(superbuild_depend module_name) + include("${CMAKE_SUPERBUILD_DEPS_ROOT_DIR}/deps/${module_name}.cmake") + endfunction() +endif() diff --git a/cpp/plugins/cucim.kit.cumed/src/cumed/cumed.cpp b/cpp/plugins/cucim.kit.cumed/src/cumed/cumed.cpp new file mode 100644 index 000000000..82f86ca89 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/src/cumed/cumed.cpp @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define CUCIM_EXPORTS + +#include "cumed.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include + + +const struct cucim::PluginImplDesc kPluginImpl = { + "cucim.kit.cumed", // name + { 0, 1, 0 }, // version + "dev", // build + "clara team", // author + "cumed", // description + "cumed plugin", // long_description + "Apache-2.0", // license + "https://github.com/rapidsai/cucim", // url + "linux", // platforms, + cucim::PluginHotReload::kDisabled, // hot_reload +}; + +// Using CARB_PLUGIN_IMPL_MINIMAL instead of CARB_PLUGIN_IMPL +// This minimal macro doesn't define global variables for logging, profiler, crash reporting, +// and also doesn't call for the client registration for those systems +CUCIM_PLUGIN_IMPL_MINIMAL(kPluginImpl, cucim::io::format::IImageFormat) +CUCIM_PLUGIN_IMPL_NO_DEPS() + + +static void set_enabled(bool val) +{ + (void)val; +} + +static bool is_enabled() +{ + return true; +} + +static const char* get_format_name() +{ + return "MetaIO"; +} + +static bool CUCIM_ABI checker_is_valid(const char* file_name, const char* buf, size_t size) +{ + (void)buf; + (void)size; + auto file = std::filesystem::path(file_name); + auto extension = file.extension().string(); + if (extension.compare(".mhd") == 0) + { + return true; + } + return false; +} + +static CuCIMFileHandle CUCIM_ABI parser_open(const char* file_path_) +{ + const cucim::filesystem::Path& file_path = file_path_; + + int mode = O_RDONLY; + // Copy file path (Allocated memory would be freed at close() method.) + char* file_path_cstr = static_cast(malloc(file_path.size() + 1)); + (void)file_path_cstr; + memcpy(file_path_cstr, file_path.c_str(), file_path.size()); + file_path_cstr[file_path.size()] = '\0'; + + int fd = ::open(file_path_cstr, mode, 0666); + if (fd == -1) + { + cucim_free(file_path_cstr); + throw std::invalid_argument(fmt::format("Cannot open {}!", file_path)); + } + + // TODO: make file_handle_ object to pointer + auto file_handle = CuCIMFileHandle{ fd, nullptr, FileHandleType::kPosix, file_path_cstr, nullptr }; + + return file_handle; +} + +static bool CUCIM_ABI parser_parse(CuCIMFileHandle* handle, cucim::io::format::ImageMetadataDesc* out_metadata_desc) +{ + if (!out_metadata_desc || !out_metadata_desc->handle) + { + throw std::runtime_error("out_metadata_desc shouldn't be nullptr!"); + } + cucim::io::format::ImageMetadata& out_metadata = + *reinterpret_cast(out_metadata_desc->handle); + + + // + // Metadata Setup + // + + // Note: int-> uint16_t due to type differences between ImageMetadataDesc.ndim and DLTensor.ndim + const uint16_t ndim = 3; + auto& resource = out_metadata.get_resource(); + + std::string_view dims{ "YXC" }; + + std::pmr::vector shape({ 256, 256, 3 }, &resource); + + DLDataType dtype{ kDLUInt, 8, 1 }; + + // Assume RGB + std::pmr::vector channel_names( + { std::string_view{ "R" }, std::string_view{ "G" }, std::string_view{ "B" } }, &resource); + + std::pmr::vector spacing(&resource); + spacing.reserve(ndim); + spacing.insert(spacing.end(), ndim, 1.0); + + std::pmr::vector spacing_units(&resource); + spacing_units.reserve(ndim); + spacing_units.emplace_back(std::string_view{ "pixel" }); + spacing_units.emplace_back(std::string_view{ "pixel" }); + spacing_units.emplace_back(std::string_view{ "color" }); + + std::pmr::vector origin({ 0.0, 0.0, 0.0 }, &resource); + // Direction cosines (size is always 3x3) + // clang-format off + std::pmr::vector direction({ 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0}, &resource); + // clang-format on + + // The coordinate frame in which the direction cosines are measured (either 'LPS'(ITK/DICOM) or 'RAS'(NIfTI/3D + // Slicer)) + std::string_view coord_sys{ "LPS" }; + + size_t level_count = 1; + const uint16_t level_ndim = 2; // {'X', 'Y'} + std::pmr::vector level_dimensions(&resource); + level_dimensions.reserve(level_count * 2); + for (size_t i = 0; i < level_count; ++i) + { + level_dimensions.emplace_back(256); + level_dimensions.emplace_back(256); + } + + std::pmr::vector level_downsamples(&resource); + float orig_width = static_cast(shape[1]); + float orig_height = static_cast(shape[0]); + for (size_t i = 0; i < level_count; ++i) + { + level_downsamples.emplace_back(1.0); + } + + std::pmr::vector level_tile_sizes(&resource); + level_tile_sizes.reserve(level_count * 2); + for (size_t i = 0; i < level_count; ++i) + { + level_tile_sizes.emplace_back(256); + level_tile_sizes.emplace_back(256); + } + + const size_t associated_image_count = 0; + std::pmr::vector associated_image_names(&resource); + + std::string_view raw_data{ "" }; + + // Dynamically allocate memory for json_data (need to be freed manually); + const std::string& json_str = std::string{}; + char* json_data_ptr = static_cast(cucim_malloc(json_str.size() + 1)); + memcpy(json_data_ptr, json_str.data(), json_str.size() + 1); + std::string_view json_data{ json_data_ptr, json_str.size() }; + + out_metadata.ndim(ndim); + out_metadata.dims(std::move(dims)); + out_metadata.shape(std::move(shape)); + out_metadata.dtype(dtype); + out_metadata.channel_names(std::move(channel_names)); + out_metadata.spacing(std::move(spacing)); + out_metadata.spacing_units(std::move(spacing_units)); + out_metadata.origin(std::move(origin)); + out_metadata.direction(std::move(direction)); + out_metadata.coord_sys(std::move(coord_sys)); + out_metadata.level_count(level_count); + out_metadata.level_ndim(level_ndim); + out_metadata.level_dimensions(std::move(level_dimensions)); + out_metadata.level_downsamples(std::move(level_downsamples)); + out_metadata.level_tile_sizes(std::move(level_tile_sizes)); + out_metadata.image_count(associated_image_count); + out_metadata.image_names(std::move(associated_image_names)); + out_metadata.raw_data(raw_data); + out_metadata.json_data(json_data); + + return true; +} + +static bool CUCIM_ABI parser_close(CuCIMFileHandle* handle) +{ + if (handle->path) + { + cucim_free(handle->path); + handle->path = nullptr; + } + if (handle->client_data) + { + // TODO: comment out and reinterpret_cast when needed. + // delete reinterpret_cast(handle->client_data); + handle->client_data = nullptr; + } + + return true; +} + +static bool CUCIM_ABI reader_read(const CuCIMFileHandle* handle, + const cucim::io::format::ImageMetadataDesc* metadata, + const cucim::io::format::ImageReaderRegionRequestDesc* request, + cucim::io::format::ImageDataDesc* out_image_data, + cucim::io::format::ImageMetadataDesc* out_metadata_desc = nullptr) +{ + (void)metadata; + + std::string device_name(request->device); + if (request->shm_name) + { + device_name = device_name + fmt::format("[{}]", request->shm_name); // TODO: check performance + } + cucim::io::Device out_device(device_name); + + uint8_t* raster = nullptr; + uint32_t width = 256; + uint32_t height = 256; + uint32_t samples_per_pixel = 3; + size_t raster_size = width * height * samples_per_pixel; + + // Raw metadata for the associated image + const char* raw_data_ptr = nullptr; + size_t raw_data_len = 0; + // Json metadata for the associated image + char* json_data_ptr = nullptr; + + // Populate image data + const uint16_t ndim = 3; + + int64_t* container_shape = static_cast(cucim_malloc(sizeof(int64_t) * ndim)); + container_shape[0] = height; + container_shape[1] = width; + container_shape[2] = 3; // hard-coded for 'C' + + // Copy the raster memory and free it if needed. + cucim::memory::move_raster_from_host((void**)&raster, raster_size, out_device); + + auto& out_image_container = out_image_data->container; + out_image_container.data = raster; + out_image_container.ctx = DLContext{ static_cast(out_device.type()), out_device.index() }; + out_image_container.ndim = ndim; + out_image_container.dtype = { kDLUInt, 8, 1 }; + out_image_container.shape = container_shape; + out_image_container.strides = nullptr; // Tensor is compact and row-majored + out_image_container.byte_offset = 0; + + auto& shm_name = out_device.shm_name(); + size_t shm_name_len = shm_name.size(); + if (shm_name_len != 0) + { + out_image_data->shm_name = static_cast(cucim_malloc(shm_name_len + 1)); + memcpy(out_image_data->shm_name, shm_name.c_str(), shm_name_len + 1); + } + else + { + out_image_data->shm_name = nullptr; + } + + // Populate metadata + if (out_metadata_desc && out_metadata_desc->handle) + { + cucim::io::format::ImageMetadata& out_metadata = + *reinterpret_cast(out_metadata_desc->handle); + auto& resource = out_metadata.get_resource(); + + std::string_view dims{ "YXC" }; + + std::pmr::vector shape(&resource); + shape.reserve(ndim); + shape.insert(shape.end(), &container_shape[0], &container_shape[ndim]); + + DLDataType dtype{ kDLUInt, 8, 1 }; + + // TODO: Do not assume channel names as 'RGB' + std::pmr::vector channel_names( + { std::string_view{ "R" }, std::string_view{ "G" }, std::string_view{ "B" } }, &resource); + + + // We don't know physical pixel size for associated image so fill it with default value 1 + std::pmr::vector spacing(&resource); + spacing.reserve(ndim); + spacing.insert(spacing.end(), ndim, 1.0); + + std::pmr::vector spacing_units(&resource); + spacing_units.reserve(ndim); + spacing_units.emplace_back(std::string_view{ "micrometer" }); + spacing_units.emplace_back(std::string_view{ "micrometer" }); + spacing_units.emplace_back(std::string_view{ "color" }); + + std::pmr::vector origin({ 0.0, 0.0, 0.0 }, &resource); + + // Direction cosines (size is always 3x3) + // clang-format off + std::pmr::vector direction({ 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0}, &resource); + // clang-format on + + // The coordinate frame in which the direction cosines are measured (either 'LPS'(ITK/DICOM) or 'RAS'(NIfTI/3D + // Slicer)) + std::string_view coord_sys{ "LPS" }; + + // Manually set resolution dimensions to 2 + const uint16_t level_ndim = 2; + std::pmr::vector level_dimensions(&resource); + level_dimensions.reserve(level_ndim * 1); // it has only one size + level_dimensions.emplace_back(shape[1]); // width + level_dimensions.emplace_back(shape[0]); // height + + std::pmr::vector level_downsamples(&resource); + level_downsamples.reserve(1); + level_downsamples.emplace_back(1.0); + + std::pmr::vector level_tile_sizes(&resource); + level_tile_sizes.reserve(level_ndim * 1); // it has only one size + level_tile_sizes.emplace_back(shape[1]); // tile_width + level_tile_sizes.emplace_back(shape[0]); // tile_height + + // Empty associated images + const size_t associated_image_count = 0; + std::pmr::vector associated_image_names(&resource); + + std::string_view raw_data{ raw_data_ptr ? raw_data_ptr : "", raw_data_len }; + std::string_view json_data{ json_data_ptr ? json_data_ptr : "" }; + + out_metadata.ndim(ndim); + out_metadata.dims(std::move(dims)); + out_metadata.shape(std::move(shape)); + out_metadata.dtype(dtype); + out_metadata.channel_names(std::move(channel_names)); + out_metadata.spacing(std::move(spacing)); + out_metadata.spacing_units(std::move(spacing_units)); + out_metadata.origin(std::move(origin)); + out_metadata.direction(std::move(direction)); + out_metadata.coord_sys(std::move(coord_sys)); + out_metadata.level_count(1); + out_metadata.level_ndim(2); + out_metadata.level_dimensions(std::move(level_dimensions)); + out_metadata.level_downsamples(std::move(level_downsamples)); + out_metadata.level_tile_sizes(std::move(level_tile_sizes)); + out_metadata.image_count(associated_image_count); + out_metadata.image_names(std::move(associated_image_names)); + out_metadata.raw_data(raw_data); + out_metadata.json_data(json_data); + } + + return true; +} + +static bool CUCIM_ABI writer_write(const CuCIMFileHandle* handle, + const cucim::io::format::ImageMetadataDesc* metadata, + const cucim::io::format::ImageDataDesc* image_data) +{ + (void)handle; + (void)metadata; + (void)image_data; + + return true; +} + +void fill_interface(cucim::io::format::IImageFormat& iface) +{ + static cucim::io::format::ImageCheckerDesc image_checker = { 0, 0, checker_is_valid }; + static cucim::io::format::ImageParserDesc image_parser = { parser_open, parser_parse, parser_close }; + + static cucim::io::format::ImageReaderDesc image_reader = { reader_read }; + static cucim::io::format::ImageWriterDesc image_writer = { writer_write }; + + // clang-format off + static cucim::io::format::ImageFormatDesc image_format_desc = { + set_enabled, + is_enabled, + get_format_name, + image_checker, + image_parser, + image_reader, + image_writer + }; + // clang-format on + + // clang-format off + iface = + { + &image_format_desc, + 1 + }; + // clang-format on +} diff --git a/cpp/plugins/cucim.kit.cumed/src/cumed/cumed.h b/cpp/plugins/cucim.kit.cumed/src/cumed/cumed.h new file mode 100644 index 000000000..2802f20bc --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/src/cumed/cumed.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CUMED_CUMED_H +#define CUMED_CUMED_H +#endif // CUMED_CUMED_H diff --git a/cpp/plugins/cucim.kit.cumed/test_data b/cpp/plugins/cucim.kit.cumed/test_data new file mode 120000 index 000000000..6c6c77553 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/test_data @@ -0,0 +1 @@ +../../../test_data \ No newline at end of file diff --git a/cpp/plugins/cucim.kit.cumed/tests/CMakeLists.txt b/cpp/plugins/cucim.kit.cumed/tests/CMakeLists.txt new file mode 100644 index 000000000..25ae8cffd --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/tests/CMakeLists.txt @@ -0,0 +1,68 @@ +# +# Copyright (c) 2021, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include(CTest) +enable_testing() + +################################################################################ +# Add executable: cumed_tests +################################################################################ +add_executable(cumed_tests + config.h + main.cpp + test_basic.cpp + ) +set_source_files_properties(test_basic.cpp PROPERTIES LANGUAGE CUDA) # failed with CLI11 library + +set_target_properties(cumed_tests + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO + CUDA_STANDARD 17 + CUDA_STANDARD_REQUIRED YES + CUDA_EXTENSIONS NO + CUDA_SEPARABLE_COMPILATION ON + CUDA_RUNTIME_LIBRARY Shared +) +target_compile_features(cumed_tests PRIVATE ${CUCIM_REQUIRED_FEATURES}) +# Use generator expression to avoid `nvcc fatal : Value '-std=c++17' is not defined for option 'Werror'` +target_compile_options(cumed_tests PRIVATE $<$:-Werror -Wall -Wextra>) +target_compile_definitions(cumed_tests + PUBLIC + CUMED_VERSION=${PROJECT_VERSION} + CUMED_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} + CUMED_VERSION_MINOR=${PROJECT_VERSION_MINOR} + CUMED_VERSION_PATCH=${PROJECT_VERSION_PATCH} + CUMED_VERSION_BUILD=${PROJECT_VERSION_BUILD} +) +target_link_libraries(cumed_tests + PRIVATE + cucim::cucim + ${CUCIM_PLUGIN_NAME} + deps::catch2 + deps::cli11 + deps::fmt + ) + +# Add headers in src +target_include_directories(cumed_tests + PUBLIC + $ + ) + +include(ParseAndAddCatchTests) +# See https://github.com/catchorg/Catch2/blob/master/docs/cmake-integration.md#parseandaddcatchtestscmake for other options +ParseAndAddCatchTests(cumed_tests) diff --git a/cpp/plugins/cucim.kit.cumed/tests/config.h b/cpp/plugins/cucim.kit.cumed/tests/config.h new file mode 100644 index 000000000..506c9bc35 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/tests/config.h @@ -0,0 +1,63 @@ +/* + * Apache License, Version 2.0 + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CUMED_TESTS_CONFIG_H +#define CUMED_TESTS_CONFIG_H + +#include +#include + +struct AppConfig +{ + std::string test_folder; + std::string test_file; + std::string temp_folder = "/tmp"; + std::string get_input_path(const char* default_value = "private/generic_tiff_000.tif") const + { + // If `test_file` is absolute path + if (!test_folder.empty() && test_file.substr(0, 1) == "/") + { + return test_file; + } + else + { + std::string test_data_folder = test_folder; + if (test_data_folder.empty()) + { + if (const char* env_p = std::getenv("CUCIM_TESTDATA_FOLDER")) + { + test_data_folder = env_p; + } + else + { + test_data_folder = "test_data"; + } + } + if (test_file.empty()) + { + return test_data_folder + "/" + default_value; + } + else + { + return test_data_folder + "/" + test_file; + } + } + } +}; + +extern AppConfig g_config; + +#endif // CUMED_TESTS_CONFIG_H diff --git a/cpp/plugins/cucim.kit.cumed/tests/main.cpp b/cpp/plugins/cucim.kit.cumed/tests/main.cpp new file mode 100644 index 000000000..aa02e0309 --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/tests/main.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define CATCH_CONFIG_MAIN +//#include + +// Implement main explicitly to handle additional parameters. +#define CATCH_CONFIG_RUNNER +#include "config.h" +#include "cucim/core/framework.h" + +#include +#include +#include + +CUCIM_FRAMEWORK_GLOBALS("sample.app") + +// Global config object +AppConfig g_config; + +/** + * Extract `--[option]` or `--[option]=` string from command and set the value to g_config object. + * + * @param argc number of arguments used for command + * @param argv arguments for command + * @param obj object reference to modify + * @param argument name of argument(option) + * @return true if it extracted the value for the option + */ +static bool extract_test_file_option(int* argc, char** argv, std::string& obj, const char* argument) +{ + std::string arg_str = fmt::format("--{}=", argument); // test_file => --test_file= + std::string arg_str2 = fmt::format("--{}", argument); // test_file => --test_file + + char* value_ptr = nullptr; + for (int i = 1; argc && i < *argc; ++i) + { + if (strncmp(argv[i], arg_str.c_str(), arg_str.size()) == 0) + { + value_ptr = &argv[i][arg_str.size()]; + for (int j = i + 1; argc && j < *argc; ++j) + { + argv[j - 1] = argv[j]; + } + --(*argc); + argv[*argc] = nullptr; + break; + } + if (strncmp(argv[i], arg_str2.c_str(), arg_str2.size()) == 0 && i + 1 < *argc) + { + value_ptr = argv[i + 1]; + for (int j = i + 2; argc && j < *argc; ++j) + { + argv[j - 2] = argv[j]; + } + *argc -= 2; + argv[*argc] = nullptr; + argv[*argc + 1] = nullptr; + break; + } + } + + if (value_ptr) { + obj = value_ptr; + return true; + } + else { + return false; + } +} + +int main (int argc, char** argv) { + extract_test_file_option(&argc, argv, g_config.test_folder, "test_folder"); + extract_test_file_option(&argc, argv, g_config.test_file, "test_file"); + extract_test_file_option(&argc, argv, g_config.temp_folder, "temp_folder"); + printf("Target test folder: %s (use --test_folder option to change this)\n", g_config.test_folder.c_str()); + printf("Target test file : %s (use --test_file option to change this)\n", g_config.test_file.c_str()); + printf("Temp folder : %s (use --temp_folder option to change this)\n", g_config.temp_folder.c_str()); + int result = Catch::Session().run(argc, argv); + return result; +} diff --git a/cpp/plugins/cucim.kit.cumed/tests/test_basic.cpp b/cpp/plugins/cucim.kit.cumed/tests/test_basic.cpp new file mode 100644 index 000000000..1191db58e --- /dev/null +++ b/cpp/plugins/cucim.kit.cumed/tests/test_basic.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" + +#include +#include + +TEST_CASE("Verify file", "[test_basic.cpp]") +{ + REQUIRE(1 == 1); +} diff --git a/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp b/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp index 055473892..173381239 100644 --- a/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp +++ b/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ #include #include #include +#include #include using json = nlohmann::json; @@ -68,12 +69,17 @@ static const char* get_format_name() return "Generic TIFF"; } -static bool CUCIM_ABI checker_is_valid(const char* file_name, const char* buf) // TODO: need buffer size parameter +static bool CUCIM_ABI checker_is_valid(const char* file_name, const char* buf, size_t size) { - // TODO implement this - (void)file_name; (void)buf; - return true; + (void)size; + auto file = std::filesystem::path(file_name); + auto extension = file.extension().string(); + if (extension.compare(".tif") == 0 || extension.compare(".tiff") == 0) + { + return true; + } + return false; } static CuCIMFileHandle CUCIM_ABI parser_open(const char* file_path) @@ -277,7 +283,7 @@ static bool CUCIM_ABI writer_write(const CuCIMFileHandle* handle, void fill_interface(cucim::io::format::IImageFormat& iface) { - static cucim::io::format::ImageCheckerDesc image_checker = { 0, 80, checker_is_valid }; + static cucim::io::format::ImageCheckerDesc image_checker = { 0, 0, checker_is_valid }; static cucim::io::format::ImageParserDesc image_parser = { parser_open, parser_parse, parser_close }; static cucim::io::format::ImageReaderDesc image_reader = { reader_read }; diff --git a/cpp/plugins/cucim.kit.cuslide/src/cuslide/tiff/tiff.cpp b/cpp/plugins/cucim.kit.cuslide/src/cuslide/tiff/tiff.cpp index f35e44891..be13f2604 100644 --- a/cpp/plugins/cucim.kit.cuslide/src/cuslide/tiff/tiff.cpp +++ b/cpp/plugins/cucim.kit.cuslide/src/cuslide/tiff/tiff.cpp @@ -206,6 +206,11 @@ TIFF::TIFF(const cucim::filesystem::Path& file_path, int mode) : file_path_(file throw std::invalid_argument(fmt::format("Cannot open {}!", file_path)); } tiff_client_ = ::TIFFFdOpen(fd, file_path_cstr, "rm"); // Add 'm' to disable memory-mapped file + if (tiff_client_ == nullptr) + { + cucim_free(file_path_cstr); + throw std::invalid_argument(fmt::format("Cannot load {}!", file_path)); + } // TODO: make file_handle_ object to pointer file_handle_ = CuCIMFileHandle{ fd, nullptr, FileHandleType::kPosix, file_path_cstr, this }; diff --git a/cpp/src/config/config.cpp b/cpp/src/config/config.cpp index 89010673a..dffd30dc7 100644 --- a/cpp/src/config/config.cpp +++ b/cpp/src/config/config.cpp @@ -55,6 +55,11 @@ cucim::cache::ImageCacheConfig& Config::cache() return cache_; } +cucim::plugin::PluginConfig& Config::plugin() +{ + return plugin_; +} + std::string Config::shm_name() const { return fmt::format("cucim-shm.{}", pgid()); @@ -113,11 +118,18 @@ bool Config::parse_config(std::string& path) { std::ifstream ifs(path); json obj = json::parse(ifs, nullptr /*cb*/, true /*allow_exceptions*/, true /*ignore_comments*/); + json cache = obj["cache"]; if (cache.is_object()) { cache_.load_config(&cache); } + + json plugin = obj["plugin"]; + if (plugin.is_object()) + { + plugin_.load_config(&plugin); + } } catch (const json::parse_error& e) { diff --git a/cpp/src/core/cucim_framework.cpp b/cpp/src/core/cucim_framework.cpp index 66be59081..026f32f21 100644 --- a/cpp/src/core/cucim_framework.cpp +++ b/cpp/src/core/cucim_framework.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -131,6 +131,25 @@ bool CuCIMFramework::register_plugin(const std::shared_ptr& plugin) return true; } +size_t CuCIMFramework::get_plugin_count() const +{ + ScopedLock g(mutex_); + return plugin_manager_.get_plugin_indices().size(); +} + +void CuCIMFramework::get_plugins(PluginDesc* out_plugins) const +{ + ScopedLock g(mutex_); + const std::unordered_set& plugins = plugin_manager_.get_plugin_indices(); + size_t i = 0; + for (const auto& plugin_index : plugins) + { + if (out_plugins) + { + out_plugins[i++] = plugin_manager_.get_plugin(plugin_index)->get_plugin_desc(); + } + } +} size_t CuCIMFramework::get_plugin_index(const char* name) const { @@ -603,6 +622,23 @@ bool CuCIMFramework::register_plugin(const std::string& file_path, bool reloadab } // cuCIM-specific methods + +void CuCIMFramework::load_plugin(const char* library_path) +{ + + ScopedLock g(mutex_); + + const std::string canonical_library_path(library_path); + + Plugin* plugin = get_plugin_by_library_path(canonical_library_path); + // Check if plugin with this library path was already loaded + if (!plugin) + { + // It was not loaded, try to register such plugin and get it again + register_plugin(canonical_library_path); + } +} + std::string& CuCIMFramework::get_plugin_root() { return plugin_root_path_; diff --git a/cpp/src/core/cucim_framework.h b/cpp/src/core/cucim_framework.h index 966c33ba8..3f0b56530 100644 --- a/cpp/src/core/cucim_framework.h +++ b/cpp/src/core/cucim_framework.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,12 +58,15 @@ class CuCIMFramework const InterfaceDesc& desc, const char* library_path, bool optional = false); + size_t get_plugin_count() const; + void get_plugins(PluginDesc* out_plugins) const; size_t get_plugin_index(const char* name) const; Plugin* get_plugin(size_t index) const; Plugin* get_plugin(const char* name) const; Plugin* get_plugin_by_library_path(const std::string& library_path); // cuCIM-specific methods; + void load_plugin(const char* library_path); std::string& get_plugin_root(); void set_plugin_root(const char* path); diff --git a/cpp/src/core/cucim_plugin.cpp b/cpp/src/core/cucim_plugin.cpp index 8e6aa11bc..86b75ba5e 100644 --- a/cpp/src/core/cucim_plugin.cpp +++ b/cpp/src/core/cucim_plugin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ #include #include +#include namespace cucim { @@ -49,8 +50,19 @@ Plugin::Plugin() Plugin::Plugin(const std::string& file_path) : Plugin() { + auto file = std::filesystem::path(file_path); + auto filename = file.filename().string(); + std::size_t pivot = filename.find("@"); + if (pivot != std::string::npos) + { + std::string plugin_name = filename.substr(0, pivot); + name_ = std::move(plugin_name); + } + else + { + name_ = "cucim.unknown"; + } library_path_ = file_path; - name_ = "cucim.kit.cuslide"; // TODO: Get plgin name from file_path } Plugin::~Plugin() @@ -257,7 +269,7 @@ bool Plugin::try_load(int version, bool full) { return is_loaded_; } - CUCIM_LOG_VERBOSE("[Plugin: %s] %s", name_cstr(), full ? "Loading..." : "Preloading..."); + // CUCIM_LOG_VERBOSE("[Plugin: %s] %s", name_cstr(), full ? "Loading..." : "Preloading..."); std::string lib_file; if (!prepare_file_to_load(lib_file, version)) @@ -297,11 +309,14 @@ bool Plugin::try_load(int version, bool full) // Register if (!fill_registration_data(version, full, lib_file)) { + CUCIM_LOG_ERROR("[Plugin: %s] Could not load the dynamic library from %s. Error: fill_registration_data() failed!", + name_cstr(), lib_file.c_str()); return false; } // Load was successful - CUCIM_LOG_VERBOSE("[Plugin: %s] %s successfully. Version: %d", name_cstr(), full ? "loaded" : "preloaded", version); + // CUCIM_LOG_VERBOSE("[Plugin: %s] %s successfully. Version: %d", name_cstr(), full ? "loaded" : "preloaded", + // version); is_loaded_ = true; return is_loaded_; } diff --git a/cpp/src/core/framework.cpp b/cpp/src/core/framework.cpp index 15765db68..b5c8f0227 100644 --- a/cpp/src/core/framework.cpp +++ b/cpp/src/core/framework.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,8 @@ static bool register_plugin(const char* client_name, const PluginRegistrationDes return g_framework->register_plugin(client_name, desc); } +// TODO: need to update for better plugin support - https://github.com/rapidsai/cucim/issues/134 + // static void load_plugins(const PluginLoadingDesc& desc) //{ // CUCIM_ASSERT(g_framework); @@ -57,6 +59,12 @@ static void unload_all_plugins() g_framework->unload_all_plugins(); } +static void load_plugin(const char* library_path) +{ + CUCIM_ASSERT(g_framework); + g_framework->load_plugin(library_path); +} + static const char* get_plugin_root() { CUCIM_ASSERT(g_framework); @@ -77,6 +85,7 @@ static Framework get_framework_impl() register_plugin, acquire_interface_from_library_with_client, unload_all_plugins, + load_plugin, get_plugin_root, set_plugin_root, }; diff --git a/cpp/src/cuimage.cpp b/cpp/src/cuimage.cpp index 8942c557e..044626135 100644 --- a/cpp/src/cuimage.cpp +++ b/cpp/src/cuimage.cpp @@ -135,23 +135,23 @@ DetectedFormat detect_format(filesystem::Path path) Framework* CuImage::framework_ = cucim::acquire_framework("cucim"); std::unique_ptr CuImage::config_ = std::make_unique(); std::unique_ptr CuImage::cache_manager_ = std::make_unique(); +std::unique_ptr CuImage::image_format_plugins_ = std::make_unique(); CuImage::CuImage(const filesystem::Path& path) { // printf("[cuCIM] CuImage::CuImage(filesystem::Path path)\n"); ensure_init(); - (void)path; + image_format_ = image_format_plugins_->detect_image_format(path); // TODO: need to detect available format for the file path - file_handle_ = image_formats_->formats[0].image_parser.open(path.c_str()); + file_handle_ = image_format_->image_parser.open(path.c_str()); // printf("[GB] file_handle: %s\n", file_handle_.path); // fmt::print("[GB] CuImage path char: '{}'\n", file_handle_.path[0]); - io::format::ImageMetadata& image_metadata = *(new io::format::ImageMetadata{}); image_metadata_ = &image_metadata.desc(); - is_loaded_ = image_formats_->formats[0].image_parser.parse(&file_handle_, image_metadata_); + is_loaded_ = image_format_->image_parser.parse(&file_handle_, image_metadata_); dim_indices_ = DimIndices(image_metadata_->dims); auto& associated_image_info = image_metadata_->associated_image_info; @@ -183,7 +183,7 @@ CuImage::CuImage(CuImage&& cuimg) : std::enable_shared_from_this() // printf("[cuCIM] CuImage::CuImage(CuImage&& cuimg) %s\n", cuimg.file_handle_.path); (void)cuimg; std::swap(file_handle_, cuimg.file_handle_); - std::swap(image_formats_, cuimg.image_formats_); + std::swap(image_format_, cuimg.image_format_); std::swap(image_metadata_, cuimg.image_metadata_); std::swap(image_data_, cuimg.image_data_); std::swap(is_loaded_, cuimg.is_loaded_); @@ -201,7 +201,7 @@ CuImage::CuImage(const CuImage* cuimg, // cucim::io::format::ImageDataDesc* image_data)\n"); // file_handle_ = cuimg->file_handle_; ==> Don't do this. it will cause a double free. - image_formats_ = cuimg->image_formats_; + image_format_ = cuimg->image_format_; image_metadata_ = image_metadata; image_data_ = image_data; is_loaded_ = true; @@ -230,7 +230,7 @@ CuImage::~CuImage() { // printf("[cuCIM] CuImage::~CuImage()\n"); close(); - image_formats_ = nullptr; // memory release is handled by the framework + image_format_ = nullptr; // memory release is handled by the framework if (image_metadata_) { // Memory for json_data needs to be manually released if image_metadata_->json_data is not "" @@ -616,7 +616,7 @@ CuImage CuImage::read_region(std::vector&& location, { throw std::runtime_error("[Error] The image file is closed!"); } - if (!image_formats_->formats[0].image_reader.read( + if (!image_format_->image_reader.read( &file_handle_, image_metadata_, &request, image_data, nullptr /*out_metadata*/)) { cucim_free(image_data); @@ -796,7 +796,7 @@ CuImage CuImage::associated_image(const std::string& name, const io::Device& dev io::format::ImageMetadata& out_metadata = *(new io::format::ImageMetadata{}); - if (!image_formats_->formats[0].image_reader.read( + if (!image_format_->image_reader.read( &file_handle_, image_metadata_, &request, out_image_data, &out_metadata.desc())) { cucim_free(out_image_data); @@ -865,7 +865,7 @@ void CuImage::close() { if (file_handle_.client_data) { - image_formats_->formats[0].image_parser.close(&file_handle_); + image_format_->image_parser.close(&file_handle_); } file_handle_.cufile = nullptr; file_handle_.path = nullptr; @@ -880,27 +880,34 @@ void CuImage::ensure_init() { CUCIM_ERROR("Framework is not initialized!"); } - if (!image_formats_) + if (!(*image_format_plugins_)) { - auto plugin_root = framework_->get_plugin_root(); - // TODO: Here 'LINUX' path separator is used. Need to make it generalize once filesystem library is - // available. - std::string plugin_file_path = - (plugin_root && *plugin_root != 0) ? - fmt::format("{}/cucim.kit.cuslide@{}.{}.{}.so", plugin_root, XSTR(CUCIM_VERSION_MAJOR), - XSTR(CUCIM_VERSION_MINOR), XSTR(CUCIM_VERSION_PATCH)) : - fmt::format("cucim.kit.cuslide@{}.{}.{}.so", XSTR(CUCIM_VERSION_MAJOR), XSTR(CUCIM_VERSION_MINOR), - XSTR(CUCIM_VERSION_PATCH)); - if (!cucim::util::file_exists(plugin_file_path.c_str())) - { - plugin_file_path = fmt::format("cucim.kit.cuslide@" XSTR(CUCIM_VERSION) ".so"); - } - image_formats_ = - framework_->acquire_interface_from_library(plugin_file_path.c_str()); - if (image_formats_ == nullptr) + image_format_plugins_ = std::make_unique(); + + const std::vector& plugin_names = get_config()->plugin().plugin_names; + + const char* plugin_root = framework_->get_plugin_root(); + for (auto& plugin_name : plugin_names) { - throw std::runtime_error( - fmt::format("Dependent library 'cucim.kit.cuslide@" XSTR(CUCIM_VERSION) ".so' cannot be loaded!")); + // TODO: Here 'LINUX' path separator is used. Need to make it generalize once filesystem library is + // available. + std::string plugin_file_path = (plugin_root && *plugin_root != 0) ? + fmt::format("{}/{}", plugin_root, plugin_name) : + fmt::format("{}", plugin_name); + if (!cucim::util::file_exists(plugin_file_path.c_str())) + { + plugin_file_path = fmt::format("{}", plugin_name); + } + + const auto& image_formats = + framework_->acquire_interface_from_library(plugin_file_path.c_str()); + + image_format_plugins_->add_interfaces(image_formats); + + if (image_formats == nullptr) + { + throw std::runtime_error(fmt::format("Dependent library '{}' cannot be loaded!", plugin_file_path)); + } } } } diff --git a/cpp/src/io/format/image_format.cpp b/cpp/src/io/format/image_format.cpp index 38009465d..9816f9130 100644 --- a/cpp/src/io/format/image_format.cpp +++ b/cpp/src/io/format/image_format.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/src/plugin/image_format.cpp b/cpp/src/plugin/image_format.cpp new file mode 100644 index 000000000..4d5615218 --- /dev/null +++ b/cpp/src/plugin/image_format.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020-2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "cucim/io/format/image_format.h" +#include "cucim/plugin/image_format.h" + +#include + + +namespace cucim::plugin +{ + +bool ImageFormat::add_interfaces(const cucim::io::format::IImageFormat* image_formats) +{ + if (image_formats && image_formats->format_count > 0) + { + for (size_t i = 0; i < image_formats->format_count; ++i) + { + cucim::io::format::ImageFormatDesc* format = &(image_formats->formats[i]); + image_formats_.push_back(format); + } + } + else + { + return false; + } + return true; +} + +cucim::io::format::ImageFormatDesc* ImageFormat::detect_image_format(const cucim::filesystem::Path& path) +{ + for (auto& format : image_formats_) + { + if (format->image_checker.is_valid(path.c_str(), nullptr, 0)) + { + return format; + } + } + throw std::invalid_argument(fmt::format("Cannot find a plugin to handle '{}'!", path)); +} + +} // namespace cucim::plugin diff --git a/cpp/src/plugin/plugin_config.cpp b/cpp/src/plugin/plugin_config.cpp new file mode 100644 index 000000000..c16e009b6 --- /dev/null +++ b/cpp/src/plugin/plugin_config.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cucim/plugin/plugin_config.h" + +#include +#include + +using json = nlohmann::json; + +namespace cucim::plugin +{ + +void PluginConfig::load_config(const void* json_obj) +{ + const json& plugin_config = *(static_cast(json_obj)); + + if (plugin_config["names"].is_array()) + { + std::vector names; + names.reserve(16); + for (const auto& name : plugin_config["names"]) + { + names.push_back(name); + } + plugin_names = std::move(names); + } +} + +} // namespace cucim::plugin diff --git a/run b/run index f87116d0e..e2cd109a3 100755 --- a/run +++ b/run @@ -301,6 +301,27 @@ build_local_cuslide_() { popd } +build_local_cumed_() { + local source_folder=${1:-${TOP}/cpp/plugins/cucim.kit.cumed} + local build_type=${2:-debug} + local build_type_str=${3:-Debug} + local prefix=${4:-} + local build_folder=${source_folder}/build-${build_type} + local CMAKE_CMD=${CMAKE_CMD:-cmake} + + pushd ${source_folder} > /dev/null + + ${CMAKE_CMD} -S ${source_folder} -B ${build_folder} -G "Unix Makefiles" \ + -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE \ + -DCMAKE_BUILD_TYPE=${build_type_str} \ + -DCMAKE_PREFIX_PATH=${prefix} \ + -DCMAKE_INSTALL_PREFIX=${source_folder}/install + ${CMAKE_CMD} --build ${build_folder} --config ${build_type_str} --target cucim.kit.cumed -- -j $(nproc) + ${CMAKE_CMD} --build ${build_folder} --config ${build_type_str} --target install -- -j $(nproc) + + popd +} + build_local_cucim_() { local source_folder=${1:-${TOP}/python} local build_type=${2:-debug} @@ -333,7 +354,7 @@ build_local_desc() { echo 'Build locally Compile binaries locally Arguments: - $1 - subcommand [all|clean_cache|clean|libcucim|cuslide|cucim] (default: all) + $1 - subcommand [all|clean_cache|clean|libcucim|cuslide|cumed|cucim] (default: all) $2 - build type [debug|release|rel-debug] (default: debug) ' } @@ -355,6 +376,7 @@ build_local() { if [ "$subcommand" = "clean_cache" ]; then rm -f ${TOP}/build-*/CMakeCache.txt rm -f ${TOP}/cpp/plugins/cucim.kit.cuslide/build-*/CMakeCache.txt + rm -f ${TOP}/cpp/plugins/cucim.kit.cumed/build-*/CMakeCache.txt rm -f ${TOP}/python/build-*/CMakeCache.txt fi @@ -363,6 +385,8 @@ build_local() { rm -rf ${TOP}/install rm -rf ${TOP}/cpp/plugins/cucim.kit.cuslide/build-*/ rm -rf ${TOP}/cpp/plugins/cucim.kit.cuslide/install + rm -rf ${TOP}/cpp/plugins/cucim.kit.cumed/build-*/ + rm -rf ${TOP}/cpp/plugins/cucim.kit.cumed/install rm -rf ${TOP}/python/build-* rm -rf ${TOP}/python/install fi @@ -375,6 +399,10 @@ build_local() { build_local_cuslide_ ${TOP}/cpp/plugins/cucim.kit.cuslide ${build_type} ${build_type_str} ${prefix} fi + if [ "$subcommand" = "all" ] || [ "$subcommand" = "cumed" ]; then + build_local_cumed_ ${TOP}/cpp/plugins/cucim.kit.cumed ${build_type} ${build_type_str} ${prefix} + fi + if [ "$subcommand" = "all" ] || [ "$subcommand" = "cucim" ]; then build_local_cucim_ ${TOP}/python ${build_type} ${build_type_str} ${prefix} fi @@ -385,11 +413,12 @@ build_local() { if [ "$subcommand" = "all" ] || [ "$subcommand" = "cucim" ]; then # We don't need to copy binary if executed by conda-build if [ "${CONDA_BUILD:-}" != "1" ]; then - # Copy .so files from libcucim & cuslide's build folders to cuCIM's Python source folder + # Copy .so files from libcucim & cuslide/cumed's build folders to cuCIM's Python source folder # Since wheel file doesn't support symbolic link (https://github.com/pypa/wheel/issues/203), # we don't need to copy symbolic links. Instead copy only libcucim.so.${major_version} (without symbolic link) cp ${TOP}/build-$build_type/lib*/libcucim.so.${major_version} ${TOP}/python/cucim/src/cucim/clara/ cp -P ${TOP}/cpp/plugins/cucim.kit.cuslide/build-$build_type/lib*/cucim* ${TOP}/python/cucim/src/cucim/clara/ + cp -P ${TOP}/cpp/plugins/cucim.kit.cumed/build-$build_type/lib*/cucim* ${TOP}/python/cucim/src/cucim/clara/ # Copy .so files from pybind's build folder to cuCIM's Python source folder cp ${TOP}/python/build-$build_type/lib/cucim/_cucim.*.so ${TOP}/python/cucim/src/cucim/clara/ @@ -489,6 +518,7 @@ build_python_package_() { # Clear CMakeCache.txt to use the latest options run_command rm -f ${BUILD_ROOT}/libcucim/CMakeCache.txt run_command rm -f ${BUILD_ROOT}/cuslide/CMakeCache.txt + run_command rm -f ${BUILD_ROOT}/cumed/CMakeCache.txt run_command rm -f ${BUILD_ROOT}/cucim/CMakeCache.txt # Create a folder for .whl file that repair_wheel_ is not applied. @@ -513,6 +543,8 @@ build_python_package_() { rm -rf ${BUILD_ROOT}/libcucim/install/lib*/lib* rm -rf ${BUILD_ROOT}/cuslide/lib/* rm -rf ${BUILD_ROOT}/cuslide/install/lib*/cucim* + rm -rf ${BUILD_ROOT}/cumed/lib/* + rm -rf ${BUILD_ROOT}/cumed/install/lib*/cucim* rm -rf ${BUILD_ROOT}/cucim/lib/cucim/*.so* rm -rf ${BUILD_ROOT}/cucim/install/lib/*.so* @@ -531,6 +563,14 @@ build_python_package_() { ${CMAKE_CMD} --build ${BUILD_ROOT}/cuslide --config ${CMAKE_BUILD_TYPE} --target cucim.kit.cuslide -- -j ${NUM_THREADS} ${CMAKE_CMD} --build ${BUILD_ROOT}/cuslide --config ${CMAKE_BUILD_TYPE} --target install -- -j ${NUM_THREADS} + # Build cumed plugin + ${CMAKE_CMD} -S ${SRC_ROOT}/cpp/plugins/cucim.kit.cumed -B ${BUILD_ROOT}/cumed \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DCMAKE_INSTALL_PREFIX=${BUILD_ROOT}/cumed/install \ + -DCUCIM_SDK_PATH=${CUCIM_SDK_PATH} + ${CMAKE_CMD} --build ${BUILD_ROOT}/cumed --config ${CMAKE_BUILD_TYPE} --target cucim.kit.cumed -- -j ${NUM_THREADS} + ${CMAKE_CMD} --build ${BUILD_ROOT}/cumed --config ${CMAKE_BUILD_TYPE} --target install -- -j ${NUM_THREADS} + # Build Python bind pybins="$(echo /opt/python/*/bin)" if [ "${pybins}" = "/opt/python/*/bin" ]; then @@ -558,6 +598,7 @@ build_python_package_() { # (it uses -P to copy symbolic links as they are) cp -P ${BUILD_ROOT}/libcucim/install/lib*/lib* ${BUILD_ROOT}/cucim/install/lib/ cp -P ${BUILD_ROOT}/cuslide/install/lib*/cucim* ${BUILD_ROOT}/cucim/install/lib/ + cp -P ${BUILD_ROOT}/cumed/install/lib*/cucim* ${BUILD_ROOT}/cucim/install/lib/ # Copy .so files from pybind's build folder to cucim Python source folder # Since wheel file doesn't support symbolic link (https://github.com/pypa/wheel/issues/203), @@ -604,6 +645,7 @@ build_python_package_() { mkdir -p ${DEST_ROOT}/examples/cpp cp -P -r ${BUILD_ROOT}/libcucim/install ${DEST_ROOT}/ cp -P -r ${BUILD_ROOT}/cuslide/install/lib/*.so ${DEST_ROOT}/install/lib/ + cp -P -r ${BUILD_ROOT}/cumed/install/lib/*.so ${DEST_ROOT}/install/lib/ cp -r ${SRC_ROOT}/examples/cpp/tiff_image ${DEST_ROOT}/examples/cpp/ cp ${BUILD_ROOT}/libcucim/CMakeLists.txt.examples.release ${DEST_ROOT}/examples/cpp/CMakeLists.txt