/* * SPDX-FileCopyrightText: Copyright (c) 2025 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. */ /** @file common_host.cu * @author Thomas Müller, NVIDIA */ #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION #ifdef __CUDACC__ # ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ # pragma nv_diag_suppress 550 # else # pragma diag_suppress 550 # endif #endif #include #include #ifdef __CUDACC__ # ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ # pragma nv_diag_default 550 # else # pragma diag_default 550 # endif #endif #ifdef _WIN32 # include #else # include # include #endif #undef min #undef max #undef near #undef far namespace ngp { bool is_wsl() { #ifdef _WIN32 return false; #else fs::path path = "/proc/sys/kernel/osrelease"; if (!path.exists()) { return false; } std::ifstream f{native_string(path)}; std::string content((std::istreambuf_iterator(f)), (std::istreambuf_iterator())); return content.find("microsoft") != std::string::npos; #endif } #ifdef _WIN32 std::string utf16_to_utf8(const std::wstring& utf16) { std::string utf8; if (!utf16.empty()) { int size = WideCharToMultiByte(CP_UTF8, 0, &utf16[0], (int)utf16.size(), NULL, 0, NULL, NULL); utf8.resize(size, 0); WideCharToMultiByte(CP_UTF8, 0, &utf16[0], (int)utf16.size(), &utf8[0], size, NULL, NULL); } return utf8; } std::wstring utf8_to_utf16(const std::string& utf8) { std::wstring utf16; if (!utf8.empty()) { int size = MultiByteToWideChar(CP_UTF8, 0, &utf8[0], (int)utf8.size(), NULL, 0); utf16.resize(size, 0); MultiByteToWideChar(CP_UTF8, 0, &utf8[0], (int)utf8.size(), &utf16[0], size); } return utf16; } std::wstring native_string(const fs::path& path) { return path.wstr(); } #else std::string native_string(const fs::path& path) { return path.str(); } #endif fs::path discover_executable_dir() { #ifdef _WIN32 WCHAR path[1024]; if (GetModuleFileNameW(NULL, path, 1024) == 0) { return "."; } return fs::path{std::wstring{path}}.parent_path(); #else char path[PATH_MAX]; ssize_t count = readlink("/proc/self/exe", path, PATH_MAX); if (count == -1) { return "."; } return fs::path{std::string{path}}.parent_path(); #endif } fs::path discover_root_dir() { auto executable_dir = discover_executable_dir(); fs::path exists_in_root_dir = "scripts"; for (const auto& candidate : { fs::path{"."} / exists_in_root_dir, fs::path{".."} / exists_in_root_dir, executable_dir / exists_in_root_dir, executable_dir / ".." / exists_in_root_dir, }) { if (candidate.exists()) { return candidate.parent_path(); } } tlog::warning() << "Could not find root directory."; return "."; } bool ends_with(const std::string& str, const std::string& ending) { if (ending.length() > str.length()) { return false; } return std::equal(std::rbegin(ending), std::rend(ending), std::rbegin(str)); } bool ends_with_case_insensitive(const std::string& str, const std::string& ending) { return ends_with(to_lower(str), to_lower(ending)); } ETestbedMode mode_from_scene(const std::string& scene) { return ETestbedMode::None; } ETestbedMode mode_from_string(const std::string& str) { if (equals_case_insensitive(str, "image")) { return ETestbedMode::Gen3c; } else { return ETestbedMode::None; } } std::string to_string(ETestbedMode mode) { switch (mode) { case ETestbedMode::Gen3c: return "gen3c"; case ETestbedMode::None: return "none"; default: throw std::runtime_error{fmt::format("Can not convert mode {} to string.", (int)mode)}; } } static const stbi_io_callbacks istream_stbi_callbacks = { // Read [](void* context, char* data, int size) { auto stream = reinterpret_cast(context); stream->read(data, size); return (int)stream->gcount(); }, // Seek [](void* context, int size) { reinterpret_cast(context)->seekg(size, std::ios_base::cur); }, // EOF [](void* context) { return (int)!!(*reinterpret_cast(context)); }, }; void istream_stbi_write_func(void* context, void* data, int size) { reinterpret_cast(context)->write(reinterpret_cast(data), size); } uint8_t* load_stbi(const fs::path& path, int* width, int* height, int* comp, int req_comp) { std::ifstream f{native_string(path), std::ios::in | std::ios::binary}; return stbi_load_from_callbacks(&istream_stbi_callbacks, &f, width, height, comp, req_comp); } float* load_stbi_float(const fs::path& path, int* width, int* height, int* comp, int req_comp) { std::ifstream f{native_string(path), std::ios::in | std::ios::binary}; return stbi_loadf_from_callbacks(&istream_stbi_callbacks, &f, width, height, comp, req_comp); } uint16_t* load_stbi_16(const fs::path& path, int* width, int* height, int* comp, int req_comp) { std::ifstream f{native_string(path), std::ios::in | std::ios::binary}; return stbi_load_16_from_callbacks(&istream_stbi_callbacks, &f, width, height, comp, req_comp); } bool is_hdr_stbi(const fs::path& path) { std::ifstream f{native_string(path), std::ios::in | std::ios::binary}; return stbi_is_hdr_from_callbacks(&istream_stbi_callbacks, &f); } int write_stbi(const fs::path& path, int width, int height, int comp, const uint8_t* pixels, int quality) { std::ofstream f{native_string(path), std::ios::out | std::ios::binary}; if (equals_case_insensitive(path.extension(), "jpg") || equals_case_insensitive(path.extension(), "jpeg")) { return stbi_write_jpg_to_func(istream_stbi_write_func, &f, width, height, comp, pixels, quality); } else if (equals_case_insensitive(path.extension(), "png")) { return stbi_write_png_to_func(istream_stbi_write_func, &f, width, height, comp, pixels, width * comp); } else if (equals_case_insensitive(path.extension(), "tga")) { return stbi_write_tga_to_func(istream_stbi_write_func, &f, width, height, comp, pixels); } else if (equals_case_insensitive(path.extension(), "bmp")) { return stbi_write_bmp_to_func(istream_stbi_write_func, &f, width, height, comp, pixels); } else { throw std::runtime_error{fmt::format("write_stbi: unknown image extension '{}'", path.extension())}; } } FILE* native_fopen(const fs::path& path, const char* mode) { #ifdef _WIN32 return _wfopen(path.wstr().c_str(), utf8_to_utf16(mode).c_str()); #else return fopen(path.str().c_str(), mode); #endif } GPUMemory load_stbi_gpu(const fs::path& path, int* width, int* height) { bool is_hdr = is_hdr_stbi(path); void* data; // width * height * RGBA int comp; if (is_hdr) { data = load_stbi_float(path, width, height, &comp, 4); } else { data = load_stbi(path, width, height, &comp, 4); } if (!data) { throw std::runtime_error{std::string{stbi_failure_reason()}}; } ScopeGuard mem_guard{[&]() { stbi_image_free(data); }}; if (*width == 0 || *height == 0) { throw std::runtime_error{"Image has zero pixels."}; } GPUMemory result((*width) * (*height) * 4); if (is_hdr) { result.copy_from_host((float*)data); } else { GPUMemory bytes((*width) * (*height) * 4); bytes.copy_from_host((uint8_t*)data); linear_kernel(from_rgba32, 0, nullptr, (*width) * (*height), bytes.data(), result.data(), false, false, 0); } return result; } std::ostream& operator<<(std::ostream& os, const BoundingBox& bb) { os << "["; os << "min=[" << bb.min.x << "," << bb.min.y << "," << bb.min.z << "], "; os << "max=[" << bb.max.x << "," << bb.max.y << "," << bb.max.z << "]"; os << "]"; return os; } std::ostream& operator<<(std::ostream& os, const Triangle& triangle) { os << "["; os << "a=[" << triangle.a.x << "," << triangle.a.y << "," << triangle.a.z << "], "; os << "b=[" << triangle.b.x << "," << triangle.b.y << "," << triangle.b.z << "], "; os << "c=[" << triangle.c.x << "," << triangle.c.y << "," << triangle.c.z << "]"; os << "]"; return os; } } // namespace ngp