diff --git a/src/build-scripts/ci-test.bash b/src/build-scripts/ci-test.bash index 0d5e8de8a..857e2e35d 100755 --- a/src/build-scripts/ci-test.bash +++ b/src/build-scripts/ci-test.bash @@ -13,6 +13,14 @@ set -ex $OSL_ROOT/bin/testshade --help +echo ; echo "Results of testshade --buildinfo:" +$OSL_ROOT/bin/testshade --buildinfo | tee -a "${GITHUB_STEP_SUMMARY:-/dev/null}" +# Note: the above both echos the --buildinfo output, and also appends it to the +# message that will be visible in the GHA summary log page so it's easy to +# see at a glance which build chain & dependencies were used across all the +# tests. +echo + echo "Parallel test ${CTEST_PARALLEL_LEVEL}" echo "Default timeout ${CTEST_TEST_TIMEOUT}" echo "Test exclusions '${CTEST_EXCLUSIONS}'" diff --git a/src/include/OSL/oslexec.h b/src/include/OSL/oslexec.h index b007d3d4d..6ed814e66 100644 --- a/src/include/OSL/oslexec.h +++ b/src/include/OSL/oslexec.h @@ -392,7 +392,24 @@ class OSLEXECPUBLIC ShadingSystem { /// - `string osl:simd`, `string hw:simd` : A comma-separated list of /// CPU hardware SIMD features. The `osl:simd` is a list of features /// enabled when OSL was built, and `hw:simd` is a list of features - /// detected on the actual hardware at runtime. + /// detected on the actual hardware at runtime. `build:simd` is a + /// synonym for `osl:simd`. + /// + /// - `string build:compiler` : The compiler used to build OSL (for + /// example, "gcc 11.2" or "Apple clang 17.0"). + /// + /// - `string build:platform` : The OS and CPU architecture OSL was + /// built for (for example, "Linux/x86_64" or "MacOS/ARM"). + /// + /// - `string build:dependencies` : A semicolon-separated list of the + /// dependencies examined when OSL was built, each as "name version", + /// or "name NONE" if it was not found or not enabled. + /// + /// - `string build:batched`, `string hw:batched` : A comma-separated + /// list of batched SIMD targets (for example, "b8_AVX2,b16_AVX512"). + /// The `build:batched` lists all targets that were built into OSL, + /// and `hw:batched` lists only those that the hardware we're running + /// on can execute. Both are empty if batched execution was not built. /// /// - `string osl:cuda_version` : The version string of the Cuda version /// OSL is using (empty string if no Cuda support was enabled at build diff --git a/src/liboslexec/CMakeLists.txt b/src/liboslexec/CMakeLists.txt index a72a50aef..5f54a9455 100644 --- a/src/liboslexec/CMakeLists.txt +++ b/src/liboslexec/CMakeLists.txt @@ -540,6 +540,8 @@ foreach(batched_target ${BATCHED_TARGET_LIST}) endforeach(batched_target) +configure_file (buildopts.h.in "${CMAKE_CURRENT_BINARY_DIR}/buildopts.h" @ONLY) + add_library (${local_lib} ${lib_src}) target_include_directories (${local_lib} PUBLIC @@ -547,6 +549,7 @@ target_include_directories (${local_lib} ${IMATH_INCLUDES} PRIVATE "${CMAKE_SOURCE_DIR}/src/liboslcomp" + "${CMAKE_CURRENT_BINARY_DIR}" ${ROBINMAP_INCLUDES} ) diff --git a/src/liboslexec/buildopts.h.in b/src/liboslexec/buildopts.h.in new file mode 100644 index 000000000..bc60b091c --- /dev/null +++ b/src/liboslexec/buildopts.h.in @@ -0,0 +1,11 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +// Build options recorded at configure time, private to liboslexec. + +#pragma once + +// Semicolon-separated list of "Package version" (or "Package NONE") for +// every dependency that checked_find_package looked for. +#define OSL_ALL_BUILD_DEPS_FOUND "@CFP_ALL_BUILD_DEPS_FOUND@" diff --git a/src/liboslexec/shadingsys.cpp b/src/liboslexec/shadingsys.cpp index de265b4f9..2707daada 100644 --- a/src/liboslexec/shadingsys.cpp +++ b/src/liboslexec/shadingsys.cpp @@ -28,6 +28,7 @@ #include #include +#include "buildopts.h" #include "opcolor.h" using namespace OSL; @@ -1585,6 +1586,7 @@ osl_simd_caps() if (OIIO_AVX512CD_ENABLED) caps.emplace_back ("avx512cd"); if (OIIO_AVX512BW_ENABLED) caps.emplace_back ("avx512bw"); if (OIIO_AVX512VL_ENABLED) caps.emplace_back ("avx512vl"); + if (OIIO_SIMD_NEON) caps.emplace_back ("neon"); if (OIIO_FMA_ENABLED) caps.emplace_back ("fma"); if (OIIO_F16C_ENABLED) caps.emplace_back ("f16c"); // if (OIIO_POPCOUNT_ENABLED) caps.emplace_back ("popcnt"); @@ -1594,6 +1596,103 @@ osl_simd_caps() +// Return a string describing the compiler used to build OSL. +// (Modeled on oiio_build_compiler in OIIO's imageio.cpp.) +static std::string +osl_build_compiler() +{ +#if OSL_INTEL_LLVM_COMPILER_VERSION + return fmtformat("Intel icx {}.{}", __clang_major__, __clang_minor__); +#elif OSL_APPLE_CLANG_VERSION + return fmtformat("Apple clang {}.{}", __clang_major__, __clang_minor__); +#elif OSL_CLANG_VERSION + return fmtformat("clang {}.{}", __clang_major__, __clang_minor__); +#elif OSL_GNUC_VERSION + return fmtformat("gcc {}.{}", __GNUC__, __GNUC_MINOR__); +#elif OSL_MSVS_VERSION + return fmtformat("MSVS {}", OSL_MSVS_VERSION); +#else + return "unknown compiler?"; +#endif +} + + + +// Return "OS/arch" for the platform OSL was built for. +// (Modeled on oiio_build_platform in OIIO's imageio.cpp.) +static std::string +osl_build_platform() +{ + std::string platform; +#if defined(__linux__) + platform = "Linux"; +#elif defined(__APPLE__) + platform = "MacOS"; +#elif defined(_WIN32) + platform = "Windows"; +#elif defined(__MINGW32__) + platform = "MinGW"; +#elif defined(__FreeBSD__) + platform = "FreeBSD"; +#else + platform = "UnknownOS"; +#endif + platform += "/"; +#if defined(__x86_64__) || defined(_M_AMD64) + platform += "x86_64"; +#elif defined(__i386__) || defined(_M_IX86) + platform += "i386"; +#elif defined(_M_ARM64) || defined(__aarch64__) || defined(__aarch64) \ + || defined(__ARM_ARCH) + platform += "ARM"; +#else + platform += "unknown arch?"; +#endif + return platform; +} + + + +// Return a comma-separated list of the batched SIMD targets that were +// compiled into OSL (e.g. "b8_AVX2,b16_AVX512"). If hw_only is true, list +// only the ones that the hardware we're running on can execute. +static std::string +osl_batched_targets(bool hw_only) +{ + std::vector targets; + OSL_MAYBE_UNUSED auto add = [&](string_view name, TargetISA isa) { + if (!hw_only || LLVM_Util::supports_isa(isa)) + targets.emplace_back(name); + }; +#ifdef __OSL_SUPPORTS_b4_SSE2 + add("b4_SSE2", TargetISA::x64); +#endif +#ifdef __OSL_SUPPORTS_b8_AVX + add("b8_AVX", TargetISA::AVX); +#endif +#ifdef __OSL_SUPPORTS_b8_AVX2 + add("b8_AVX2", TargetISA::AVX2); +#endif +#ifdef __OSL_SUPPORTS_b8_AVX2_noFMA + add("b8_AVX2_noFMA", TargetISA::AVX2_noFMA); +#endif +#ifdef __OSL_SUPPORTS_b8_AVX512 + add("b8_AVX512", TargetISA::AVX512); +#endif +#ifdef __OSL_SUPPORTS_b8_AVX512_noFMA + add("b8_AVX512_noFMA", TargetISA::AVX512_noFMA); +#endif +#ifdef __OSL_SUPPORTS_b16_AVX512 + add("b16_AVX512", TargetISA::AVX512); +#endif +#ifdef __OSL_SUPPORTS_b16_AVX512_noFMA + add("b16_AVX512_noFMA", TargetISA::AVX512_noFMA); +#endif + return OIIO::Strutil::join(targets, ","); +} + + + bool ShadingSystemImpl::attribute(string_view name, TypeDesc type, const void* val) { @@ -2086,19 +2185,40 @@ ShadingSystemImpl::getattribute(string_view name, TypeDesc type, void* val) == (char*)&colorsystem().colorspace()); return true; } - if (name == "osl:simd" && type == TypeDesc::STRING) { + if ((name == "osl:simd" || name == "build:simd") + && type == TypeDesc::STRING) { *(const char**)val = ustring(osl_simd_caps()).c_str(); return true; } + if (name == "build:compiler" && type == TypeDesc::STRING) { + *(const char**)val = ustring(osl_build_compiler()).c_str(); + return true; + } + if (name == "build:platform" && type == TypeDesc::STRING) { + *(const char**)val = ustring(osl_build_platform()).c_str(); + return true; + } + if (name == "build:dependencies" && type == TypeDesc::STRING) { + *(const char**)val = ustring(OSL_ALL_BUILD_DEPS_FOUND).c_str(); + return true; + } + if (name == "build:batched" && type == TypeDesc::STRING) { + *(const char**)val = ustring(osl_batched_targets(false)).c_str(); + return true; + } + if (name == "hw:batched" && type == TypeDesc::STRING) { + *(const char**)val = ustring(osl_batched_targets(true)).c_str(); + return true; + } if (name == "hw:simd" && type == TypeDesc::STRING) { - return OIIO::getattribute("hw:string", type, val); + return OIIO::getattribute("hw:simd", type, val); } if (name == "osl:cuda_version" && type == TypeDesc::STRING) { - *(const char**)val = OSL_CUDA_VERSION; + *(const char**)val = ustring(OSL_CUDA_VERSION).c_str(); return true; } if (name == "osl:optix_version" && type == TypeDesc::STRING) { - *(const char**)val = OSL_OPTIX_VERSION; + *(const char**)val = ustring(OSL_OPTIX_VERSION).c_str(); return true; } if (name == "osl:dependencies" && type == TypeDesc::STRING) { diff --git a/src/testshade/testshade.cpp b/src/testshade/testshade.cpp index 6ac981221..c71c94ba9 100644 --- a/src/testshade/testshade.cpp +++ b/src/testshade/testshade.cpp @@ -696,6 +696,97 @@ print_info() +// Print the build toolchain, back ends, dependencies, and the hardware +// we're running on. Almost all of it comes from ShadingSystem attributes. +static void +print_build_info() +{ + using OIIO::Strutil::fmt::format; + int columns = OIIO::Sysutil::terminal_columns() - 2; + auto print_wrapped = [&](const std::string& s) { + OIIO::print("{}\n", OIIO::Strutil::wordwrap(s, columns, 4)); + }; + + SimpleRenderer rend; + auto texturesys = TextureSystem::create(); +#if OIIO_TEXTURESYSTEM_CREATE_SHARED + ShadingSystem ss(&rend, texturesys.get(), &rend.errhandler()); +#else + ShadingSystem ss(&rend, texturesys, &rend.errhandler()); +#endif + auto ss_string_attr = [&](string_view name) { + ustring val; + ss.getattribute(name, val); + return val.string(); + }; + + std::string platform = ss_string_attr("build:platform"); + print_wrapped(format("OSL {} | {}", OSL_LIBRARY_VERSION_STRING, platform)); + print_wrapped(format(" Build compiler: {} | C++{}/{}", + ss_string_attr("build:compiler"), OSL_BUILD_CPP, + __cplusplus)); + std::string simd = ss_string_attr("build:simd"); + print_wrapped(format(" HW features enabled at build: {}", + simd.size() ? simd : std::string("no SIMD"))); + + // Back ends: the single-point CPU path is always present; batched and + // OptiX only if they were enabled when OSL was built. + std::vector backends; + size_t slash = platform.find('/'); + backends.push_back(format("{} single", slash != std::string::npos + ? platform.substr(slash + 1) + : platform)); + std::string batched = ss_string_attr("build:batched"); + std::string hw_batched = ss_string_attr("hw:batched"); + if (batched.size()) { + if (hw_batched == batched) + backends.push_back("batched " + batched); + else if (hw_batched.empty()) + backends.push_back( + format("batched {} (none usable on this HW)", batched)); + else + backends.push_back( + format("batched {} (this HW: {})", batched, hw_batched)); + } + if (ss_string_attr("osl:optix_version").size()) + backends.push_back("OptiX"); + print_wrapped(" Back ends: " + OIIO::Strutil::join(backends, ", ")); + + // Note if the OIIO we're running with differs from the one we built + // against. + std::vector deps; + std::string alldeps = ss_string_attr("build:dependencies"); + std::string oiio_runtime = OIIO::get_string_attribute("version"); + for (auto dep : OIIO::Strutil::splitsv(alldeps, ";")) { + std::string d(dep); + if (OIIO::Strutil::starts_with(dep, "OpenImageIO ") + && oiio_runtime != OIIO_VERSION_STRING) + d += format(" (runtime {})", oiio_runtime); + deps.push_back(d); + } + print_wrapped(" Dependencies: " + OIIO::Strutil::join(deps, ", ")); + + std::string hw = format(" Running on: {} cores, {:.1f} GB, SIMD {}", + OIIO::Sysutil::hardware_concurrency(), + OIIO::Sysutil::physical_memory() / double(1 << 30), + ss_string_attr("hw:simd")); +#if OSL_USE_OPTIX + int ndevices = 0, driver = 0; + if (cudaGetDeviceCount(&ndevices) == cudaSuccess && ndevices > 0) { + cudaDeviceProp prop; + cudaDriverGetVersion(&driver); + if (cudaGetDeviceProperties(&prop, 0) == cudaSuccess) + hw += format(", GPU {} (CUDA driver {}.{})", prop.name, + driver / 1000, (driver % 1000) / 10); + } else { + hw += ", no CUDA device"; + } +#endif + print_wrapped(hw); +} + + + static void getargs(int argc, const char* argv[]) { @@ -715,6 +806,9 @@ getargs(int argc, const char* argv[]) .action([&](cspan argv){ stash_shader_arg(argv); }); ap.arg("-v", &verbose) .help("Verbose messages"); + ap.arg("--buildinfo") + .help("Print OSL build information, then exit") + .action([&](cspan){ print_build_info(); exit(EXIT_SUCCESS); }); ap.arg("-t %d:NTHREADS", &num_threads) .help("Set thread count (default = 0: auto-detect #cores)"); ap.arg("--optix", &use_optix)