diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0e61da44c..2b3e2065a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -32,7 +32,8 @@ } }, "runArgs": [ - "" + "--net=host", + //"" ], "containerEnv": { // X11 support @@ -81,6 +82,7 @@ "ms-python.vscode-pylance", "ms-vscode.cpptools-extension-pack", "redhat.vscode-yaml", + "rioj7.command-variable", "shardulm94.trailing-spaces", "shd101wyy.markdown-preview-enhanced", "stkb.rewrap" @@ -100,4 +102,4 @@ "HOLOHUB_DATA_DIR": "${containerWorkspaceFolder}/data" }, "remoteUser": "holoscan" -} \ No newline at end of file +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 1cfdcf304..4594a159e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -149,7 +149,6 @@ } }, //#endregion asr_to_llm - //#region sam2 { "name": "(debugpy) sam2/python", @@ -178,7 +177,6 @@ } }, //#endregion sam2 - //#region colonoscopy_segmentation { "name": "(debugpy) colonoscopy_segmentation/python", @@ -858,10 +856,10 @@ "name": "(gdb) Holoviz examples", "type": "cppdbg", "request": "launch", - "preLaunchTask": "Build ${input:holoviz_example_name}", + "preLaunchTask": "Build Holoviz examples", "program": "${workspaceFolder}/build/${input:holoviz_example_name}/applications/holoviz/${input:holoviz_example_name}/${input:holoviz_example_name}", - "args": [ ], - "environment": [ ], + "args": [], + "environment": [], "stopAtEntry": false, "cwd": "${workspaceFolder}/build/${input:holoviz_example_name}/applications/holoviz/${input:holoviz_example_name}", "externalConsole": false, @@ -1071,15 +1069,19 @@ "inputs": [ { "id": "holoviz_example_name", - "description": "Select Holoviz example program", - "type": "pickString", - "options": [ - "holoviz_hdr", - "holoviz_srgb", - "holoviz_vsync", - "holoviz_yuv", - ], - "default": "holoviz_srgb" - } + "type": "command", + "command": "extension.commandvariable.pickStringRemember", + "args": { + "description": "Select Holoviz example program", + "options": [], + "fileName": "${workspaceFolder}/applications/holoviz/template/generate_projects.sh", + "fileFormat": "pattern", + "pattern": { + "regexp": "^generate [\"](.*?)[\"].*$", + "flags": "gm", + }, + "key": "holoviz_example_name" + } + }, ], } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ab08bc7f5..a53a76a11 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,4 +1,5 @@ { + "version": "2.0.0", "tasks": [ { "type": "shell", @@ -307,62 +308,11 @@ }, { "type": "shell", - "label": "Build holoviz_hdr", + "label": "Build Holoviz examples", "command": "./run", "args": [ "build", - "holoviz_hdr", - "--type", - "debug" - ], - "options": { - "cwd": "${env:WORKSPACE_DIR}" - }, - "group": "build", - "problemMatcher": [], - "detail": "CMake template build task" - }, - { - "type": "shell", - "label": "Build holoviz_srgb", - "command": "./run", - "args": [ - "build", - "holoviz_srgb", - "--type", - "debug" - ], - "options": { - "cwd": "${env:WORKSPACE_DIR}" - }, - "group": "build", - "problemMatcher": [], - "detail": "CMake template build task" - }, - { - "type": "shell", - "label": "Build holoviz_vsync", - "command": "./run", - "args": [ - "build", - "holoviz_vsync", - "--type", - "debug" - ], - "options": { - "cwd": "${env:WORKSPACE_DIR}" - }, - "group": "build", - "problemMatcher": [], - "detail": "CMake template build task" - }, - { - "type": "shell", - "label": "Build holoviz_yuv", - "command": "./run", - "args": [ - "build", - "holoviz_yuv", + "${input:remember_holoviz_example_name}", "--type", "debug" ], @@ -481,5 +431,13 @@ "description": "Select Application", "type": "promptString", }, + { + "id": "remember_holoviz_example_name", + "type": "command", + "command": "extension.commandvariable.remember", + "args": { + "key": "holoviz_example_name" + } + } ] } diff --git a/applications/holoviz/CMakeLists.txt b/applications/holoviz/CMakeLists.txt index d22a72016..ff5c264b0 100644 --- a/applications/holoviz/CMakeLists.txt +++ b/applications/holoviz/CMakeLists.txt @@ -15,5 +15,6 @@ add_holohub_application(holoviz_hdr) add_holohub_application(holoviz_srgb) +add_holohub_application(holoviz_ui) add_holohub_application(holoviz_vsync) add_holohub_application(holoviz_yuv) diff --git a/applications/holoviz/holoviz_hdr/holoviz_hdr.cpp b/applications/holoviz/holoviz_hdr/holoviz_hdr.cpp index 66141e244..baf03d246 100644 --- a/applications/holoviz/holoviz_hdr/holoviz_hdr.cpp +++ b/applications/holoviz/holoviz_hdr/holoviz_hdr.cpp @@ -147,6 +147,7 @@ class App : public holoscan::Application { add_flow(source, holoviz, {{"output", "receivers"}}); } + private: const int count_; }; diff --git a/applications/holoviz/holoviz_srgb/holoviz_srgb.cpp b/applications/holoviz/holoviz_srgb/holoviz_srgb.cpp index 4606f21a6..33cd685e7 100644 --- a/applications/holoviz/holoviz_srgb/holoviz_srgb.cpp +++ b/applications/holoviz/holoviz_srgb/holoviz_srgb.cpp @@ -125,6 +125,7 @@ class App : public holoscan::Application { add_flow(source, holoviz, {{"output", "receivers"}}); } + private: const int count_; }; diff --git a/applications/holoviz/holoviz_ui/CMakeLists.txt b/applications/holoviz/holoviz_ui/CMakeLists.txt new file mode 100644 index 000000000..a03e1c611 --- /dev/null +++ b/applications/holoviz/holoviz_ui/CMakeLists.txt @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# 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. + +cmake_minimum_required(VERSION 3.20) + +project(holoviz_ui) + +find_package(holoscan 2.5 REQUIRED CONFIG + PATHS "/opt/nvidia/holoscan" "/workspace/holoscan-sdk/install") + +add_executable(holoviz_ui + holoviz_ui.cpp +) + +target_link_libraries(holoviz_ui + PRIVATE + holoscan::core + holoscan::ops::holoviz + holoscan::viz::imgui + ) + +if(BUILD_TESTING) + # Add test + add_test(NAME holoviz_ui_test + COMMAND holoviz_ui + --count=10 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + set_tests_properties(holoviz_ui_test PROPERTIES + PASS_REGULAR_EXPRESSION "Application has finished running.") +endif() diff --git a/applications/holoviz/holoviz_ui/README.md b/applications/holoviz/holoviz_ui/README.md new file mode 100644 index 000000000..5a82b1c94 --- /dev/null +++ b/applications/holoviz/holoviz_ui/README.md @@ -0,0 +1,12 @@ +# Holoviz UI + +![](holoviz_ui.png) +This application uses the layer callback provided by the Holoviz operator and leverages the Holoviz module API to add an UI layer with `Dear ImGui` elements and a geometry layer dynamically changing based on user input. + +## Run Instructions + +To build and start the application: + +```bash +./dev_container build_and_run holoviz_ui +``` diff --git a/applications/holoviz/holoviz_ui/holoviz_ui.cpp b/applications/holoviz/holoviz_ui/holoviz_ui.cpp new file mode 100644 index 000000000..c3599abce --- /dev/null +++ b/applications/holoviz/holoviz_ui/holoviz_ui.cpp @@ -0,0 +1,203 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 +#include +#include +#include +#include + +#include + +namespace holoscan::ops { + +class SourceOp : public Operator { + public: + HOLOSCAN_OPERATOR_FORWARD_ARGS(SourceOp); + + void initialize() override { + const int32_t width = 64, height = 64; + shape_ = nvidia::gxf::Shape{width, height, 3}; + element_type_ = nvidia::gxf::PrimitiveType::kUnsigned8; + element_size_ = nvidia::gxf::PrimitiveTypeSize(element_type_); + strides_ = nvidia::gxf::ComputeTrivialStrides(shape_, element_size_); + + data_.resize(strides_[0] * shape_.dimension(0)); + + // create an RGB image with smooth color transitions + for (size_t y = 0; y < shape_.dimension(0); ++y) { + for (size_t x = 0; x < shape_.dimension(1); ++x) { + float rgb[3]; + for (size_t component = 0; component < 3; ++component) { + switch (component) { + case 0: + rgb[component] = float(x) / shape_.dimension(1); + break; + case 1: + rgb[component] = float(y) / shape_.dimension(0); + break; + case 2: + rgb[component] = 1.f - (float(x) / shape_.dimension(1)); + break; + } + data_[y * strides_[0] + x * strides_[1] + component] = + uint8_t((rgb[component] * 255.f) + 0.5f); + } + } + } + + Operator::initialize(); + } + + void setup(OperatorSpec& spec) override { spec.output("output"); } + + void compute(InputContext& input, OutputContext& output, ExecutionContext& context) override { + auto entity = holoscan::gxf::Entity::New(&context); + auto tensor = static_cast(entity).add("image"); + tensor.value()->wrapMemory(shape_, + element_type_, + element_size_, + strides_, + nvidia::gxf::MemoryStorageType::kSystem, + data_.data(), + nullptr); + output.emit(entity, "output"); + } + + private: + nvidia::gxf::Shape shape_; + nvidia::gxf::PrimitiveType element_type_; + uint64_t element_size_; + nvidia::gxf::Tensor::stride_array_t strides_; + std::vector data_; +}; + +} // namespace holoscan::ops + +class App : public holoscan::Application { + public: + explicit App(int count) : count_(count) {} + App() = delete; + + void compose() override { + using namespace holoscan; + + auto source = + make_operator("source", + // stop application count + make_condition("count-condition", count_)); + + auto holoviz = make_operator( + "holoviz", + // set the layer callback to execute a member function of the App class. + Arg("layer_callback", + ops::HolovizOp::LayerCallbackFunction( + std::bind(&App::layer_callback, this, std::placeholders::_1))), + Arg("window_title", std::string("Holoviz UI")), + Arg("cuda_stream_pool", make_resource("cuda_stream_pool", 0, 0, 0, 1, 5))); + + add_flow(source, holoviz, {{"output", "receivers"}}); + } + void layer_callback(const std::vector& inputs) { + using namespace holoscan; + + // The layer callback is executed after the Holoviz operator has finished drawing all layers. We + // now can add our own layers after that. + // For more information on Holoviz layers see + // https://docs.nvidia.com/holoscan/sdk-user-guide/visualization.html#layers. + + // Add a simple UI, Holoviz supports a `Dear ImGui` layer. For more information on `Dear ImGui` + // check https://github.com/ocornut/imgui. + viz::BeginImGuiLayer(); + ImGui::Begin("UI", nullptr, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Checkbox("Checkbox", &checkbox_selected_); + static const char* combo_items[] = { + "Item 1", + "Item 2", + "Item 3", + }; + ImGui::Combo("Combo", &combo_item_, combo_items, IM_ARRAYSIZE(combo_items)); + ImGui::SliderFloat("Slider Float", &slider_float_value_, 0.F, 1.F); + ImGui::SliderInt("Slider Int", &slider_int_value_, -10, 10); + ImGui::Separator(); + ImGui::ColorEdit4("Color", color_value_, ImGuiColorEditFlags_DefaultOptions_); + viz::EndLayer(); + + // Now create a geometry layer using the values of the UI. + viz::BeginGeometryLayer(); + // Draw the text from combo with the color set by the user and the position set by the sliders. + viz::Color(color_value_[0], color_value_[1], color_value_[2], color_value_[3]); + viz::Text(slider_float_value_, + float(slider_int_value_ + 10) / 20.F, + checkbox_selected_ ? 0.1F : 0.05F, + combo_items[combo_item_]); + viz::EndLayer(); + } + + private: + const int count_; + bool checkbox_selected_ = false; + int combo_item_ = 0; + float slider_float_value_ = 0.F; + int slider_int_value_ = 0; + float color_value_[4]{1.F, 1.F, 1.F, 1.F}; +}; + +int main(int argc, char** argv) { + int count = -1; + + struct option long_options[] = { + {"help", no_argument, 0, 'h'}, {"count", optional_argument, 0, 'c'}, {0, 0, 0, 0}}; + + // parse options + while (true) { + int option_index = 0; + + const int c = getopt_long(argc, argv, "hc:", long_options, &option_index); + + if (c == -1) { break; } + + const std::string argument(optarg ? optarg : ""); + switch (c) { + case 'h': + std::cout << "Holoviz UI" << std::endl + << "Usage: " << argv[0] << " [options]" << std::endl + << "Options:" << std::endl + << " -h, --help Display this information" << std::endl + << " -c , --count execute operators times (default " + "'-1' for unlimited)" + << std::endl; + return 0; + + case 'c': + count = stoi(argument); + break; + + case '?': + // unknown option, error already printed by getop_long + break; + default: + holoscan::log_error("Unhandled option '{}'", static_cast(c)); + } + } + + auto app = holoscan::make_application(count); + app->run(); + + holoscan::log_info("Application has finished running."); + return 0; +} diff --git a/applications/holoviz/holoviz_ui/holoviz_ui.png b/applications/holoviz/holoviz_ui/holoviz_ui.png new file mode 120000 index 000000000..c4149ca65 --- /dev/null +++ b/applications/holoviz/holoviz_ui/holoviz_ui.png @@ -0,0 +1 @@ +../template/cookiecutter-holoviz/holoviz_ui.png \ No newline at end of file diff --git a/applications/holoviz/holoviz_ui/metadata.json b/applications/holoviz/holoviz_ui/metadata.json new file mode 100644 index 000000000..194552b93 --- /dev/null +++ b/applications/holoviz/holoviz_ui/metadata.json @@ -0,0 +1,35 @@ +{ + "application": { + "name": "Holoviz UI", + "authors": [ + { + "name": "Holoscan Team", + "affiliation": "NVIDIA" + } + ], + "language": "C++", + "version": "1.0.0", + "changelog": { + "1.0": "Initial Release" + }, + "holoscan_sdk": { + "minimum_required_version": "2.5", + "tested_versions": [ + "2.5" + ] + }, + "platforms": [ + "amd64", + "arm64" + ], + "tags": [ + "Holoviz UI" + ], + "ranking": 1, + "dependencies": {}, + "run": { + "command": "/holoviz_ui", + "workdir": "holohub_bin" + } + } +} \ No newline at end of file diff --git a/applications/holoviz/holoviz_vsync/holoviz_vsync.cpp b/applications/holoviz/holoviz_vsync/holoviz_vsync.cpp index bc2fd9a7a..31f384299 100644 --- a/applications/holoviz/holoviz_vsync/holoviz_vsync.cpp +++ b/applications/holoviz/holoviz_vsync/holoviz_vsync.cpp @@ -125,6 +125,7 @@ class App : public holoscan::Application { add_flow(source, holoviz, {{"output", "receivers"}}); } + private: const int count_; }; diff --git a/applications/holoviz/holoviz_yuv/holoviz_yuv.cpp b/applications/holoviz/holoviz_yuv/holoviz_yuv.cpp index 671d0fd90..ee1be26c8 100644 --- a/applications/holoviz/holoviz_yuv/holoviz_yuv.cpp +++ b/applications/holoviz/holoviz_yuv/holoviz_yuv.cpp @@ -161,6 +161,7 @@ class App : public holoscan::Application { add_flow(source, holoviz, {{"output", "receivers"}}); } + private: const int count_; }; diff --git a/applications/holoviz/template/cookiecutter-holoviz/cookiecutter.json b/applications/holoviz/template/cookiecutter-holoviz/cookiecutter.json index 0960683b2..9e6db700e 100644 --- a/applications/holoviz/template/cookiecutter-holoviz/cookiecutter.json +++ b/applications/holoviz/template/cookiecutter-holoviz/cookiecutter.json @@ -5,6 +5,12 @@ "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_') }}", "project_description": "Holoviz Boilerplate contains all the boilerplate you need to create a Holoviz example application.", "holoscan_version": "2.3", - "example": [ "sRGB", "vsync", "YUV", "HDR" ], + "example": [ + "HDR", + "sRGB", + "UI", + "vsync", + "YUV" + ], "tags": "" } \ No newline at end of file diff --git a/applications/holoviz/template/cookiecutter-holoviz/holoviz_ui.png b/applications/holoviz/template/cookiecutter-holoviz/holoviz_ui.png new file mode 100644 index 000000000..953e2c6a8 Binary files /dev/null and b/applications/holoviz/template/cookiecutter-holoviz/holoviz_ui.png differ diff --git a/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/CMakeLists.txt b/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/CMakeLists.txt index 99343e7b2..f3c416880 100644 --- a/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/CMakeLists.txt +++ b/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/CMakeLists.txt @@ -28,6 +28,9 @@ target_link_libraries({{ cookiecutter.project_slug }} PRIVATE holoscan::core holoscan::ops::holoviz +{%- if cookiecutter.example == "UI" %} + holoscan::viz::imgui +{%- endif %} ) if(BUILD_TESTING) diff --git a/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/README.md b/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/README.md index 862642980..49cd5bb91 100644 --- a/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/README.md +++ b/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/README.md @@ -46,6 +46,9 @@ By default, the frame buffer is using linear color space. To use the sRGB color Arg("framebuffer_srbg", true)); ``` +{%- elif cookiecutter.example == "UI" %} +This application uses the layer callback provided by the Holoviz operator and leverages the Holoviz module API to add an UI layer with `Dear ImGui` elements and a geometry layer dynamically changing based on user input. + {%- elif cookiecutter.example == "vsync" %} This application demonstrates the capability of the Holoviz operator to wait for the vertical blank of the display before updating the current image. It prints the displayed frames per second to the console, if sync to vertical blank is enabled the frames per second are capped to the display refresh rate. diff --git a/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}.cpp b/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}.cpp index e60195e1e..f442ea2e1 100644 --- a/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}.cpp +++ b/applications/holoviz/template/cookiecutter-holoviz/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}.cpp @@ -29,6 +29,11 @@ #include #include +{%- if cookiecutter.example == "UI" %} +#include +#include + +{%- endif %} {%- if example.print_fps %} #include @@ -297,6 +302,12 @@ class App : public holoscan::Application { {%- if cookiecutter.example == "vsync" %} // enable synchronization to vertical blank Arg("vsync", true), +{%- endif %} +{%- if cookiecutter.example == "UI" %} + // set the layer callback to execute a member function of the App class. + Arg("layer_callback", + ops::HolovizOp::LayerCallbackFunction( + std::bind(&App::layer_callback, this, std::placeholders::_1))), {%- endif %} Arg("window_title", std::string("{{ cookiecutter.project_name }}")), Arg("cuda_stream_pool", make_resource("cuda_stream_pool", 0, 0, 0, 1, 5))); @@ -306,7 +317,55 @@ class App : public holoscan::Application { {%- endraw %} } +{%- if cookiecutter.example == "UI" %} + void layer_callback(const std::vector& inputs) { + using namespace holoscan; + + // The layer callback is executed after the Holoviz operator has finished drawing all layers. We + // now can add our own layers after that. + // For more information on Holoviz layers see + // https://docs.nvidia.com/holoscan/sdk-user-guide/visualization.html#layers. + + // Add a simple UI, Holoviz supports a `Dear ImGui` layer. For more information on `Dear ImGui` + // check https://github.com/ocornut/imgui. + viz::BeginImGuiLayer(); + ImGui::Begin("UI", nullptr, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Checkbox("Checkbox", &checkbox_selected_); + static const char* combo_items[] = { + "Item 1", + "Item 2", + "Item 3", + }; + ImGui::Combo("Combo", &combo_item_, combo_items, IM_ARRAYSIZE(combo_items)); + ImGui::SliderFloat("Slider Float", &slider_float_value_, 0.F, 1.F); + ImGui::SliderInt("Slider Int", &slider_int_value_, -10, 10); + ImGui::Separator(); + ImGui::ColorEdit4("Color", color_value_, ImGuiColorEditFlags_DefaultOptions_); + viz::EndLayer(); + + // Now create a geometry layer using the values of the UI. + viz::BeginGeometryLayer(); + // Draw the text from combo with the color set by the user and the position set by the sliders. + viz::Color(color_value_[0], color_value_[1], color_value_[2], color_value_[3]); + viz::Text(slider_float_value_, + float(slider_int_value_ + 10) / 20.F, + checkbox_selected_ ? 0.1F : 0.05F, + combo_items[combo_item_]); + viz::EndLayer(); + } + +{%- endif %} + +private: const int count_; + +{%- if cookiecutter.example == "UI" %} + bool checkbox_selected_ = false; + int combo_item_ = 0; + float slider_float_value_ = 0.F; + int slider_int_value_ = 0; + float color_value_[4]{1.F, 1.F, 1.F, 1.F}; +{%- endif %} }; int main(int argc, char** argv) { diff --git a/applications/holoviz/template/generate_projects.sh b/applications/holoviz/template/generate_projects.sh index 558a40d1d..56ad5cc4b 100755 --- a/applications/holoviz/template/generate_projects.sh +++ b/applications/holoviz/template/generate_projects.sh @@ -37,5 +37,6 @@ generate() { generate "holoviz_hdr" "HDR" "Holoviz HDR" "tags=,\"BT.2020\",\"ST.2084\",\"EOTF\"" "holoscan_version=2.5" generate "holoviz_srgb" "sRGB" "Holoviz sRGB" +generate "holoviz_ui" "UI" "Holoviz UI" "holoscan_version=2.5" generate "holoviz_vsync" "vsync" "Holoviz vsync" generate "holoviz_yuv" "YUV" "Holoviz YUV" "tags=,\"YCbCr\"" "holoscan_version=2.4" diff --git a/dev_container b/dev_container index 965236cf2..0b5e63250 100755 --- a/dev_container +++ b/dev_container @@ -454,13 +454,14 @@ check_nvidia_ctk() { #=============================================================================== get_host_gpu() { - if lsmod | grep -q nvidia_drm && command -v nvidia-smi >/dev/null; then - echo -n "dgpu" - elif lsmod | grep -q nvgpu; then - echo -n "igpu" + if ! command -v nvidia-smi >/dev/null; then + echo "Could not find any GPU drivers on host. Defaulting build to target dGPU/CPU stack." + echo -n "dgpu"; + elif [[ ! $(nvidia-smi --query-gpu=name --format=csv,noheader) ]] || \ + [[ $(nvidia-smi --query-gpu=name --format=csv,noheader -i 0) =~ "Orin (nvgpu)" ]]; then + echo -n "igpu"; else - c_echo_err Y "Could not find any GPU drivers on host. Defaulting build to target dGPU/CPU stack." - echo -n "dgpu" + echo -n "dgpu"; fi } @@ -485,6 +486,7 @@ Usage: ./dev_container build [options] --img : Specify fully qualified container name --verbose : Print variables passed to docker build command --no-cache : Do not use cache when building the image + --build-args : provides extra arguments to docker build command ' } @@ -498,6 +500,7 @@ build() { local compute_capacity=$(get_compute_capacity) local print_verbose=0 local no_cache= + local extra_args= # Check if buildx exists if ! $(docker buildx version &>/dev/null); then @@ -540,6 +543,16 @@ build() { exit 1 fi ;; + --build-args) + if [[ -n "$2" ]]; then + extra_args+=" $2" + shift 2 + else + echo "Error: --build-args requires a value" + build_desc + exit 1 + fi + ;; --no-cache) no_cache="--no-cache" shift @@ -572,6 +585,7 @@ build() { --build-arg GPU_TYPE=${gpu_type} \ --build-arg COMPUTE_CAPACITY=${compute_capacity} \ --network=host \ + ${extra_args} \ ${no_cache} \ -f ${docker_file_path} \ -t ${img} \ @@ -843,6 +857,7 @@ build_and_run_desc() { c_echo 'Build and run a requested application in a Docker --no_run : Skip launching the application. --install: Install the application into install/ if supported. --build_args : Build the app with additional CMake configuration arguments + --build_docker_args : additional docker arguments when building the container --build_with : List of optional operators that should be built separated by semicolons (;) --run_args : Run the app with additional args --verbose : Print extra output to console @@ -853,7 +868,7 @@ build_and_run_desc() { c_echo 'Build and run a requested application in a Docker build_and_run() { local app_name="" local app_language="" - local container_build_args="" + local container_build_args=() local container_launch_args="" local docker_opts="--entrypoint=bash" local build_app=1 @@ -871,7 +886,7 @@ build_and_run() { case "$1" in --base_img) if [[ -n "$2" && ! "$2" =~ ^-- ]]; then - container_build_args+=" --base_img $2" + container_build_args+=(--base_img $2) shift 2 else echo "Error: --base_img requires a value" @@ -933,6 +948,17 @@ build_and_run() { exit 1 fi ;; + --build_docker_args) + if [[ -n "$2" ]]; then + container_build_args+=(--build-args) + container_build_args+=("$2") + shift 2 + else + echo "Error: --build_docker_args requires a value" + build_and_run_desc + exit 1 + fi + ;; --run_args) if [[ -n "$2" ]]; then extra_run_args="--extra_args '$2'" @@ -952,7 +978,7 @@ build_and_run() { shift 1 ;; --verbose) - container_build_args+=" --verbose" + container_build_args+=(--verbose) container_launch_args+=" --verbose" shift ;; @@ -968,7 +994,7 @@ build_and_run() { ;; --container_args) if [[ -n "$2" ]]; then - docker_opts+=" $2" + docker_opts+="$2" shift 2 else echo "Error: --container_args requires a value" @@ -997,7 +1023,7 @@ build_and_run() { docker_file=$(./run get_app_dockerfile ${app_name} ${app_language:-cpp}) fi if [[ -n "${docker_file}" ]]; then - container_build_args+=" --docker_file ${docker_file}" + container_build_args+=(--docker_file ${docker_file}) fi if [[ -n "${configure_args}" ]]; then @@ -1012,11 +1038,11 @@ build_and_run() { fi fi - container_build_args+=" --img $image_name" + container_build_args+=(--img $image_name) container_launch_args+=" --img $image_name" c_echo "Building application container..." - run_command ${SCRIPT_DIR}/dev_container build $container_build_args + run_command ${SCRIPT_DIR}/dev_container build "${container_build_args[@]}" if [[ $build_app == 1 ]]; then c_echo "Building application..." run_command ${SCRIPT_DIR}/dev_container launch $container_launch_args --docker_opts "$docker_opts" -- -c "./run build $app_name $install $extra_build_with $extra_build_args" @@ -1225,7 +1251,7 @@ vscode() { mkdir -p "${tmpdir}/.devcontainer" cp -arL "${dev_container_path}/devcontainer.json" "${tmpdir}/.devcontainer" sed -i -e "s|\${localWorkspaceFolder}|$(pwd)|g" "${tmpdir}/.devcontainer/devcontainer.json" \ - -e "s|\"\"|$devcontainer_env_file|g" "${tmpdir}/.devcontainer/devcontainer.json" + -e "s|//\"\"|$devcontainer_env_file|g" "${tmpdir}/.devcontainer/devcontainer.json" local hash="$(echo -n "${tmpdir}" | xxd -pu - | tr -d '[:space:]')" local url="vscode://vscode-remote/dev-container+${hash}/workspace/holohub"