From 5c23776e7ec3aa308df6335b7c0ad770b5f1e76b Mon Sep 17 00:00:00 2001 From: wuye9036 Date: Sun, 20 Feb 2022 20:47:27 -0800 Subject: [PATCH] Manage dependencies by VCPKG. (#34) Now dependencies are installed and managed by vcpkg. Removed build and install python scripts. Use CMake-Install to generate redistributable binaries. --- .gitattributes | 5 + .gitignore | 15 +- .hgignore | 20 - .hgtags | 24 - 3rd_party/threadpool/boost/threadpool.hpp | 28 + .../boost/threadpool/detail/future.hpp | 215 ++ .../boost/threadpool/detail/locking_ptr.hpp | 85 + .../boost/threadpool/detail/pool_core.hpp | 453 ++++ .../boost/threadpool/detail/scope_guard.hpp | 65 + .../boost/threadpool/detail/worker_thread.hpp | 115 + .../threadpool/boost/threadpool/future.hpp | 144 ++ .../threadpool/boost/threadpool/pool.hpp | 232 ++ .../boost/threadpool/pool_adaptors.hpp | 70 + .../boost/threadpool/scheduling_policies.hpp | 262 ++ .../boost/threadpool/shutdown_policies.hpp | 83 + .../boost/threadpool/size_policies.hpp | 99 + .../boost/threadpool/task_adaptors.hpp | 171 ++ BOOTSTRAP_SAMPLE.bat | 5 - CMakeLists.txt | 76 +- README.md | 38 +- blibs/__init__.py | 0 blibs/benchmark.py | 198 -- blibs/boost_build.py | 38 - blibs/copy.py | 26 - blibs/cpuinfo.py | 2257 ----------------- blibs/deps.py | 177 -- blibs/diagnostic.py | 22 - blibs/download_list.py | 24 - blibs/env.py | 214 -- blibs/fhash.py | 12 - blibs/project.py | 333 --- blibs/util.py | 82 - blibs/vswhere.exe | Bin 458336 -> 0 bytes bootstrap.py | 420 --- build.bat | 28 + cmake/CMakeLists.txt | 80 +- cmake/ConfigBoostLibs.cmake | 66 - cmake/ConfigLLVMLibs.cmake | 24 - cmake/ConfigurePath.cmake | 30 - cmake/MSVC.cmake | 2 +- cmake/SystemInfo.cmake | 74 - cmake/Variables.cmake | 11 - eflib/CMakeLists.txt | 4 +- res/Logo3D.max | Bin 311296 -> 131 bytes resources/astro_boy/astroBoy_walk_Maya.dae | 3 + .../M134 Predator.MESHML.model_bin | 3 + resources/cup/cup.jpg | 3 + resources/cup/cup.mtl | 21 + resources/cup/cup.obj | 3 + .../font/1001fonts-anglicantext-eula.txt | 31 + resources/font/AnglicanText.ttf | Bin 0 -> 92060 bytes resources/font/font_enu.png | Bin 0 -> 12712 bytes resources/morph/Text3D.max | 3 + resources/morph/dst.dae | 3 + resources/morph/src.dae | 3 + resources/sponza_lq/Sponza.mtl | 227 ++ resources/sponza_lq/part_of_sponza.mtl | 44 + resources/sponza_lq/part_of_sponza.obj | 3 + resources/sponza_lq/sponza.obj | 3 + resources/sponza_lq/textures/background.jpg | 3 + .../sponza_lq/textures/background_ddn.jpg | 3 + .../sponza_lq/textures/chain_texture.jpg | 3 + .../sponza_lq/textures/chain_texture_ddn.jpg | 3 + .../sponza_lq/textures/chain_texture_mask.jpg | 3 + resources/sponza_lq/textures/lion.jpg | 3 + resources/sponza_lq/textures/lion2_ddn.jpg | 3 + resources/sponza_lq/textures/lion_ddn.jpg | 3 + .../sponza_lq/textures/spnza_bricks_a_ddn.jpg | 3 + .../textures/spnza_bricks_a_diff.jpg | 3 + .../textures/spnza_bricks_a_spec.jpg | 3 + .../sponza_lq/textures/sponza_arch_ddn.jpg | 3 + .../sponza_lq/textures/sponza_arch_diff.jpg | 3 + .../sponza_lq/textures/sponza_arch_spec.jpg | 3 + .../textures/sponza_ceiling_a_diff.jpg | 3 + .../textures/sponza_ceiling_a_spec.jpg | 3 + .../textures/sponza_column_a_ddn.jpg | 3 + .../textures/sponza_column_a_diff.jpg | 3 + .../textures/sponza_column_a_spec.jpg | 3 + .../textures/sponza_column_b_ddn.jpg | 3 + .../textures/sponza_column_b_diff.jpg | 3 + .../textures/sponza_column_b_spec.jpg | 3 + .../textures/sponza_column_c_ddn.jpg | 3 + .../textures/sponza_column_c_diff.jpg | 3 + .../textures/sponza_column_c_spec.jpg | 3 + .../textures/sponza_curtain_blue_diff.jpg | 3 + .../textures/sponza_curtain_diff.jpg | 3 + .../textures/sponza_curtain_green_diff.jpg | 3 + .../textures/sponza_details_diff.jpg | 3 + .../textures/sponza_details_spec.jpg | 3 + .../textures/sponza_fabric_blue_diff.jpg | 3 + .../sponza_lq/textures/sponza_fabric_diff.jpg | 3 + .../textures/sponza_fabric_green_diff.jpg | 3 + .../sponza_lq/textures/sponza_fabric_spec.jpg | 3 + .../textures/sponza_flagpole_diff.jpg | 3 + .../textures/sponza_flagpole_spec.jpg | 3 + .../textures/sponza_floor_a_diff.jpg | 3 + .../textures/sponza_floor_a_spec.jpg | 3 + .../sponza_lq/textures/sponza_roof_diff.jpg | 3 + .../sponza_lq/textures/sponza_thorn_ddn.jpg | 3 + .../sponza_lq/textures/sponza_thorn_diff.jpg | 3 + .../sponza_lq/textures/sponza_thorn_mask.jpg | 3 + .../sponza_lq/textures/sponza_thorn_spec.jpg | 3 + resources/sponza_lq/textures/vase_ddn.jpg | 3 + resources/sponza_lq/textures/vase_dif.jpg | 3 + resources/sponza_lq/textures/vase_hanging.jpg | 3 + resources/sponza_lq/textures/vase_plant.jpg | 3 + .../sponza_lq/textures/vase_plant_mask.jpg | 3 + .../sponza_lq/textures/vase_plant_spec.jpg | 3 + resources/sponza_lq/textures/vase_round.jpg | 3 + .../sponza_lq/textures/vase_round_ddn.jpg | 3 + .../sponza_lq/textures/vase_round_spec.jpg | 3 + resources/ssm/Draw.saps | 33 + resources/ssm/Draw.savs | 34 + resources/ssm/GenSM.saps | 3 + resources/ssm/GenSM.savs | 6 + resources/texture_and_blending/Dirt.jpg | 3 + resources/texture_and_blending/chessboard.png | Bin 0 -> 193 bytes salviar/CMakeLists.txt | 7 +- salviau/CMakeLists.txt | 37 +- salviax/CMakeLists.txt | 13 +- salviax/src/resource/font/font.cpp | 14 +- .../src/resource/mesh/sa/mesh_io_collada.cpp | 4 +- samples/CMakeLists.txt | 6 + samples/ConfigureSample.cmake | 13 +- samples/Font/CMakeLists.txt | 29 +- sasl/ConfigureSASLProject.cmake | 3 +- sasl/enums/CMakeLists.txt | 5 +- sasl/src/codegen/cg_caster.cpp | 2 + sasl/src/codegen/cg_extension.cpp | 72 +- sasl/src/codegen/cg_intrins.cpp | 67 +- sasl/src/codegen/cg_simd.cpp | 2 +- sasl/src/codegen/cg_vs.cpp | 2 +- sasl/src/codegen/cgs.cpp | 40 +- sasl/src/codegen/ty_cache.cpp | 8 +- sasl/src/command/CMakeLists.txt | 5 +- sasl/src/drivers/CMakeLists.txt | 53 +- sasl/src/host/CMakeLists.txt | 7 +- sasl/test/abi_test/CMakeLists.txt | 9 +- sasl/test/death_test/CMakeLists.txt | 4 +- sasl/test/jit_test/CMakeLists.txt | 17 +- sasl/test/repo/CMakeLists.txt | 10 +- vcpkg.json | 33 + 142 files changed, 3042 insertions(+), 4332 deletions(-) create mode 100644 .gitattributes delete mode 100644 .hgignore delete mode 100644 .hgtags create mode 100644 3rd_party/threadpool/boost/threadpool.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/detail/future.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/detail/locking_ptr.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/detail/pool_core.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/detail/scope_guard.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/detail/worker_thread.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/future.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/pool.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/pool_adaptors.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/scheduling_policies.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/shutdown_policies.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/size_policies.hpp create mode 100644 3rd_party/threadpool/boost/threadpool/task_adaptors.hpp delete mode 100644 BOOTSTRAP_SAMPLE.bat delete mode 100644 blibs/__init__.py delete mode 100644 blibs/benchmark.py delete mode 100644 blibs/boost_build.py delete mode 100644 blibs/copy.py delete mode 100644 blibs/cpuinfo.py delete mode 100644 blibs/deps.py delete mode 100644 blibs/diagnostic.py delete mode 100644 blibs/download_list.py delete mode 100644 blibs/env.py delete mode 100644 blibs/fhash.py delete mode 100644 blibs/project.py delete mode 100644 blibs/util.py delete mode 100644 blibs/vswhere.exe delete mode 100644 bootstrap.py create mode 100644 build.bat delete mode 100644 cmake/ConfigBoostLibs.cmake delete mode 100644 cmake/ConfigLLVMLibs.cmake delete mode 100644 cmake/ConfigurePath.cmake delete mode 100644 cmake/SystemInfo.cmake delete mode 100644 cmake/Variables.cmake create mode 100644 resources/astro_boy/astroBoy_walk_Maya.dae create mode 100644 resources/complex_mesh/M134 Predator.MESHML.model_bin create mode 100644 resources/cup/cup.jpg create mode 100644 resources/cup/cup.mtl create mode 100644 resources/cup/cup.obj create mode 100644 resources/font/1001fonts-anglicantext-eula.txt create mode 100644 resources/font/AnglicanText.ttf create mode 100644 resources/font/font_enu.png create mode 100644 resources/morph/Text3D.max create mode 100644 resources/morph/dst.dae create mode 100644 resources/morph/src.dae create mode 100644 resources/sponza_lq/Sponza.mtl create mode 100644 resources/sponza_lq/part_of_sponza.mtl create mode 100644 resources/sponza_lq/part_of_sponza.obj create mode 100644 resources/sponza_lq/sponza.obj create mode 100644 resources/sponza_lq/textures/background.jpg create mode 100644 resources/sponza_lq/textures/background_ddn.jpg create mode 100644 resources/sponza_lq/textures/chain_texture.jpg create mode 100644 resources/sponza_lq/textures/chain_texture_ddn.jpg create mode 100644 resources/sponza_lq/textures/chain_texture_mask.jpg create mode 100644 resources/sponza_lq/textures/lion.jpg create mode 100644 resources/sponza_lq/textures/lion2_ddn.jpg create mode 100644 resources/sponza_lq/textures/lion_ddn.jpg create mode 100644 resources/sponza_lq/textures/spnza_bricks_a_ddn.jpg create mode 100644 resources/sponza_lq/textures/spnza_bricks_a_diff.jpg create mode 100644 resources/sponza_lq/textures/spnza_bricks_a_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_arch_ddn.jpg create mode 100644 resources/sponza_lq/textures/sponza_arch_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_arch_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_ceiling_a_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_ceiling_a_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_a_ddn.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_a_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_a_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_b_ddn.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_b_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_b_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_c_ddn.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_c_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_column_c_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_curtain_blue_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_curtain_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_curtain_green_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_details_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_details_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_fabric_blue_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_fabric_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_fabric_green_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_fabric_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_flagpole_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_flagpole_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_floor_a_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_floor_a_spec.jpg create mode 100644 resources/sponza_lq/textures/sponza_roof_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_thorn_ddn.jpg create mode 100644 resources/sponza_lq/textures/sponza_thorn_diff.jpg create mode 100644 resources/sponza_lq/textures/sponza_thorn_mask.jpg create mode 100644 resources/sponza_lq/textures/sponza_thorn_spec.jpg create mode 100644 resources/sponza_lq/textures/vase_ddn.jpg create mode 100644 resources/sponza_lq/textures/vase_dif.jpg create mode 100644 resources/sponza_lq/textures/vase_hanging.jpg create mode 100644 resources/sponza_lq/textures/vase_plant.jpg create mode 100644 resources/sponza_lq/textures/vase_plant_mask.jpg create mode 100644 resources/sponza_lq/textures/vase_plant_spec.jpg create mode 100644 resources/sponza_lq/textures/vase_round.jpg create mode 100644 resources/sponza_lq/textures/vase_round_ddn.jpg create mode 100644 resources/sponza_lq/textures/vase_round_spec.jpg create mode 100644 resources/ssm/Draw.saps create mode 100644 resources/ssm/Draw.savs create mode 100644 resources/ssm/GenSM.saps create mode 100644 resources/ssm/GenSM.savs create mode 100644 resources/texture_and_blending/Dirt.jpg create mode 100644 resources/texture_and_blending/chessboard.png create mode 100644 vcpkg.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..ba23c0dae --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +*.model_bin filter=lfs diff=lfs merge=lfs -text +*.dae filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.obj filter=lfs diff=lfs merge=lfs -text +*.max filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 8518d3723..010760cb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,5 @@ -downloads/ -3rd_party/ -resources/ -build/ -bin/ -lib/ -*.pyc -proj.py -*.suo -salvia-git.pyproj -salvia-git.sln test_main.cpp -PerformanceAnalysis/ -Advisor/ .idea/ .vscode/ +*.suo +*.pyc diff --git a/.hgignore b/.hgignore deleted file mode 100644 index c308f848f..000000000 --- a/.hgignore +++ /dev/null @@ -1,20 +0,0 @@ -syntax: glob -build -sasl/enums/compiler_infos.cmake.rule -sasl/enums/enums.cmake.rule -bin -lib -sasl/test/auto_test/src/config.py -sasl/test/auto_test/src/config.pyc -sasl/test/cgllvm_test/test_main.cpp -sasl/test/parser_test/test_main.cpp -sasl/test/semantic_test/test_main.cpp -sasl/test/st_combinator_test/test_main.cpp -sasl/test/regression_test/test_main.cpp -doc/roadmap.mpp -*.pyc -proj.py -build.log -bin_release -.idea -*.pyo \ No newline at end of file diff --git a/.hgtags b/.hgtags deleted file mode 100644 index e1abcb99f..000000000 --- a/.hgtags +++ /dev/null @@ -1,24 +0,0 @@ -0ef76592c01961414205e4bf493ac4b9c44c3a16 0.5.1 -0faca7787b93b104e6075cacc3577c4089583121 0.2.1 -113576ef3b56d2c7676d5a67f568c6983f391e01 0.5.6 -1c62ebaa2d2231a4c3891aa76ec3f6180503544b 0.2.0 -24d2cc08487a1eb3588846ab127cc5fa22c1856d 0.2.4 -33f96fd1bcc6c6d94533f1b29f43e5008744fcf0 0.2.3 -3784e7b0f4312f280b10e0d5e87e36fc17d3f61c 0.5.2 -3b700554327ed722effad6f8534cb70106566b89 0.3.3 -3d29ed772c15bb97c40c7c3255ee8bac25f73a50 0.5.7 -4f92999511396838c685acf9b528126bcd1e1c62 0.2.2 -50ebb219027aa5c5ea667a4debaf9ba28edcb3ca 0.3.1 -5f9694575508642627e6dcec127d3aec29d4042a 0.3.2 -6f50b3502f14347066a2723f0e72be6060d008f0 0.3 -725e6068e7f0af0846c483e69433614fac259bc8 0.3.6 -731334f7ff782780d1c5b9f63ecd192621a793ac 0.1.1 -74c89970ca71920a287864a0ca2c3b988595b458 0.2.6 -8b97a5277d2e0732b72459ab7899b0d17b80aaae 0.5.4 -a3daf9642ec037aadd60b459e6ff980cd6f28574 0.5.5 -bd9c8d3b0ec6681beba6f8ed5a9cee5ec68617f7 0.1.2 -c2717e3faf52a3cceded44007cd416fc9020aaee 0.5.3 -d61261d6ba6816f3502a594282708e530ab8ff3d 0.2.5 -d90c4a80b5654360d0db0edb7cb75477027622d1 0.1 -eff58e20aa88a424a1d6a07cc6066af017cb614d 0.3.4 -f88e87f666de189579509d99c170941cd6ad549f 0.3.5 diff --git a/3rd_party/threadpool/boost/threadpool.hpp b/3rd_party/threadpool/boost/threadpool.hpp new file mode 100644 index 000000000..7d9ee4801 --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool.hpp @@ -0,0 +1,28 @@ +/*! \file +* \brief Main include. +* +* This is the only file you have to include in order to use the +* complete threadpool library. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + +#ifndef THREADPOOL_HPP_INCLUDED +#define THREADPOOL_HPP_INCLUDED + +#include "./threadpool/future.hpp" +#include "./threadpool/pool.hpp" + +#include "./threadpool/pool_adaptors.hpp" +#include "./threadpool/task_adaptors.hpp" + + +#endif // THREADPOOL_HPP_INCLUDED + diff --git a/3rd_party/threadpool/boost/threadpool/detail/future.hpp b/3rd_party/threadpool/boost/threadpool/detail/future.hpp new file mode 100644 index 000000000..4e4b35beb --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/detail/future.hpp @@ -0,0 +1,215 @@ +/*! \file +* \brief TODO. +* +* TODO. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_DETAIL_FUTURE_IMPL_HPP_INCLUDED +#define THREADPOOL_DETAIL_FUTURE_IMPL_HPP_INCLUDED + + +#include "locking_ptr.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace threadpool { namespace detail +{ + +template +class future_impl +{ +public: + typedef Result const & result_type; //!< Indicates the functor's result type. + + typedef Result future_result_type; //!< Indicates the future's result type. + typedef future_impl future_type; + +private: + volatile bool m_ready; + volatile future_result_type m_result; + + mutable mutex m_monitor; + mutable condition m_condition_ready; + + volatile bool m_is_cancelled; + volatile bool m_executing; + +public: + + +public: + + future_impl() + : m_ready(false) + , m_is_cancelled(false) + { + } + + bool ready() const volatile + { + return m_ready; + } + + void wait() const volatile + { + const future_type* self = const_cast(this); + mutex::scoped_lock lock(self->m_monitor); + + while(!m_ready) + { + self->m_condition_ready.wait(lock); + } + } + + + bool timed_wait(boost::xtime const & timestamp) const + { + const future_type* self = const_cast(this); + mutex::scoped_lock lock(self->m_monitor); + + while(!m_ready) + { + if(!self->m_condition_ready.timed_wait(lock, timestamp)) return false; + } + + return true; + } + + + result_type operator()() const volatile + { + wait(); +/* + if( throw_exception_ != 0 ) + { + throw_exception_( this ); + } +*/ + + return *(const_cast(&m_result)); + } + + + void set_value(future_result_type const & r) volatile + { + locking_ptr lockedThis(*this, m_monitor); + if(!m_ready && !m_is_cancelled) + { + lockedThis->m_result = r; + lockedThis->m_ready = true; + lockedThis->m_condition_ready.notify_all(); + } + } +/* + template void set_exception() // throw() + { + m_impl->template set_exception(); + } + + template void set_exception( char const * what ) // throw() + { + m_impl->template set_exception( what ); + } + */ + + + bool cancel() volatile + { + if(!m_ready || m_executing) + { + m_is_cancelled = true; + return true; + } + else + { + return false; + } + } + + + bool is_cancelled() const volatile + { + return m_is_cancelled; + } + + + void set_execution_status(bool executing) volatile + { + m_executing = executing; + } +}; + + +template< + template class Future, + typename Function +> +class future_impl_task_func +{ + +public: + typedef void result_type; //!< Indicates the functor's result type. + + typedef Function function_type; //!< Indicates the function's type. + typedef typename result_of::type future_result_type; //!< Indicates the future's result type. + typedef Future future_type; //!< Indicates the future's type. + + // The task is required to be a nullary function. + BOOST_STATIC_ASSERT(function_traits::arity == 0); + + // The task function's result type is required not to be void. + BOOST_STATIC_ASSERT(!is_void::value); + +private: + function_type m_function; + shared_ptr m_future; + +public: + future_impl_task_func(function_type const & function, shared_ptr const & future) + : m_function(function) + , m_future(future) + { + } + + void operator()() + { + if(m_function) + { + m_future->set_execution_status(true); + if(!m_future->is_cancelled()) + { + // TODO future exeception handling + m_future->set_value(m_function()); + } + m_future->set_execution_status(false); // TODO consider exceptions + } + } + +}; + + + + + +} } } // namespace boost::threadpool::detail + +#endif // THREADPOOL_DETAIL_FUTURE_IMPL_HPP_INCLUDED + + diff --git a/3rd_party/threadpool/boost/threadpool/detail/locking_ptr.hpp b/3rd_party/threadpool/boost/threadpool/detail/locking_ptr.hpp new file mode 100644 index 000000000..57ba560b5 --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/detail/locking_ptr.hpp @@ -0,0 +1,85 @@ +/*! \file +* \brief The locking_ptr is smart pointer with a scoped locking mechanism. +* +* The class is a wrapper for a volatile pointer. It enables synchronized access to the +* internal pointer by locking the passed mutex. +* locking_ptr is based on Andrei Alexandrescu's LockingPtr. For more information +* see article "volatile - Multithreaded Programmer's Best Friend" by A. Alexandrescu. +* +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_DETAIL_LOCKING_PTR_HPP_INCLUDED +#define THREADPOOL_DETAIL_LOCKING_PTR_HPP_INCLUDED + +#include +#include + + +namespace boost { namespace threadpool { namespace detail +{ + +/*! \brief Smart pointer with a scoped locking mechanism. + * + * This class is a wrapper for a volatile pointer. It enables synchronized access to the + * internal pointer by locking the passed mutex. + */ + template + class locking_ptr + : private noncopyable + { + T* m_obj; //!< The instance pointer. + Mutex & m_mutex; //!< Mutex is used for scoped locking. + + public: + /// Constructor. + locking_ptr(volatile T& obj, const volatile Mutex& mtx) + : m_obj(const_cast(&obj)) + , m_mutex(*const_cast(&mtx)) + { + // Lock mutex + m_mutex.lock(); + } + + + /// Destructor. + ~locking_ptr() + { + // Unlock mutex + m_mutex.unlock(); + } + + + /*! Returns a reference to the stored instance. + * \return The instance's reference. + */ + T& operator*() const + { + return *m_obj; + } + + + /*! Returns a pointer to the stored instance. + * \return The instance's pointer. + */ + T* operator->() const + { + return m_obj; + } + }; + + +} } } // namespace boost::threadpool::detail + + +#endif // THREADPOOL_DETAIL_LOCKING_PTR_HPP_INCLUDED + diff --git a/3rd_party/threadpool/boost/threadpool/detail/pool_core.hpp b/3rd_party/threadpool/boost/threadpool/detail/pool_core.hpp new file mode 100644 index 000000000..8c72803df --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/detail/pool_core.hpp @@ -0,0 +1,453 @@ +/*! \file +* \brief Thread pool core. +* +* This file contains the threadpool's core class: pool. +* +* Thread pools are a mechanism for asynchronous and parallel processing +* within the same process. The pool class provides a convenient way +* for dispatching asynchronous tasks as functions objects. The scheduling +* of these tasks can be easily controlled by using customized schedulers. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_POOL_CORE_HPP_INCLUDED +#define THREADPOOL_POOL_CORE_HPP_INCLUDED + + + + +#include "locking_ptr.hpp" +#include "worker_thread.hpp" + +#include "../task_adaptors.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/// The namespace threadpool contains a thread pool and related utility classes. +namespace boost { namespace threadpool { namespace detail +{ + + /*! \brief Thread pool. + * + * Thread pools are a mechanism for asynchronous and parallel processing + * within the same process. The pool class provides a convenient way + * for dispatching asynchronous tasks as functions objects. The scheduling + * of these tasks can be easily controlled by using customized schedulers. + * A task must not throw an exception. + * + * A pool_impl is DefaultConstructible and NonCopyable. + * + * \param Task A function object which implements the operator 'void operator() (void) const'. The operator () is called by the pool to execute the task. Exceptions are ignored. + * \param Scheduler A task container which determines how tasks are scheduled. It is guaranteed that this container is accessed only by one thread at a time. The scheduler shall not throw exceptions. + * + * \remarks The pool class is thread-safe. + * + * \see Tasks: task_func, prio_task_func + * \see Scheduling policies: fifo_scheduler, lifo_scheduler, prio_scheduler + */ + template < + typename Task, + + template class SchedulingPolicy, + template class SizePolicy, + template class SizePolicyController, + template class ShutdownPolicy + > + class pool_core + : public enable_shared_from_this< pool_core > + , private noncopyable + { + + public: // Type definitions + typedef Task task_type; //!< Indicates the task's type. + typedef SchedulingPolicy scheduler_type; //!< Indicates the scheduler's type. + typedef pool_core pool_type; //!< Indicates the thread pool's type. + typedef SizePolicy size_policy_type; //!< Indicates the sizer's type. + //typedef typename size_policy_type::size_controller size_controller_type; + + typedef SizePolicyController size_controller_type; + +// typedef SizePolicy::size_controller size_controller_type; + typedef ShutdownPolicy shutdown_policy_type;//!< Indicates the shutdown policy's type. + + typedef worker_thread worker_type; + + // The task is required to be a nullary function. + BOOST_STATIC_ASSERT(function_traits::arity == 0); + + // The task function's result type is required to be void. + BOOST_STATIC_ASSERT(is_void::type >::value); + + + private: // Friends + friend class worker_thread; + +#if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x580) // Tested with CC: Sun C++ 5.8 Patch 121018-08 2006/12/06 + friend class SizePolicy; + friend class ShutdownPolicy; +#else + friend class SizePolicy; + friend class ShutdownPolicy; +#endif + + private: // The following members may be accessed by _multiple_ threads at the same time: + volatile size_t m_worker_count; + volatile size_t m_target_worker_count; + volatile size_t m_active_worker_count; + + + + private: // The following members are accessed only by _one_ thread at the same time: + scheduler_type m_scheduler; + scoped_ptr m_size_policy; // is never null + + bool m_terminate_all_workers; // Indicates if termination of all workers was triggered. + std::vector > m_terminated_workers; // List of workers which are terminated but not fully destructed. + + private: // The following members are implemented thread-safe: + mutable recursive_mutex m_monitor; + mutable condition m_worker_idle_or_terminated_event; // A worker is idle or was terminated. + mutable condition m_task_or_terminate_workers_event; // Task is available OR total worker count should be reduced. + + public: + /// Constructor. + pool_core() + : m_worker_count(0) + , m_target_worker_count(0) + , m_active_worker_count(0) + , m_terminate_all_workers(false) + { + pool_type volatile & self_ref = *this; + m_size_policy.reset(new size_policy_type(self_ref)); + + m_scheduler.clear(); + } + + + /// Destructor. + ~pool_core() + { + } + + /*! Gets the size controller which manages the number of threads in the pool. + * \return The size controller. + * \see SizePolicy + */ + size_controller_type size_controller() + { + return size_controller_type(*m_size_policy, this->shared_from_this()); + } + + /*! Gets the number of threads in the pool. + * \return The number of threads. + */ + size_t size() const volatile + { + return m_worker_count; + } + +// TODO is only called once + void shutdown() + { + ShutdownPolicy::shutdown(*this); + } + + /*! Schedules a task for asynchronous execution. The task will be executed once only. + * \param task The task function object. It should not throw execeptions. + * \return true, if the task could be scheduled and false otherwise. + */ + bool schedule(task_type const & task) volatile + { + locking_ptr lockedThis(*this, m_monitor); + + if(lockedThis->m_scheduler.push(task)) + { + lockedThis->m_task_or_terminate_workers_event.notify_one(); + return true; + } + else + { + return false; + } + } + + + /*! Returns the number of tasks which are currently executed. + * \return The number of active tasks. + */ + size_t active() const volatile + { + return m_active_worker_count; + } + + + /*! Returns the number of tasks which are ready for execution. + * \return The number of pending tasks. + */ + size_t pending() const volatile + { + locking_ptr lockedThis(*this, m_monitor); + return lockedThis->m_scheduler.size(); + } + + + /*! Removes all pending tasks from the pool's scheduler. + */ + void clear() volatile + { + locking_ptr lockedThis(*this, m_monitor); + lockedThis->m_scheduler.clear(); + } + + + /*! Indicates that there are no tasks pending. + * \return true if there are no tasks ready for execution. + * \remarks This function is more efficient that the check 'pending() == 0'. + */ + bool empty() const volatile + { + locking_ptr lockedThis(*this, m_monitor); + return lockedThis->m_scheduler.empty(); + } + + + /*! The current thread of execution is blocked until the sum of all active + * and pending tasks is equal or less than a given threshold. + * \param task_threshold The maximum number of tasks in pool and scheduler. + */ + void wait(size_t const task_threshold = 0) const volatile + { + const pool_type* self = const_cast(this); + recursive_mutex::scoped_lock lock(self->m_monitor); + + if(0 == task_threshold) + { + while(0 != self->m_active_worker_count || !self->m_scheduler.empty()) + { + self->m_worker_idle_or_terminated_event.wait(lock); + } + } + else + { + while(task_threshold < self->m_active_worker_count + self->m_scheduler.size()) + { + self->m_worker_idle_or_terminated_event.wait(lock); + } + } + } + + /*! The current thread of execution is blocked until the timestamp is met + * or the sum of all active and pending tasks is equal or less + * than a given threshold. + * \param timestamp The time when function returns at the latest. + * \param task_threshold The maximum number of tasks in pool and scheduler. + * \return true if the task sum is equal or less than the threshold, false otherwise. + */ + bool wait(xtime const & timestamp, size_t const task_threshold = 0) const volatile + { + const pool_type* self = const_cast(this); + recursive_mutex::scoped_lock lock(self->m_monitor); + + if(0 == task_threshold) + { + while(0 != self->m_active_worker_count || !self->m_scheduler.empty()) + { + if(!self->m_worker_idle_or_terminated_event.timed_wait(lock, timestamp)) return false; + } + } + else + { + while(task_threshold < self->m_active_worker_count + self->m_scheduler.size()) + { + if(!self->m_worker_idle_or_terminated_event.timed_wait(lock, timestamp)) return false; + } + } + + return true; + } + + + private: + + + void terminate_all_workers(bool const wait) volatile + { + pool_type* self = const_cast(this); + recursive_mutex::scoped_lock lock(self->m_monitor); + + self->m_terminate_all_workers = true; + + m_target_worker_count = 0; + self->m_task_or_terminate_workers_event.notify_all(); + + if(wait) + { + while(m_active_worker_count > 0) + { + self->m_worker_idle_or_terminated_event.wait(lock); + } + + for(typename std::vector >::iterator it = self->m_terminated_workers.begin(); + it != self->m_terminated_workers.end(); + ++it) + { + (*it)->join(); + } + self->m_terminated_workers.clear(); + } + } + + + /*! Changes the number of worker threads in the pool. The resizing + * is handled by the SizePolicy. + * \param threads The new number of worker threads. + * \return true, if pool will be resized and false if not. + */ + bool resize(size_t const worker_count) volatile + { + locking_ptr lockedThis(*this, m_monitor); + + if(!m_terminate_all_workers) + { + m_target_worker_count = worker_count; + } + else + { + return false; + } + + + if(m_worker_count <= m_target_worker_count) + { // increase worker count + while(m_worker_count < m_target_worker_count) + { + try + { + worker_thread::create_and_attach(lockedThis->shared_from_this()); + m_worker_count++; + m_active_worker_count++; + } + catch(thread_resource_error) + { + return false; + } + } + } + else + { // decrease worker count + lockedThis->m_task_or_terminate_workers_event.notify_all(); // TODO: Optimize number of notified workers + } + + return true; + } + + + // worker died with unhandled exception + void worker_died_unexpectedly(shared_ptr worker) volatile + { + locking_ptr lockedThis(*this, m_monitor); + + m_worker_count--; + m_active_worker_count--; + lockedThis->m_worker_idle_or_terminated_event.notify_all(); + + if(m_terminate_all_workers) + { + lockedThis->m_terminated_workers.push_back(worker); + } + else + { + lockedThis->m_size_policy->worker_died_unexpectedly(m_worker_count); + } + } + + void worker_destructed(shared_ptr worker) volatile + { + locking_ptr lockedThis(*this, m_monitor); + m_worker_count--; + m_active_worker_count--; + lockedThis->m_worker_idle_or_terminated_event.notify_all(); + + if(m_terminate_all_workers) + { + lockedThis->m_terminated_workers.push_back(worker); + } + } + + + bool execute_task() volatile + { + function0 task; + + { // fetch task + pool_type* lockedThis = const_cast(this); + recursive_mutex::scoped_lock lock(lockedThis->m_monitor); + + // decrease number of threads if necessary + if(m_worker_count > m_target_worker_count) + { + return false; // terminate worker + } + + + // wait for tasks + while(lockedThis->m_scheduler.empty()) + { + // decrease number of workers if necessary + if(m_worker_count > m_target_worker_count) + { + return false; // terminate worker + } + else + { + m_active_worker_count--; + lockedThis->m_worker_idle_or_terminated_event.notify_all(); + lockedThis->m_task_or_terminate_workers_event.wait(lock); + m_active_worker_count++; + } + } + + task = lockedThis->m_scheduler.top(); + lockedThis->m_scheduler.pop(); + } + + // call task function + if(task) + { + task(); + } + + //guard->disable(); + return true; + } + }; + + + + +} } } // namespace boost::threadpool::detail + +#endif // THREADPOOL_POOL_CORE_HPP_INCLUDED diff --git a/3rd_party/threadpool/boost/threadpool/detail/scope_guard.hpp b/3rd_party/threadpool/boost/threadpool/detail/scope_guard.hpp new file mode 100644 index 000000000..68634654b --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/detail/scope_guard.hpp @@ -0,0 +1,65 @@ +/*! \file +* \brief TODO. +* +* TODO. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_DETAIL_SCOPE_GUARD_HPP_INCLUDED +#define THREADPOOL_DETAIL_SCOPE_GUARD_HPP_INCLUDED + + + +#include + + +namespace boost { namespace threadpool { namespace detail +{ + +// TODO documentation +class scope_guard +: private boost::noncopyable +{ + function0 const m_function; + bool m_is_active; + +public: + scope_guard(function0 const & call_on_exit) + : m_function(call_on_exit) + , m_is_active(true) + { + } + + ~scope_guard() + { + if(m_is_active && m_function) + { + m_function(); + } + } + + void disable() + { + m_is_active = false; + } +}; + + + + + + +} } } // namespace boost::threadpool::detail + +#endif // THREADPOOL_DETAIL_SCOPE_GUARD_HPP_INCLUDED + + diff --git a/3rd_party/threadpool/boost/threadpool/detail/worker_thread.hpp b/3rd_party/threadpool/boost/threadpool/detail/worker_thread.hpp new file mode 100644 index 000000000..d33467a52 --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/detail/worker_thread.hpp @@ -0,0 +1,115 @@ +/*! \file +* \brief Thread pool worker. +* +* The worker thread instance is attached to a pool +* and executes tasks of this pool. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + +#ifndef THREADPOOL_DETAIL_WORKER_THREAD_HPP_INCLUDED +#define THREADPOOL_DETAIL_WORKER_THREAD_HPP_INCLUDED + + +#include "scope_guard.hpp" + +#include +#include +#include +#include +#include + + +namespace boost { namespace threadpool { namespace detail +{ + + /*! \brief Thread pool worker. + * + * A worker_thread represents a thread of execution. The worker is attached to a + * thread pool and processes tasks of that pool. The lifetime of the worker and its + * internal boost::thread is managed automatically. + * + * This class is a helper class and cannot be constructed or accessed directly. + * + * \see pool_core + */ + template + class worker_thread + : public enable_shared_from_this< worker_thread > + , private noncopyable + { + public: + typedef Pool pool_type; //!< Indicates the pool's type. + + private: + shared_ptr m_pool; //!< Pointer to the pool which created the worker. + shared_ptr m_thread; //!< Pointer to the thread which executes the run loop. + + + /*! Constructs a new worker. + * \param pool Pointer to it's parent pool. + * \see function create_and_attach + */ + worker_thread(shared_ptr const & pool) + : m_pool(pool) + { + assert(pool); + } + + + /*! Notifies that an exception occurred in the run loop. + */ + void died_unexpectedly() + { + m_pool->worker_died_unexpectedly(this->shared_from_this()); + } + + + public: + /*! Executes pool's tasks sequentially. + */ + void run() + { + scope_guard notify_exception(bind(&worker_thread::died_unexpectedly, this)); + + while(m_pool->execute_task()) {} + + notify_exception.disable(); + m_pool->worker_destructed(this->shared_from_this()); + } + + + /*! Joins the worker's thread. + */ + void join() + { + m_thread->join(); + } + + + /*! Constructs a new worker thread and attaches it to the pool. + * \param pool Pointer to the pool. + */ + static void create_and_attach(shared_ptr const & pool) + { + shared_ptr worker(new worker_thread(pool)); + if(worker) + { + worker->m_thread.reset(new boost::thread(bind(&worker_thread::run, worker))); + } + } + + }; + + +} } } // namespace boost::threadpool::detail + +#endif // THREADPOOL_DETAIL_WORKER_THREAD_HPP_INCLUDED + diff --git a/3rd_party/threadpool/boost/threadpool/future.hpp b/3rd_party/threadpool/boost/threadpool/future.hpp new file mode 100644 index 000000000..f4a6e1224 --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/future.hpp @@ -0,0 +1,144 @@ +/*! \file +* \brief TODO. +* +* TODO. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + +#ifndef THREADPOOL_FUTURE_HPP_INCLUDED +#define THREADPOOL_FUTURE_HPP_INCLUDED + + + +#include "./detail/future.hpp" +#include + +//#include "pool.hpp" +//#include + +//#include + + +namespace boost { namespace threadpool +{ + + /*! \brief Experimental. Do not use in production code. TODO. + * + * TODO Future + * + * \see TODO + * + */ + + +template +class future +{ +private: + shared_ptr > m_impl; + +public: + typedef Result const & result_type; //!< Indicates the functor's result type. + typedef Result future_result_type; //!< Indicates the future's result type. + + +public: + + future() + : m_impl(new detail::future_impl()) // TODO remove this + { + } + + // only for internal usage + future(shared_ptr > const & impl) + : m_impl(impl) + { + } + + bool ready() const + { + return m_impl->ready(); + } + + void wait() const + { + m_impl->wait(); + } + + bool timed_wait(boost::xtime const & timestamp) const + { + return m_impl->timed_wait(timestamp); + } + + result_type operator()() // throw( thread::cancelation_exception, ... ) + { + return (*m_impl)(); + } + + result_type get() // throw( thread::cancelation_exception, ... ) + { + return (*m_impl)(); + } + + bool cancel() + { + return m_impl->cancel(); + } + + bool is_cancelled() const + { + return m_impl->is_cancelled(); + } +}; + + + + + +template +typename disable_if < + is_void< typename result_of< Function() >::type >, + future< typename result_of< Function() >::type > +>::type +schedule(Pool& pool, const Function& task) +{ + typedef typename result_of< Function() >::type future_result_type; + + // create future impl and future + shared_ptr > impl(new detail::future_impl); + future res(impl); + + // schedule future impl + pool.schedule(detail::future_impl_task_func(task, impl)); + + // return future + return res; + +/* + TODO + if(pool->schedule(bind(&Future::run, future))) + { + return future; + } + else + { + // construct empty future + return error_future; + } + */ +} + + + +} } // namespace boost::threadpool + +#endif // THREADPOOL_FUTURE_HPP_INCLUDED + diff --git a/3rd_party/threadpool/boost/threadpool/pool.hpp b/3rd_party/threadpool/boost/threadpool/pool.hpp new file mode 100644 index 000000000..a4b6676ea --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/pool.hpp @@ -0,0 +1,232 @@ +/*! \file +* \brief Thread pool core. +* +* This file contains the threadpool's core class: pool. +* +* Thread pools are a mechanism for asynchronous and parallel processing +* within the same process. The pool class provides a convenient way +* for dispatching asynchronous tasks as functions objects. The scheduling +* of these tasks can be easily controlled by using customized schedulers. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_POOL_HPP_INCLUDED +#define THREADPOOL_POOL_HPP_INCLUDED + +#include + +#include "./detail/pool_core.hpp" + +#include "task_adaptors.hpp" + +#include "./detail/locking_ptr.hpp" + +#include "scheduling_policies.hpp" +#include "size_policies.hpp" +#include "shutdown_policies.hpp" + + + +/// The namespace threadpool contains a thread pool and related utility classes. +namespace boost { namespace threadpool +{ + + + + /*! \brief Thread pool. + * + * Thread pools are a mechanism for asynchronous and parallel processing + * within the same process. The pool class provides a convenient way + * for dispatching asynchronous tasks as functions objects. The scheduling + * of these tasks can be easily controlled by using customized schedulers. + * A task must not throw an exception. + * + * A pool is DefaultConstructible, CopyConstructible and Assignable. + * It has reference semantics; all copies of the same pool are equivalent and interchangeable. + * All operations on a pool except assignment are strongly thread safe or sequentially consistent; + * that is, the behavior of concurrent calls is as if the calls have been issued sequentially in an unspecified order. + * + * \param Task A function object which implements the operator 'void operator() (void) const'. The operator () is called by the pool to execute the task. Exceptions are ignored. + * \param SchedulingPolicy A task container which determines how tasks are scheduled. It is guaranteed that this container is accessed only by one thread at a time. The scheduler shall not throw exceptions. + * + * \remarks The pool class is thread-safe. + * + * \see Tasks: task_func, prio_task_func + * \see Scheduling policies: fifo_scheduler, lifo_scheduler, prio_scheduler + */ + template < + typename Task = task_func, + template class SchedulingPolicy = fifo_scheduler, + template class SizePolicy = static_size, + template class SizePolicyController = resize_controller, + template class ShutdownPolicy = wait_for_all_tasks + > + class thread_pool + { + typedef detail::pool_core pool_core_type; + shared_ptr m_core; // pimpl idiom + shared_ptr m_shutdown_controller; // If the last pool holding a pointer to the core is deleted the controller shuts the pool down. + + public: // Type definitions + typedef Task task_type; //!< Indicates the task's type. + typedef SchedulingPolicy scheduler_type; //!< Indicates the scheduler's type. + /* typedef thread_pool pool_type; //!< Indicates the thread pool's type. + */ + typedef SizePolicy size_policy_type; + typedef SizePolicyController size_controller_type; + + + public: + /*! Constructor. + * \param initial_threads The pool is immediately resized to set the specified number of threads. The pool's actual number threads depends on the SizePolicy. + */ + thread_pool(size_t initial_threads = 0) + : m_core(new pool_core_type) + , m_shutdown_controller(static_cast(0), bind(&pool_core_type::shutdown, m_core)) + { + size_policy_type::init(*m_core, initial_threads); + } + + + /*! Gets the size controller which manages the number of threads in the pool. + * \return The size controller. + * \see SizePolicy + */ + size_controller_type size_controller() + { + return m_core->size_controller(); + } + + + /*! Gets the number of threads in the pool. + * \return The number of threads. + */ + size_t size() const + { + return m_core->size(); + } + + + /*! Schedules a task for asynchronous execution. The task will be executed once only. + * \param task The task function object. It should not throw execeptions. + * \return true, if the task could be scheduled and false otherwise. + */ + bool schedule(task_type const & task) + { + return m_core->schedule(task); + } + + + /*! Returns the number of tasks which are currently executed. + * \return The number of active tasks. + */ + size_t active() const + { + return m_core->active(); + } + + + /*! Returns the number of tasks which are ready for execution. + * \return The number of pending tasks. + */ + size_t pending() const + { + return m_core->pending(); + } + + + /*! Removes all pending tasks from the pool's scheduler. + */ + void clear() + { + m_core->clear(); + } + + + /*! Indicates that there are no tasks pending. + * \return true if there are no tasks ready for execution. + * \remarks This function is more efficient that the check 'pending() == 0'. + */ + bool empty() const + { + return m_core->empty(); + } + + + /*! The current thread of execution is blocked until the sum of all active + * and pending tasks is equal or less than a given threshold. + * \param task_threshold The maximum number of tasks in pool and scheduler. + */ + void wait(size_t task_threshold = 0) const + { + m_core->wait(task_threshold); + } + + + /*! The current thread of execution is blocked until the timestamp is met + * or the sum of all active and pending tasks is equal or less + * than a given threshold. + * \param timestamp The time when function returns at the latest. + * \param task_threshold The maximum number of tasks in pool and scheduler. + * \return true if the task sum is equal or less than the threshold, false otherwise. + */ + bool wait(xtime const & timestamp, size_t task_threshold = 0) const + { + return m_core->wait(timestamp, task_threshold); + } + }; + + + + /*! \brief Fifo pool. + * + * The pool's tasks are fifo scheduled task_func functors. + * + */ + typedef thread_pool fifo_pool; + + + /*! \brief Lifo pool. + * + * The pool's tasks are lifo scheduled task_func functors. + * + */ + typedef thread_pool lifo_pool; + + + /*! \brief Pool for prioritized task. + * + * The pool's tasks are prioritized prio_task_func functors. + * + */ + typedef thread_pool prio_pool; + + + /*! \brief A standard pool. + * + * The pool's tasks are fifo scheduled task_func functors. + * + */ + typedef fifo_pool pool; + + + +} } // namespace boost::threadpool + +#endif // THREADPOOL_POOL_HPP_INCLUDED diff --git a/3rd_party/threadpool/boost/threadpool/pool_adaptors.hpp b/3rd_party/threadpool/boost/threadpool/pool_adaptors.hpp new file mode 100644 index 000000000..6cde152d2 --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/pool_adaptors.hpp @@ -0,0 +1,70 @@ +/*! \file +* \brief Pool adaptors. +* +* This file contains an easy-to-use adaptor similar to a smart +* pointer for the pool class. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_POOL_ADAPTORS_HPP_INCLUDED +#define THREADPOOL_POOL_ADAPTORS_HPP_INCLUDED + +#include + + +namespace boost { namespace threadpool +{ + + +// TODO convenience scheduling function + /*! Schedules a Runnable for asynchronous execution. A Runnable is an arbitrary class with a run() + * member function. This a convenience shorthand for pool->schedule(bind(&Runnable::run, task_object)). + * \param + * \param obj The Runnable object. The member function run() will be exectued and should not throw execeptions. + * \return true, if the task could be scheduled and false otherwise. + */ + template + bool schedule(Pool& pool, shared_ptr const & obj) + { + return pool->schedule(bind(&Runnable::run, obj)); + } + + /*! Schedules a task for asynchronous execution. The task will be executed once only. + * \param task The task function object. + */ + template + typename enable_if < + is_void< typename result_of< typename Pool::task_type() >::type >, + bool + >::type + schedule(Pool& pool, typename Pool::task_type const & task) + { + return pool.schedule(task); + } + + + template + typename enable_if < + is_void< typename result_of< typename Pool::task_type() >::type >, + bool + >::type + schedule(shared_ptr const pool, typename Pool::task_type const & task) + { + return pool->schedule(task); + } + + +} } // namespace boost::threadpool + +#endif // THREADPOOL_POOL_ADAPTORS_HPP_INCLUDED + + diff --git a/3rd_party/threadpool/boost/threadpool/scheduling_policies.hpp b/3rd_party/threadpool/boost/threadpool/scheduling_policies.hpp new file mode 100644 index 000000000..2d6c7c1ba --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/scheduling_policies.hpp @@ -0,0 +1,262 @@ +/*! \file +* \brief Task scheduling policies. +* +* This file contains some fundamental scheduling policies for the pool class. +* A scheduling policy is realized by a task container which controls the access to +* the tasks. Fundamentally the container determines the order the tasks are processed +* by the thread pool. +* The task containers need not to be thread-safe because they are used by the pool +* in thread-safe way. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_SCHEDULING_POLICIES_HPP_INCLUDED +#define THREADPOOL_SCHEDULING_POLICIES_HPP_INCLUDED + + +#include +#include + +#include "task_adaptors.hpp" + +namespace boost { namespace threadpool +{ + + /*! \brief SchedulingPolicy which implements FIFO ordering. + * + * This container implements a FIFO scheduling policy. + * The first task to be added to the scheduler will be the first to be removed. + * The processing proceeds sequentially in the same order. + * FIFO stands for "first in, first out". + * + * \param Task A function object which implements the operator()(void). + * + */ + template + class fifo_scheduler + { + public: + typedef Task task_type; //!< Indicates the scheduler's task type. + + protected: + std::deque m_container; //!< Internal task container. + + + public: + /*! Adds a new task to the scheduler. + * \param task The task object. + * \return true, if the task could be scheduled and false otherwise. + */ + bool push(task_type const & task) + { + m_container.push_back(task); + return true; + } + + /*! Removes the task which should be executed next. + */ + void pop() + { + m_container.pop_front(); + } + + /*! Gets the task which should be executed next. + * \return The task object to be executed. + */ + task_type const & top() const + { + return m_container.front(); + } + + /*! Gets the current number of tasks in the scheduler. + * \return The number of tasks. + * \remarks Prefer empty() to size() == 0 to check if the scheduler is empty. + */ + size_t size() const + { + return m_container.size(); + } + + /*! Checks if the scheduler is empty. + * \return true if the scheduler contains no tasks, false otherwise. + * \remarks Is more efficient than size() == 0. + */ + bool empty() const + { + return m_container.empty(); + } + + /*! Removes all tasks from the scheduler. + */ + void clear() + { + m_container.clear(); + } + }; + + + + /*! \brief SchedulingPolicy which implements LIFO ordering. + * + * This container implements a LIFO scheduling policy. + * The last task to be added to the scheduler will be the first to be removed. + * LIFO stands for "last in, first out". + * + * \param Task A function object which implements the operator()(void). + * + */ + template + class lifo_scheduler + { + public: + typedef Task task_type; //!< Indicates the scheduler's task type. + + protected: + std::deque m_container; //!< Internal task container. + + public: + /*! Adds a new task to the scheduler. + * \param task The task object. + * \return true, if the task could be scheduled and false otherwise. + */ + bool push(task_type const & task) + { + m_container.push_front(task); + return true; + } + + /*! Removes the task which should be executed next. + */ + void pop() + { + m_container.pop_front(); + } + + /*! Gets the task which should be executed next. + * \return The task object to be executed. + */ + task_type const & top() const + { + return m_container.front(); + } + + /*! Gets the current number of tasks in the scheduler. + * \return The number of tasks. + * \remarks Prefer empty() to size() == 0 to check if the scheduler is empty. + */ + size_t size() const + { + return m_container.size(); + } + + /*! Checks if the scheduler is empty. + * \return true if the scheduler contains no tasks, false otherwise. + * \remarks Is more efficient than size() == 0. + */ + bool empty() const + { + return m_container.empty(); + } + + /*! Removes all tasks from the scheduler. + */ + void clear() + { + m_container.clear(); + } + + }; + + + + /*! \brief SchedulingPolicy which implements prioritized ordering. + * + * This container implements a scheduling policy based on task priorities. + * The task with highest priority will be the first to be removed. + * It must be possible to compare two tasks using operator<. + * + * \param Task A function object which implements the operator() and operator<. operator< must be a partial ordering. + * + * \see prio_thread_func + * + */ + template + class prio_scheduler + { + public: + typedef Task task_type; //!< Indicates the scheduler's task type. + + protected: + std::priority_queue m_container; //!< Internal task container. + + + public: + /*! Adds a new task to the scheduler. + * \param task The task object. + * \return true, if the task could be scheduled and false otherwise. + */ + bool push(task_type const & task) + { + m_container.push(task); + return true; + } + + /*! Removes the task which should be executed next. + */ + void pop() + { + m_container.pop(); + } + + /*! Gets the task which should be executed next. + * \return The task object to be executed. + */ + task_type const & top() const + { + return m_container.top(); + } + + /*! Gets the current number of tasks in the scheduler. + * \return The number of tasks. + * \remarks Prefer empty() to size() == 0 to check if the scheduler is empty. + */ + size_t size() const + { + return m_container.size(); + } + + /*! Checks if the scheduler is empty. + * \return true if the scheduler contains no tasks, false otherwise. + * \remarks Is more efficient than size() == 0. + */ + bool empty() const + { + return m_container.empty(); + } + + /*! Removes all tasks from the scheduler. + */ + void clear() + { + while(!m_container.empty()) + { + m_container.pop(); + } + } + }; + + +} } // namespace boost::threadpool + + +#endif // THREADPOOL_SCHEDULING_POLICIES_HPP_INCLUDED + diff --git a/3rd_party/threadpool/boost/threadpool/shutdown_policies.hpp b/3rd_party/threadpool/boost/threadpool/shutdown_policies.hpp new file mode 100644 index 000000000..047a6eb51 --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/shutdown_policies.hpp @@ -0,0 +1,83 @@ +/*! \file +* \brief Shutdown policies. +* +* This file contains shutdown policies for thread_pool. +* A shutdown policy controls the pool's behavior from the time +* when the pool is not referenced any longer. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_SHUTDOWN_POLICIES_HPP_INCLUDED +#define THREADPOOL_SHUTDOWN_POLICIES_HPP_INCLUDED + + + +/// The namespace threadpool contains a thread pool and related utility classes. +namespace boost { namespace threadpool +{ + + +/*! \brief ShutdownPolicy which waits for the completion of all tasks + * and the worker termination afterwards. + * + * \param Pool The pool's core type. + */ + template + class wait_for_all_tasks + { + public: + static void shutdown(Pool& pool) + { + pool.wait(); + pool.terminate_all_workers(true); + } + }; + + + /*! \brief ShutdownPolicy which waits for the completion of all active tasks + * and the worker termination afterwards. + * + * \param Pool The pool's core type. + */ + template + class wait_for_active_tasks + { + public: + static void shutdown(Pool& pool) + { + pool.clear(); + pool.wait(); + pool.terminate_all_workers(true); + } + }; + + + /*! \brief ShutdownPolicy which does not wait for any tasks or worker termination. + * + * This policy does not wait for any tasks. Nevertheless all active tasks will be processed completely. + * + * \param Pool The pool's core type. + */ + template + class immediately + { + public: + static void shutdown(Pool& pool) + { + pool.clear(); + pool.terminate_all_workers(false); + } + }; + +} } // namespace boost::threadpool + +#endif // THREADPOOL_SHUTDOWN_POLICIES_HPP_INCLUDED diff --git a/3rd_party/threadpool/boost/threadpool/size_policies.hpp b/3rd_party/threadpool/boost/threadpool/size_policies.hpp new file mode 100644 index 000000000..e3c08038d --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/size_policies.hpp @@ -0,0 +1,99 @@ +/*! \file +* \brief Size policies. +* +* This file contains size policies for thread_pool. A size +* policy controls the number of worker threads in the pool. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_SIZE_POLICIES_HPP_INCLUDED +#define THREADPOOL_SIZE_POLICIES_HPP_INCLUDED + + + +/// The namespace threadpool contains a thread pool and related utility classes. +namespace boost { namespace threadpool +{ + + /*! \brief SizePolicyController which provides no functionality. + * + * \param Pool The pool's core type. + */ + template + struct empty_controller + { + empty_controller(typename Pool::size_policy_type&, shared_ptr) {} + }; + + + /*! \brief SizePolicyController which allows resizing. + * + * \param Pool The pool's core type. + */ + template< typename Pool > + class resize_controller + { + typedef typename Pool::size_policy_type size_policy_type; + reference_wrapper m_policy; + shared_ptr m_pool; //!< to make sure that the pool is alive (the policy pointer is valid) as long as the controller exists + + public: + resize_controller(size_policy_type& policy, shared_ptr pool) + : m_policy(policy) + , m_pool(pool) + { + } + + bool resize(size_t worker_count) + { + return m_policy.get().resize(worker_count); + } + }; + + + /*! \brief SizePolicy which preserves the thread count. + * + * \param Pool The pool's core type. + */ + template + class static_size + { + reference_wrapper m_pool; + + public: + static void init(Pool& pool, size_t const worker_count) + { + pool.resize(worker_count); + } + + static_size(Pool volatile & pool) + : m_pool(pool) + {} + + bool resize(size_t const worker_count) + { + return m_pool.get().resize(worker_count); + } + + void worker_died_unexpectedly(size_t const new_worker_count) + { + m_pool.get().resize(new_worker_count + 1); + } + + // TODO this functions are not called yet + void task_scheduled() {} + void task_finished() {} + }; + +} } // namespace boost::threadpool + +#endif // THREADPOOL_SIZE_POLICIES_HPP_INCLUDED diff --git a/3rd_party/threadpool/boost/threadpool/task_adaptors.hpp b/3rd_party/threadpool/boost/threadpool/task_adaptors.hpp new file mode 100644 index 000000000..78a89df8a --- /dev/null +++ b/3rd_party/threadpool/boost/threadpool/task_adaptors.hpp @@ -0,0 +1,171 @@ +/*! \file +* \brief Task adaptors. +* +* This file contains adaptors for task function objects. +* +* Copyright (c) 2005-2007 Philipp Henkel +* +* Use, modification, and distribution are subject to the +* Boost Software License, Version 1.0. (See accompanying file +* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +* +* http://threadpool.sourceforge.net +* +*/ + + +#ifndef THREADPOOL_TASK_ADAPTERS_HPP_INCLUDED +#define THREADPOOL_TASK_ADAPTERS_HPP_INCLUDED + + +#include +#include +#include + +#if BOOST_VERSION < 105000 +#define TIME_UTC_ TIME_UTC +#endif + +namespace boost { namespace threadpool +{ + + /*! \brief Standard task function object. + * + * This function object wraps a nullary function which returns void. + * The wrapped function is invoked by calling the operator (). + * + * \see boost function library + * + */ + typedef function0 task_func; + + + + + /*! \brief Prioritized task function object. + * + * This function object wraps a task_func object and binds a priority to it. + * prio_task_funcs can be compared using the operator < which realises a partial ordering. + * The wrapped task function is invoked by calling the operator (). + * + * \see prio_scheduler + * + */ + class prio_task_func + { + private: + unsigned int m_priority; //!< The priority of the task's function. + task_func m_function; //!< The task's function. + + public: + typedef void result_type; //!< Indicates the functor's result type. + + public: + /*! Constructor. + * \param priority The priority of the task. + * \param function The task's function object. + */ + prio_task_func(unsigned int const priority, task_func const & function) + : m_priority(priority) + , m_function(function) + { + } + + /*! Executes the task function. + */ + void operator() (void) const + { + if(m_function) + { + m_function(); + } + } + + /*! Comparison operator which realises a partial ordering based on priorities. + * \param rhs The object to compare with. + * \return true if the priority of *this is less than right hand side's priority, false otherwise. + */ + bool operator< (const prio_task_func& rhs) const + { + return m_priority < rhs.m_priority; + } + + }; // prio_task_func + + + + + + + + + /*! \brief Looped task function object. + * + * This function object wraps a boolean thread function object. + * The wrapped task function is invoked by calling the operator () and it is executed in regular + * time intervals until false is returned. The interval length may be zero. + * Please note that a pool's thread is engaged as long as the task is looped. + * + */ + class looped_task_func + { + private: + function0 m_function; //!< The task's function. + unsigned int m_break_s; //!< Duration of breaks in seconds. + unsigned int m_break_ns; //!< Duration of breaks in nano seconds. + + public: + typedef void result_type; //!< Indicates the functor's result type. + + public: + /*! Constructor. + * \param function The task's function object which is looped until false is returned. + * \param interval The minimum break time in milli seconds before the first execution of the task function and between the following ones. + */ + looped_task_func(function0 const & function, unsigned int const interval = 0) + : m_function(function) + { + m_break_s = interval / 1000; + m_break_ns = (interval - m_break_s * 1000) * 1000 * 1000; + } + + /*! Executes the task function. + */ + void operator() (void) const + { + if(m_function) + { + if(m_break_s > 0 || m_break_ns > 0) + { // Sleep some time before first execution + xtime xt; + xtime_get(&xt, TIME_UTC_); + xt.nsec += m_break_ns; + xt.sec += m_break_s; + thread::sleep(xt); + } + + while(m_function()) + { + if(m_break_s > 0 || m_break_ns > 0) + { + xtime xt; + xtime_get(&xt, TIME_UTC_); + xt.nsec += m_break_ns; + xt.sec += m_break_s; + thread::sleep(xt); + } + else + { + thread::yield(); // Be fair to other threads + } + } + } + } + + }; // looped_task_func + + +} } // namespace boost::threadpool + +#endif // THREADPOOL_TASK_ADAPTERS_HPP_INCLUDED + diff --git a/BOOTSTRAP_SAMPLE.bat b/BOOTSTRAP_SAMPLE.bat deleted file mode 100644 index b18c1d0c7..000000000 --- a/BOOTSTRAP_SAMPLE.bat +++ /dev/null @@ -1,5 +0,0 @@ -REM Build salvia only. -python bootstrap.py build salvia --build-root build --install-root . --arch x64 --toolset msvc-14.2 --toolset-dir D:\Software\VS2019\VC\Auxiliary\Build --build-config RelWithDebInfo --cmake D:\Software\CMake\bin\cmake.exe - -REM Launch benchmark test. -python bootstrap.py benchmark --binary-folder bin\ntx64_msvc142\RelWithDebInfo --git "C:\Users\Ye Wu\AppData\Local\Atlassian\SourceTree\git_local\bin\git.exe" --repeat 16 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bffbad2a..6ab05268a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,61 @@ -PROJECT( salvia ) +project(salvia) -CMAKE_MINIMUM_REQUIRED( VERSION 3.14 ) +cmake_minimum_required(VERSION 3.21) -option(SALVIA_BUILD_WITH_UNICODE "Build with unicode(wide-byte) or ansi(multi-byte)." TRUE) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set(SALVIA_HOME_DIR ${CMAKE_HOME_DIRECTORY}) -SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) -set( SALVIA_HOME_DIR ${CMAKE_HOME_DIRECTORY} ) +# Configure dependencies +find_package( + Boost REQUIRED COMPONENTS + log + program_options + unit_test_framework + wave + thread + date_time + locale + chrono + filesystem + atomic + system +) +# Workaround for linking failure of Boost.UUID +if (MSVC) + list(APPEND Boost_LIBRARIES bcrypt.lib) +endif() +find_package(llvm CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") +include(HandleLLVMOptions) +add_definitions(${LLVM_DEFINITIONS}) +llvm_map_components_to_libnames(LLVM_LIBS + Analysis + Core + ExecutionEngine + InstCombine + Object + OrcJIT + RuntimeDyld + ScalarOpts + Support + native + MCJIT + X86CodeGen +) +find_package(freeimage CONFIG REQUIRED) -# Configuring -include (${CMAKE_HOME_DIRECTORY}/cmake/SystemInfo.cmake) -include (${CMAKE_HOME_DIRECTORY}/cmake/ConfigCompiler.cmake) -include (${CMAKE_HOME_DIRECTORY}/cmake/Variables.cmake) -include (${CMAKE_HOME_DIRECTORY}/cmake/ConfigurePath.cmake) -include (${CMAKE_HOME_DIRECTORY}/cmake/ConfigBoostLibs.cmake) -include (${CMAKE_HOME_DIRECTORY}/cmake/ConfigLLVMLibs.cmake) -ADD_SUBDIRECTORY( cmake ) +include (cmake/ConfigCompiler.cmake) +add_subdirectory( cmake ) # Add projects -ADD_SUBDIRECTORY( eflib ) -ADD_SUBDIRECTORY( sasl ) -ADD_SUBDIRECTORY( salviar ) -ADD_SUBDIRECTORY( salviax ) -ADD_SUBDIRECTORY( salviau ) -ADD_SUBDIRECTORY( samples ) -if( SALVIA_BUILD_SW_DRIVER ) - ADD_SUBDIRECTORY( salvia_d3d_sw_driver ) -endif() +add_subdirectory(eflib) +add_subdirectory(sasl) +add_subdirectory(salviar) +add_subdirectory(salviax) +add_subdirectory(salviau) +add_subdirectory(samples) + +# if( SALVIA_BUILD_SW_DRIVER ) +# add_subdirectory( salvia_d3d_sw_driver ) +# endif() diff --git a/README.md b/README.md index 191ad412b..d50822366 100644 --- a/README.md +++ b/README.md @@ -11,35 +11,21 @@ * Reshaper C++ ## Requirements - * Windows 10 - * Visual Studio 2019. - * Python 3.5 or later - * CMake 3.15+ + * Windows 10/11 + * Git + * Need to install LFS plug-in. + * [About Git LFS](https://git-lfs.github.com/) + * Visual Studio 2019/2022 + * Need C++17 support. + * CMake 3.21+ + * vcpkg (See build steps) * Linux - * Mint 16 and Mint 17 were tested. - * GCC 8 or later. - * Python 3.5 or later - * CMake 3.15 or later + * NOT tested in 22.02. Will be tested in following version. ## Build steps - * Running bootstrap.py for build, running benchmark or generating benchmark report CSV file. Example command lines: - * Build - ``` - python bootstrap.py build salvia --build-root .\build --install-root . --arch x64 --toolset msvc-14.2 --toolset-dir D:\Software\VS2019\VC\Auxiliary\Build --build-config RelWithDebInfo --cmake D:\Software\CMake\bin\cmake.exe - ``` - * Run benchmark - ``` - python bootstrap.py benchmark --binary-folder bin\ntx64_msvc142\RelWithDebInfo --git D:\Software\Git\git.exe --change-desc "Performance improved" --repeat 16 - ``` - * Generate benchmark report - ``` - python bootstrap.py bm_report - ``` - * Run `python bootstrap.py --help` to get more details. - - -## REMARK - * While `bootstrap.py` build was run, only one configuration was avaiable for build. For IDEs which support multiple configurations, such as Visual Studio, you need to run bootstrap several times to make the configurations available for building. + * Install [vcpkg](https://github.com/microsoft/vcpkg). + * Edit SAMPLE_Build.bat. + * Run the modified script. ## Support Info diff --git a/blibs/__init__.py b/blibs/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/blibs/benchmark.py b/blibs/benchmark.py deleted file mode 100644 index 7f4a125cb..000000000 --- a/blibs/benchmark.py +++ /dev/null @@ -1,198 +0,0 @@ -import datetime -import json -import os -import platform -import subprocess - -from dateutil import tz -from . import cpuinfo, util, diagnostic - -def benchmark_db_path(root_dir: str): - ret = os.path.join(root_dir, "doc", "contents", "materials", "benchmark.db.txt") - return ret - -class benchmark_runner: - def __init__(self, source_root_dir, binary_dir, git_path): - self._root_dir = source_root_dir - self._binary_dir = binary_dir - self._git_path = git_path - - @staticmethod - def _get_platform_info(): - cpu_info = cpuinfo.get_cpu_info() - cpu_name = cpu_info["brand"] - os_name = platform.system() - - return { - "cpu": f"{cpu_name}", - "os": f"{os_name}" - } - - def _get_git_commit(self): - with util.scoped_cd(self._root_dir): - status = subprocess.check_output([self._git_path, "status"]) - git_commit = subprocess.check_output([self._git_path, "rev-parse", "HEAD"]) - - status = status.decode("utf-8") - git_commit = git_commit.decode("utf-8") - - has_changed_files = False - for line in status.split('\n'): - if line.startswith("Changes to be committed:") or line.startswith("Changes not staged for commit"): - has_changed_files = True - break - - return git_commit, has_changed_files - - def _execute_benchmark(self, benchmark_name): - exe_file_relative_path = benchmark_runner._exe_relative_path(benchmark_name) - try: - with util.scoped_cd(self._binary_dir): - _ = subprocess.check_output([exe_file_relative_path, "-m", "b"]) - return True - except OSError: - return False - - @staticmethod - def _exe_relative_path(benchmark_name: str): - return f"{benchmark_name}.exe" - - def _exe_full_path(self, benchmark_name): - exe_file_path = os.path.join(self._binary_dir, benchmark_runner._exe_relative_path(benchmark_name)) - return exe_file_path - - def _result_full_path(self, benchmark_name): - result_file_path = os.path.join(self._binary_dir, benchmark_name + "_Profiling.json") - return result_file_path - - def run_single(self, benchmark_name, repeat_count): - result_file_path = self._result_full_path(benchmark_name) - - results = [] - - for i in range(repeat_count): - diagnostic.report_info(f"Running benchmark <{benchmark_name}>, round {i+1} out of {repeat_count}.") - if os.path.exists(result_file_path): - os.remove(result_file_path) - success = self._execute_benchmark(benchmark_name) - if not success: - raise NotImplementedError - if not os.path.isfile(result_file_path): - diagnostic.report_warning( - f"Benchmark was completed successful, but cannot find benchmark result <{result_file_path}>." - f" Will skip remaining round of benchmark <{benchmark_name}>." - ) - break - diagnostic.report_info(f"Done. Collecting data ...") - - with open(result_file_path, encoding="utf-8") as result_file: - result = json.load(result_file) - results.append(result) - diagnostic.report_info("Collected.") - - return results - - def run_all(self, changes_from_head: str, repeat_count: int): - BENCHMARKS = [ - "Sponza", - "PartOfSponza", - "AstroBoy", - "ComplexMesh", - "AntiAliasing", - "ColorizedTriangle", - "TextureAndBlending", - "VertexTextureFetch", - "StandardShadowMap", - "StencilMirror" - ] - assert repeat_count > 0 - REPEAT_COUNT = repeat_count - - start_time = datetime.datetime.now() - git_commit, has_changed_files = self._get_git_commit() - cpu_info = cpuinfo.get_cpu_info() - if has_changed_files and len(changes_from_head) == 0: - diagnostic.report_error( - "Files were changed from last commit. Should have detailed description about new changes." - ) - - task_result = \ - { - "date_time": datetime.datetime.now().astimezone(tz.tzlocal()).isoformat(), - "end_time": None, - "repeat_count": REPEAT_COUNT, - "node": platform.node(), - "cpu": cpu_info['brand'], - "os": platform.platform(), - "git_commit": git_commit, - "changes": changes_from_head, - "results": None - } - - diagnostic.report_info("Running info:") - for k, v in task_result.items(): - if v is None: - continue - if isinstance(v, str) and len(v) == 0: - continue - diagnostic.report_info(f" - {k}: {str(v)}") - - results = { - benchmark_name: self.run_single(benchmark_name, REPEAT_COUNT) - for benchmark_name in BENCHMARKS - } - - diagnostic.report_info("Benchmark running done. Generating json ...") - task_result.update({ - "end_time": datetime.datetime.now().astimezone(tz.tzlocal()).isoformat(), - "results": results - }) - - diagnostic.report_info("Dumping performance data ...") - result_one_line_json = json.dumps(task_result) - - with open(benchmark_db_path(self._root_dir), "a", encoding="utf-8") as db_file: - db_file.write(result_one_line_json) - db_file.write("\n") - - diagnostic.report_info("Done.") - -def flatten_json_to_table(path_to_item, item): - if isinstance(item, dict): - ret_dict = {} - for k, v in item.items(): - path_to_child = f"{path_to_item}.{k}" - child_flatten_items = flatten_json_to_table(path_to_child, v) - ret_dict.update(child_flatten_items) - return ret_dict - else: - return {path_to_item: item} - -def generate_csv_report(source_root_dir: str): - columns_A = ["date_time", "cpu", "os", "git_commit", "changes"] - bm_db_path = benchmark_db_path(source_root_dir) - bm_db_csv_path = bm_db_path + ".metrics.csv" - with open(bm_db_path, encoding="utf-8") as db_file, open(bm_db_csv_path, "w", encoding="utf-8") as metrics_file: - metrics_file.write(",".join(columns_A) + ",compiler,benchmark,round,metric,value\n") - for line in db_file: - perf_obj = json.loads(line) - for bm_name, bm_rounds in perf_obj["results"].items(): - for i_round, bm_round in enumerate(bm_rounds): - compiler_name = bm_round["compiler"] - overall_metrics = flatten_json_to_table("App", bm_round[bm_name]) - stage_metrics = flatten_json_to_table("async", bm_round["async"]) - - performance_metrics = {} - performance_metrics.update(overall_metrics) - performance_metrics.update(stage_metrics) - - for metric_name, metric_value in performance_metrics.items(): - metrics_file.write( - ",".join(f"{perf_obj[field_name]}".strip() for field_name in columns_A)) - metrics_file.write(f",{compiler_name},{bm_name},{i_round},{metric_name},{metric_value}\n") - - - - - - diff --git a/blibs/boost_build.py b/blibs/boost_build.py deleted file mode 100644 index adb06a3ff..000000000 --- a/blibs/boost_build.py +++ /dev/null @@ -1,38 +0,0 @@ -import os, re -from . import util - -class version_object: - def __init__(self, version_int): - self.version_int = version_int - self.major = version_int / 100000 - self.minor = version_int / 100 % 1000 - self.patch = version_int % 100 - self.version_str = '%d.%d.%d' % (self.major, self.minor, self.patch) - - def __str__(self): - return self.version_str - - def __cmp__(self, other): - return self.version_int - other.version_int - -def boost_version( boost_root ): - """ - Get boost version. - If it didn't include boost or unknown versioned boost, - it will return None. - """ - version_hpp = os.path.join( boost_root, 'boost', 'version.hpp' ) - try: - f = open(version_hpp) - except: - util.report_error('Cannot find boost/version.hpp. Please specify correct boost directory.') - return None - - version_lines = f.readlines() - f.close() - for line in version_lines: - matched = re.match('\s*#\s*define\s+BOOST_VERSION\s+(?P\d+)\s*', line ) - if matched and not matched.groupdict() is None: - if 'version' in matched.groupdict(): - return version_object( int(matched.group('version')) ) - return None diff --git a/blibs/copy.py b/blibs/copy.py deleted file mode 100644 index efad7b21e..000000000 --- a/blibs/copy.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys, os, shutil -from . import util - -def copy_diff( src, dst ): - pass - -def copy_newer( src, dst ): - if not( os.path.isfile(src) ): - util.report_error('Error: src parameter of copy_newer is a file path.') - target = None - - if os.path.isdir(dst): - target = os.path.join( dst, os.path.basename(src) ) - else: - target = dst - - if os.path.exists(target): - if os.path.getmtime(target) < os.path.getmtime(src): - os.remove(target) - - if not os.path.exists(target): - shutil.copy2( src, target ) - return True - else: - return False - \ No newline at end of file diff --git a/blibs/cpuinfo.py b/blibs/cpuinfo.py deleted file mode 100644 index b8fc7e5e0..000000000 --- a/blibs/cpuinfo.py +++ /dev/null @@ -1,2257 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - -# Copyright (c) 2014-2019, Matthew Brennan Jones -# Py-cpuinfo gets CPU info with pure Python 2 & 3 -# It uses the MIT License -# It is hosted at: https://github.com/workhorsy/py-cpuinfo -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -CPUINFO_VERSION = (5, 0, 0) - -import os, sys -import platform -import multiprocessing -import ctypes - -try: - import _winreg as winreg -except ImportError as err: - try: - import winreg - except ImportError as err: - pass - -IS_PY2 = sys.version_info[0] == 2 - - -class DataSource(object): - bits = platform.architecture()[0] - cpu_count = multiprocessing.cpu_count() - is_windows = platform.system().lower() == 'windows' - raw_arch_string = platform.machine() - can_cpuid = True - - @staticmethod - def has_proc_cpuinfo(): - return os.path.exists('/proc/cpuinfo') - - @staticmethod - def has_dmesg(): - return len(_program_paths('dmesg')) > 0 - - @staticmethod - def has_var_run_dmesg_boot(): - uname = platform.system().strip().strip('"').strip("'").strip().lower() - return 'linux' in uname and os.path.exists('/var/run/dmesg.boot') - - @staticmethod - def has_cpufreq_info(): - return len(_program_paths('cpufreq-info')) > 0 - - @staticmethod - def has_sestatus(): - return len(_program_paths('sestatus')) > 0 - - @staticmethod - def has_sysctl(): - return len(_program_paths('sysctl')) > 0 - - @staticmethod - def has_isainfo(): - return len(_program_paths('isainfo')) > 0 - - @staticmethod - def has_kstat(): - return len(_program_paths('kstat')) > 0 - - @staticmethod - def has_sysinfo(): - return len(_program_paths('sysinfo')) > 0 - - @staticmethod - def has_lscpu(): - return len(_program_paths('lscpu')) > 0 - - @staticmethod - def has_ibm_pa_features(): - return len(_program_paths('lsprop')) > 0 - - @staticmethod - def has_wmic(): - returncode, output = _run_and_get_stdout(['wmic', 'os', 'get', 'Version']) - return returncode == 0 and len(output) > 0 - - @staticmethod - def cat_proc_cpuinfo(): - return _run_and_get_stdout(['cat', '/proc/cpuinfo']) - - @staticmethod - def cpufreq_info(): - return _run_and_get_stdout(['cpufreq-info']) - - @staticmethod - def sestatus_allow_execheap(): - return _run_and_get_stdout(['sestatus', '-b'], ['grep', '-i', '"allow_execheap"'])[1].strip().lower().endswith('on') - - @staticmethod - def sestatus_allow_execmem(): - return _run_and_get_stdout(['sestatus', '-b'], ['grep', '-i', '"allow_execmem"'])[1].strip().lower().endswith('on') - - @staticmethod - def dmesg_a(): - return _run_and_get_stdout(['dmesg', '-a']) - - @staticmethod - def cat_var_run_dmesg_boot(): - return _run_and_get_stdout(['cat', '/var/run/dmesg.boot']) - - @staticmethod - def sysctl_machdep_cpu_hw_cpufrequency(): - return _run_and_get_stdout(['sysctl', 'machdep.cpu', 'hw.cpufrequency']) - - @staticmethod - def isainfo_vb(): - return _run_and_get_stdout(['isainfo', '-vb']) - - @staticmethod - def kstat_m_cpu_info(): - return _run_and_get_stdout(['kstat', '-m', 'cpu_info']) - - @staticmethod - def sysinfo_cpu(): - return _run_and_get_stdout(['sysinfo', '-cpu']) - - @staticmethod - def lscpu(): - return _run_and_get_stdout(['lscpu']) - - @staticmethod - def ibm_pa_features(): - import glob - - ibm_features = glob.glob('/proc/device-tree/cpus/*/ibm,pa-features') - if ibm_features: - return _run_and_get_stdout(['lsprop', ibm_features[0]]) - - @staticmethod - def wmic_cpu(): - return _run_and_get_stdout(['wmic', 'cpu', 'get', 'Name,CurrentClockSpeed,L2CacheSize,L3CacheSize,Description,Caption,Manufacturer', '/format:list']) - - @staticmethod - def winreg_processor_brand(): - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") - processor_brand = winreg.QueryValueEx(key, "ProcessorNameString")[0] - winreg.CloseKey(key) - return processor_brand - - @staticmethod - def winreg_vendor_id(): - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") - vendor_id = winreg.QueryValueEx(key, "VendorIdentifier")[0] - winreg.CloseKey(key) - return vendor_id - - @staticmethod - def winreg_raw_arch_string(): - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment") - raw_arch_string = winreg.QueryValueEx(key, "PROCESSOR_ARCHITECTURE")[0] - winreg.CloseKey(key) - return raw_arch_string - - @staticmethod - def winreg_hz_actual(): - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") - hz_actual = winreg.QueryValueEx(key, "~Mhz")[0] - winreg.CloseKey(key) - hz_actual = _to_hz_string(hz_actual) - return hz_actual - - @staticmethod - def winreg_feature_bits(): - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"Hardware\Description\System\CentralProcessor\0") - feature_bits = winreg.QueryValueEx(key, "FeatureSet")[0] - winreg.CloseKey(key) - return feature_bits - - -def _program_paths(program_name): - paths = [] - exts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep)) - path = os.environ['PATH'] - for p in os.environ['PATH'].split(os.pathsep): - p = os.path.join(p, program_name) - if os.access(p, os.X_OK): - paths.append(p) - for e in exts: - pext = p + e - if os.access(pext, os.X_OK): - paths.append(pext) - return paths - -def _run_and_get_stdout(command, pipe_command=None): - from subprocess import Popen, PIPE - - if not pipe_command: - p1 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE) - output = p1.communicate()[0] - if not IS_PY2: - output = output.decode(encoding='UTF-8') - return p1.returncode, output - else: - p1 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE) - p2 = Popen(pipe_command, stdin=p1.stdout, stdout=PIPE, stderr=PIPE) - p1.stdout.close() - output = p2.communicate()[0] - if not IS_PY2: - output = output.decode(encoding='UTF-8') - return p2.returncode, output - -# Make sure we are running on a supported system -def _check_arch(): - arch, bits = _parse_arch(DataSource.raw_arch_string) - if not arch in ['X86_32', 'X86_64', 'ARM_7', 'ARM_8', 'PPC_64']: - raise Exception("py-cpuinfo currently only works on X86 and some PPC and ARM CPUs.") - -def _obj_to_b64(thing): - import pickle - import base64 - - a = thing - b = pickle.dumps(a) - c = base64.b64encode(b) - d = c.decode('utf8') - return d - -def _b64_to_obj(thing): - import pickle - import base64 - - try: - a = base64.b64decode(thing) - b = pickle.loads(a) - return b - except: - return {} - -def _utf_to_str(input): - if IS_PY2 and isinstance(input, unicode): - return input.encode('utf-8') - elif isinstance(input, list): - return [_utf_to_str(element) for element in input] - elif isinstance(input, dict): - return {_utf_to_str(key): _utf_to_str(value) - for key, value in input.items()} - else: - return input - -def _copy_new_fields(info, new_info): - keys = [ - 'vendor_id', 'hardware', 'brand', 'hz_advertised', 'hz_actual', - 'hz_advertised_raw', 'hz_actual_raw', 'arch', 'bits', 'count', - 'raw_arch_string', 'l2_cache_size', 'l2_cache_line_size', - 'l2_cache_associativity', 'stepping', 'model', 'family', - 'processor_type', 'extended_model', 'extended_family', 'flags', - 'l3_cache_size', 'l1_data_cache_size', 'l1_instruction_cache_size' - ] - - for key in keys: - if new_info.get(key, None) and not info.get(key, None): - info[key] = new_info[key] - elif key == 'flags' and new_info.get('flags'): - for f in new_info['flags']: - if f not in info['flags']: info['flags'].append(f) - info['flags'].sort() - -def _get_field_actual(cant_be_number, raw_string, field_names): - for line in raw_string.splitlines(): - for field_name in field_names: - field_name = field_name.lower() - if ':' in line: - left, right = line.split(':', 1) - left = left.strip().lower() - right = right.strip() - if left == field_name and len(right) > 0: - if cant_be_number: - if not right.isdigit(): - return right - else: - return right - - return None - -def _get_field(cant_be_number, raw_string, convert_to, default_value, *field_names): - retval = _get_field_actual(cant_be_number, raw_string, field_names) - - # Convert the return value - if retval and convert_to: - try: - retval = convert_to(retval) - except: - retval = default_value - - # Return the default if there is no return value - if retval is None: - retval = default_value - - return retval - -def _get_hz_string_from_brand(processor_brand): - # Just return 0 if the processor brand does not have the Hz - if not 'hz' in processor_brand.lower(): - return (1, '0.0') - - hz_brand = processor_brand.lower() - scale = 1 - - if hz_brand.endswith('mhz'): - scale = 6 - elif hz_brand.endswith('ghz'): - scale = 9 - if '@' in hz_brand: - hz_brand = hz_brand.split('@')[1] - else: - hz_brand = hz_brand.rsplit(None, 1)[1] - - hz_brand = hz_brand.rstrip('mhz').rstrip('ghz').strip() - hz_brand = _to_hz_string(hz_brand) - - return (scale, hz_brand) - -def _to_friendly_hz(ticks, scale): - # Get the raw Hz as a string - left, right = _to_raw_hz(ticks, scale) - ticks = '{0}.{1}'.format(left, right) - - # Get the location of the dot, and remove said dot - dot_index = ticks.index('.') - ticks = ticks.replace('.', '') - - # Get the Hz symbol and scale - symbol = "Hz" - scale = 0 - if dot_index > 9: - symbol = "GHz" - scale = 9 - elif dot_index > 6: - symbol = "MHz" - scale = 6 - elif dot_index > 3: - symbol = "KHz" - scale = 3 - - # Get the Hz with the dot at the new scaled point - ticks = '{0}.{1}'.format(ticks[:-scale-1], ticks[-scale-1:]) - - # Format the ticks to have 4 numbers after the decimal - # and remove any superfluous zeroes. - ticks = '{0:.4f} {1}'.format(float(ticks), symbol) - ticks = ticks.rstrip('0') - - return ticks - -def _to_raw_hz(ticks, scale): - # Scale the numbers - ticks = ticks.lstrip('0') - old_index = ticks.index('.') - ticks = ticks.replace('.', '') - ticks = ticks.ljust(scale + old_index+1, '0') - new_index = old_index + scale - ticks = '{0}.{1}'.format(ticks[:new_index], ticks[new_index:]) - left, right = ticks.split('.') - left, right = int(left), int(right) - return (left, right) - -def _to_hz_string(ticks): - # Convert to string - ticks = '{0}'.format(ticks) - - # Add decimal if missing - if '.' not in ticks: - ticks = '{0}.0'.format(ticks) - - # Remove trailing zeros - ticks = ticks.rstrip('0') - - # Add one trailing zero for empty right side - if ticks.endswith('.'): - ticks = '{0}0'.format(ticks) - - return ticks - -def _to_friendly_bytes(input): - import re - - if not input: - return input - input = "{0}".format(input) - - formats = { - r"^[0-9]+B$" : 'B', - r"^[0-9]+K$" : 'KB', - r"^[0-9]+M$" : 'MB', - r"^[0-9]+G$" : 'GB' - } - - for pattern, friendly_size in formats.items(): - if re.match(pattern, input): - return "{0} {1}".format(input[ : -1].strip(), friendly_size) - - return input - -def _parse_cpu_string(cpu_string): - # Get location of fields at end of string - fields_index = cpu_string.find('(', cpu_string.find('@')) - #print(fields_index) - - # Processor Brand - processor_brand = cpu_string - if fields_index != -1: - processor_brand = cpu_string[0 : fields_index].strip() - #print('processor_brand: ', processor_brand) - - fields = None - if fields_index != -1: - fields = cpu_string[fields_index : ] - #print('fields: ', fields) - - # Hz - scale, hz_brand = _get_hz_string_from_brand(processor_brand) - - # Various fields - vendor_id, stepping, model, family = (None, None, None, None) - if fields: - try: - fields = fields.rsplit('(', 1)[1].split(')')[0].split(',') - fields = [f.strip().lower() for f in fields] - fields = [f.split(':') for f in fields] - fields = [{f[0].strip() : f[1].strip()} for f in fields] - #print('fields: ', fields) - for field in fields: - name = list(field.keys())[0] - value = list(field.values())[0] - #print('name:{0}, value:{1}'.format(name, value)) - if name == 'origin': - vendor_id = value.strip('"') - elif name == 'stepping': - stepping = int(value.lstrip('0x'), 16) - elif name == 'model': - model = int(value.lstrip('0x'), 16) - elif name in ['fam', 'family']: - family = int(value.lstrip('0x'), 16) - except: - #raise - pass - - return (processor_brand, hz_brand, scale, vendor_id, stepping, model, family) - -def _parse_dmesg_output(output): - try: - # Get all the dmesg lines that might contain a CPU string - lines = output.split(' CPU0:')[1:] + \ - output.split(' CPU1:')[1:] + \ - output.split(' CPU:')[1:] + \ - output.split('\nCPU0:')[1:] + \ - output.split('\nCPU1:')[1:] + \ - output.split('\nCPU:')[1:] - lines = [l.split('\n')[0].strip() for l in lines] - - # Convert the lines to CPU strings - cpu_strings = [_parse_cpu_string(l) for l in lines] - - # Find the CPU string that has the most fields - best_string = None - highest_count = 0 - for cpu_string in cpu_strings: - count = sum([n is not None for n in cpu_string]) - if count > highest_count: - highest_count = count - best_string = cpu_string - - # If no CPU string was found, return {} - if not best_string: - return {} - - processor_brand, hz_actual, scale, vendor_id, stepping, model, family = best_string - - # Origin - if ' Origin=' in output: - fields = output[output.find(' Origin=') : ].split('\n')[0] - fields = fields.strip().split() - fields = [n.strip().split('=') for n in fields] - fields = [{n[0].strip().lower() : n[1].strip()} for n in fields] - #print('fields: ', fields) - for field in fields: - name = list(field.keys())[0] - value = list(field.values())[0] - #print('name:{0}, value:{1}'.format(name, value)) - if name == 'origin': - vendor_id = value.strip('"') - elif name == 'stepping': - stepping = int(value.lstrip('0x'), 16) - elif name == 'model': - model = int(value.lstrip('0x'), 16) - elif name in ['fam', 'family']: - family = int(value.lstrip('0x'), 16) - #print('FIELDS: ', (vendor_id, stepping, model, family)) - - # Features - flag_lines = [] - for category in [' Features=', ' Features2=', ' AMD Features=', ' AMD Features2=']: - if category in output: - flag_lines.append(output.split(category)[1].split('\n')[0]) - - flags = [] - for line in flag_lines: - line = line.split('<')[1].split('>')[0].lower() - for flag in line.split(','): - flags.append(flag) - flags.sort() - - # Convert from GHz/MHz string to Hz - scale, hz_advertised = _get_hz_string_from_brand(processor_brand) - - info = { - 'vendor_id' : vendor_id, - 'brand' : processor_brand, - - 'stepping' : stepping, - 'model' : model, - 'family' : family, - 'flags' : flags - } - - if hz_advertised and hz_advertised != '0.0': - info['hz_advertised'] = _to_friendly_hz(hz_advertised, scale) - info['hz_actual'] = _to_friendly_hz(hz_actual, scale) - - if hz_advertised and hz_advertised != '0.0': - info['hz_advertised_raw'] = _to_raw_hz(hz_advertised, scale) - info['hz_actual_raw'] = _to_raw_hz(hz_actual, scale) - - return {k: v for k, v in info.items() if v} - except: - #raise - pass - - return {} - -def _parse_arch(raw_arch_string): - import re - - arch, bits = None, None - raw_arch_string = raw_arch_string.lower() - - # X86 - if re.match('^i\d86$|^x86$|^x86_32$|^i86pc$|^ia32$|^ia-32$|^bepc$', raw_arch_string): - arch = 'X86_32' - bits = 32 - elif re.match('^x64$|^x86_64$|^x86_64t$|^i686-64$|^amd64$|^ia64$|^ia-64$', raw_arch_string): - arch = 'X86_64' - bits = 64 - # ARM - elif re.match('^armv8-a|aarch64$', raw_arch_string): - arch = 'ARM_8' - bits = 64 - elif re.match('^armv7$|^armv7[a-z]$|^armv7-[a-z]$|^armv6[a-z]$', raw_arch_string): - arch = 'ARM_7' - bits = 32 - elif re.match('^armv8$|^armv8[a-z]$|^armv8-[a-z]$', raw_arch_string): - arch = 'ARM_8' - bits = 32 - # PPC - elif re.match('^ppc32$|^prep$|^pmac$|^powermac$', raw_arch_string): - arch = 'PPC_32' - bits = 32 - elif re.match('^powerpc$|^ppc64$|^ppc64le$', raw_arch_string): - arch = 'PPC_64' - bits = 64 - # SPARC - elif re.match('^sparc32$|^sparc$', raw_arch_string): - arch = 'SPARC_32' - bits = 32 - elif re.match('^sparc64$|^sun4u$|^sun4v$', raw_arch_string): - arch = 'SPARC_64' - bits = 64 - - return (arch, bits) - -def _is_bit_set(reg, bit): - mask = 1 << bit - is_set = reg & mask > 0 - return is_set - - -class CPUID(object): - def __init__(self): - self.prochandle = None - - # Figure out if SE Linux is on and in enforcing mode - self.is_selinux_enforcing = False - - # Just return if the SE Linux Status Tool is not installed - if not DataSource.has_sestatus(): - return - - # Figure out if we can execute heap and execute memory - can_selinux_exec_heap = DataSource.sestatus_allow_execheap() - can_selinux_exec_memory = DataSource.sestatus_allow_execmem() - self.is_selinux_enforcing = (not can_selinux_exec_heap or not can_selinux_exec_memory) - - def _asm_func(self, restype=None, argtypes=(), byte_code=[]): - byte_code = bytes.join(b'', byte_code) - address = None - - if DataSource.is_windows: - # Allocate a memory segment the size of the byte code, and make it executable - size = len(byte_code) - # Alloc at least 1 page to ensure we own all pages that we want to change protection on - if size < 0x1000: size = 0x1000 - MEM_COMMIT = ctypes.c_ulong(0x1000) - PAGE_READWRITE = ctypes.c_ulong(0x4) - pfnVirtualAlloc = ctypes.windll.kernel32.VirtualAlloc - pfnVirtualAlloc.restype = ctypes.c_void_p - address = pfnVirtualAlloc(None, ctypes.c_size_t(size), MEM_COMMIT, PAGE_READWRITE) - if not address: - raise Exception("Failed to VirtualAlloc") - - # Copy the byte code into the memory segment - memmove = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)(ctypes._memmove_addr) - if memmove(address, byte_code, size) < 0: - raise Exception("Failed to memmove") - - # Enable execute permissions - PAGE_EXECUTE = ctypes.c_ulong(0x10) - old_protect = ctypes.c_ulong(0) - pfnVirtualProtect = ctypes.windll.kernel32.VirtualProtect - res = pfnVirtualProtect(ctypes.c_void_p(address), ctypes.c_size_t(size), PAGE_EXECUTE, ctypes.byref(old_protect)) - if not res: - raise Exception("Failed VirtualProtect") - - # Flush Instruction Cache - # First, get process Handle - if not self.prochandle: - pfnGetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess - pfnGetCurrentProcess.restype = ctypes.c_void_p - self.prochandle = ctypes.c_void_p(pfnGetCurrentProcess()) - # Actually flush cache - res = ctypes.windll.kernel32.FlushInstructionCache(self.prochandle, ctypes.c_void_p(address), ctypes.c_size_t(size)) - if not res: - raise Exception("Failed FlushInstructionCache") - else: - # Allocate a memory segment the size of the byte code - size = len(byte_code) - pfnvalloc = ctypes.pythonapi.valloc - pfnvalloc.restype = ctypes.c_void_p - address = pfnvalloc(ctypes.c_size_t(size)) - if not address: - raise Exception("Failed to valloc") - - # Mark the memory segment as writeable only - if not self.is_selinux_enforcing: - WRITE = 0x2 - if ctypes.pythonapi.mprotect(ctypes.c_void_p(address), size, WRITE) < 0: - raise Exception("Failed to mprotect") - - # Copy the byte code into the memory segment - if ctypes.pythonapi.memmove(ctypes.c_void_p(address), byte_code, ctypes.c_size_t(size)) < 0: - raise Exception("Failed to memmove") - - # Mark the memory segment as writeable and executable only - if not self.is_selinux_enforcing: - WRITE_EXECUTE = 0x2 | 0x4 - if ctypes.pythonapi.mprotect(ctypes.c_void_p(address), size, WRITE_EXECUTE) < 0: - raise Exception("Failed to mprotect") - - # Cast the memory segment into a function - functype = ctypes.CFUNCTYPE(restype, *argtypes) - fun = functype(address) - return fun, address - - def _run_asm(self, *byte_code): - # Convert the byte code into a function that returns an int - restype = ctypes.c_uint32 - argtypes = () - func, address = self._asm_func(restype, argtypes, byte_code) - - # Call the byte code like a function - retval = func() - - byte_code = bytes.join(b'', byte_code) - size = ctypes.c_size_t(len(byte_code)) - - # Free the function memory segment - if DataSource.is_windows: - MEM_RELEASE = ctypes.c_ulong(0x8000) - ctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(address), ctypes.c_size_t(0), MEM_RELEASE) - else: - # Remove the executable tag on the memory - READ_WRITE = 0x1 | 0x2 - if ctypes.pythonapi.mprotect(ctypes.c_void_p(address), size, READ_WRITE) < 0: - raise Exception("Failed to mprotect") - - ctypes.pythonapi.free(ctypes.c_void_p(address)) - - return retval - - # FIXME: We should not have to use different instructions to - # set eax to 0 or 1, on 32bit and 64bit machines. - def _zero_eax(self): - return ( - b"\x31\xC0" # xor eax,eax - ) - - def _zero_ecx(self): - return ( - b"\x31\xC9" # xor ecx,ecx - ) - def _one_eax(self): - return ( - b"\xB8\x01\x00\x00\x00" # mov eax,0x1" - ) - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID - def get_vendor_id(self): - # EBX - ebx = self._run_asm( - self._zero_eax(), - b"\x0F\xA2" # cpuid - b"\x89\xD8" # mov ax,bx - b"\xC3" # ret - ) - - # ECX - ecx = self._run_asm( - self._zero_eax(), - b"\x0f\xa2" # cpuid - b"\x89\xC8" # mov ax,cx - b"\xC3" # ret - ) - - # EDX - edx = self._run_asm( - self._zero_eax(), - b"\x0f\xa2" # cpuid - b"\x89\xD0" # mov ax,dx - b"\xC3" # ret - ) - - # Each 4bits is a ascii letter in the name - vendor_id = [] - for reg in [ebx, edx, ecx]: - for n in [0, 8, 16, 24]: - vendor_id.append(chr((reg >> n) & 0xFF)) - vendor_id = ''.join(vendor_id) - - return vendor_id - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits - def get_info(self): - # EAX - eax = self._run_asm( - self._one_eax(), - b"\x0f\xa2" # cpuid - b"\xC3" # ret - ) - - # Get the CPU info - stepping = (eax >> 0) & 0xF # 4 bits - model = (eax >> 4) & 0xF # 4 bits - family = (eax >> 8) & 0xF # 4 bits - processor_type = (eax >> 12) & 0x3 # 2 bits - extended_model = (eax >> 16) & 0xF # 4 bits - extended_family = (eax >> 20) & 0xFF # 8 bits - - return { - 'stepping' : stepping, - 'model' : model, - 'family' : family, - 'processor_type' : processor_type, - 'extended_model' : extended_model, - 'extended_family' : extended_family - } - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000000h:_Get_Highest_Extended_Function_Supported - def get_max_extension_support(self): - # Check for extension support - max_extension_support = self._run_asm( - b"\xB8\x00\x00\x00\x80" # mov ax,0x80000000 - b"\x0f\xa2" # cpuid - b"\xC3" # ret - ) - - return max_extension_support - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits - def get_flags(self, max_extension_support): - # EDX - edx = self._run_asm( - self._one_eax(), - b"\x0f\xa2" # cpuid - b"\x89\xD0" # mov ax,dx - b"\xC3" # ret - ) - - # ECX - ecx = self._run_asm( - self._one_eax(), - b"\x0f\xa2" # cpuid - b"\x89\xC8" # mov ax,cx - b"\xC3" # ret - ) - - # Get the CPU flags - flags = { - 'fpu' : _is_bit_set(edx, 0), - 'vme' : _is_bit_set(edx, 1), - 'de' : _is_bit_set(edx, 2), - 'pse' : _is_bit_set(edx, 3), - 'tsc' : _is_bit_set(edx, 4), - 'msr' : _is_bit_set(edx, 5), - 'pae' : _is_bit_set(edx, 6), - 'mce' : _is_bit_set(edx, 7), - 'cx8' : _is_bit_set(edx, 8), - 'apic' : _is_bit_set(edx, 9), - #'reserved1' : _is_bit_set(edx, 10), - 'sep' : _is_bit_set(edx, 11), - 'mtrr' : _is_bit_set(edx, 12), - 'pge' : _is_bit_set(edx, 13), - 'mca' : _is_bit_set(edx, 14), - 'cmov' : _is_bit_set(edx, 15), - 'pat' : _is_bit_set(edx, 16), - 'pse36' : _is_bit_set(edx, 17), - 'pn' : _is_bit_set(edx, 18), - 'clflush' : _is_bit_set(edx, 19), - #'reserved2' : _is_bit_set(edx, 20), - 'dts' : _is_bit_set(edx, 21), - 'acpi' : _is_bit_set(edx, 22), - 'mmx' : _is_bit_set(edx, 23), - 'fxsr' : _is_bit_set(edx, 24), - 'sse' : _is_bit_set(edx, 25), - 'sse2' : _is_bit_set(edx, 26), - 'ss' : _is_bit_set(edx, 27), - 'ht' : _is_bit_set(edx, 28), - 'tm' : _is_bit_set(edx, 29), - 'ia64' : _is_bit_set(edx, 30), - 'pbe' : _is_bit_set(edx, 31), - - 'pni' : _is_bit_set(ecx, 0), - 'pclmulqdq' : _is_bit_set(ecx, 1), - 'dtes64' : _is_bit_set(ecx, 2), - 'monitor' : _is_bit_set(ecx, 3), - 'ds_cpl' : _is_bit_set(ecx, 4), - 'vmx' : _is_bit_set(ecx, 5), - 'smx' : _is_bit_set(ecx, 6), - 'est' : _is_bit_set(ecx, 7), - 'tm2' : _is_bit_set(ecx, 8), - 'ssse3' : _is_bit_set(ecx, 9), - 'cid' : _is_bit_set(ecx, 10), - #'reserved3' : _is_bit_set(ecx, 11), - 'fma' : _is_bit_set(ecx, 12), - 'cx16' : _is_bit_set(ecx, 13), - 'xtpr' : _is_bit_set(ecx, 14), - 'pdcm' : _is_bit_set(ecx, 15), - #'reserved4' : _is_bit_set(ecx, 16), - 'pcid' : _is_bit_set(ecx, 17), - 'dca' : _is_bit_set(ecx, 18), - 'sse4_1' : _is_bit_set(ecx, 19), - 'sse4_2' : _is_bit_set(ecx, 20), - 'x2apic' : _is_bit_set(ecx, 21), - 'movbe' : _is_bit_set(ecx, 22), - 'popcnt' : _is_bit_set(ecx, 23), - 'tscdeadline' : _is_bit_set(ecx, 24), - 'aes' : _is_bit_set(ecx, 25), - 'xsave' : _is_bit_set(ecx, 26), - 'osxsave' : _is_bit_set(ecx, 27), - 'avx' : _is_bit_set(ecx, 28), - 'f16c' : _is_bit_set(ecx, 29), - 'rdrnd' : _is_bit_set(ecx, 30), - 'hypervisor' : _is_bit_set(ecx, 31) - } - - # Get a list of only the flags that are true - flags = [k for k, v in flags.items() if v] - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D7.2C_ECX.3D0:_Extended_Features - if max_extension_support >= 7: - # EBX - ebx = self._run_asm( - self._zero_ecx(), - b"\xB8\x07\x00\x00\x00" # mov eax,7 - b"\x0f\xa2" # cpuid - b"\x89\xD8" # mov ax,bx - b"\xC3" # ret - ) - - # ECX - ecx = self._run_asm( - self._zero_ecx(), - b"\xB8\x07\x00\x00\x00" # mov eax,7 - b"\x0f\xa2" # cpuid - b"\x89\xC8" # mov ax,cx - b"\xC3" # ret - ) - - # Get the extended CPU flags - extended_flags = { - #'fsgsbase' : _is_bit_set(ebx, 0), - #'IA32_TSC_ADJUST' : _is_bit_set(ebx, 1), - 'sgx' : _is_bit_set(ebx, 2), - 'bmi1' : _is_bit_set(ebx, 3), - 'hle' : _is_bit_set(ebx, 4), - 'avx2' : _is_bit_set(ebx, 5), - #'reserved' : _is_bit_set(ebx, 6), - 'smep' : _is_bit_set(ebx, 7), - 'bmi2' : _is_bit_set(ebx, 8), - 'erms' : _is_bit_set(ebx, 9), - 'invpcid' : _is_bit_set(ebx, 10), - 'rtm' : _is_bit_set(ebx, 11), - 'pqm' : _is_bit_set(ebx, 12), - #'FPU CS and FPU DS deprecated' : _is_bit_set(ebx, 13), - 'mpx' : _is_bit_set(ebx, 14), - 'pqe' : _is_bit_set(ebx, 15), - 'avx512f' : _is_bit_set(ebx, 16), - 'avx512dq' : _is_bit_set(ebx, 17), - 'rdseed' : _is_bit_set(ebx, 18), - 'adx' : _is_bit_set(ebx, 19), - 'smap' : _is_bit_set(ebx, 20), - 'avx512ifma' : _is_bit_set(ebx, 21), - 'pcommit' : _is_bit_set(ebx, 22), - 'clflushopt' : _is_bit_set(ebx, 23), - 'clwb' : _is_bit_set(ebx, 24), - 'intel_pt' : _is_bit_set(ebx, 25), - 'avx512pf' : _is_bit_set(ebx, 26), - 'avx512er' : _is_bit_set(ebx, 27), - 'avx512cd' : _is_bit_set(ebx, 28), - 'sha' : _is_bit_set(ebx, 29), - 'avx512bw' : _is_bit_set(ebx, 30), - 'avx512vl' : _is_bit_set(ebx, 31), - - 'prefetchwt1' : _is_bit_set(ecx, 0), - 'avx512vbmi' : _is_bit_set(ecx, 1), - 'umip' : _is_bit_set(ecx, 2), - 'pku' : _is_bit_set(ecx, 3), - 'ospke' : _is_bit_set(ecx, 4), - #'reserved' : _is_bit_set(ecx, 5), - 'avx512vbmi2' : _is_bit_set(ecx, 6), - #'reserved' : _is_bit_set(ecx, 7), - 'gfni' : _is_bit_set(ecx, 8), - 'vaes' : _is_bit_set(ecx, 9), - 'vpclmulqdq' : _is_bit_set(ecx, 10), - 'avx512vnni' : _is_bit_set(ecx, 11), - 'avx512bitalg' : _is_bit_set(ecx, 12), - #'reserved' : _is_bit_set(ecx, 13), - 'avx512vpopcntdq' : _is_bit_set(ecx, 14), - #'reserved' : _is_bit_set(ecx, 15), - #'reserved' : _is_bit_set(ecx, 16), - #'mpx0' : _is_bit_set(ecx, 17), - #'mpx1' : _is_bit_set(ecx, 18), - #'mpx2' : _is_bit_set(ecx, 19), - #'mpx3' : _is_bit_set(ecx, 20), - #'mpx4' : _is_bit_set(ecx, 21), - 'rdpid' : _is_bit_set(ecx, 22), - #'reserved' : _is_bit_set(ecx, 23), - #'reserved' : _is_bit_set(ecx, 24), - #'reserved' : _is_bit_set(ecx, 25), - #'reserved' : _is_bit_set(ecx, 26), - #'reserved' : _is_bit_set(ecx, 27), - #'reserved' : _is_bit_set(ecx, 28), - #'reserved' : _is_bit_set(ecx, 29), - 'sgx_lc' : _is_bit_set(ecx, 30), - #'reserved' : _is_bit_set(ecx, 31) - } - - # Get a list of only the flags that are true - extended_flags = [k for k, v in extended_flags.items() if v] - flags += extended_flags - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000001h:_Extended_Processor_Info_and_Feature_Bits - if max_extension_support >= 0x80000001: - # EBX - ebx = self._run_asm( - b"\xB8\x01\x00\x00\x80" # mov ax,0x80000001 - b"\x0f\xa2" # cpuid - b"\x89\xD8" # mov ax,bx - b"\xC3" # ret - ) - - # ECX - ecx = self._run_asm( - b"\xB8\x01\x00\x00\x80" # mov ax,0x80000001 - b"\x0f\xa2" # cpuid - b"\x89\xC8" # mov ax,cx - b"\xC3" # ret - ) - - # Get the extended CPU flags - extended_flags = { - 'fpu' : _is_bit_set(ebx, 0), - 'vme' : _is_bit_set(ebx, 1), - 'de' : _is_bit_set(ebx, 2), - 'pse' : _is_bit_set(ebx, 3), - 'tsc' : _is_bit_set(ebx, 4), - 'msr' : _is_bit_set(ebx, 5), - 'pae' : _is_bit_set(ebx, 6), - 'mce' : _is_bit_set(ebx, 7), - 'cx8' : _is_bit_set(ebx, 8), - 'apic' : _is_bit_set(ebx, 9), - #'reserved' : _is_bit_set(ebx, 10), - 'syscall' : _is_bit_set(ebx, 11), - 'mtrr' : _is_bit_set(ebx, 12), - 'pge' : _is_bit_set(ebx, 13), - 'mca' : _is_bit_set(ebx, 14), - 'cmov' : _is_bit_set(ebx, 15), - 'pat' : _is_bit_set(ebx, 16), - 'pse36' : _is_bit_set(ebx, 17), - #'reserved' : _is_bit_set(ebx, 18), - 'mp' : _is_bit_set(ebx, 19), - 'nx' : _is_bit_set(ebx, 20), - #'reserved' : _is_bit_set(ebx, 21), - 'mmxext' : _is_bit_set(ebx, 22), - 'mmx' : _is_bit_set(ebx, 23), - 'fxsr' : _is_bit_set(ebx, 24), - 'fxsr_opt' : _is_bit_set(ebx, 25), - 'pdpe1gp' : _is_bit_set(ebx, 26), - 'rdtscp' : _is_bit_set(ebx, 27), - #'reserved' : _is_bit_set(ebx, 28), - 'lm' : _is_bit_set(ebx, 29), - '3dnowext' : _is_bit_set(ebx, 30), - '3dnow' : _is_bit_set(ebx, 31), - - 'lahf_lm' : _is_bit_set(ecx, 0), - 'cmp_legacy' : _is_bit_set(ecx, 1), - 'svm' : _is_bit_set(ecx, 2), - 'extapic' : _is_bit_set(ecx, 3), - 'cr8_legacy' : _is_bit_set(ecx, 4), - 'abm' : _is_bit_set(ecx, 5), - 'sse4a' : _is_bit_set(ecx, 6), - 'misalignsse' : _is_bit_set(ecx, 7), - '3dnowprefetch' : _is_bit_set(ecx, 8), - 'osvw' : _is_bit_set(ecx, 9), - 'ibs' : _is_bit_set(ecx, 10), - 'xop' : _is_bit_set(ecx, 11), - 'skinit' : _is_bit_set(ecx, 12), - 'wdt' : _is_bit_set(ecx, 13), - #'reserved' : _is_bit_set(ecx, 14), - 'lwp' : _is_bit_set(ecx, 15), - 'fma4' : _is_bit_set(ecx, 16), - 'tce' : _is_bit_set(ecx, 17), - #'reserved' : _is_bit_set(ecx, 18), - 'nodeid_msr' : _is_bit_set(ecx, 19), - #'reserved' : _is_bit_set(ecx, 20), - 'tbm' : _is_bit_set(ecx, 21), - 'topoext' : _is_bit_set(ecx, 22), - 'perfctr_core' : _is_bit_set(ecx, 23), - 'perfctr_nb' : _is_bit_set(ecx, 24), - #'reserved' : _is_bit_set(ecx, 25), - 'dbx' : _is_bit_set(ecx, 26), - 'perftsc' : _is_bit_set(ecx, 27), - 'pci_l2i' : _is_bit_set(ecx, 28), - #'reserved' : _is_bit_set(ecx, 29), - #'reserved' : _is_bit_set(ecx, 30), - #'reserved' : _is_bit_set(ecx, 31) - } - - # Get a list of only the flags that are true - extended_flags = [k for k, v in extended_flags.items() if v] - flags += extended_flags - - flags.sort() - return flags - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000002h.2C80000003h.2C80000004h:_Processor_Brand_String - def get_processor_brand(self, max_extension_support): - processor_brand = "" - - # Processor brand string - if max_extension_support >= 0x80000004: - instructions = [ - b"\xB8\x02\x00\x00\x80", # mov ax,0x80000002 - b"\xB8\x03\x00\x00\x80", # mov ax,0x80000003 - b"\xB8\x04\x00\x00\x80" # mov ax,0x80000004 - ] - for instruction in instructions: - # EAX - eax = self._run_asm( - instruction, # mov ax,0x8000000? - b"\x0f\xa2" # cpuid - b"\x89\xC0" # mov ax,ax - b"\xC3" # ret - ) - - # EBX - ebx = self._run_asm( - instruction, # mov ax,0x8000000? - b"\x0f\xa2" # cpuid - b"\x89\xD8" # mov ax,bx - b"\xC3" # ret - ) - - # ECX - ecx = self._run_asm( - instruction, # mov ax,0x8000000? - b"\x0f\xa2" # cpuid - b"\x89\xC8" # mov ax,cx - b"\xC3" # ret - ) - - # EDX - edx = self._run_asm( - instruction, # mov ax,0x8000000? - b"\x0f\xa2" # cpuid - b"\x89\xD0" # mov ax,dx - b"\xC3" # ret - ) - - # Combine each of the 4 bytes in each register into the string - for reg in [eax, ebx, ecx, edx]: - for n in [0, 8, 16, 24]: - processor_brand += chr((reg >> n) & 0xFF) - - # Strip off any trailing NULL terminators and white space - processor_brand = processor_brand.strip("\0").strip() - - return processor_brand - - # http://en.wikipedia.org/wiki/CPUID#EAX.3D80000006h:_Extended_L2_Cache_Features - def get_cache(self, max_extension_support): - cache_info = {} - - # Just return if the cache feature is not supported - if max_extension_support < 0x80000006: - return cache_info - - # ECX - ecx = self._run_asm( - b"\xB8\x06\x00\x00\x80" # mov ax,0x80000006 - b"\x0f\xa2" # cpuid - b"\x89\xC8" # mov ax,cx - b"\xC3" # ret - ) - - cache_info = { - 'size_kb' : ecx & 0xFF, - 'line_size_b' : (ecx >> 12) & 0xF, - 'associativity' : (ecx >> 16) & 0xFFFF - } - - return cache_info - - def get_ticks(self): - retval = None - - if DataSource.bits == '32bit': - # Works on x86_32 - restype = None - argtypes = (ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)) - get_ticks_x86_32, address = self._asm_func(restype, argtypes, - [ - b"\x55", # push bp - b"\x89\xE5", # mov bp,sp - b"\x31\xC0", # xor ax,ax - b"\x0F\xA2", # cpuid - b"\x0F\x31", # rdtsc - b"\x8B\x5D\x08", # mov bx,[di+0x8] - b"\x8B\x4D\x0C", # mov cx,[di+0xc] - b"\x89\x13", # mov [bp+di],dx - b"\x89\x01", # mov [bx+di],ax - b"\x5D", # pop bp - b"\xC3" # ret - ] - ) - - high = ctypes.c_uint32(0) - low = ctypes.c_uint32(0) - - get_ticks_x86_32(ctypes.byref(high), ctypes.byref(low)) - retval = ((high.value << 32) & 0xFFFFFFFF00000000) | low.value - elif DataSource.bits == '64bit': - # Works on x86_64 - restype = ctypes.c_uint64 - argtypes = () - get_ticks_x86_64, address = self._asm_func(restype, argtypes, - [ - b"\x48", # dec ax - b"\x31\xC0", # xor ax,ax - b"\x0F\xA2", # cpuid - b"\x0F\x31", # rdtsc - b"\x48", # dec ax - b"\xC1\xE2\x20", # shl dx,byte 0x20 - b"\x48", # dec ax - b"\x09\xD0", # or ax,dx - b"\xC3", # ret - ] - ) - retval = get_ticks_x86_64() - - return retval - - def get_raw_hz(self): - import time - - start = self.get_ticks() - - time.sleep(1) - - end = self.get_ticks() - - ticks = (end - start) - - return ticks - -def _actual_get_cpu_info_from_cpuid(queue): - ''' - Warning! This function has the potential to crash the Python runtime. - Do not call it directly. Use the _get_cpu_info_from_cpuid function instead. - It will safely call this function in another process. - ''' - - # Pipe all output to nothing - sys.stdout = open(os.devnull, 'w') - sys.stderr = open(os.devnull, 'w') - - # Get the CPU arch and bits - arch, bits = _parse_arch(DataSource.raw_arch_string) - - # Return none if this is not an X86 CPU - if not arch in ['X86_32', 'X86_64']: - queue.put(_obj_to_b64({})) - return - - # Return none if SE Linux is in enforcing mode - cpuid = CPUID() - if cpuid.is_selinux_enforcing: - queue.put(_obj_to_b64({})) - return - - # Get the cpu info from the CPUID register - max_extension_support = cpuid.get_max_extension_support() - cache_info = cpuid.get_cache(max_extension_support) - info = cpuid.get_info() - - processor_brand = cpuid.get_processor_brand(max_extension_support) - - # Get the Hz and scale - hz_actual = cpuid.get_raw_hz() - hz_actual = _to_hz_string(hz_actual) - - # Get the Hz and scale - scale, hz_advertised = _get_hz_string_from_brand(processor_brand) - info = { - 'vendor_id' : cpuid.get_vendor_id(), - 'hardware' : '', - 'brand' : processor_brand, - - 'hz_advertised' : _to_friendly_hz(hz_advertised, scale), - 'hz_actual' : _to_friendly_hz(hz_actual, 0), - 'hz_advertised_raw' : _to_raw_hz(hz_advertised, scale), - 'hz_actual_raw' : _to_raw_hz(hz_actual, 0), - - 'l2_cache_size' : _to_friendly_bytes(cache_info['size_kb']), - 'l2_cache_line_size' : cache_info['line_size_b'], - 'l2_cache_associativity' : hex(cache_info['associativity']), - - 'stepping' : info['stepping'], - 'model' : info['model'], - 'family' : info['family'], - 'processor_type' : info['processor_type'], - 'extended_model' : info['extended_model'], - 'extended_family' : info['extended_family'], - 'flags' : cpuid.get_flags(max_extension_support) - } - - info = {k: v for k, v in info.items() if v} - queue.put(_obj_to_b64(info)) - -def _get_cpu_info_from_cpuid(): - ''' - Returns the CPU info gathered by querying the X86 cpuid register in a new process. - Returns {} on non X86 cpus. - Returns {} if SELinux is in enforcing mode. - ''' - from multiprocessing import Process, Queue - - # Return {} if can't cpuid - if not DataSource.can_cpuid: - return {} - - # Get the CPU arch and bits - arch, bits = _parse_arch(DataSource.raw_arch_string) - - # Return {} if this is not an X86 CPU - if not arch in ['X86_32', 'X86_64']: - return {} - - try: - # Start running the function in a subprocess - queue = Queue() - p = Process(target=_actual_get_cpu_info_from_cpuid, args=(queue,)) - p.start() - - # Wait for the process to end, while it is still alive - while p.is_alive(): - p.join(0) - - # Return {} if it failed - if p.exitcode != 0: - return {} - - # Return the result, only if there is something to read - if not queue.empty(): - output = queue.get() - return _b64_to_obj(output) - except: - pass - - # Return {} if everything failed - return {} - -def _get_cpu_info_from_proc_cpuinfo(): - ''' - Returns the CPU info gathered from /proc/cpuinfo. - Returns {} if /proc/cpuinfo is not found. - ''' - try: - # Just return {} if there is no cpuinfo - if not DataSource.has_proc_cpuinfo(): - return {} - - returncode, output = DataSource.cat_proc_cpuinfo() - if returncode != 0: - return {} - - # Various fields - vendor_id = _get_field(False, output, None, '', 'vendor_id', 'vendor id', 'vendor') - processor_brand = _get_field(True, output, None, None, 'model name','cpu', 'processor') - cache_size = _get_field(False, output, None, '', 'cache size') - stepping = _get_field(False, output, int, 0, 'stepping') - model = _get_field(False, output, int, 0, 'model') - family = _get_field(False, output, int, 0, 'cpu family') - hardware = _get_field(False, output, None, '', 'Hardware') - # Flags - flags = _get_field(False, output, None, None, 'flags', 'Features') - if flags: - flags = flags.split() - flags.sort() - - # Convert from MHz string to Hz - hz_actual = _get_field(False, output, None, '', 'cpu MHz', 'cpu speed', 'clock') - hz_actual = hz_actual.lower().rstrip('mhz').strip() - hz_actual = _to_hz_string(hz_actual) - - # Convert from GHz/MHz string to Hz - scale, hz_advertised = (0, None) - try: - scale, hz_advertised = _get_hz_string_from_brand(processor_brand) - except Exception: - pass - - info = { - 'hardware' : hardware, - 'brand' : processor_brand, - - 'l3_cache_size' : _to_friendly_bytes(cache_size), - 'flags' : flags, - 'vendor_id' : vendor_id, - 'stepping' : stepping, - 'model' : model, - 'family' : family, - } - - # Make the Hz the same for actual and advertised if missing any - if not hz_advertised or hz_advertised == '0.0': - hz_advertised = hz_actual - scale = 6 - elif not hz_actual or hz_actual == '0.0': - hz_actual = hz_advertised - - # Add the Hz if there is one - if _to_raw_hz(hz_advertised, scale) > (0, 0): - info['hz_advertised'] = _to_friendly_hz(hz_advertised, scale) - info['hz_advertised_raw'] = _to_raw_hz(hz_advertised, scale) - if _to_raw_hz(hz_actual, scale) > (0, 0): - info['hz_actual'] = _to_friendly_hz(hz_actual, 6) - info['hz_actual_raw'] = _to_raw_hz(hz_actual, 6) - - info = {k: v for k, v in info.items() if v} - return info - except: - #raise # NOTE: To have this throw on error, uncomment this line - return {} - -def _get_cpu_info_from_cpufreq_info(): - ''' - Returns the CPU info gathered from cpufreq-info. - Returns {} if cpufreq-info is not found. - ''' - try: - scale, hz_brand = 1, '0.0' - - if not DataSource.has_cpufreq_info(): - return {} - - returncode, output = DataSource.cpufreq_info() - if returncode != 0: - return {} - - hz_brand = output.split('current CPU frequency is')[1].split('\n')[0] - i = hz_brand.find('Hz') - assert(i != -1) - hz_brand = hz_brand[0 : i+2].strip().lower() - - if hz_brand.endswith('mhz'): - scale = 6 - elif hz_brand.endswith('ghz'): - scale = 9 - hz_brand = hz_brand.rstrip('mhz').rstrip('ghz').strip() - hz_brand = _to_hz_string(hz_brand) - - info = { - 'hz_advertised' : _to_friendly_hz(hz_brand, scale), - 'hz_actual' : _to_friendly_hz(hz_brand, scale), - 'hz_advertised_raw' : _to_raw_hz(hz_brand, scale), - 'hz_actual_raw' : _to_raw_hz(hz_brand, scale), - } - - info = {k: v for k, v in info.items() if v} - return info - except: - #raise # NOTE: To have this throw on error, uncomment this line - return {} - -def _get_cpu_info_from_lscpu(): - ''' - Returns the CPU info gathered from lscpu. - Returns {} if lscpu is not found. - ''' - try: - if not DataSource.has_lscpu(): - return {} - - returncode, output = DataSource.lscpu() - if returncode != 0: - return {} - - info = {} - - new_hz = _get_field(False, output, None, None, 'CPU max MHz', 'CPU MHz') - if new_hz: - new_hz = _to_hz_string(new_hz) - scale = 6 - info['hz_advertised'] = _to_friendly_hz(new_hz, scale) - info['hz_actual'] = _to_friendly_hz(new_hz, scale) - info['hz_advertised_raw'] = _to_raw_hz(new_hz, scale) - info['hz_actual_raw'] = _to_raw_hz(new_hz, scale) - - vendor_id = _get_field(False, output, None, None, 'Vendor ID') - if vendor_id: - info['vendor_id'] = vendor_id - - brand = _get_field(False, output, None, None, 'Model name') - if brand: - info['brand'] = brand - - family = _get_field(False, output, None, None, 'CPU family') - if family and family.isdigit(): - info['family'] = int(family) - - stepping = _get_field(False, output, None, None, 'Stepping') - if stepping and stepping.isdigit(): - info['stepping'] = int(stepping) - - model = _get_field(False, output, None, None, 'Model') - if model and model.isdigit(): - info['model'] = int(model) - - l1_data_cache_size = _get_field(False, output, None, None, 'L1d cache') - if l1_data_cache_size: - info['l1_data_cache_size'] = _to_friendly_bytes(l1_data_cache_size) - - l1_instruction_cache_size = _get_field(False, output, None, None, 'L1i cache') - if l1_instruction_cache_size: - info['l1_instruction_cache_size'] = _to_friendly_bytes(l1_instruction_cache_size) - - l2_cache_size = _get_field(False, output, None, None, 'L2 cache') - if l2_cache_size: - info['l2_cache_size'] = _to_friendly_bytes(l2_cache_size) - - l3_cache_size = _get_field(False, output, None, None, 'L3 cache') - if l3_cache_size: - info['l3_cache_size'] = _to_friendly_bytes(l3_cache_size) - - # Flags - flags = _get_field(False, output, None, None, 'flags', 'Features') - if flags: - flags = flags.split() - flags.sort() - info['flags'] = flags - - info = {k: v for k, v in info.items() if v} - return info - except: - #raise # NOTE: To have this throw on error, uncomment this line - return {} - -def _get_cpu_info_from_dmesg(): - ''' - Returns the CPU info gathered from dmesg. - Returns {} if dmesg is not found or does not have the desired info. - ''' - # Just return {} if there is no dmesg - if not DataSource.has_dmesg(): - return {} - - # If dmesg fails return {} - returncode, output = DataSource.dmesg_a() - if output == None or returncode != 0: - return {} - - return _parse_dmesg_output(output) - - -# https://openpowerfoundation.org/wp-content/uploads/2016/05/LoPAPR_DRAFT_v11_24March2016_cmt1.pdf -# page 767 -def _get_cpu_info_from_ibm_pa_features(): - ''' - Returns the CPU info gathered from lsprop /proc/device-tree/cpus/*/ibm,pa-features - Returns {} if lsprop is not found or ibm,pa-features does not have the desired info. - ''' - try: - # Just return {} if there is no lsprop - if not DataSource.has_ibm_pa_features(): - return {} - - # If ibm,pa-features fails return {} - returncode, output = DataSource.ibm_pa_features() - if output == None or returncode != 0: - return {} - - # Filter out invalid characters from output - value = output.split("ibm,pa-features")[1].lower() - value = [s for s in value if s in list('0123456789abcfed')] - value = ''.join(value) - - # Get data converted to Uint32 chunks - left = int(value[0 : 8], 16) - right = int(value[8 : 16], 16) - - # Get the CPU flags - flags = { - # Byte 0 - 'mmu' : _is_bit_set(left, 0), - 'fpu' : _is_bit_set(left, 1), - 'slb' : _is_bit_set(left, 2), - 'run' : _is_bit_set(left, 3), - #'reserved' : _is_bit_set(left, 4), - 'dabr' : _is_bit_set(left, 5), - 'ne' : _is_bit_set(left, 6), - 'wtr' : _is_bit_set(left, 7), - - # Byte 1 - 'mcr' : _is_bit_set(left, 8), - 'dsisr' : _is_bit_set(left, 9), - 'lp' : _is_bit_set(left, 10), - 'ri' : _is_bit_set(left, 11), - 'dabrx' : _is_bit_set(left, 12), - 'sprg3' : _is_bit_set(left, 13), - 'rislb' : _is_bit_set(left, 14), - 'pp' : _is_bit_set(left, 15), - - # Byte 2 - 'vpm' : _is_bit_set(left, 16), - 'dss_2.05' : _is_bit_set(left, 17), - #'reserved' : _is_bit_set(left, 18), - 'dar' : _is_bit_set(left, 19), - #'reserved' : _is_bit_set(left, 20), - 'ppr' : _is_bit_set(left, 21), - 'dss_2.02' : _is_bit_set(left, 22), - 'dss_2.06' : _is_bit_set(left, 23), - - # Byte 3 - 'lsd_in_dscr' : _is_bit_set(left, 24), - 'ugr_in_dscr' : _is_bit_set(left, 25), - #'reserved' : _is_bit_set(left, 26), - #'reserved' : _is_bit_set(left, 27), - #'reserved' : _is_bit_set(left, 28), - #'reserved' : _is_bit_set(left, 29), - #'reserved' : _is_bit_set(left, 30), - #'reserved' : _is_bit_set(left, 31), - - # Byte 4 - 'sso_2.06' : _is_bit_set(right, 0), - #'reserved' : _is_bit_set(right, 1), - #'reserved' : _is_bit_set(right, 2), - #'reserved' : _is_bit_set(right, 3), - #'reserved' : _is_bit_set(right, 4), - #'reserved' : _is_bit_set(right, 5), - #'reserved' : _is_bit_set(right, 6), - #'reserved' : _is_bit_set(right, 7), - - # Byte 5 - 'le' : _is_bit_set(right, 8), - 'cfar' : _is_bit_set(right, 9), - 'eb' : _is_bit_set(right, 10), - 'lsq_2.07' : _is_bit_set(right, 11), - #'reserved' : _is_bit_set(right, 12), - #'reserved' : _is_bit_set(right, 13), - #'reserved' : _is_bit_set(right, 14), - #'reserved' : _is_bit_set(right, 15), - - # Byte 6 - 'dss_2.07' : _is_bit_set(right, 16), - #'reserved' : _is_bit_set(right, 17), - #'reserved' : _is_bit_set(right, 18), - #'reserved' : _is_bit_set(right, 19), - #'reserved' : _is_bit_set(right, 20), - #'reserved' : _is_bit_set(right, 21), - #'reserved' : _is_bit_set(right, 22), - #'reserved' : _is_bit_set(right, 23), - - # Byte 7 - #'reserved' : _is_bit_set(right, 24), - #'reserved' : _is_bit_set(right, 25), - #'reserved' : _is_bit_set(right, 26), - #'reserved' : _is_bit_set(right, 27), - #'reserved' : _is_bit_set(right, 28), - #'reserved' : _is_bit_set(right, 29), - #'reserved' : _is_bit_set(right, 30), - #'reserved' : _is_bit_set(right, 31), - } - - # Get a list of only the flags that are true - flags = [k for k, v in flags.items() if v] - flags.sort() - - info = { - 'flags' : flags - } - info = {k: v for k, v in info.items() if v} - - return info - except: - return {} - - -def _get_cpu_info_from_cat_var_run_dmesg_boot(): - ''' - Returns the CPU info gathered from /var/run/dmesg.boot. - Returns {} if dmesg is not found or does not have the desired info. - ''' - # Just return {} if there is no /var/run/dmesg.boot - if not DataSource.has_var_run_dmesg_boot(): - return {} - - # If dmesg.boot fails return {} - returncode, output = DataSource.cat_var_run_dmesg_boot() - if output == None or returncode != 0: - return {} - - return _parse_dmesg_output(output) - - -def _get_cpu_info_from_sysctl(): - ''' - Returns the CPU info gathered from sysctl. - Returns {} if sysctl is not found. - ''' - try: - # Just return {} if there is no sysctl - if not DataSource.has_sysctl(): - return {} - - # If sysctl fails return {} - returncode, output = DataSource.sysctl_machdep_cpu_hw_cpufrequency() - if output == None or returncode != 0: - return {} - - # Various fields - vendor_id = _get_field(False, output, None, None, 'machdep.cpu.vendor') - processor_brand = _get_field(True, output, None, None, 'machdep.cpu.brand_string') - cache_size = _get_field(False, output, None, None, 'machdep.cpu.cache.size') - stepping = _get_field(False, output, int, 0, 'machdep.cpu.stepping') - model = _get_field(False, output, int, 0, 'machdep.cpu.model') - family = _get_field(False, output, int, 0, 'machdep.cpu.family') - - # Flags - flags = _get_field(False, output, None, '', 'machdep.cpu.features').lower().split() - flags.extend(_get_field(False, output, None, '', 'machdep.cpu.leaf7_features').lower().split()) - flags.extend(_get_field(False, output, None, '', 'machdep.cpu.extfeatures').lower().split()) - flags.sort() - - # Convert from GHz/MHz string to Hz - scale, hz_advertised = _get_hz_string_from_brand(processor_brand) - hz_actual = _get_field(False, output, None, None, 'hw.cpufrequency') - hz_actual = _to_hz_string(hz_actual) - - info = { - 'vendor_id' : vendor_id, - 'brand' : processor_brand, - - 'hz_advertised' : _to_friendly_hz(hz_advertised, scale), - 'hz_actual' : _to_friendly_hz(hz_actual, 0), - 'hz_advertised_raw' : _to_raw_hz(hz_advertised, scale), - 'hz_actual_raw' : _to_raw_hz(hz_actual, 0), - - 'l2_cache_size' : _to_friendly_bytes(cache_size), - - 'stepping' : stepping, - 'model' : model, - 'family' : family, - 'flags' : flags - } - - info = {k: v for k, v in info.items() if v} - return info - except: - return {} - - -def _get_cpu_info_from_sysinfo(): - ''' - Returns the CPU info gathered from sysinfo. - Returns {} if sysinfo is not found. - ''' - info = _get_cpu_info_from_sysinfo_v1() - info.update(_get_cpu_info_from_sysinfo_v2()) - return info - -def _get_cpu_info_from_sysinfo_v1(): - ''' - Returns the CPU info gathered from sysinfo. - Returns {} if sysinfo is not found. - ''' - try: - # Just return {} if there is no sysinfo - if not DataSource.has_sysinfo(): - return {} - - # If sysinfo fails return {} - returncode, output = DataSource.sysinfo_cpu() - if output == None or returncode != 0: - return {} - - # Various fields - vendor_id = '' #_get_field(False, output, None, None, 'CPU #0: ') - processor_brand = output.split('CPU #0: "')[1].split('"\n')[0] - cache_size = '' #_get_field(False, output, None, None, 'machdep.cpu.cache.size') - stepping = int(output.split(', stepping ')[1].split(',')[0].strip()) - model = int(output.split(', model ')[1].split(',')[0].strip()) - family = int(output.split(', family ')[1].split(',')[0].strip()) - - # Flags - flags = [] - for line in output.split('\n'): - if line.startswith('\t\t'): - for flag in line.strip().lower().split(): - flags.append(flag) - flags.sort() - - # Convert from GHz/MHz string to Hz - scale, hz_advertised = _get_hz_string_from_brand(processor_brand) - hz_actual = hz_advertised - - info = { - 'vendor_id' : vendor_id, - 'brand' : processor_brand, - - 'hz_advertised' : _to_friendly_hz(hz_advertised, scale), - 'hz_actual' : _to_friendly_hz(hz_actual, scale), - 'hz_advertised_raw' : _to_raw_hz(hz_advertised, scale), - 'hz_actual_raw' : _to_raw_hz(hz_actual, scale), - - 'l2_cache_size' : _to_friendly_bytes(cache_size), - - 'stepping' : stepping, - 'model' : model, - 'family' : family, - 'flags' : flags - } - - info = {k: v for k, v in info.items() if v} - return info - except: - return {} - -def _get_cpu_info_from_sysinfo_v2(): - ''' - Returns the CPU info gathered from sysinfo. - Returns {} if sysinfo is not found. - ''' - try: - # Just return {} if there is no sysinfo - if not DataSource.has_sysinfo(): - return {} - - # If sysinfo fails return {} - returncode, output = DataSource.sysinfo_cpu() - if output == None or returncode != 0: - return {} - - # Various fields - vendor_id = '' #_get_field(False, output, None, None, 'CPU #0: ') - processor_brand = output.split('CPU #0: "')[1].split('"\n')[0] - cache_size = '' #_get_field(False, output, None, None, 'machdep.cpu.cache.size') - signature = output.split('Signature:')[1].split('\n')[0].strip() - # - stepping = int(signature.split('stepping ')[1].split(',')[0].strip()) - model = int(signature.split('model ')[1].split(',')[0].strip()) - family = int(signature.split('family ')[1].split(',')[0].strip()) - - # Flags - def get_subsection_flags(output): - retval = [] - for line in output.split('\n')[1:]: - if not line.startswith(' '): break - for entry in line.strip().lower().split(' '): - retval.append(entry) - return retval - - flags = get_subsection_flags(output.split('Features: ')[1]) + \ - get_subsection_flags(output.split('Extended Features (0x00000001): ')[1]) + \ - get_subsection_flags(output.split('Extended Features (0x80000001): ')[1]) - flags.sort() - - # Convert from GHz/MHz string to Hz - scale, hz_advertised = _get_hz_string_from_brand(processor_brand) - hz_actual = hz_advertised - - info = { - 'vendor_id' : vendor_id, - 'brand' : processor_brand, - - 'hz_advertised' : _to_friendly_hz(hz_advertised, scale), - 'hz_actual' : _to_friendly_hz(hz_actual, scale), - 'hz_advertised_raw' : _to_raw_hz(hz_advertised, scale), - 'hz_actual_raw' : _to_raw_hz(hz_actual, scale), - - 'l2_cache_size' : _to_friendly_bytes(cache_size), - - 'stepping' : stepping, - 'model' : model, - 'family' : family, - 'flags' : flags - } - - info = {k: v for k, v in info.items() if v} - return info - except: - return {} - -def _get_cpu_info_from_wmic(): - ''' - Returns the CPU info gathered from WMI. - Returns {} if not on Windows, or wmic is not installed. - ''' - - try: - # Just return {} if not Windows or there is no wmic - if not DataSource.is_windows or not DataSource.has_wmic(): - return {} - - returncode, output = DataSource.wmic_cpu() - if output == None or returncode != 0: - return {} - - # Break the list into key values pairs - value = output.split("\n") - value = [s.rstrip().split('=') for s in value if '=' in s] - value = {k: v for k, v in value if v} - - # Get the advertised MHz - processor_brand = value.get('Name') - scale_advertised, hz_advertised = _get_hz_string_from_brand(processor_brand) - - # Get the actual MHz - hz_actual = value.get('CurrentClockSpeed') - scale_actual = 6 - if hz_actual: - hz_actual = _to_hz_string(hz_actual) - - # Get cache sizes - l2_cache_size = value.get('L2CacheSize') - if l2_cache_size: - l2_cache_size = l2_cache_size + ' KB' - - l3_cache_size = value.get('L3CacheSize') - if l3_cache_size: - l3_cache_size = l3_cache_size + ' KB' - - # Get family, model, and stepping - family, model, stepping = '', '', '' - description = value.get('Description') or value.get('Caption') - entries = description.split(' ') - - if 'Family' in entries and entries.index('Family') < len(entries)-1: - i = entries.index('Family') - family = int(entries[i + 1]) - - if 'Model' in entries and entries.index('Model') < len(entries)-1: - i = entries.index('Model') - model = int(entries[i + 1]) - - if 'Stepping' in entries and entries.index('Stepping') < len(entries)-1: - i = entries.index('Stepping') - stepping = int(entries[i + 1]) - - info = { - 'vendor_id' : value.get('Manufacturer'), - 'brand' : processor_brand, - - 'hz_advertised' : _to_friendly_hz(hz_advertised, scale_advertised), - 'hz_actual' : _to_friendly_hz(hz_actual, scale_actual), - 'hz_advertised_raw' : _to_raw_hz(hz_advertised, scale_advertised), - 'hz_actual_raw' : _to_raw_hz(hz_actual, scale_actual), - - 'l2_cache_size' : l2_cache_size, - 'l3_cache_size' : l3_cache_size, - - 'stepping' : stepping, - 'model' : model, - 'family' : family, - } - - info = {k: v for k, v in info.items() if v} - return info - except: - #raise # NOTE: To have this throw on error, uncomment this line - return {} - -def _get_cpu_info_from_registry(): - ''' - FIXME: Is missing many of the newer CPU flags like sse3 - Returns the CPU info gathered from the Windows Registry. - Returns {} if not on Windows. - ''' - try: - # Just return {} if not on Windows - if not DataSource.is_windows: - return {} - - # Get the CPU name - processor_brand = DataSource.winreg_processor_brand() - - # Get the CPU vendor id - vendor_id = DataSource.winreg_vendor_id() - - # Get the CPU arch and bits - raw_arch_string = DataSource.winreg_raw_arch_string() - arch, bits = _parse_arch(raw_arch_string) - - # Get the actual CPU Hz - hz_actual = DataSource.winreg_hz_actual() - hz_actual = _to_hz_string(hz_actual) - - # Get the advertised CPU Hz - scale, hz_advertised = _get_hz_string_from_brand(processor_brand) - - # Get the CPU features - feature_bits = DataSource.winreg_feature_bits() - - def is_set(bit): - mask = 0x80000000 >> bit - retval = mask & feature_bits > 0 - return retval - - # http://en.wikipedia.org/wiki/CPUID - # http://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean - # http://www.lohninger.com/helpcsuite/public_constants_cpuid.htm - flags = { - 'fpu' : is_set(0), # Floating Point Unit - 'vme' : is_set(1), # V86 Mode Extensions - 'de' : is_set(2), # Debug Extensions - I/O breakpoints supported - 'pse' : is_set(3), # Page Size Extensions (4 MB pages supported) - 'tsc' : is_set(4), # Time Stamp Counter and RDTSC instruction are available - 'msr' : is_set(5), # Model Specific Registers - 'pae' : is_set(6), # Physical Address Extensions (36 bit address, 2MB pages) - 'mce' : is_set(7), # Machine Check Exception supported - 'cx8' : is_set(8), # Compare Exchange Eight Byte instruction available - 'apic' : is_set(9), # Local APIC present (multiprocessor operation support) - 'sepamd' : is_set(10), # Fast system calls (AMD only) - 'sep' : is_set(11), # Fast system calls - 'mtrr' : is_set(12), # Memory Type Range Registers - 'pge' : is_set(13), # Page Global Enable - 'mca' : is_set(14), # Machine Check Architecture - 'cmov' : is_set(15), # Conditional MOVe instructions - 'pat' : is_set(16), # Page Attribute Table - 'pse36' : is_set(17), # 36 bit Page Size Extensions - 'serial' : is_set(18), # Processor Serial Number - 'clflush' : is_set(19), # Cache Flush - #'reserved1' : is_set(20), # reserved - 'dts' : is_set(21), # Debug Trace Store - 'acpi' : is_set(22), # ACPI support - 'mmx' : is_set(23), # MultiMedia Extensions - 'fxsr' : is_set(24), # FXSAVE and FXRSTOR instructions - 'sse' : is_set(25), # SSE instructions - 'sse2' : is_set(26), # SSE2 (WNI) instructions - 'ss' : is_set(27), # self snoop - #'reserved2' : is_set(28), # reserved - 'tm' : is_set(29), # Automatic clock control - 'ia64' : is_set(30), # IA64 instructions - '3dnow' : is_set(31) # 3DNow! instructions available - } - - # Get a list of only the flags that are true - flags = [k for k, v in flags.items() if v] - flags.sort() - - info = { - 'vendor_id' : vendor_id, - 'brand' : processor_brand, - - 'hz_advertised' : _to_friendly_hz(hz_advertised, scale), - 'hz_actual' : _to_friendly_hz(hz_actual, 6), - 'hz_advertised_raw' : _to_raw_hz(hz_advertised, scale), - 'hz_actual_raw' : _to_raw_hz(hz_actual, 6), - - 'flags' : flags - } - - info = {k: v for k, v in info.items() if v} - return info - except: - return {} - -def _get_cpu_info_from_kstat(): - ''' - Returns the CPU info gathered from isainfo and kstat. - Returns {} if isainfo or kstat are not found. - ''' - try: - # Just return {} if there is no isainfo or kstat - if not DataSource.has_isainfo() or not DataSource.has_kstat(): - return {} - - # If isainfo fails return {} - returncode, flag_output = DataSource.isainfo_vb() - if flag_output == None or returncode != 0: - return {} - - # If kstat fails return {} - returncode, kstat = DataSource.kstat_m_cpu_info() - if kstat == None or returncode != 0: - return {} - - # Various fields - vendor_id = kstat.split('\tvendor_id ')[1].split('\n')[0].strip() - processor_brand = kstat.split('\tbrand ')[1].split('\n')[0].strip() - stepping = int(kstat.split('\tstepping ')[1].split('\n')[0].strip()) - model = int(kstat.split('\tmodel ')[1].split('\n')[0].strip()) - family = int(kstat.split('\tfamily ')[1].split('\n')[0].strip()) - - # Flags - flags = flag_output.strip().split('\n')[-1].strip().lower().split() - flags.sort() - - # Convert from GHz/MHz string to Hz - scale = 6 - hz_advertised = kstat.split('\tclock_MHz ')[1].split('\n')[0].strip() - hz_advertised = _to_hz_string(hz_advertised) - - # Convert from GHz/MHz string to Hz - hz_actual = kstat.split('\tcurrent_clock_Hz ')[1].split('\n')[0].strip() - hz_actual = _to_hz_string(hz_actual) - - info = { - 'vendor_id' : vendor_id, - 'brand' : processor_brand, - - 'hz_advertised' : _to_friendly_hz(hz_advertised, scale), - 'hz_actual' : _to_friendly_hz(hz_actual, 0), - 'hz_advertised_raw' : _to_raw_hz(hz_advertised, scale), - 'hz_actual_raw' : _to_raw_hz(hz_actual, 0), - - 'stepping' : stepping, - 'model' : model, - 'family' : family, - 'flags' : flags - } - - info = {k: v for k, v in info.items() if v} - return info - except: - return {} - -def _get_cpu_info_internal(): - ''' - Returns the CPU info by using the best sources of information for your OS. - Returns {} if nothing is found. - ''' - - # Get the CPU arch and bits - arch, bits = _parse_arch(DataSource.raw_arch_string) - - friendly_maxsize = { 2**31-1: '32 bit', 2**63-1: '64 bit' }.get(sys.maxsize) or 'unknown bits' - friendly_version = "{0}.{1}.{2}.{3}.{4}".format(*sys.version_info) - PYTHON_VERSION = "{0} ({1})".format(friendly_version, friendly_maxsize) - - info = { - 'python_version' : PYTHON_VERSION, - 'cpuinfo_version' : CPUINFO_VERSION, - 'arch' : arch, - 'bits' : bits, - 'count' : DataSource.cpu_count, - 'raw_arch_string' : DataSource.raw_arch_string, - } - - # Try the Windows wmic - _copy_new_fields(info, _get_cpu_info_from_wmic()) - - # Try the Windows registry - _copy_new_fields(info, _get_cpu_info_from_registry()) - - # Try /proc/cpuinfo - _copy_new_fields(info, _get_cpu_info_from_proc_cpuinfo()) - - # Try cpufreq-info - _copy_new_fields(info, _get_cpu_info_from_cpufreq_info()) - - # Try LSCPU - _copy_new_fields(info, _get_cpu_info_from_lscpu()) - - # Try sysctl - _copy_new_fields(info, _get_cpu_info_from_sysctl()) - - # Try kstat - _copy_new_fields(info, _get_cpu_info_from_kstat()) - - # Try dmesg - _copy_new_fields(info, _get_cpu_info_from_dmesg()) - - # Try /var/run/dmesg.boot - _copy_new_fields(info, _get_cpu_info_from_cat_var_run_dmesg_boot()) - - # Try lsprop ibm,pa-features - _copy_new_fields(info, _get_cpu_info_from_ibm_pa_features()) - - # Try sysinfo - _copy_new_fields(info, _get_cpu_info_from_sysinfo()) - - # Try querying the CPU cpuid register - _copy_new_fields(info, _get_cpu_info_from_cpuid()) - - return info - -def get_cpu_info_json(): - ''' - Returns the CPU info by using the best sources of information for your OS. - Returns the result in a json string - ''' - - import json - - output = None - - # If running under pyinstaller, run normally - if getattr(sys, 'frozen', False): - info = _get_cpu_info_internal() - output = json.dumps(info) - output = "{0}".format(output) - # if not running under pyinstaller, run in another process. - # This is done because multiprocesing has a design flaw that - # causes non main programs to run multiple times on Windows. - else: - from subprocess import Popen, PIPE - - command = [sys.executable, __file__, '--json'] - p1 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE) - output = p1.communicate()[0] - - if p1.returncode != 0: - return "{}" - - if not IS_PY2: - output = output.decode(encoding='UTF-8') - - return output - -def get_cpu_info(): - ''' - Returns the CPU info by using the best sources of information for your OS. - Returns the result in a dict - ''' - - import json - - output = get_cpu_info_json() - - # Convert JSON to Python with non unicode strings - output = json.loads(output, object_hook = _utf_to_str) - - return output - -def main(): - from argparse import ArgumentParser - import json - - # Parse args - parser = ArgumentParser(description='Gets CPU info with pure Python 2 & 3') - parser.add_argument('--json', action='store_true', help='Return the info in JSON format') - args = parser.parse_args() - - try: - _check_arch() - except Exception as err: - sys.stderr.write(str(err) + "\n") - sys.exit(1) - - info = _get_cpu_info_internal() - - if not info: - sys.stderr.write("Failed to find cpu info\n") - sys.exit(1) - - if args.json: - print(json.dumps(info)) - else: - print('Python Version: {0}'.format(info.get('python_version', ''))) - print('Cpuinfo Version: {0}'.format(info.get('cpuinfo_version', ''))) - print('Vendor ID: {0}'.format(info.get('vendor_id', ''))) - print('Hardware Raw: {0}'.format(info.get('hardware', ''))) - print('Brand: {0}'.format(info.get('brand', ''))) - print('Hz Advertised: {0}'.format(info.get('hz_advertised', ''))) - print('Hz Actual: {0}'.format(info.get('hz_actual', ''))) - print('Hz Advertised Raw: {0}'.format(info.get('hz_advertised_raw', ''))) - print('Hz Actual Raw: {0}'.format(info.get('hz_actual_raw', ''))) - print('Arch: {0}'.format(info.get('arch', ''))) - print('Bits: {0}'.format(info.get('bits', ''))) - print('Count: {0}'.format(info.get('count', ''))) - - print('Raw Arch String: {0}'.format(info.get('raw_arch_string', ''))) - - print('L1 Data Cache Size: {0}'.format(info.get('l1_data_cache_size', ''))) - print('L1 Instruction Cache Size: {0}'.format(info.get('l1_instruction_cache_size', ''))) - print('L2 Cache Size: {0}'.format(info.get('l2_cache_size', ''))) - print('L2 Cache Line Size: {0}'.format(info.get('l2_cache_line_size', ''))) - print('L2 Cache Associativity: {0}'.format(info.get('l2_cache_associativity', ''))) - print('L3 Cache Size: {0}'.format(info.get('l3_cache_size', ''))) - print('Stepping: {0}'.format(info.get('stepping', ''))) - print('Model: {0}'.format(info.get('model', ''))) - print('Family: {0}'.format(info.get('family', ''))) - print('Processor Type: {0}'.format(info.get('processor_type', ''))) - print('Extended Model: {0}'.format(info.get('extended_model', ''))) - print('Extended Family: {0}'.format(info.get('extended_family', ''))) - print('Flags: {0}'.format(', '.join(info.get('flags', '')))) - - -if __name__ == '__main__': - main() -else: - _check_arch() diff --git a/blibs/deps.py b/blibs/deps.py deleted file mode 100644 index e99d4ab43..000000000 --- a/blibs/deps.py +++ /dev/null @@ -1,177 +0,0 @@ -import urllib.request -import os -import shutil -import subprocess -from . import env -from . import util -from . import fhash -from . import download_list -from . download_list import * - -def GITHUB_RES_URL(commit): - return "https://raw.githubusercontent.com/wuye9036/SalviaDeps/%s/release/" % commit - -def OS_PATH(p): - return os.path.join( *p.split('/') ) - -class download_info(object): - def __init__(self, source, prj_root, res_path, res_type, need_distribute, tag): - assert isinstance(res_path, str) - - rel_path = OS_PATH(res_path) - - self.res_path = res_path - self.source = source - self.tag = tag - self.res_type = res_type - self.need_distribute = need_distribute - self.is_patch = res_path.startswith("__patches__") - - if self.res_type == RAW_FILE: - store_rel_path = rel_path - self.res_url = self.source + self.res_path - else: - store_rel_path = rel_path + ".7z" - self.res_url = self.source + self.res_path + ".7z" - - self.store_path = os.path.join(prj_root, 'downloads', store_rel_path) - if self.res_type == RAW_FILE: - if self.is_patch: - self.dist_path = os.path.join(prj_root, store_rel_path[len('__patches__/'):]) - else: - self.dist_path = os.path.join(prj_root, store_rel_path) - elif self.res_type == COMPRESSED_FILE or self.res_type == COMPRESSED_FOLDER: - self.dist_path = os.path.join(prj_root, rel_path) - else: - self.dist_path = None - -def download_file(url, file_path): - if os.path.isfile(file_path): - os.remove(file_path) - - if url.startswith("http://") or url.startswith("https://"): - u = urllib.request.urlopen(url) - meta = u.info() - file_size = int(meta["Content-Length"][0]) - else: - file_size = os.path.getsize(url) - u = open(url, "rb") - - try: - file_dir = os.path.dirname(file_path) - if not os.path.isdir(file_dir): - os.makedirs(file_dir) - - with open(file_path, 'wb') as f: - f = open(file_path, 'wb') - util.report_info( "Downloading: %s Bytes: %s" % (file_path, file_size) ) - file_size_dl = 0 - block_sz = 32 * 1024 - while True: - buffer = u.read(block_sz) - if not buffer: break - file_size_dl += len(buffer) - f.write(buffer) - status = r"%7d KB [%3.1f%%]" % (file_size_dl / 1024, file_size_dl * 100. / file_size) - status = status + chr(8)*(len(status)+1) - print(status, end="") - except: - if os.path.exists(file_path): - os.remove(file_path) - raise - -class installer(object): - def __init__(self, commit, proj_root): - assert isinstance(commit, str) - assert isinstance(proj_root, str) - - self.COMMIT = commit - self.PRJ_ROOT = proj_root - self.DOWNLOAD_FILE_LIST = [ - download_info(GITHUB_RES_URL(self.COMMIT), self.PRJ_ROOT, res_path, res_type, not "7z" in res_path, tag) - for res_path, res_type, tag in DOWNLOAD_LIST - ] - - if env.systems.current() == env.systems.win32: - self.decompressor = [dl for dl in self.DOWNLOAD_FILE_LIST if dl.res_path.endswith("win32/7z.exe")][0] - elif env.systems.current() == env.systems.linux: - self.decompressor = [dl for dl in self.DOWNLOAD_FILE_LIST if dl.res_path.endswith("linux/7z")][0] - else: - raise NotImplementedError("Cannot support other systems.") - - def update_all(self): - download_candidates = [] - distribute_candidates = [] - - for dl_info in self.DOWNLOAD_FILE_LIST: - need_download, need_distribute = self.__check_update(dl_info) - if need_download: download_candidates += [dl_info] - if need_distribute: distribute_candidates += [dl_info] - - for dl_info in download_candidates: - self.__download(dl_info) - - for dl_info in distribute_candidates: - self.__distribute(dl_info) - - def __check_update(self, dl_info): - assert isinstance(dl_info, download_info) - - need_download = False - need_distribute = False - - if dl_info.need_distribute: - if dl_info.res_type == COMPRESSED_FOLDER: - if not os.path.isdir(dl_info.dist_path): - need_distribute = True - else: - if not os.path.isfile(dl_info.dist_path): - need_distribute = True - - if not os.path.isfile(dl_info.store_path): - need_download = True - need_distribute = dl_info.need_distribute - elif fhash.hash_file(dl_info.store_path) != dl_info.tag: - need_download = True - need_distribute = dl_info.need_distribute - - return (need_download, need_distribute) - - def __download(self, dl_info): - assert isinstance(dl_info, download_info) - util.report_info("Downloading <%s> ..." % dl_info.res_path) - download_file(dl_info.res_url, dl_info.store_path) - - def __decompress(self, source_path, dist_parent): - try: - o = subprocess.check_output([self.decompressor.store_path, "x", source_path, '-o%s' % dist_parent]) - except: - util.report_error("Found error while decompressing <%s>" % source_path) - - def __distribute(self, dl_info): - assert isinstance(dl_info, download_info) - - util.report_info("Verifying <%s> ..." % dl_info.res_path) - - # Verify distribute source - if not os.path.isfile(dl_info.store_path): - util.report_error("File <%s> is not existed. Please check your network state and re-run build script." % dl_info.res_path) - - if fhash.hash_file(dl_info.store_path) != dl_info.tag: - util.report_error("File <%s> verificaition is failed. Please check your network state and re-run build script." % dl_info.res_path) - - util.report_info("Distributing <%s> ..." % dl_info.res_path) - - # Clean target if file is existed. - if dl_info.res_type in [RAW_FILE, COMPRESSED_FILE]: - if os.path.isfile(dl_info.dist_path): - os.remove(dl_info.dist_path) - elif dl_info.res_type in [COMPRESSED_FOLDER]: - if os.path.isdir(dl_info.dist_path): - shutil.rmtree(dl_info.dist_path) - - dist_parent = os.path.dirname(dl_info.dist_path) - if dl_info.res_type in [COMPRESSED_FILE, COMPRESSED_FOLDER]: - self.__decompress(dl_info.store_path, dist_parent) - else: - shutil.copy(dl_info.store_path, dist_parent) diff --git a/blibs/diagnostic.py b/blibs/diagnostic.py deleted file mode 100644 index e73624c14..000000000 --- a/blibs/diagnostic.py +++ /dev/null @@ -1,22 +0,0 @@ - - -class build_error(BaseException): - def __init__(self, error_desc): - self.error_desc = error_desc - - def message(self): - return self.error_desc - - -def report_error(message): - raise build_error(message) - - -def report_info(message): - print("[I] %s" % message) - - -def report_warning(message): - print("[W] %s" % message) - - diff --git a/blibs/download_list.py b/blibs/download_list.py deleted file mode 100644 index 7d1138a05..000000000 --- a/blibs/download_list.py +++ /dev/null @@ -1,24 +0,0 @@ -RAW_FILE = "RAW_FILE" -COMPRESSED_FILE = "CMP_FILE" -COMPRESSED_FOLDER = "CMP_FLDR" -DOWNLOAD_LIST = [ - ("3rd_party/boost", COMPRESSED_FOLDER, "4a8a1a7c77fb6029ac30865ad7339e52"), - ("3rd_party/FreeImage", COMPRESSED_FOLDER, "260599ca08ae81f68d42b64104e7458f"), - ("3rd_party/freetype2", COMPRESSED_FOLDER, "88c7fa3bdc6a180592359aee994be199"), - ("3rd_party/llvm", COMPRESSED_FOLDER, "e3a2d6d9ccd132dba317e68f1cdf2cc1"), - ("3rd_party/threadpool", COMPRESSED_FOLDER, "0ccb614305e10bb0e6bb12af46c35997"), - ("resources/astro_boy", COMPRESSED_FOLDER, "af69f995eb33d36fa50e800d92e0eb5c"), - ("resources/complex_mesh", COMPRESSED_FOLDER, "d0df59b45e5769713f61cd3517b4b6d1"), - ("resources/cup", COMPRESSED_FOLDER, "4c40b18cacdb3f9cab9e27a665d059ba"), - ("resources/font", COMPRESSED_FOLDER, "faa561a3e23593ea2e994f58061a336d"), - ("resources/morph", COMPRESSED_FOLDER, "a7b65c2b04663577981d51a7e0ef7a25"), - ("resources/sponza_lq", COMPRESSED_FOLDER, "575b0f14b5383fd9f83ebcb32a40aa59"), - ("resources/ssm", COMPRESSED_FOLDER, "ed6fbbd9da7eaf8eafbebd3d088e01dc"), - ("resources/texture_and_blending", COMPRESSED_FOLDER, "396336ded94e96f37a36fe3e9174cedf"), - ("basic_tools/linux/7z", RAW_FILE, "1735cf489c1bf2a89764631c0adc75e8"), - ("basic_tools/linux/7z.so", RAW_FILE, "24b3cc182b8572ef2e082a0af5614b08"), - ("basic_tools/win32/7z.dll", RAW_FILE, "04ad4b80880b32c94be8d0886482c774"), - ("basic_tools/win32/7z.exe", RAW_FILE, "a51d90f2f9394f5ea0a3acae3bd2b219"), - ("__patches__/resources/sponza_lq/part_of_sponza.mtl", RAW_FILE, "6028084c57c09a405c03bbdc3b343285"), - ("__patches__/3rd_party/FreeImage/CMakeLists.txt", RAW_FILE, "f253f36bbf70f4c39fe378d0887cf092"), -] diff --git a/blibs/env.py b/blibs/env.py deleted file mode 100644 index 3c4c9fb75..000000000 --- a/blibs/env.py +++ /dev/null @@ -1,214 +0,0 @@ -import os -import platform -import subprocess -import enum -from .diagnostic import report_error - - -class arch(enum.Enum): - unknown = 0 - x86 = (32, "x86") - x64 = (64, "x64") - - @staticmethod - def from_machine(machine): - if machine == 'x86' or machine == 'i386': - return arch.x86 - if machine == 'x64' or machine == 'AMD64': - return arch.x64 - return arch.unknown - - @property - def bits(self): - return self.value[0] - - @property - def name(self): - return self.value[1] - - @staticmethod - def current(): - return arch.from_machine(platform.machine()) - - -class systems(enum.Enum): - unknown = 'unknown' - win32 = 'nt' - linux = 'linux' - - @staticmethod - def current(): - if platform.system() == 'Windows': - return systems.win32 - if platform.system() == 'Linux': - return systems.linux - return systems.unknown - - @property - def name(self): - return self.value - - -class toolset: - def __init__(self, ide_name, compiler_name, major_version, minor_version, patch_version): - self.ide_name = ide_name - self.compiler_name = compiler_name - self.major_ver = major_version - self.minor_ver = minor_version - self.patch_ver = patch_version - - def support_x64(self): - if self.compiler_name == "mingw": - return False - return True - - def boost_name(self): - if self.compiler_name == "msvc": - return "%s-%d.%d" % (self.compiler_name, self.major_ver, self.minor_ver) - elif self.compiler_name in ["mingw", "mingw64", "gcc"]: - return "gcc" - - def short_name(self): - ret = "%s%d" % ( self.compiler_name, self.major_ver ) - need_patch_ver = ( self.patch_ver and self.patch_ver != 0 ) - need_minor_ver = need_patch_ver or ( self.minor_ver and self.minor_ver != 0 ) - if need_minor_ver: ret += str(self.minor_ver) - if need_patch_ver: ret += str(self.patch_ver) - return ret - - def boost_lib_name(self): - ret = "%s%d%d" % ( self.short_compiler_name(), self.major_ver, self.minor_ver ) - return ret - - def short_compiler_name(self): - if self.compiler_name == "msvc": - return "vc" - elif self.compiler_name == "mingw" or self.compiler_name == "mingw64": - return "mgw" - elif self.compiler_name == "gcc": - return "gcc" - else: - return None - - def vs_version_string(self): - if self.compiler_name == "msvc": - return '%d.%d' % (self.major_ver, self.minor_ver) - return None - - -def detect_cmake(candidate_cmake_executable): - cmake = candidate_cmake_executable \ - if not candidate_cmake_executable is None else "cmake" - try: - subprocess.call([cmake, "--version"]) - return cmake - except: - return None - - -def detect_gcc(gcc_dir, min_major_ver, min_minor_ver): - gcc_executables = [] - if gcc_dir is not None: - gcc_executables.append( os.path.join(gcc_dir, "g++") ) - - line_separator = None - if systems.current() == systems.win32: - line_separator = '\r\n' - elif systems.current() == systems.linux: - line_separator = '\n' - - try: - gcc_paths = None - if systems.current() == systems.win32: - gcc_paths = subprocess.check_output(["where", "g++"]) - elif systems.current() == systems.linux: - gcc_paths = subprocess.check_output(["which", "g++"]) - gcc_executables += gcc_paths.split(line_separator) - except: - pass - - if systems.current() == systems.win32: - gcc_executables.append(os.path.join("C:/MinGW/bin", "g++")) - - for gcc_executable in gcc_executables: - try: - version_str = subprocess.check_output([gcc_executable, "-dumpversion"]) - version_digits = [int(s) for s in version_str.split('.')] - while len(version_digits) < 3: - version_digits.append(None) - - if version_digits[0] < min_major_ver: - continue - elif version_digits[0] == min_major_ver: - if version_digits[1] < min_minor_ver: - continue - - machine_name = subprocess.check_output([gcc_executable, "-dumpmachine"]).strip() - if machine_name == "x86_64-w64-mingw32": - compiler_name = "mingw64" - elif machine_name == "mingw32": - compiler_name = "mingw" - else: - compiler_name = "gcc" - gcc_toolset = toolset( None, compiler_name, - version_digits[0], version_digits[1], version_digits[2] - ) - return gcc_toolset, os.path.dirname(gcc_executable) - except: - pass - return None, None - - -def add_binpath(env, dirs): - separator = ';' if systems.current() == systems.win32 else ':' - new_env = env.copy() - for dir in dirs: - new_env['PATH'] = env['PATH'] + separator + dir - return new_env - - -def detect_vs_installer(): - if systems.current() != systems.win32: - report_error("Visual Studio Installer detection only works on Windows OS.") - vsJson = subprocess.check_output([r"blibs\vswhere.exe", "-all", "-format", "json"]) - print(vsJson) - - -def windows_kit_dirs(): - if systems.current() != systems.win32: - report_error("Windows Kits only works on windows system.") - - close_key = None - - try: - import winreg as winreg - winkit_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows Kits\Installed Roots") - if winkit_key is None: - return None - - def CloseKey(): - winreg.CloseKey(winkit_key) - - close_key = CloseKey - kit_key_names = ["KitsRoot", "KitsRoot81", "KitRoots10"] - kits = [] - for kit_key_name in kit_key_names: - try: - kit_value = winreg.QueryValueEx(winkit_key, kit_key_name)[0] - kits.append(str(kit_value)) - except Exception: - continue - if close_key: - close_key() - return kits - - except ImportError as e: - if close_key: - close_key() - report_error("_winreg library is not existed in Python on Win32 platform.") - - except WindowsError as e: - if close_key: - close_key() - report_error('Windows error occurs: "%s" when reading Windows Kits reg.' % e.strerror) - diff --git a/blibs/fhash.py b/blibs/fhash.py deleted file mode 100644 index 1c300c25c..000000000 --- a/blibs/fhash.py +++ /dev/null @@ -1,12 +0,0 @@ -import hashlib - -BLOCK_SIZE = 64 * 1024 - -def hash_file(fileName): - m = hashlib.md5() - with open(fileName , "rb") as f: - while True: - buf = f.read(BLOCK_SIZE) - if not buf: break - m.update( buf ) - return m.hexdigest() \ No newline at end of file diff --git a/blibs/project.py b/blibs/project.py deleted file mode 100644 index 3944ff3e9..000000000 --- a/blibs/project.py +++ /dev/null @@ -1,333 +0,0 @@ -import sys -import os -from functools import reduce -from .env import arch, systems, detect_cmake, detect_gcc, windows_kit_dirs, toolset -from .diagnostic import report_error, report_warning -from .boost_build import boost_version - - -class project: - def __init__(self, props, cwd): - self.props_ = props - - self.boost_ver_ = None - self.build_root_ = props.build_root - self.install_root_ = props.install_root - self.arch_ = arch.from_machine(props.arch) - self.os_ = systems.current() - self.config_ = props.build_config - self.target_projects_ = props.targets - self.cwd_ = cwd - - self.cmake_ = detect_cmake(props.cmake) - env_vars = os.environ - default_toolset = props.toolset - candidate_gcc_dir = None - try: - candidate_gcc_dir = props.toolset_dir - except AttributeError: - pass - default_gcc, default_gcc_dir = detect_gcc(candidate_gcc_dir, 4, 7) - - # vs_installers = detect_vs_installer() - - if default_toolset is None or default_toolset == "": - if "VS140COMNTOOLS" in env_vars: - default_toolset = "msvc-14.0" - elif default_gcc: - # gcc or mingw - default_toolset = "gcc" - else: - report_error('Cannot found any valid compiler on your computer. Please set toolset in "proj.py" ') - - if default_toolset == "msvc-14.2": - self.toolset_ = toolset('vs', 'msvc', 14, 2, None) - self.builder_root_ = "" - elif default_toolset == "gcc" and default_gcc is not None: - self.toolset_ = default_gcc - self.builder_root_ = default_gcc_dir - else: - report_error(f'Unsupported toolset name <{default_toolset}>.') - - self.use_win_kit_ = self.toolset_.short_compiler_name() == "vc" and self.toolset().major_ver >= 11 - self.directx_ = (self.toolset_.short_compiler_name() == "vc") and ("DXSDK_DIR" in env_vars or self.use_win_kit_) - self.dx_dlls_dirs_ = None - - if self.use_win_kit_: - wdk_dirs = windows_kit_dirs() - arch_subdir = None - if self.arch_ == arch.x64: - arch_subdir = "x64" - else: - report_error("Unsupported arch when finding Windows Kits.") - self.dx_dlls_dirs_ = [os.path.join(wdk_dir, "Redist", "D3D", arch_subdir) for wdk_dir in wdk_dirs] - - def check(self): - if self.cmake_ is None: - report_error("CMake is not available. Please install CMake and set proj.py correctly.") - if not self.boost_version(): - report_error("Boost is not found. Please specify boost path in \'proj.py\'.") - if self.toolset().short_compiler_name() == "mgw" and self.arch() == arch.x64: - if not self.toolset().support_x64(): - report_error('Salvia cannot build as 64-bit when MinGW32 is used.') - if self.arch() != arch.current() and systems.current() != systems.win32: - report_warning( - "In some systems such as linux, cross compiling may cause some errors. " - "We suggest that you should use same arch for compiling as system's." - ) - if self.toolset().short_compiler_name() in ['mgw', 'gcc'] \ - and self.customized_toolset_dir() != self.props_.toolset_dir: - used_toolset_dir = \ - "system-specified path" if self.customized_toolset_dir() is None else self.customized_toolset_dir() - report_warning(f"Item 'toolset_dir' is set but not used. The used toolset dir is <{used_toolset_dir}>") - - def print_props(self): - print('=' * 25 + ' Checking Project Properties ' + '=' * 25) - print(' * Target Projects ......... %s' % ",".join(self.target_projects_)) - print(' * Current Arch ............ %s' % arch.current().name) - print(' * Current OS .............. %s' % systems.current().name) - print(' * Toolset ................. %s' % self.toolset().short_name()) - print(' * Env Script(win32 only) .. %s' % os.path.normpath(self.env_setup_commands())) - print(' * CMake Executable ........ %s' % self.cmake_exe()) - print(' * CMake Generator ......... %s' % self.generator()) - print(' * Make tool ............... %s' % self.maker_name()) - print(' * Use Windows Kits ........ %s' % self.use_win_kit_) - print(' * Use Direct3D ............ %s' % self.directx_) - print(' ') - print(' * Target .................. %s' % self.target_modifier(['platform', 'tool', 'config'])) - print(' * Source .................. %s' % self.source_root()) - print(' * Build ................... %s' % self.build_root()) - print(' * Install ................. %s' % self.install_root()) - print(' * Install Binaries ........ %s' % self.install_bin()) - print(' * Install Libraries ....... %s' % self.install_lib()) - print(' ') - print(' * Boost ................... %s' % self.boost_root()) - print(' * Boost Version ........... %s' % self.boost_version()) - print(' * Boost Stage ............. %s' % self.boost_stage()) - print(' * LLVM .................... %s' % self.llvm_root()) - print(' * LLVM Build .............. %s' % self.llvm_build()) - print(' * LLVM Install ............ %s' % self.llvm_install()) - print(' * FreeImage ............... %s' % self.freeimage_root()) - print(' * FreeImage Build ......... %s' % self.freeimage_build()) - print(' * FreeImage Install ....... %s' % self.freeimage_install()) - print(' * FreeType2 ............... %s' % self.freetype_root()) - print(' * FreeType2 Build ......... %s' % self.freetype_build()) - print(' * FreeType2 Install ....... %s' % self.freetype_install()) - print(' ') - print(' * SALVIA Build ............ %s' % self.salvia_build()) - print(' * SALVIA Binaries ......... %s' % self.salvia_bin()) - print("========== Project Properties ==========") - - def arch(self): - return self.arch_ - - def os(self): - return self.os_ - - def toolset(self) -> toolset: - return self.toolset_ - - def customized_toolset_dir(self): - if self.toolset().short_compiler_name() == "msvc": - return None - if self.toolset().short_compiler_name() in ["mgw", "gcc"]: - return self.builder_root_ - - def cmake_exe(self): - return self.cmake_ - - def target_modifier(self, hints): - hint_dict = dict() - hint_dict["os"] = self.os().name - hint_dict["arch"] = self.arch().name - hint_dict["tool"] = self.toolset().short_name() - hint_dict["platform"] = self.os().name + self.arch().name - hint_dict["config"] = self.config_ - return reduce(lambda ret, s: ret + "_" + s, [hint_dict[hnt] for hnt in hints]) - - def env_setup_commands(self): - major_ver = self.toolset().major_ver - minor_ver = self.toolset().minor_ver - - if self.os() == systems.win32: - if self.toolset().short_compiler_name() == 'vc': - assert major_ver > 14 or (major_ver == 14 and minor_ver >= 2) - return os.path.join(self.props_.toolset_dir, r"vcvars64.bat") - elif self.toolset().short_compiler_name() == "mgw": - if self.build_root_ is not None: - return 'set PATH=%s;%%PATH%%' % self.builder_root_ - if self.os() == systems.linux and self.build_root_ is not None: - return 'PATH=%s:$PATH' % self.builder_root_ - report_error("Unrecognized OS or toolset.") - - def maker_name(self): - if self.toolset().short_compiler_name() == 'vc': - return 'MSBuild' - elif self.toolset().short_compiler_name() == "mgw": - return 'mingw32-make' - elif self.toolset().short_compiler_name() == "gcc": - return 'make' - else: - report_error('Cannot find valid make/build program.') - - def directx(self): - return self.directx_ - - def dx_dlls_dirs(self): - return self.dx_dlls_dirs_ - - def config_name(self): - return self.config_ - - def msvc_platform_name(self): - platform_name_in_msvc = "" - if self.arch() == arch.x86: - platform_name_in_msvc = "Win32" - elif self.arch() == arch.x64: - platform_name_in_msvc = "x64" - return platform_name_in_msvc - - def project_file_ext(self): - if self.toolset_.short_compiler_name() == "vc": - return "vcxproj" - - def msvc_config_name_with_platform(self): - return '"' + self.config_ + "|" + self.msvc_platform_name() + '"' - - def to_abs(self, path): - if os.path.isabs(path): - return path - else: - return os.path.abspath(os.path.join(self.source_root(), path)) - - def source_root(self): - source_root_path = os.path.dirname(sys.argv[0]) - if os.path.isabs(source_root_path): - return source_root_path - else: - return os.path.abspath(os.path.join(self.cwd_, source_root_path)) - - def build_root(self): - return self.to_abs(self.build_root_) - - def install_root(self): - return self.to_abs(self.install_root_) - - def install_bin(self): - return os.path.join(self.install_root(), "bin") - - def install_lib(self): - return os.path.join(self.install_root(), "lib") - - # Boost builds. - def boost_root(self): - return os.path.join(self.source_root(), "3rd_party", "boost") - - def boost_version(self): - if self.boost_ver_ is None: - self.boost_ver_ = boost_version(self.boost_root()) - return self.boost_ver_ - - def boost_stage(self): - return self.common_install_dir('boost') - - def boost_configs(self): - if self.config_ == "Debug": - return ["optimization=off", "debug-symbols=on", "inlining=off", "runtime-debugging=on"] - elif self.config_ == "RelWithDebInfo": - return ["optimization=speed", "debug-symbols=on", "inlining=full", "runtime-debugging=off"] - elif self.config_ == "Release": - return ["optimization=speed", "debug-symbols=off", "inlining=full", "runtime-debugging=off"] - elif self.config_ == "MinSizeRel": - return ["optimization=space", "debug-symbols=off", "inlining=on", "runtime-debugging=off"] - else: - report_error("Configuration <%s> cannot be recognized." % self.config_) - - def boost_lib_dir(self): - return os.path.join(self.boost_stage(), "lib") - - def boost_lib_dir_in_msvc(self): - return os.path.join(self.common_msvc_install_dir('boost'), 'lib') - - def common_msvc_install_dir(self, lib_name): - if self.toolset().short_compiler_name() == 'vc': - return os.path.join(self.install_lib(), - lib_name + "_" + self.target_modifier(['platform', 'tool']) + '_$(ConfigurationName)') - report_error("Toolset is not set or not MSVC.") - - def common_install_dir(self, lib_name): - return os.path.join(self.install_lib(), lib_name + "_" + self.target_modifier(['platform', 'tool', 'config'])) - - def common_build_dir(self, lib_name): - if self.toolset().short_compiler_name() == "vc": - return os.path.join(self.build_root(), lib_name + "_" + self.target_modifier(['platform', 'tool'])) - return os.path.join(self.build_root(), lib_name + "_" + self.target_modifier(['platform', 'tool', 'config'])) - - def llvm_root(self): - return os.path.join(self.source_root(), "3rd_party", "llvm") - - def llvm_build(self): - return self.common_build_dir("llvm") - - def llvm_install(self): - return self.common_install_dir("llvm") - - def llvm_install_in_msvc(self): - return self.common_msvc_install_dir("llvm") - - def freeimage_root(self): - return os.path.join(self.source_root(), "3rd_party", "FreeImage") - - def freeimage_build(self): - return self.common_build_dir('freeimage') - - def freeimage_install(self): - return self.common_install_dir('freeimage') - - def freeimage_install_in_msvc(self): - return self.common_msvc_install_dir('freeimage') - - def freetype_root(self): - return os.path.join(self.source_root(), "3rd_party", "freetype2") - - def freetype_build(self): - return self.common_build_dir('freetype') - - def freetype_install(self): - return self.common_install_dir('freetype') - - def freetype_install_in_msvc(self): - return self.common_msvc_install_dir('freetype') - - def salvia_build(self): - return self.common_build_dir("salvia") - - def salvia_lib(self): - return os.path.join(self.install_lib(), self.target_modifier(['platform', 'tool']), - self.target_modifier(['config'])) - - def salvia_bin(self): - return os.path.join(self.install_bin(), self.target_modifier(['platform', 'tool']), - self.target_modifier(['config'])) - - def generator(self): - if self.toolset().short_compiler_name() == "mgw": - return "CodeBlocks - MinGW Makefiles" - elif self.toolset().short_compiler_name() == "gcc": - return "CodeBlocks - Unix Makefiles" - elif self.toolset().short_compiler_name() == 'vc': - assert self.arch() == arch.x64 - if self.toolset().short_name() == "msvc142": - return "Visual Studio 16 2019" - - report_error("Unknown generator.") - - def target_projects(self): - return self.target_projects_ - - def is_target_project_enabled(self, proj_name): - if proj_name in self.target_projects_: - return True - if 'all' in self.target_projects_: - return True - return False diff --git a/blibs/util.py b/blibs/util.py deleted file mode 100644 index 460adb37d..000000000 --- a/blibs/util.py +++ /dev/null @@ -1,82 +0,0 @@ -import os -import hashlib -import datetime -from . import env -from .diagnostic import report_error - - -class scoped_cd: - def __init__(self, cd: str): - self._new_cwd = cd - self._saved_cwd = os.getcwd() - - def __enter__(self): - os.chdir(self._new_cwd) - - def __exit__(self, exc_type, exc_val, exc_tb): - os.chdir(self._saved_cwd) - - @property - def new_cwd(self): - return self._new_cwd - - @property - def saved_cwd(self): - return self._saved_cwd - - -class batch_command: - def __init__(self, working_dir, target_sys=env.systems.current()): - self.dir_ = working_dir - self.commands_ = [] - - # Environ setup - if target_sys == env.systems.win32: - self.execute_template_ = "%s" - self.file_suffix_ = ".bat" - self.error_exit_template_ = \ - "@%s\n" + \ - "@if %%ERRORLEVEL%% neq 0 exit /B 1" - elif target_sys == env.systems.linux: - self.execute_template_ = "sh %s" - self.file_suffix_ = ".sh" - self.error_exit_template_ = \ - "%s || exit $?" - else: - report_error("OS is unsupported by batch_command.") - - def add_native_command(self, cmd): - self.commands_ += [cmd] - - def add_execmd(self, cmd): - self.commands_ += [cmd] - - def add_execmd_with_error_exit(self, cmd): - self.commands_ += [self.error_exit_template_ % cmd] - - def execute(self, keep_bat=False): - tmp_gen = hashlib.md5() - dt = datetime.datetime.now() - tmp_gen.update( str(dt).encode('utf-8') ) - batch_file_name = tmp_gen.hexdigest() + self.file_suffix_ - cur_dir = os.path.abspath(os.curdir) - os.chdir(self.dir_) - batch_f = open(batch_file_name, "w") - batch_f.writelines([cmd_line + "\n" for cmd_line in self.commands_]) - batch_f.close() - ret_code = os.system(self.execute_template_ % batch_file_name) - if not keep_bat: - os.remove(batch_file_name) - os.chdir(cur_dir) - return ret_code - - -def executable_file_name(base_name, target_sys): - if target_sys == env.systems.win32: - return base_name + ".exe" - if target_sys == env.systems.linux: - return base_name - report_error("Unknown system: %s" % target_sys) - - - diff --git a/blibs/vswhere.exe b/blibs/vswhere.exe deleted file mode 100644 index 096d016fb5599ba73a6fbb873321d3b6269bf25d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458336 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

``Vt*=X>o+8+iM$e(uep@9Eiabm8>zyZ4-g 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 diff --git a/bootstrap.py b/bootstrap.py deleted file mode 100644 index 62d885eca..000000000 --- a/bootstrap.py +++ /dev/null @@ -1,420 +0,0 @@ -#!/usr/bin/env python -import argparse -import functools -import multiprocessing -import os -import shutil -import subprocess -import sys - -from blibs import deps -from blibs.benchmark import benchmark_runner, generate_csv_report -from blibs.copy import copy_newer -from blibs.diagnostic import build_error, report_error, report_info -from blibs.env import add_binpath, systems -from blibs.project import project -from blibs.util import batch_command, executable_file_name, scoped_cd - -RESOURCE_COMMIT = "f18fc82afd6ca2f64e4a6eac9275553bc47d42a1" - - -def guarded_rmtree(path): - if os.path.isdir(path): - shutil.rmtree(path) - - -def make_bjam(prj: project): - assert isinstance(prj, project) - src = prj.boost_root() - - with scoped_cd(src): - bjam_file_name = executable_file_name("bjam", systems.current()) - - if os.path.exists(bjam_file_name): - report_info('Bjam is existed.') - return True - - customized_toolset_dir = prj.customized_toolset_dir() - env_vars = os.environ.copy() - if systems.current() == systems.win32: - env_var_separator = ';' - bootstrap_script = "bootstrap.bat" - else: - env_var_separator = ':' - bootstrap_script = "./bootstrap.sh" - - if customized_toolset_dir: - env_vars['PATH'] = "%s%s%s" % (env_vars['PATH'], env_var_separator, customized_toolset_dir) - - try: - subprocess.call(bootstrap_script, env=env_vars) - except OSError: - report_error("Unknown error occurred when building bjam.") - - if not os.path.exists(bjam_file_name): - report_error("Bjam wasn't built.") - - return True - - -def clean_boost(proj): - src = proj.boost_root() - - os.chdir(src) - if systems.current() == systems.win32: - boost_cmd = "bjam --clean" - print('Executing: %s' % boost_cmd) - os.system(boost_cmd) - else: - return False - - os.chdir(proj.source_root()) - guarded_rmtree(proj.boost_stage()) - - -def make_boost(proj): - src = proj.boost_root() - stage = proj.boost_stage() - - # Get boost build command - # Add configs - libs = ["test", "wave", "program_options", "locale"] - address_model = f'address-model={proj.arch().bits}' - options = ["--build-dir=./", "--hash", "link=shared", "runtime-link=shared", "threading=multi", "stage"] - current_toolset = proj.toolset() - defs = [] - cxxflags = [] - if current_toolset.short_compiler_name() == 'vc': - defs = ["_CRT_SECURE_NO_DEPRECATE", "_SCL_SECURE_NO_DEPRECATE"] - cxxflags = ["-wd4819", "-wd4910", "-wd4244", "-wd4996"] - - # bjam current_toolset stagedir address-model defs cxxflags libs options - bjam_executable = None - if systems.current() == systems.win32: - bjam_executable = 'bjam' - elif systems.current() == systems.linux: - bjam_executable = './bjam' - else: - report_error("Unsupported OS.") - - # configs to cmd - libs_cmd = ["--with-%s" % lib for lib in libs] - defs_cmd = [] - if len(defs) > 0: - defs_cmd = ["define=%s" % dfn for dfn in defs] - cxxflags_cmd = [] - if len(cxxflags) > 0: - cxxflags_cmd = ["cxxflags=%s" % flag for flag in cxxflags] - - toolset_cmd = "--current_toolset=%s" % current_toolset.boost_name() - stage_cmd = '--stagedir=%s' % stage - - boost_cmd = [bjam_executable, toolset_cmd, stage_cmd, address_model] \ - + defs_cmd + cxxflags_cmd + libs_cmd + options + proj.boost_configs() - report_info('Executing: %s' % boost_cmd) - - env_vars = os.environ - if not proj.customized_toolset_dir() is None: - env_vars = add_binpath(os.environ, [proj.customized_toolset_dir()]) - - boost_build_bat = batch_command(src) - if proj.toolset().short_compiler_name() == "vc": - boost_build_bat.add_native_command('@call "%s"' % proj.env_setup_commands()) - boost_build_bat.add_execmd_with_error_exit(" ".join(boost_cmd)) - boost_build_bat.execute(keep_bat=False) - else: - os.chdir(src) - if subprocess.call(boost_cmd, env=env_vars, stdout=sys.stdout) != 0: - report_error("Boost build failed.") - os.chdir(proj.source_root()) - report_info("Boost build done.") - return True - - -def clean_llvm(proj): - guarded_rmtree(proj.llvm_build()) - guarded_rmtree(proj.llvm_install()) - - -def config_and_make_cmake_project(project_name, additional_params, source_dir, build_dir, install_dir, proj): - if additional_params is None: - conf_params = dict() - else: - conf_params = additional_params.copy() - if proj.toolset().short_compiler_name() != "vc": - conf_params["CMAKE_BUILD_TYPE"] = ("STRING", proj.config_name()) - conf_params["CMAKE_INFO_ARCH_NAME"] = ("STRING", proj.arch().name) - if install_dir is not None: - conf_params["CMAKE_INSTALL_PREFIX"] = ("PATH", install_dir) - - params_cmd = functools.reduce( - lambda cmd, lib: cmd + lib, - [f' -D {k}:{v[0]}="{v[1]}"' for (k, v) in conf_params.items()] - ) - - report_info("Configuring %s ..." % project_name) - if not os.path.exists(build_dir): - os.makedirs(build_dir) - - conf_cmd = batch_command(build_dir) - - if proj.customized_toolset_dir(): - if systems.current() == systems.win32: - conf_cmd.add_native_command('@set PATH=%s;%%PATH%%' % proj.customized_toolset_dir()) - elif systems.current() == systems.linux: - conf_cmd.add_native_command('PATH=%s:$PATH' % proj.customized_toolset_dir()) - else: - report_error("Unsupported OS.") - conf_cmd.add_execmd_with_error_exit( - f'"{proj.cmake_exe()}" -G "{proj.generator()}" -A "{proj.arch().name}" {params_cmd} {source_dir}' - ) - - if conf_cmd.execute() != 0: - report_error("%s configure failed." % project_name) - - report_info("Making %s on %s ..." % (project_name, proj.config_name())) - if proj.toolset().short_compiler_name() == "vc": - make_cmd = batch_command(build_dir) - make_cmd.add_native_command('@call "%s"' % proj.env_setup_commands()) - make_cmd.add_native_command('@set VisualStudioVersion=%s' % proj.toolset().vs_version_string()) - make_cmd.add_execmd_with_error_exit( - f'{proj.maker_name()} ALL_BUILD.{proj.project_file_ext()} /m /v:m /p:Configuration={proj.config_name()}' - ) - - if install_dir is not None: - make_cmd.add_execmd_with_error_exit( - f'{proj.maker_name()} INSTALL.{proj.project_file_ext()} /m /v:m /p:Configuration={proj.config_name()}' - ) - if make_cmd.execute() != 0: - report_error("%s make failed." % project_name) - elif proj.toolset().short_compiler_name() in ["mgw", "gcc"]: - make_cmd = batch_command(build_dir) - make_cmd.add_execmd_with_error_exit('%s' % proj.env_setup_commands()) - make_cmd.add_execmd_with_error_exit('%s -j%d' % (proj.maker_name(), multiprocessing.cpu_count())) - if install_dir is not None: - make_cmd.add_execmd_with_error_exit('%s install' % proj.maker_name()) - if make_cmd.execute() != 0: - report_error("%s make failed." % project_name) - else: - report_error("Unsupported toolset or OS.") - - -def config_and_make_llvm(proj): - # Add definitions here - configuration = { - "PYTHON_EXECUTABLE": ("PATH", sys.executable), - "LLVM_INCLUDE_TESTS": ("BOOL", "FALSE"), - "LLVM_INCLUDE_TOOLS": ("BOOL", "FALSE"), - "LLVM_INCLUDE_UTILS": ("BOOL", "FALSE"), - "LLVM_INCLUDE_EXAMPLES": ("BOOL", "FALSE"), - "LLVM_INCLUDE_BENCHMARKS": ("BOOL", "FALSE"), - "LLVM_BUILD_TESTS": ("BOOL", "FALSE"), - "LLVM_BUILD_TOOLS": ("BOOL", "FALSE"), - "LLVM_BUILD_UTILS": ("BOOL", "FALSE"), - "LLVM_TARGETS_TO_BUILD": ("STRING", "X86"), - # "LLVM_OPTIMIZED_TABLEGEN": ("BOOL", "TRUE") - } - config_and_make_cmake_project('LLVM', configuration, proj.llvm_root(), proj.llvm_build(), proj.llvm_install(), proj) - - -def config_and_make_freeimage(proj): - config_and_make_cmake_project( - 'FreeImage', None, proj.freeimage_root(), proj.freeimage_build(), proj.freeimage_install(), proj - ) - - -def config_and_make_freetype(proj): - config_and_make_cmake_project( - 'FreeType', None, proj.freetype_root(), proj.freetype_build(), proj.freetype_install(), proj - ) - - -def clean_salvia(proj): - guarded_rmtree(proj.salvia_build()) - guarded_rmtree(proj.salvia_lib()) - guarded_rmtree(proj.salvia_bin()) - - -def config_and_make_salvia(proj): - defs = dict() - defs["SALVIA_BOOST_DIR"] = ("PATH", proj.boost_root()) - defs["PYTHON_EXECUTABLE"] = ("PATH", sys.executable) - if proj.toolset().short_compiler_name() == 'vc': - defs["SALVIA_BOOST_LIB_DIR"] = ("PATH", proj.boost_lib_dir_in_msvc()) - else: - defs["SALVIA_BOOST_LIB_DIR"] = ("PATH", proj.boost_lib_dir()) - if proj.toolset().short_compiler_name() == "vc": - defs["SALVIA_FREEIMAGE_DIR"] = ("PATH", proj.freeimage_install_in_msvc()) - defs["SALVIA_LLVM_INSTALL_DIR"] = ("PATH", proj.llvm_install_in_msvc()) - defs["SALVIA_FREETYPE_DIR"] = ("PATH", proj.freetype_install_in_msvc()) - else: - defs["SALVIA_FREEIMAGE_DIR"] = ("PATH", proj.freeimage_install()) - defs["SALVIA_LLVM_INSTALL_DIR"] = ("PATH", proj.llvm_install()) - defs["SALVIA_FREETYPE_DIR"] = ("PATH", proj.freetype_install()) - defs["SALVIA_BUILD_WITH_DIRECTX"] = ("BOOL", "TRUE" if proj.directx() else "FALSE") - config_and_make_cmake_project('SALVIA', defs, proj.source_root(), proj.salvia_build(), None, proj) - - -def install_prebuild_binaries(proj): - report_info("Installing dependencies ...") - - dynamic_lib_ext = None - if proj.os() == systems.win32: - dynamic_lib_ext = ".dll" - elif proj.os() == systems.linux: - dynamic_lib_ext = ".so" - else: - report_error("Target OS cannot be supported.") - - # Copy mingw runtime - if proj.toolset().short_compiler_name() == "mgw": - mgw_dir = proj.customized_toolset_dir() - libs = ["stdc++", "sjlj", "seh"] - for f in os.listdir(mgw_dir): - if os.path.splitext(f)[1] != dynamic_lib_ext: - continue - for lib_name in libs: - if lib_name in f: - copy_newer(os.path.join(mgw_dir, f), proj.salvia_bin()) - - # Copy FreeImage DLLs - freeimage_dist_dir = os.path.join(proj.freeimage_install(), "Dist") - files = os.listdir(freeimage_dist_dir) - for f in files: - name, ext = os.path.splitext(f) - if ext != dynamic_lib_ext: - continue - src = os.path.join(freeimage_dist_dir, f) - copy_newer(src, proj.salvia_bin()) - - # Copy d3dcompile_xx.dll - if proj.dx_dlls_dirs(): - for d in proj.dx_dlls_dirs(): - for f in os.listdir(d): - copy_newer(os.path.join(d, f), proj.salvia_bin()) - - # Copy boost DLLs - files = os.listdir(proj.boost_lib_dir()) - need_copy = [] - for f in files: - name, ext = os.path.splitext(f) - if ext != dynamic_lib_ext: - continue - if not proj.toolset().boost_lib_name() in name: - continue - need_copy.append(os.path.join(proj.boost_lib_dir(), f)) - - for f in need_copy: - copy_newer(f, proj.salvia_bin()) - - -def clean_all(proj): - print('clean Boost ...') - clean_boost(proj) - print('clean LLVM ...') - clean_llvm(proj) - print('clean SALVIA ...') - clean_salvia(proj) - pass - - -def build(args): - proj = project(args, os.getcwd()) - - inst = deps.installer(RESOURCE_COMMIT, proj.source_root()) - inst.update_all() - - proj.print_props() - proj.check() - - if proj.is_target_project_enabled('boost'): - make_bjam(proj) - make_boost(proj) - - if proj.is_target_project_enabled('freetype'): - config_and_make_freetype(proj) - - if proj.is_target_project_enabled('freeimage'): - config_and_make_freeimage(proj) - - if proj.is_target_project_enabled('llvm'): - config_and_make_llvm(proj) - - if proj.is_target_project_enabled('salvia'): - config_and_make_salvia(proj) - - install_prebuild_binaries(proj) - - -def _main(): - parser = argparse.ArgumentParser( - "Build, test and run benchmark for SALVIA. Please see BUILD_README for more details.") - subparsers = parser.add_subparsers(dest="command", help="sub-command help") - - parser_build = subparsers.add_parser("build") - parser_build.add_argument( - "targets", metavar='TARGETS', type=str, nargs='+', - help="Specify build targets. " - "Available targets are: freetype freeimage boost llvm salvia all. " - "if no target is specified, script just outputs environment variables." - ) - parser_build.add_argument( - "--build-root", dest="build_root", type=str, required=True, help="Folder path for build.") - parser_build.add_argument( - "--install-root", dest="install_root", type=str, required=True, help="Folder path for placing binaries.") - parser_build.add_argument("--arch", choices=['x64'], required=True, help="Target architecture.") - parser_build.add_argument( - "--toolset", metavar="TOOLSET_NAME", type=str, required=True, - help="Specify toolset name and version. The name convention is same as Boost.Build. For e.g.: msvc-14.2") - parser_build.add_argument( - "--toolset-dir", dest="toolset_dir", type=str, required=True, help="Please see BUILD_README.") - parser_build.add_argument( - "--build-config", choices=['Debug', 'RelWithDebInfo'], dest="build_config", type=str, required=True) - parser_build.add_argument( - "--cmake", metavar="CMAKE_EXECUTABLE", type=str, required=True, help="Path of CMake executable.") - - parser_benchmark = subparsers.add_parser("benchmark") - parser_benchmark.add_argument( - "--git", metavar="GIT_EXECUTABLE", type=str, required=True, help="Path of git executable.") - parser_benchmark.add_argument( - "--binary-folder", dest="binary_folder", type=str, required=True, help="Folder of binaries.") - parser_benchmark.add_argument( - "--change-desc", dest="change_desc", type=str, default="", help="Description for un-commit changes." - ) - parser_benchmark.add_argument( - "--repeat", type=int, default=16, help="Repeat count for each benchmark." - ) - - subparsers.add_parser("bm_report") - subparsers.add_parser("clean") - - args = parser.parse_args() - - if args.command == "clean": - report_info("Cleaning ...") - proj = project(args, os.getcwd()) - proj.print_props() - clean_all(proj) - elif args.command == "build": - report_info("Building ...") - try: - build(args) - report_info("Salvia building done.") - except build_error as err: - print("[E] " + err.message()) - print("[E] Salvia built failed.") - sys.exit(1) - elif args.command == "benchmark": - report_info("Benchmarking ...") - source_root_dir = os.path.dirname(os.path.abspath(__file__)) - bm_runner = benchmark_runner(source_root_dir, args.binary_folder, args.git) - bm_runner.run_all(args.change_desc, args.repeat) - elif args.command == "bm_report": - source_root_dir = os.path.dirname(os.path.abspath(__file__)) - generate_csv_report(source_root_dir) - else: - report_error(f"Command <{args.command}> is unknown..") - - -if __name__ == "__main__": - _main() diff --git a/build.bat b/build.bat new file mode 100644 index 000000000..afdbefdf3 --- /dev/null +++ b/build.bat @@ -0,0 +1,28 @@ +@REM --------------------------------------- +@REM Configure the path to your cmake and vcpkg. +@set CMAKE_EXE="" +@set VCPKG_CMAKE="" +@REM --------------------------------------- + + +@REM --------------------------------------- +@REM Configure the build parameters. +@set SALVIA_GENERATOR="Visual Studio 17 2022" +@set SALVIA_CONFIG=RelWithDebInfo +@set SALVIA_PLATFORM=x64 +@REM --------------------------------------- + + +@REM --------------------------------------- +@REM Configure your build and install folder +@set SALVIA_BUILD=..\salvia.msvc1930_win64_rel.build +@set SALVIA_INSTALL=..\salvia.msvc1930_win64_rel.redist +@REM --------------------------------------- + + +@REM --------------------------------------- +@REM Build and Install. +%CMAKE_EXE% -G %SALVIA_GENERATOR% -B %SALVIA_BUILD% -S . -DCMAKE_BUILD_TYPE=%SALVIA_CONFIG% -A %SALVIA_PLATFORM% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_CMAKE% +%CMAKE_EXE% --build %SALVIA_BUILD% --config %SALVIA_CONFIG% +%CMAKE_EXE% --install %SALVIA_BUILD% --config %SALVIA_CONFIG% --prefix %SALVIA_INSTALL% +@REM --------------------------------------- \ No newline at end of file diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 54b2180ac..b4c94822d 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -1,33 +1,23 @@ -macro(SALVIA_CONFIG_OUTPUT_PATHS TARGETNAME) - set_target_properties( ${TARGETNAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${SALVIA_RUNTIME_OUTPUT_DIRECTORY} - ARCHIVE_OUTPUT_DIRECTORY ${SALVIA_LIBRARY_OUTPUT_DIRECTORY} - LIBRARY_OUTPUT_DIRECTORY ${SALVIA_RUNTIME_OUTPUT_DIRECTORY} - DEBUG_POSTFIX "_d" - ) -endmacro(SALVIA_CONFIG_OUTPUT_PATHS) - macro(SALVIA_CHECK_BUILD_WITH_UNICODE) - if(SALVIA_BUILD_WITH_UNICODE) - ADD_DEFINITIONS( - -DUNICODE - -D_UNICODE - ) - endif () + if(SALVIA_BUILD_WITH_UNICODE) + ADD_DEFINITIONS( + -DUNICODE + -D_UNICODE + ) + endif () endmacro(SALVIA_CHECK_BUILD_WITH_UNICODE) macro(SALVIA_GROUP_BY_DIRECTORY root_group prefix sources) - foreach(file_path ${${sources}}) - STRING(REGEX REPLACE ${prefix}/\(.*\) \\1 filterated_file_path ${file_path}) - STRING(REGEX REPLACE "\(.*\)/.*" \\1 group_name ${filterated_file_path}) - STRING(COMPARE EQUAL ${filterated_file_path} ${group_name} no_group) - STRING(REPLACE "/" "\\" group_name ${group_name}) - if(no_group) - set(group_name "\\") - endif(no_group) - SOURCE_GROUP( "${root_group}\\${group_name}" FILES ${file_path}) - endforeach(file_path) + foreach(file_path ${${sources}}) + STRING(REGEX REPLACE ${prefix}/\(.*\) \\1 filterated_file_path ${file_path}) + STRING(REGEX REPLACE "\(.*\)/.*" \\1 group_name ${filterated_file_path}) + STRING(COMPARE EQUAL ${filterated_file_path} ${group_name} no_group) + STRING(REPLACE "/" "\\" group_name ${group_name}) + if(no_group) + set(group_name "\\") + endif(no_group) + SOURCE_GROUP( "${root_group}\\${group_name}" FILES ${file_path}) + endforeach(file_path) endmacro(SALVIA_GROUP_BY_DIRECTORY) # create vcproj.user file for Visual Studio to set debug working directory @@ -35,26 +25,26 @@ function(SALVIA_CREATE_VCPROJ_USERFILE TARGETNAME) if(MSVC) set(SYSTEM_NAME $ENV{USERDOMAIN}) set(USER_NAME $ENV{USERNAME}) - if(MSVC_VERSION GREATER 1920) - configure_file( - ${CMAKE_HOME_DIRECTORY}/cmake/VisualStudio2019UserFile.vcxproj.user.in - ${CMAKE_CURRENT_BINARY_DIR}/${TARGETNAME}.vcxproj.user - @ONLY - ) - elseif(MSVC_VERSION EQUAL 1800) + if(MSVC_VERSION GREATER 1920) + configure_file( + ${CMAKE_HOME_DIRECTORY}/cmake/VisualStudio2019UserFile.vcxproj.user.in + ${CMAKE_CURRENT_BINARY_DIR}/${TARGETNAME}.vcxproj.user + @ONLY + ) + elseif(MSVC_VERSION EQUAL 1800) configure_file( - ${CMAKE_HOME_DIRECTORY}/cmake/VisualStudio2013UserFile.vcxproj.user.in - ${CMAKE_CURRENT_BINARY_DIR}/${TARGETNAME}.vcxproj.user - @ONLY - ) + ${CMAKE_HOME_DIRECTORY}/cmake/VisualStudio2013UserFile.vcxproj.user.in + ${CMAKE_CURRENT_BINARY_DIR}/${TARGETNAME}.vcxproj.user + @ONLY + ) elseif(MSVC_VERSION EQUAL 1700) configure_file( - ${CMAKE_HOME_DIRECTORY}/cmake/VisualStudio2012UserFile.vcxproj.user.in - ${CMAKE_CURRENT_BINARY_DIR}/${TARGETNAME}.vcxproj.user - @ONLY - ) - else() - MESSAGE(FATAL_ERROR "Cannot support Microsoft Visual C++ version ${MSVC_VERSION}. Build will be stopped." ) - endif() + ${CMAKE_HOME_DIRECTORY}/cmake/VisualStudio2012UserFile.vcxproj.user.in + ${CMAKE_CURRENT_BINARY_DIR}/${TARGETNAME}.vcxproj.user + @ONLY + ) + else() + MESSAGE(FATAL_ERROR "Cannot support Microsoft Visual C++ version ${MSVC_VERSION}. Build will be stopped." ) + endif() endif() -endfunction(SALVIA_CREATE_VCPROJ_USERFILE) \ No newline at end of file +endfunction(SALVIA_CREATE_VCPROJ_USERFILE) diff --git a/cmake/ConfigBoostLibs.cmake b/cmake/ConfigBoostLibs.cmake deleted file mode 100644 index 6c81a5ad6..000000000 --- a/cmake/ConfigBoostLibs.cmake +++ /dev/null @@ -1,66 +0,0 @@ - -set(SALVIA_BOOST_DIR CACHE PATH "Specify a path to boost.") - -if(SALVIA_BOOST_DIR) - set( BOOST_ROOT ${SALVIA_BOOST_DIR} ) -else(SALVIA_BOOST_DIR) - MESSAGE( FATAL_ERROR "Please specify a path with 'SALVIA_BOOST_DIR' or run build_all.py." ) -endif(SALVIA_BOOST_DIR) - -FIND_PACKAGE( Boost 1.53.0 ) - -if( Boost_FOUND ) - set( SALVIA_BOOST_INCLUDE_DIR ${SALVIA_BOOST_DIR} ) -else ( Boost_FOUND ) - MESSAGE( FATAL_ERROR "Cannot find boost 1.53 or later. Please specify a path with 'SALVIA_BOOST_DIR' or run './build_all.py'." ) -endif() - -if( NOT SALVIA_BOOST_LIB_DIR ) - MESSAGE( FATAL_ERROR "Cannot find variable SALVIA_BOOST_LIB_DIR. Please compile libraries and copy lib files into directory or run './build_all.py'.") -endif() - -set( SALVIA_BOOST_VERSION_STRING "${Boost_MAJOR_VERSION}_${Boost_MINOR_VERSION}" ) - -# From short name to full path name. -macro( boost_lib_fullname FULL_NAME SHORT_NAME ) - if( SALVIA_BUILD_TYPE_LOWERCASE STREQUAL "debug" ) - set ( SALVIA_BOOST_LIBS_POSTFIX "-d" ) - endif() - if(MSVC) - MESSAGE( WARNING "MSVC will be auto-linked to boost library." ) - elseif(MINGW) - if(CMAKE_INFO_BUILD_TYPE STREQUAL "Debug") - set( ${FULL_NAME} "boost_${SHORT_NAME}-mgw${GCC_VERSION_STR_MAJOR_MINOR}-mt${SALVIA_BOOST_LIBS_POSTFIX}-d-${SALVIA_BOOST_VERSION_STRING}" ) - else(CMAKE_INFO_BUILD_TYPE STREQUAL "Debug") - set( ${FULL_NAME} "boost_${SHORT_NAME}-mgw${GCC_VERSION_STR_MAJOR_MINOR}-mt${SALVIA_BOOST_LIBS_POSTFIX}-${SALVIA_BOOST_VERSION_STRING}" ) - endif(CMAKE_INFO_BUILD_TYPE STREQUAL "Debug") - elseif(UNIX) - set(${FULL_NAME} "boost_${SHORT_NAME}") - endif(MSVC) -endmacro( boost_lib_fullname ) - -macro( add_boost_lib SHORT_NAME ) - set( FULL_NAME "" ) - boost_lib_fullname( FULL_NAME ${SHORT_NAME} ) - set( SALVIA_BOOST_LIBS ${SALVIA_BOOST_LIBS} ${FULL_NAME} ) -endmacro( add_boost_lib ) - -macro( config_boost_libs ) - if(MSVC) - add_definitions( -DBOOST_ALL_DYN_LINK ) - set( SALVIA_BOOST_LIBS "" ) - else(MSVC) - add_boost_lib( wave ) - add_boost_lib( unit_test_framework ) - add_boost_lib( program_options ) - add_boost_lib( thread ) - add_boost_lib( date_time ) - add_boost_lib( locale ) - add_boost_lib( chrono ) - add_boost_lib( filesystem ) - add_boost_lib( atomic ) - add_boost_lib( system ) - endif(MSVC) -endmacro( config_boost_libs ) - -config_boost_libs() \ No newline at end of file diff --git a/cmake/ConfigLLVMLibs.cmake b/cmake/ConfigLLVMLibs.cmake deleted file mode 100644 index bcdad9ffb..000000000 --- a/cmake/ConfigLLVMLibs.cmake +++ /dev/null @@ -1,24 +0,0 @@ -macro( config_llvm_libs ) -if (MSVC) - set( SASL_LLVM_LIBS - LLVMX86CodeGen LLVMX86Desc LLVMX86Utils LLVMX86AsmPrinter LLVMX86Info - LLVMBitWriter LLVMBitReader LLVMAsmParser LLVMAsmPrinter - LLVMExecutionEngine LLVMMCJIT - LLVMRuntimeDyld - LLVMGlobalISel LLVMSelectionDAG - LLVMMCParser - LLVMCodeGen - LLVMScalarOpts - LLVMInstCombine - LLVMTransformUtils - LLVMAnalysis LLVMTarget LLVMMC LLVMMCDisassembler LLVMipo LLVMProfileData LLVMDebugInfoCodeView LLVMDemangle - LLVMObject LLVMCore LLVMSupport LLVMBinaryFormat - ) -else(MSVC) - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${SALVIA_LLVM_INSTALL_DIR}/share/llvm/cmake") - include(LLVMConfig) - llvm_map_components_to_libraries(SASL_LLVM_LIBS jit native interpreter) -endif(MSVC) -endmacro() - -config_llvm_libs() diff --git a/cmake/ConfigurePath.cmake b/cmake/ConfigurePath.cmake deleted file mode 100644 index a9798cf6c..000000000 --- a/cmake/ConfigurePath.cmake +++ /dev/null @@ -1,30 +0,0 @@ -set ( SALVIA_HOME_DIR "${CMAKE_HOME_DIRECTORY}" ) -set ( SASL_HOME_DIR "${CMAKE_HOME_DIRECTORY}" ) - -set(SALVIA_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin/${CMAKE_INFO_ENV_WITHOUT_BUILD_TYPE}") -set(SALVIA_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib/${CMAKE_INFO_ENV_WITHOUT_BUILD_TYPE}") - -if(NOT MSVC) - set(SALVIA_RUNTIME_OUTPUT_DIRECTORY "${SALVIA_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_INFO_BUILD_TYPE}") - set(SALVIA_LIBRARY_OUTPUT_DIRECTORY "${SALVIA_LIBRARY_OUTPUT_DIRECTORY}/${CMAKE_INFO_BUILD_TYPE}") -endif(NOT MSVC) - -MESSAGE(STATUS "Runtime directory: ${SALVIA_RUNTIME_OUTPUT_DIRECTORY}") -MESSAGE(STATUS "Library directory: ${SALVIA_LIBRARY_OUTPUT_DIRECTORY}") -# Third-party -set( SALVIA_THIRD_PARTY_DIR "${SALVIA_HOME_DIR}/3rd_party/") - -# THEAD POOL -set( SALVIA_THREAD_POOL_INCLUDE_DIR "${SALVIA_THIRD_PARTY_DIR}/threadpool" ) - -# LLVM -set( SALVIA_LLVM_INCLUDE_DIR "${SALVIA_LLVM_INSTALL_DIR}/include/") -set( SALVIA_LLVM_LIB_DIR "${SALVIA_LLVM_INSTALL_DIR}/lib" ) - -# FREETYPE -set( SALVIA_FREETYPE_INCLUDE_DIR "${SALVIA_THIRD_PARTY_DIR}/freetype2/include/") -set( SALVIA_FREETYPE_LIB_DIR "${SALVIA_FREETYPE_DIR}") - -# FREEIMAGE -set( SALVIA_FREEIMAGE_INCLUDE_DIR "${SALVIA_FREEIMAGE_DIR}/Dist") -set( SALVIA_FREEIMAGE_LIB_DIR "${SALVIA_FREEIMAGE_DIR}/Dist") diff --git a/cmake/MSVC.cmake b/cmake/MSVC.cmake index da36f42de..7e183a4c6 100644 --- a/cmake/MSVC.cmake +++ b/cmake/MSVC.cmake @@ -4,7 +4,7 @@ if(MSVC_VERSION LESS 1920) MESSAGE(FATAL_ERROR "Cannot support Microsoft Visual C++ version ${MSVC_VERSION}. Build will be stopped." ) endif() -set(CMAKE_C_FLAGS "/DWIN32 /D_WINDOWS /W4 /wd4503 /wd4251 /wd4275 /wd4819 /EHsc /GR /D_SECURE_SCL=0 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS /D_SCL_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS" ) +set(CMAKE_C_FLAGS "/DWIN32 /D_WINDOWS /W4 /wd4503 /wd4251 /wd4275 /wd4819 /EHsc /GR /D_SECURE_SCL=0 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS /D_SCL_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS /utf-8" ) set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /std:c++17") #C4503: decorated name length exceeded, name was truncated diff --git a/cmake/SystemInfo.cmake b/cmake/SystemInfo.cmake deleted file mode 100644 index d12f242e0..000000000 --- a/cmake/SystemInfo.cmake +++ /dev/null @@ -1,74 +0,0 @@ -# define a set of string with may-be useful readable name -# this file is meant to be included in a CMakeLists.txt -# not as a standalone CMake script -set(CMAKE_INFO_COMPILER_NAME "") -set(CMAKE_INFO_COMPILER_SHOWNAME "") -set(CMAKE_INFO_PLATFORM_NAME "") -set(CMAKE_INFO_PLATFORM_BITNESS "") - -if(WIN32) - set(CMAKE_INFO_OS_SHORT_NAME "nt") -elseif(UNIX) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(CMAKE_INFO_OS_SHORT_NAME "linux") - else(CMAKE_SYSTEM_NAME STREQUAL "Linux") - MESSAGE(FATAL_ERROR "Unsupported system: ${CMAKE_SYSTEM}") - endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") -endif(WIN32) - -# Compilers -# taken from http://predef.sourceforge.net/precomp.html#sec34 -if(MSVC) - if(MSVC_VERSION LESS 1920) - MESSAGE(FATAL_ERROR "Cannot support MSVC compiler earlier than VS 2019 (14.20 or later).") - endif(MSVC_VERSION LESS 1920) - - if (MSVC_VERSION GREATER 1920) - set(CMAKE_INFO_COMPILER_NAME "vc-14.2") - set(CMAKE_INFO_COMPILER_SHOWNAME "msvc142") - if(CMAKE_GENERATOR_PLATFORM STREQUAL "x64") - set(CMAKE_INFO_PLATFORM_NAME "ntx64") - set(CMAKE_INFO_PLATFORM_BITNESS 64) - else() - MESSAGE(FATAL_ERROR "Can't recognized CMAKE_GENERATOR_PLATFORM <${CMAKE_GENERATOR_PLATFORM}>.") - endif() - else() - MESSAGE(FATAL_ERROR "Can't recognized MSVC_VERSION <${MSVC_VERSION}>.") - endif(MSVC_VERSION GREATER 1920) -endif(MSVC) - -if(UNIX OR MINGW) - if(NOT CMAKE_INFO_ARCH_NAME) - MESSAGE(FATAL_ERROR "If you use MinGW or GCC compiler, please specify CMAKE_INFO_GCC_PLATFORM_NAME." ) - endif(NOT CMAKE_INFO_ARCH_NAME) - - exec_program( ${CMAKE_C_COMPILER} ARGS -dumpversion OUTPUT_VARIABLE GCC_VERSION ) - MESSAGE(STATUS "GCC version: ${GCC_VERSION}") - string( REPLACE "." "" GCC_VERSION_STR_FULL ${GCC_VERSION} ) - string( REGEX MATCH "[0-9]+\\.[0-9]+" GCC_VERSION_MAJOR_MINOR ${GCC_VERSION} ) - string( REPLACE "." "" GCC_VERSION_STR_MAJOR_MINOR ${GCC_VERSION_MAJOR_MINOR} ) - - if(CMAKE_INFO_ARCH_NAME MATCHES "x64") - set(CMAKE_INFO_PLATFORM_NAME "${CMAKE_INFO_OS_SHORT_NAME}x64") - set(CMAKE_INFO_PLATFORM_BITNESS 64) - elseif(CMAKE_INFO_ARCH_NAME MATCHES "x86") - set(CMAKE_INFO_PLATFORM_NAME "${CMAKE_INFO_OS_SHORT_NAME}x86") - set(CMAKE_INFO_PLATFORM_BITNESS 32) - else(CMAKE_INFO_ARCH_NAME MATCHES "x86") - MESSAGE(FATAL_ERROR "Value of CMAKE_INFO_GCC_PLATFORM_NAME (${CMAKE_INFO_GCC_PLATFORM_NAME}) is invalid." ) - endif(CMAKE_INFO_ARCH_NAME MATCHES "x64") - - if(MINGW) - exec_program( ${CMAKE_C_COMPILER} ARGS -dumpmachine OUTPUT_VARIABLE GCC_MACHINE ) - if(GCC_MACHINE MATCHES "x86_64") - set(CMAKE_INFO_COMPILER_SHOWNAME "mingw64${GCC_VERSION_STR_FULL}") - else(GCC_MACHINE MATCHES "x86_64") - set(CMAKE_INFO_COMPILER_SHOWNAME "mingw${GCC_VERSION_STR_FULL}") - endif(GCC_MACHINE MATCHES "x86_64") - set(CMAKE_INFO_COMPILER_NAME "mgw-${GCC_VERSION}") - else(MINGW) - set(CMAKE_INFO_COMPILER_NAME "gcc-${GCC_VERSION}") - set(CMAKE_INFO_COMPILER_SHOWNAME "gcc${GCC_VERSION_STR_FULL}") - endif(MINGW) - -endif(UNIX OR MINGW) diff --git a/cmake/Variables.cmake b/cmake/Variables.cmake deleted file mode 100644 index 634641ee8..000000000 --- a/cmake/Variables.cmake +++ /dev/null @@ -1,11 +0,0 @@ -if(MSVC) - set( CMAKE_INFO_BUILD_TYPE "$(ConfigurationName)" ) -endif(MSVC) - -if(MINGW OR UNIX) - set( CMAKE_INFO_BUILD_TYPE ${CMAKE_BUILD_TYPE} ) - string( TOLOWER ${CMAKE_INFO_BUILD_TYPE} CMAKE_INFO_BUILD_TYPE_LOWERCASE ) -endif(MINGW OR UNIX) - -set( CMAKE_INFO_ENV_WITHOUT_BUILD_TYPE "${CMAKE_INFO_PLATFORM_NAME}_${CMAKE_INFO_COMPILER_SHOWNAME}" ) -set( CMAKE_INFO_ENVIRONMENT_NAME "${CMAKE_INFO_ENV_WITHOUT_BUILD_TYPE}_${CMAKE_INFO_BUILD_TYPE}") diff --git a/eflib/CMakeLists.txt b/eflib/CMakeLists.txt index 4da874a76..c815bd792 100644 --- a/eflib/CMakeLists.txt +++ b/eflib/CMakeLists.txt @@ -1,8 +1,8 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() INCLUDE_DIRECTORIES( - ${SALVIA_BOOST_INCLUDE_DIR} ${SALVIA_HOME_DIR} + ${Boost_INCLUDE_DIRS} ) set (HEADER_FILES @@ -69,5 +69,3 @@ SET_TARGET_PROPERTIES( EFLIB PROPERTIES FOLDER "SALVIA Renderer") if(UNIX) TARGET_LINK_LIBRARIES(EFLIB dl) # This lib is for dlopen/dlsym/dlclose endif() - -SALVIA_CONFIG_OUTPUT_PATHS(EFLIB) diff --git a/res/Logo3D.max b/res/Logo3D.max index ac33ccb72c4cd26dea7e432f1114f2ae16c3edd1..72dc826fa32c568b78cddbfec9f9c6cc7222f004 100644 GIT binary patch literal 131 zcmWN?K@!3s3;@78uiyigh7=6_4J07UsC0z(;OliSd*#n){jKYq$JmW|w0V1$vHY)R zTDZT=I5L|HRBxh3jn?^Hn9#Xk1EO`53av3h0Uw+vT=^a-3j#B7l#m3FH}E-HBv&>; NQKNmcMgSYU`T_ToCv*S+ literal 311296 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 literal 0 HcmV?d00001 diff --git a/resources/morph/Text3D.max b/resources/morph/Text3D.max new file mode 100644 index 000000000..f58d48b55 --- /dev/null +++ b/resources/morph/Text3D.max @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:693a75bc6e4246628b6ad2b82ae1adc64f4671ee8ca007350ae9b17595fb07fa +size 180224 diff --git a/resources/morph/dst.dae b/resources/morph/dst.dae new file mode 100644 index 000000000..cff6d1c8e --- /dev/null +++ b/resources/morph/dst.dae @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c40467f789d261bbf1ffa8278ed8173b6a425f36c8db7d327d896b84fa5fb5c +size 307636 diff --git a/resources/morph/src.dae b/resources/morph/src.dae new file mode 100644 index 000000000..6c3e9d502 --- /dev/null +++ b/resources/morph/src.dae @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20a96d74f8e937e55cd82b20c893386435811ba8428cfb7e901cfc0134686b17 +size 307606 diff --git a/resources/sponza_lq/Sponza.mtl b/resources/sponza_lq/Sponza.mtl new file mode 100644 index 000000000..8215b5574 --- /dev/null +++ b/resources/sponza_lq/Sponza.mtl @@ -0,0 +1,227 @@ +newmtl sponza_00SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_thorn_diff.jpg +bump textures\sponza_thorn_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.36 0.36 0.36 +Ns 2.00 +newmtl sponza_01SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\vase_plant.jpg +Ni 1.00 +Ks 0.18 0.18 0.18 +Ns -0.00 +newmtl sponza_01SG1 +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\vase_round.jpg +bump textures\vase_round_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.90 0.90 0.90 +Ns 30.00 +newmtl sponza_03SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\background.jpg +bump textures\background_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.90 0.90 0.90 +Ns 30.00 +newmtl sponza_05SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\spnza_bricks_a_diff.jpg +bump textures\spnza_bricks_a_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.90 0.90 0.90 +Ns 30.00 +newmtl sponza_07SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_arch_diff.jpg +bump textures\sponza_arch_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.00 0.00 0.00 +Ns -0.93 +newmtl sponza_08SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_ceiling_a_diff.jpg +Ni 1.00 +Ks 0.90 0.90 0.90 +Ns 30.00 +newmtl sponza_09SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_column_a_diff.jpg +bump textures\sponza_column_a_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.00 0.00 0.00 +Ns -0.93 +newmtl sponza_18SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_floor_a_diff.jpg +Ni 1.00 +Ks 0.03 0.03 0.03 +Ns -0.93 +newmtl sponza_22SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_column_c_diff.jpg +bump textures\sponza_column_c_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.00 0.00 0.00 +Ns -0.93 +newmtl sponza_70SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_details_diff.jpg +Ni 1.00 +Ks 0.00 0.00 0.00 +Ns -0.93 +newmtl sponza_125SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_column_b_diff.jpg +bump textures\sponza_column_b_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.00 0.00 0.00 +Ns -0.93 +newmtl sponza_259SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_flagpole_diff.jpg +Ni 1.00 +Ks 0.68 0.68 0.68 +Ns 11.93 +newmtl sponza_282SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_fabric_green_diff.jpg +Ni 1.00 +Ks 0.45 0.45 0.45 +Ns 3.66 +newmtl sponza_283SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_fabric_blue_diff.jpg +Ni 1.00 +Ks 0.45 0.45 0.45 +Ns 3.66 +newmtl sponza_284SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_fabric_diff.jpg +Ni 1.00 +Ks 0.45 0.45 0.45 +Ns 3.66 +newmtl sponza_320SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_curtain_blue_diff.jpg +Ni 1.00 +Ks 0.45 0.45 0.45 +Ns 3.66 +newmtl sponza_321SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_curtain_diff.jpg +Ni 1.00 +Ks 0.45 0.45 0.45 +Ns 3.66 +newmtl sponza_322SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_curtain_green_diff.jpg +Ni 1.00 +Ks 0.45 0.45 0.45 +Ns 3.66 +newmtl sponza_330SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\chain_texture.jpg +bump textures\chain_texture_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.72 0.72 0.72 +Ns 14.00 +newmtl sponza_334SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\vase_hanging.jpg +Ni 1.00 +Ks 0.72 0.72 0.72 +Ns 14.00 +newmtl sponza_373SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\vase_dif.jpg +bump textures\vase_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.90 0.90 0.90 +Ns 30.00 +newmtl sponza_377SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\lion.jpg +bump textures\lion_ddn.jpg -bm 0.02 +Ni 1.00 +Ks 0.90 0.90 0.90 +Ns 30.00 +newmtl sponza_380SG +illum 4 +Kd 0.00 0.00 0.00 +Ka 0.59 0.59 0.59 +Tf 1.00 1.00 1.00 +map_Kd textures\sponza_roof_diff.jpg +Ni 1.00 +Ks 0.00 0.00 0.00 +Ns -0.93 diff --git a/resources/sponza_lq/part_of_sponza.mtl b/resources/sponza_lq/part_of_sponza.mtl new file mode 100644 index 000000000..f768dbae3 --- /dev/null +++ b/resources/sponza_lq/part_of_sponza.mtl @@ -0,0 +1,44 @@ +# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware +# ´´½¨µÄÎļþ:20.02.2012 18:25:03 + +newmtl sponza_18SG + Ns -0.9300 + Ni 1.5000 + d 1.0000 + Tr 0.0000 + Tf 1.0000 1.0000 1.0000 + illum 2 + Ka 0.5900 0.5900 0.5900 + Kd 0.0000 0.0000 0.0000 + Ks 0.0300 0.0300 0.0300 + Ke 0.0000 0.0000 0.0000 + map_Ka textures\sponza_floor_a_diff.jpg + map_Kd textures\sponza_floor_a_diff.jpg + +newmtl sponza_05SG + Ns 30.0000 + Ni 1.5000 + d 1.0000 + Tr 0.0000 + Tf 1.0000 1.0000 1.0000 + illum 2 + Ka 0.5900 0.5900 0.5900 + Kd 0.0000 0.0000 0.0000 + Ks 0.9000 0.9000 0.9000 + Ke 0.0000 0.0000 0.0000 + map_Ka textures\spnza_bricks_a_diff.jpg + map_Kd textures\spnza_bricks_a_diff.jpg + +newmtl sponza_22SG + Ns -0.9300 + Ni 1.5000 + d 1.0000 + Tr 0.0000 + Tf 1.0000 1.0000 1.0000 + illum 2 + Ka 0.5900 0.5900 0.5900 + Kd 0.0000 0.0000 0.0000 + Ks 0.0000 0.0000 0.0000 + Ke 0.0000 0.0000 0.0000 + map_Ka textures\sponza_column_c_diff.jpg + map_Kd textures\sponza_column_c_diff.jpg diff --git a/resources/sponza_lq/part_of_sponza.obj b/resources/sponza_lq/part_of_sponza.obj new file mode 100644 index 000000000..2a14f44b6 --- /dev/null +++ b/resources/sponza_lq/part_of_sponza.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20ba9fb840b57965385bb7bec9c1a8a0a6db886702eae89d4a7306006ae902a2 +size 39189 diff --git a/resources/sponza_lq/sponza.obj b/resources/sponza_lq/sponza.obj new file mode 100644 index 000000000..8039e9e09 --- /dev/null +++ b/resources/sponza_lq/sponza.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cea03a1b4792ff6fd8dccd207d954fb223b6984f856d4ac7a6a75a4b58cb8d61 +size 21244063 diff --git a/resources/sponza_lq/textures/background.jpg b/resources/sponza_lq/textures/background.jpg new file mode 100644 index 000000000..388698701 --- /dev/null +++ b/resources/sponza_lq/textures/background.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de3326932d7a80be6717b8414800224ab345af9fc4a9d1429f5b24dc9afed0b9 +size 97574 diff --git a/resources/sponza_lq/textures/background_ddn.jpg b/resources/sponza_lq/textures/background_ddn.jpg new file mode 100644 index 000000000..207ee8cca --- /dev/null +++ b/resources/sponza_lq/textures/background_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5012f8b80d5598b21cb0d8edde6e1033f782b65e7a5fea27a2aea7e7818f2fe +size 90428 diff --git a/resources/sponza_lq/textures/chain_texture.jpg b/resources/sponza_lq/textures/chain_texture.jpg new file mode 100644 index 000000000..c048fe9a0 --- /dev/null +++ b/resources/sponza_lq/textures/chain_texture.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb7a9277a3df788a4a5f52779c44c7f1b52c6e422b8ea1fa726454206362f729 +size 26261 diff --git a/resources/sponza_lq/textures/chain_texture_ddn.jpg b/resources/sponza_lq/textures/chain_texture_ddn.jpg new file mode 100644 index 000000000..f7388bb0d --- /dev/null +++ b/resources/sponza_lq/textures/chain_texture_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29dbba44f2b0b237b3652400c3c8ea23b2b2dcdfb096e26b570546b08f6d9fc2 +size 18040 diff --git a/resources/sponza_lq/textures/chain_texture_mask.jpg b/resources/sponza_lq/textures/chain_texture_mask.jpg new file mode 100644 index 000000000..e1ffb7792 --- /dev/null +++ b/resources/sponza_lq/textures/chain_texture_mask.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:290ccd6a4de800cee3a6f0981f1229642425328dc5b3aae161e1dcc6f0fd8ded +size 16073 diff --git a/resources/sponza_lq/textures/lion.jpg b/resources/sponza_lq/textures/lion.jpg new file mode 100644 index 000000000..c3321b366 --- /dev/null +++ b/resources/sponza_lq/textures/lion.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cbf781e27793ec7db44aaa9513bca4dcd81a861a3ba9baa8a83b662655afae5 +size 134159 diff --git a/resources/sponza_lq/textures/lion2_ddn.jpg b/resources/sponza_lq/textures/lion2_ddn.jpg new file mode 100644 index 000000000..1e7a70a66 --- /dev/null +++ b/resources/sponza_lq/textures/lion2_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee209ed9c0746862c871b6f4ce8da32068eab383d36c6745f56448f09d725af3 +size 129873 diff --git a/resources/sponza_lq/textures/lion_ddn.jpg b/resources/sponza_lq/textures/lion_ddn.jpg new file mode 100644 index 000000000..073922b50 --- /dev/null +++ b/resources/sponza_lq/textures/lion_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83219d86246ce14fcf18a501322b2c6bdbafb7e7cea68842548bbabe2ff89ae8 +size 125517 diff --git a/resources/sponza_lq/textures/spnza_bricks_a_ddn.jpg b/resources/sponza_lq/textures/spnza_bricks_a_ddn.jpg new file mode 100644 index 000000000..f2b5c7e7d --- /dev/null +++ b/resources/sponza_lq/textures/spnza_bricks_a_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:328ec1a0168e9a40aea41f814d9caf25386cbe6d171fb569b068d22bbf91c525 +size 71173 diff --git a/resources/sponza_lq/textures/spnza_bricks_a_diff.jpg b/resources/sponza_lq/textures/spnza_bricks_a_diff.jpg new file mode 100644 index 000000000..7019656f4 --- /dev/null +++ b/resources/sponza_lq/textures/spnza_bricks_a_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acfca7edc14c0c1e7fc563e74ad6f82a080504759cb4e793bc77751b85c136bc +size 149579 diff --git a/resources/sponza_lq/textures/spnza_bricks_a_spec.jpg b/resources/sponza_lq/textures/spnza_bricks_a_spec.jpg new file mode 100644 index 000000000..93d903eb6 --- /dev/null +++ b/resources/sponza_lq/textures/spnza_bricks_a_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78ba85678443e41119fa3bbb8f1f670c735624c7417147f6cd86145addd331b9 +size 190549 diff --git a/resources/sponza_lq/textures/sponza_arch_ddn.jpg b/resources/sponza_lq/textures/sponza_arch_ddn.jpg new file mode 100644 index 000000000..9c4c45e40 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_arch_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88491dbf37330d4807f84e9e90351da68abd916c1a4738eddda03f825c13cbe0 +size 49105 diff --git a/resources/sponza_lq/textures/sponza_arch_diff.jpg b/resources/sponza_lq/textures/sponza_arch_diff.jpg new file mode 100644 index 000000000..c378950cc --- /dev/null +++ b/resources/sponza_lq/textures/sponza_arch_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e0d502da1d34fc7dd4034bf711ac57ec66303dd7f8cea4ae6b81d537790c008 +size 123789 diff --git a/resources/sponza_lq/textures/sponza_arch_spec.jpg b/resources/sponza_lq/textures/sponza_arch_spec.jpg new file mode 100644 index 000000000..6fe4dc6d3 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_arch_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96dfcbc0c0c69344b2116aa94ecf27bedae429004e383ad7de6854c01525901f +size 106577 diff --git a/resources/sponza_lq/textures/sponza_ceiling_a_diff.jpg b/resources/sponza_lq/textures/sponza_ceiling_a_diff.jpg new file mode 100644 index 000000000..5b8e24d48 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_ceiling_a_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae29726f025b9deb2ffd3d9acf6cbb50c605cd143d633e3bc0c2ce60302d350f +size 130846 diff --git a/resources/sponza_lq/textures/sponza_ceiling_a_spec.jpg b/resources/sponza_lq/textures/sponza_ceiling_a_spec.jpg new file mode 100644 index 000000000..7c789aa28 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_ceiling_a_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18a5798cc945f23fb91a0738e116fe47947cc124e9cbbf26305a6e6123de50ca +size 129134 diff --git a/resources/sponza_lq/textures/sponza_column_a_ddn.jpg b/resources/sponza_lq/textures/sponza_column_a_ddn.jpg new file mode 100644 index 000000000..6d829809d --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_a_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:437b27d461db2ac72bfed9772fe185acc2f9fd4dbfc959602fa5f3a0fdf42888 +size 107700 diff --git a/resources/sponza_lq/textures/sponza_column_a_diff.jpg b/resources/sponza_lq/textures/sponza_column_a_diff.jpg new file mode 100644 index 000000000..d5cfe2716 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_a_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96bf668b6983b3b78cd120040e6fc08596432e21fc4261223ad2f0018c863408 +size 132961 diff --git a/resources/sponza_lq/textures/sponza_column_a_spec.jpg b/resources/sponza_lq/textures/sponza_column_a_spec.jpg new file mode 100644 index 000000000..96239e9cc --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_a_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89dbf81d14c152fe975b592ab300efea3e1fa7859076eb0d88f4c0ef69f583b4 +size 131153 diff --git a/resources/sponza_lq/textures/sponza_column_b_ddn.jpg b/resources/sponza_lq/textures/sponza_column_b_ddn.jpg new file mode 100644 index 000000000..df12f7a19 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_b_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e30f8c0ddd7eb49a3a4753e5761333d1fb162ae31efaa54d5086f60263fc7af +size 116865 diff --git a/resources/sponza_lq/textures/sponza_column_b_diff.jpg b/resources/sponza_lq/textures/sponza_column_b_diff.jpg new file mode 100644 index 000000000..6d902fd49 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_b_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:346f8bf3f6b90bd5440cf255ad38bd354a4cfcca9951022bce17afd8a7c6755e +size 141189 diff --git a/resources/sponza_lq/textures/sponza_column_b_spec.jpg b/resources/sponza_lq/textures/sponza_column_b_spec.jpg new file mode 100644 index 000000000..f9edda174 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_b_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a5862415ecc40ab720ff1175686b8ae807f6720330066b5c8bdfeeee7c8dca7f +size 128665 diff --git a/resources/sponza_lq/textures/sponza_column_c_ddn.jpg b/resources/sponza_lq/textures/sponza_column_c_ddn.jpg new file mode 100644 index 000000000..b4e813c62 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_c_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4eab9d0f334257c5052253dcc98311cdade150be1a3b56851bf424bfccdcba8 +size 132448 diff --git a/resources/sponza_lq/textures/sponza_column_c_diff.jpg b/resources/sponza_lq/textures/sponza_column_c_diff.jpg new file mode 100644 index 000000000..7cf800e1e --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_c_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2525831b505772b6549f0067d2d0fa856046d1e35b4e7079f7a2ffa2171691f5 +size 164953 diff --git a/resources/sponza_lq/textures/sponza_column_c_spec.jpg b/resources/sponza_lq/textures/sponza_column_c_spec.jpg new file mode 100644 index 000000000..38dc398da --- /dev/null +++ b/resources/sponza_lq/textures/sponza_column_c_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fae02d94344b95a3aed50555ddfb6759fc616c62a395348945f34ab116dd3c0f +size 147033 diff --git a/resources/sponza_lq/textures/sponza_curtain_blue_diff.jpg b/resources/sponza_lq/textures/sponza_curtain_blue_diff.jpg new file mode 100644 index 000000000..80791e93c --- /dev/null +++ b/resources/sponza_lq/textures/sponza_curtain_blue_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e87fa29217bd271e7a58b19978690eca9b53b236f3feb30051ec6e13a89a9b7f +size 830897 diff --git a/resources/sponza_lq/textures/sponza_curtain_diff.jpg b/resources/sponza_lq/textures/sponza_curtain_diff.jpg new file mode 100644 index 000000000..db1455489 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_curtain_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc3aecd7445978c79a204ffdfe145fef0f716fd9ee201c3f0a51db5432e0661e +size 779226 diff --git a/resources/sponza_lq/textures/sponza_curtain_green_diff.jpg b/resources/sponza_lq/textures/sponza_curtain_green_diff.jpg new file mode 100644 index 000000000..60abb85eb --- /dev/null +++ b/resources/sponza_lq/textures/sponza_curtain_green_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd79d619ffed76de8667426068a4282a4e41c36a04e64611be3c9dc8f6c58e66 +size 763358 diff --git a/resources/sponza_lq/textures/sponza_details_diff.jpg b/resources/sponza_lq/textures/sponza_details_diff.jpg new file mode 100644 index 000000000..c2065756c --- /dev/null +++ b/resources/sponza_lq/textures/sponza_details_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a2cc0c9d39f2b2bcfe2f766579e62aaedb1ad49df40a5441dd43bb0d1ab9479 +size 93480 diff --git a/resources/sponza_lq/textures/sponza_details_spec.jpg b/resources/sponza_lq/textures/sponza_details_spec.jpg new file mode 100644 index 000000000..f70e51496 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_details_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8098b361a60370fd0d28014462f4552d706d791454da48fc8cc1a7d9b3031ee +size 128290 diff --git a/resources/sponza_lq/textures/sponza_fabric_blue_diff.jpg b/resources/sponza_lq/textures/sponza_fabric_blue_diff.jpg new file mode 100644 index 000000000..39ac5c14d --- /dev/null +++ b/resources/sponza_lq/textures/sponza_fabric_blue_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b433678df27cdefdb90dcc3f01f191567dd90cd6246676befce62d88d3d548a6 +size 147941 diff --git a/resources/sponza_lq/textures/sponza_fabric_diff.jpg b/resources/sponza_lq/textures/sponza_fabric_diff.jpg new file mode 100644 index 000000000..276d8dc68 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_fabric_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63e9456a43d49e6622f19c37040bb11182b7db1a07004e29986cc1f92199f3f8 +size 160049 diff --git a/resources/sponza_lq/textures/sponza_fabric_green_diff.jpg b/resources/sponza_lq/textures/sponza_fabric_green_diff.jpg new file mode 100644 index 000000000..6b8bd6539 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_fabric_green_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33af61838ae748263b765087eb8f3a5e88ac265d272e219b757c8bf25719cb72 +size 149808 diff --git a/resources/sponza_lq/textures/sponza_fabric_spec.jpg b/resources/sponza_lq/textures/sponza_fabric_spec.jpg new file mode 100644 index 000000000..c8844878a --- /dev/null +++ b/resources/sponza_lq/textures/sponza_fabric_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18836f6a7cacadd7959efba4730c821c2ba8594c80ddddf993a0d6f02c1975c1 +size 143641 diff --git a/resources/sponza_lq/textures/sponza_flagpole_diff.jpg b/resources/sponza_lq/textures/sponza_flagpole_diff.jpg new file mode 100644 index 000000000..e6b661b7e --- /dev/null +++ b/resources/sponza_lq/textures/sponza_flagpole_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7461fe3556e54c891fbe5218a87f5fe28cef0ddae8d00ed8ac7eab6d954a8bcb +size 101285 diff --git a/resources/sponza_lq/textures/sponza_flagpole_spec.jpg b/resources/sponza_lq/textures/sponza_flagpole_spec.jpg new file mode 100644 index 000000000..1fe1bd2cb --- /dev/null +++ b/resources/sponza_lq/textures/sponza_flagpole_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11c0bf0120d4d2d5d117331e93ea8dd5399c1d2383009e817729b0b01cce0bbd +size 131338 diff --git a/resources/sponza_lq/textures/sponza_floor_a_diff.jpg b/resources/sponza_lq/textures/sponza_floor_a_diff.jpg new file mode 100644 index 000000000..e22bf1366 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_floor_a_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c442d6be5bde9d714366f4c8174b401576bae056448458ce9d74e229a05b2d7c +size 144708 diff --git a/resources/sponza_lq/textures/sponza_floor_a_spec.jpg b/resources/sponza_lq/textures/sponza_floor_a_spec.jpg new file mode 100644 index 000000000..234b28dba --- /dev/null +++ b/resources/sponza_lq/textures/sponza_floor_a_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cf00868417c487af793e0d17c94b30bcb126763077df8d6545c98d10b64806b +size 170655 diff --git a/resources/sponza_lq/textures/sponza_roof_diff.jpg b/resources/sponza_lq/textures/sponza_roof_diff.jpg new file mode 100644 index 000000000..df9973f04 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_roof_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c6bc85fd5b5d8aa2098fb0355c1a8b5cd4bed1643cf8c4ccda9a81c5dc792f9 +size 230190 diff --git a/resources/sponza_lq/textures/sponza_thorn_ddn.jpg b/resources/sponza_lq/textures/sponza_thorn_ddn.jpg new file mode 100644 index 000000000..df5688531 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_thorn_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59b0045103980bbf3ff5ae1fcd2c309c9947ebc8f01502bb7510cad31566e53b +size 14947 diff --git a/resources/sponza_lq/textures/sponza_thorn_diff.jpg b/resources/sponza_lq/textures/sponza_thorn_diff.jpg new file mode 100644 index 000000000..c25e9469a --- /dev/null +++ b/resources/sponza_lq/textures/sponza_thorn_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c407b590f35e5e109a9edf057bd92c6494626395bb78cb389ff852a956916185 +size 35038 diff --git a/resources/sponza_lq/textures/sponza_thorn_mask.jpg b/resources/sponza_lq/textures/sponza_thorn_mask.jpg new file mode 100644 index 000000000..13568b876 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_thorn_mask.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02ffbf85a8dd34d7f1d7663a2cade884883c0501f7576768ef87da01055b45df +size 55087 diff --git a/resources/sponza_lq/textures/sponza_thorn_spec.jpg b/resources/sponza_lq/textures/sponza_thorn_spec.jpg new file mode 100644 index 000000000..dbd78c773 --- /dev/null +++ b/resources/sponza_lq/textures/sponza_thorn_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bbadb9019ce486cc337039a1f16f5e371ada45dccaac1baf9696dd63f1967c5 +size 39761 diff --git a/resources/sponza_lq/textures/vase_ddn.jpg b/resources/sponza_lq/textures/vase_ddn.jpg new file mode 100644 index 000000000..7cf8f1a1f --- /dev/null +++ b/resources/sponza_lq/textures/vase_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0493b39a8558c70d24dd80c4f5a69f3b4dd474f680fb31156faedc042e00a4c0 +size 171226 diff --git a/resources/sponza_lq/textures/vase_dif.jpg b/resources/sponza_lq/textures/vase_dif.jpg new file mode 100644 index 000000000..ad962f40e --- /dev/null +++ b/resources/sponza_lq/textures/vase_dif.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17631f8ec36c59a470d3dee3c6bfe1203c2d3eb890cadef9a7b2196b58548425 +size 139226 diff --git a/resources/sponza_lq/textures/vase_hanging.jpg b/resources/sponza_lq/textures/vase_hanging.jpg new file mode 100644 index 000000000..410a37898 --- /dev/null +++ b/resources/sponza_lq/textures/vase_hanging.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d8ddafb5449cd69a4a31398c8d5f15ef7336c3fa9dc93b8f1a7614a3d812c8 +size 85052 diff --git a/resources/sponza_lq/textures/vase_plant.jpg b/resources/sponza_lq/textures/vase_plant.jpg new file mode 100644 index 000000000..5f1b67080 --- /dev/null +++ b/resources/sponza_lq/textures/vase_plant.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68bc559c8595a65491573c6263f77831716b6c367080b6d093bfe7721327784e +size 88709 diff --git a/resources/sponza_lq/textures/vase_plant_mask.jpg b/resources/sponza_lq/textures/vase_plant_mask.jpg new file mode 100644 index 000000000..31dc8d46e --- /dev/null +++ b/resources/sponza_lq/textures/vase_plant_mask.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22d47f1352bb259f16040a604c5f1c978910630ec3e09567a6c382b9d0d0e24f +size 66758 diff --git a/resources/sponza_lq/textures/vase_plant_spec.jpg b/resources/sponza_lq/textures/vase_plant_spec.jpg new file mode 100644 index 000000000..c0fe00037 --- /dev/null +++ b/resources/sponza_lq/textures/vase_plant_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4646859326fac7aa47b4db69f5f14d5f6cd1f15aaa67d1a24d44320482677087 +size 94192 diff --git a/resources/sponza_lq/textures/vase_round.jpg b/resources/sponza_lq/textures/vase_round.jpg new file mode 100644 index 000000000..2d74843dc --- /dev/null +++ b/resources/sponza_lq/textures/vase_round.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9ab3f38f1b02f93a6e30544949208eb6db06cb526e7e04fd5152ace03ec9f30 +size 150440 diff --git a/resources/sponza_lq/textures/vase_round_ddn.jpg b/resources/sponza_lq/textures/vase_round_ddn.jpg new file mode 100644 index 000000000..61a36685e --- /dev/null +++ b/resources/sponza_lq/textures/vase_round_ddn.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e935d5e157c59dca129d36469eba24b79bc9be1fd3f725cec7ef7bd3c40e65fa +size 56977 diff --git a/resources/sponza_lq/textures/vase_round_spec.jpg b/resources/sponza_lq/textures/vase_round_spec.jpg new file mode 100644 index 000000000..7f3e81256 --- /dev/null +++ b/resources/sponza_lq/textures/vase_round_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb844925c5a534dd03ce40ef17be7211d6d1eeb13dce3f7a8817a9f293354d69 +size 105145 diff --git a/resources/ssm/Draw.saps b/resources/ssm/Draw.saps new file mode 100644 index 000000000..2719e5955 --- /dev/null +++ b/resources/ssm/Draw.saps @@ -0,0 +1,33 @@ +float4x4 lightMatrix; +float4 ambient; +float4 diffuse; +float4 specular; +int shininess; +sampler texSamp; +sampler smSamp; + +struct PSIn +{ + float4 tex: TEXCOORD; + float4 norm: TEXCOORD1; + float4 lightDir: TEXCOORD2; + float4 eyeDir: TEXCOORD3; + float4 originPos: TEXCOORD4; +}; + +float4 main(PSIn in): COLOR +{ + float3 norm = normalize(in.norm.xyz); + float3 light_dir = normalize(in.lightDir.xyz); + float3 eye_dir = normalize(in.eyeDir.xyz); + float illum_diffuse = clamp(dot(light_dir, norm), 0.0f, 1.0f); + float illum_specular = clamp(dot(reflect(light_dir, norm), eye_dir), 0.0f, 1.0f); + + float4 posInLight = mul(pos, lightMatrix); + float3 posInLightProj = posInLight.xy / posInLight.w; + float illuminated = tex2D(smSamp, posInLightProj.xy) < posInLightProj.z ? 1.0f : 0.0f; + + float4 light_color = diffuse * illum_diffuse + specular * illum_specular; + float3 illum = (ambient + illuminated * light_color).xyz; + return float4(illum * tex2D(texSamp, tex.xy).xyz, 1.0f); +} \ No newline at end of file diff --git a/resources/ssm/Draw.savs b/resources/ssm/Draw.savs new file mode 100644 index 000000000..73301f01d --- /dev/null +++ b/resources/ssm/Draw.savs @@ -0,0 +1,34 @@ +float4 cameraPos; +float4x4 cameraWvp; + +float4 lightPos; +float4x4 lightWvp; + +struct VSIn +{ + float4 pos: POSITION; + float4 norm: NORMAL; + float4 tex: TEXCOORD0; +}; + +struct VSOut +{ + float4 pos: sv_position; + float4 tex: TEXCOORD0; + float4 norm: TEXCOORD1; + float4 lightDir: TEXCOORD2; + float4 cameraDir: TEXCOORD3; + float4 lightSpacePos: TEXCOORD4; +}; + +VSOut vs_main(VSIn in) +{ + VSOut out; + out.norm = in.norm; + out.pos = mul(in.pos, cameraWvp); + out.lightSpacePos = mul(in.pos, lightWvp); + out.lightDir = lightPos - in.pos; + out.cameraDir = cameraPos - in.pos; + out.tex = in.tex; + return out; +} diff --git a/resources/ssm/GenSM.saps b/resources/ssm/GenSM.saps new file mode 100644 index 000000000..919810306 --- /dev/null +++ b/resources/ssm/GenSM.saps @@ -0,0 +1,3 @@ +void main() +{ +} diff --git a/resources/ssm/GenSM.savs b/resources/ssm/GenSM.savs new file mode 100644 index 000000000..d7843ab8a --- /dev/null +++ b/resources/ssm/GenSM.savs @@ -0,0 +1,6 @@ +float4x4 wvpMatrix; + +float4 main(float4 pos: POSITION): SV_Position +{ + return mul(pos, wvpMatrix); +} \ No newline at end of file diff --git a/resources/texture_and_blending/Dirt.jpg b/resources/texture_and_blending/Dirt.jpg new file mode 100644 index 000000000..0917720ac --- /dev/null +++ b/resources/texture_and_blending/Dirt.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3472eefd24b8818dee71d02adcc2ea838162bf0702c0f8077c115bad01d3cd65 +size 32214 diff --git a/resources/texture_and_blending/chessboard.png b/resources/texture_and_blending/chessboard.png new file mode 100644 index 0000000000000000000000000000000000000000..7a2d58e593a2eeb9170065eba1992ef2260a28fd GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}c0*}aI z1_r)EAj~ML;ne^XlqhkHC<)F_D=AMbN@WO0%*-p%^K%VRC^ObGHe31pkt$G)y{C&~ zNQC>_Ya4kP3^5)0WTKtINR1#KIwi55OuxOQv0gYntboFyt=akR{03mxd{{R30 literal 0 HcmV?d00001 diff --git a/salviar/CMakeLists.txt b/salviar/CMakeLists.txt index b1dea8b13..1f918e950 100644 --- a/salviar/CMakeLists.txt +++ b/salviar/CMakeLists.txt @@ -1,9 +1,8 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() INCLUDE_DIRECTORIES( - ${SALVIA_HOME_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} - ${SALVIA_THREAD_POOL_INCLUDE_DIR} + ${SALVIA_HOME_DIR} + ${Boost_INCLUDE_DIRS} ) SET( FOUNDATION_HEADERS @@ -167,5 +166,3 @@ if(UNIX) TARGET_LINK_LIBRARIES(salviar pthread) endif() SET_TARGET_PROPERTIES( salviar PROPERTIES FOLDER "SALVIA Renderer" ) - -SALVIA_CONFIG_OUTPUT_PATHS(salviar) \ No newline at end of file diff --git a/salviau/CMakeLists.txt b/salviau/CMakeLists.txt index 04d7410ff..2e8fa25ef 100644 --- a/salviau/CMakeLists.txt +++ b/salviau/CMakeLists.txt @@ -3,13 +3,13 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() INCLUDE_DIRECTORIES( ${SALVIA_HOME_DIR} ${SALVIA_WTL_INCLUDE_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} ) LINK_DIRECTORIES( - ${SALVIA_BOOST_LIB_DIR} - ${SALVIA_FREETYPE_LIB_DIR} - ${SALVIA_FREEIMAGE_LIB_DIR} + ${Boost_LIBRARY_DIRS} + ${Freetype_LIBRARY_DIRS} + ${FreeImage_LIBRARY_DIRS} ) set( APP_COMMON_HEADERS @@ -64,7 +64,32 @@ ADD_LIBRARY( salviau SHARED ) endif(MSVC) -TARGET_LINK_LIBRARIES( salviau salviax EFLIB ${SALVIA_BOOST_LIBS}) +TARGET_LINK_LIBRARIES( salviau salviax EFLIB ${Boost_LIBRARIES}) SET_TARGET_PROPERTIES( salviau PROPERTIES FOLDER "Samples" ) +install(TARGETS salviau) +install( + CODE [[ + SET(EXCLUDE_PATTERNS + "^libcdpl.*" + "^.*linux.*" + "^libGL.*" + "^libX.*" + "^libpython.*" + "^.*system32.*" + "^.*ms-win-core.*" + ) -SALVIA_CONFIG_OUTPUT_PATHS(salviau) + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES $<$:$> + RESOLVED_DEPENDENCIES_VAR _r_deps + UNRESOLVED_DEPENDENCIES_VAR _u_deps + POST_EXCLUDE_REGEXES ${EXCLUDE_PATTERNS} + ) + file(INSTALL ${_r_deps} + DESTINATION ${CMAKE_INSTALL_PREFIX}/bin + FOLLOW_SYMLINK_CHAIN + ) + message(STATUS "Resolved deps: ${_r_deps}") + message(STATUS "Unresolved deps: ${_u_deps}") +]] +) diff --git a/salviax/CMakeLists.txt b/salviax/CMakeLists.txt index b7668b0ad..caa0cd288 100644 --- a/salviax/CMakeLists.txt +++ b/salviax/CMakeLists.txt @@ -2,9 +2,9 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() INCLUDE_DIRECTORIES( ${SALVIA_HOME_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} - ${SALVIA_FREETYPE_INCLUDE_DIR} - ${SALVIA_FREEIMAGE_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} + ${FreeType_INCLUDE_DIRS} + ${FreeImage_INCLUDE_DIRS} ) set (HEADER_FILES @@ -79,7 +79,6 @@ if( SALVIA_BUILD_WITH_DIRECTX ) ) endif() -ADD_LIBRARY( salviax STATIC ${HEADER_FILES} ${SOURCE_FILES} ) -TARGET_LINK_LIBRARIES(salviax EFLIB salviar FreeImage freetype) -SET_TARGET_PROPERTIES(salviax PROPERTIES FOLDER "SALVIA Renderer") -SALVIA_CONFIG_OUTPUT_PATHS(salviax) \ No newline at end of file +add_library( salviax STATIC ${HEADER_FILES} ${SOURCE_FILES} ) +target_link_libraries(salviax EFLIB salviar freeimage::FreeImage freeimage::FreeImagePlus freetype) +set_target_properties(salviax PROPERTIES FOLDER "SALVIA Renderer") \ No newline at end of file diff --git a/salviax/src/resource/font/font.cpp b/salviax/src/resource/font/font.cpp index 835ea4ae5..1222c2d47 100644 --- a/salviax/src/resource/font/font.cpp +++ b/salviax/src/resource/font/font.cpp @@ -216,8 +216,8 @@ class font_impl: public font int32_t target_region_left = target_pen_x + bitmap_glyph->left; int32_t target_region_top = target_pen_y - (face_.get()->size->metrics.y_ppem - bitmap_glyph->top); - int32_t target_region_bottom = std::max(target_region_top-bitmap.rows, rc.y); - int32_t target_region_right = std::min(bitmap.width+target_region_left, rc.x+rc.w); + int32_t target_region_bottom = std::max(static_cast(target_region_top - bitmap.rows), rc.y); + int32_t target_region_right = std::min(static_cast(bitmap.width + target_region_left), rc.x+rc.w); int32_t target_region_width = target_region_right - target_region_left; int32_t target_region_height = target_region_top - target_region_bottom; @@ -262,16 +262,16 @@ font_ptr font::create_in_system_path( std::string const& font_file_name, size_t { char system_directory[1024]; #if defined(EFLIB_WINDOWS) - GetWindowsDirectoryA(system_directory, 1024); - std::filesystem::path font_path(system_directory); + GetWindowsDirectoryA(system_directory, 1024); + std::filesystem::path font_path(system_directory); font_path /= "Fonts"; -#else +#else std::filesystem::path font_path("usr/share/fonts/truetype"); -#endif +#endif font_path /= font_file_name; return create(font_path.string(), face_index, size, unit); } -END_NS_SALVIAX_RESOURCE() +END_NS_SALVIAX_RESOURCE() diff --git a/salviax/src/resource/mesh/sa/mesh_io_collada.cpp b/salviax/src/resource/mesh/sa/mesh_io_collada.cpp index 2317d384f..1ab004151 100644 --- a/salviax/src/resource/mesh/sa/mesh_io_collada.cpp +++ b/salviax/src/resource/mesh/sa/mesh_io_collada.cpp @@ -541,6 +541,8 @@ vector build_animations( unordered_map& dae_node_to_matrix ) { + using namespace std::placeholders; + vector ret; for(dae_animation_ptr const& anim: animations->anims) @@ -575,7 +577,7 @@ vector build_animations( } mat44* target_data = dae_node_to_matrix[dynamic_pointer_cast(target_node)]; - anim_player_mat44->anim_info2()->applier = boost::bind( assign_mat44, target_data, _1 ); + anim_player_mat44->anim_info2()->applier = std::bind(assign_mat44, target_data, _1); } if(anim_player) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 4e1bd6d1b..184fd2912 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -19,3 +19,9 @@ if( SALVIA_ENABLE_SAMPLES ) ADD_SUBDIRECTORY( StencilMirror ) ADD_SUBDIRECTORY( AntiAliasing ) endif() + +# Copy resources required by sample to target folder +install( + DIRECTORY ${SALVIA_HOME_DIR}/resources + DESTINATION . +) \ No newline at end of file diff --git a/samples/ConfigureSample.cmake b/samples/ConfigureSample.cmake index 7ca1ba888..72000df11 100644 --- a/samples/ConfigureSample.cmake +++ b/samples/ConfigureSample.cmake @@ -13,18 +13,17 @@ MACRO(SALVIA_CONFIG_SAMPLE_PROJECT ProjectName) INCLUDE_DIRECTORIES( ${SALVIA_HOME_DIR} ${SALVIA_THREAD_POOL_INCLUDE_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} ${SALVIA_WTL_INCLUDE_DIR} - ${SALVIA_FREEIMAGE_INCLUDE_DIR} + ${FreeImage_INCLUDE_DIR} + ${Boost_INLCUDE_DIRS} ${ADDITIONAL_INCLUDE_DIRECTORIES} ) LINK_DIRECTORIES( ${SALVIA_LIBS} - ${SALVIA_BOOST_LIB_DIR} - ${SALVIA_LLVM_LIB_DIR} - ${SALVIA_FREETYPE_LIB_DIR} - ${SALVIA_FREEIMAGE_LIB_DIR} + ${Boost_LIBRARY_DIRS} + ${LLVM_LIBRARY_DIRS} + ${FREEIMAGE_LIBRARY_DIR} ${ADDITIONAL_LINK_DIRECTORIES} ) @@ -44,7 +43,7 @@ ADD_EXECUTABLE( ${ProjectName} ${SOURCE_FILES} ${HEADER_FILES} ${RESOURCE_FILES} TARGET_LINK_LIBRARIES(${ProjectName} salviau salviax salviar EFLIB ${ADDITIONAL_LIBS} ${ADDITIONAL_LIBS}) SET_TARGET_PROPERTIES(${ProjectName} PROPERTIES FOLDER "Samples") -SALVIA_CONFIG_OUTPUT_PATHS(${ProjectName}) SALVIA_CREATE_VCPROJ_USERFILE(${ProjectName}) +install(TARGETS ${ProjectName}) ENDMACRO ( SALVIA_CONFIG_SAMPLE_PROJECT ) diff --git a/samples/Font/CMakeLists.txt b/samples/Font/CMakeLists.txt index 4cc84588a..d76898700 100644 --- a/samples/Font/CMakeLists.txt +++ b/samples/Font/CMakeLists.txt @@ -1,3 +1,30 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() SET( SOURCE_FILES Font.cpp ) -SALVIA_CONFIG_SAMPLE_PROJECT( Font ) \ No newline at end of file +SALVIA_CONFIG_SAMPLE_PROJECT( Font ) + +install( + CODE [[ + SET(EXCLUDE_PATTERNS + "^libcdpl.*" + "^.*linux.*" + "^libGL.*" + "^libX.*" + "^libpython.*" + "^.*system32.*" + "^.*ms-win-core.*" + ) + + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES $<$:$> + RESOLVED_DEPENDENCIES_VAR _r_deps + UNRESOLVED_DEPENDENCIES_VAR _u_deps + POST_EXCLUDE_REGEXES ${EXCLUDE_PATTERNS} + ) + file(INSTALL ${_r_deps} + DESTINATION ${CMAKE_INSTALL_PREFIX}/bin + FOLLOW_SYMLINK_CHAIN + ) + message(STATUS "Resolved deps: ${_r_deps}") + message(STATUS "Unresolved deps: ${_u_deps}") +]] +) diff --git a/sasl/ConfigureSASLProject.cmake b/sasl/ConfigureSASLProject.cmake index a202a3d07..da626c273 100644 --- a/sasl/ConfigureSASLProject.cmake +++ b/sasl/ConfigureSASLProject.cmake @@ -5,9 +5,9 @@ MACRO(SASL_CONFIG_LIBRARY ProjectName) INCLUDE_DIRECTORIES( + ${Boost_INCLUDE_DIRS} ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} ${ADDITIONAL_INCLUDE_DIRECTORIES} ) @@ -16,7 +16,6 @@ SOURCE_GROUP( "Source Files" FILES ${SOURCE_FILES} ) ADD_LIBRARY( ${ProjectName} STATIC ${SOURCE_FILES} ${HEADER_FILES} ${ADDITIONAL_FILES} ) SET_TARGET_PROPERTIES(${ProjectName} PROPERTIES FOLDER "Shader Libs") -SALVIA_CONFIG_OUTPUT_PATHS(${ProjectName}) ENDMACRO ( SASL_CONFIG_LIBRARY ) diff --git a/sasl/enums/CMakeLists.txt b/sasl/enums/CMakeLists.txt index be7020495..2f725dfd4 100644 --- a/sasl/enums/CMakeLists.txt +++ b/sasl/enums/CMakeLists.txt @@ -5,7 +5,7 @@ include (generate_enum.cmake) INCLUDE_DIRECTORIES( ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} ) LIST(APPEND enum_confs enums.xgt ) @@ -49,5 +49,4 @@ ADD_LIBRARY( sasl_enums STATIC enums_generator.py ) -SET_TARGET_PROPERTIES( sasl_enums PROPERTIES FOLDER "Shader Libs") -SALVIA_CONFIG_OUTPUT_PATHS(sasl_enums) \ No newline at end of file +SET_TARGET_PROPERTIES( sasl_enums PROPERTIES FOLDER "Shader Libs") \ No newline at end of file diff --git a/sasl/src/codegen/cg_caster.cpp b/sasl/src/codegen/cg_caster.cpp index 594207a98..7a551ce93 100644 --- a/sasl/src/codegen/cg_caster.cpp +++ b/sasl/src/codegen/cg_caster.cpp @@ -204,6 +204,8 @@ void add_builtin_casts( pety_t* pety ) { + using namespace std::placeholders; + typedef caster_t::cast_t cast_t; shared_ptr cst = dynamic_pointer_cast(caster); diff --git a/sasl/src/codegen/cg_extension.cpp b/sasl/src/codegen/cg_extension.cpp index 1ad80cfb1..7e810bb67 100644 --- a/sasl/src/codegen/cg_extension.cpp +++ b/sasl/src/codegen/cg_extension.cpp @@ -155,14 +155,14 @@ Value* cg_extension::call_unary_intrin( Type* ret_ty, Value* v, unary_intrin_fun unary_intrin_functor cg_extension::bind_to_unary( Function* fn ) { - return [this, fn](Value* v) -> CallInst* { return builder_->CreateCall(fn, v); }; + return [this, fn](Value* v) -> CallInst* { return builder_->CreateCall(fn->getFunctionType(), fn, v); }; } binary_intrin_functor cg_extension::bind_to_binary( Function* fn ) { return [this, fn](Value* v0, Value* v1) -> CallInst* { std::array args{v0, v1}; - return builder_->CreateCall(fn, args); + return builder_->CreateCall(fn->getFunctionType(), fn, args); }; } @@ -235,8 +235,15 @@ Value* cg_extension::abs_sv( Value* v ) if( ty->isVectorTy() ) { - elem_ty = ty->getVectorElementType(); - elem_size = ty->getVectorNumElements(); + if (ty->getTypeID() == Type::FixedVectorTyID) + { + elem_ty = static_cast(ty)->getElementType(); + elem_size = static_cast(ty)->getNumElements(); + } + else + { + assert(false); + } } else { @@ -263,7 +270,7 @@ Value* cg_extension::abs_sv( Value* v ) EFLIB_ASSERT_UNIMPLEMENTED(); } - Type* int_ty = ty->isVectorTy() ? VectorType::get(elem_int_ty, elem_size) : elem_int_ty; + Type* int_ty = ty->isVectorTy() ? VectorType::get(elem_int_ty, elem_size, false) : elem_int_ty; Value* i = builder_->CreateBitCast( v, int_ty ); i = builder_->CreateAnd(i, mask); return builder_->CreateBitCast(i, ty); @@ -298,7 +305,7 @@ Value* cg_extension::shrink( Value* vec, size_t vsize ) Value* cg_extension::extract_elements( Value* src, size_t start_pos, size_t length ) { - VectorType* vty = llvm::cast(src->getType()); + auto vty = llvm::cast(src->getType()); auto elem_count = vty->getNumElements(); if( start_pos == 0 && length == elem_count ){ return src; @@ -319,7 +326,7 @@ Value* cg_extension::insert_elements( Value* dst, Value* src, size_t start_pos ) return src; } - VectorType* src_ty = llvm::cast(src->getType()); + auto src_ty = llvm::cast(src->getType()); auto count = src_ty->getNumElements(); // Expand source to dest size @@ -337,7 +344,7 @@ Value* cg_extension::i8toi1_sv( Value* v ) Type* ty = IntegerType::get( v->getContext(), 1 ); if( v->getType()->isVectorTy() ) { - ty = VectorType::get(ty, v->getType()->getVectorNumElements()); + ty = FixedVectorType::get(ty, llvm::cast(v->getType())->getNumElements()); } return builder_->CreateTruncOrBitCast(v, ty); @@ -359,7 +366,7 @@ Value* cg_extension::i1toi8_sv( Value* v ) Type* ty = IntegerType::get( v->getContext(), 8 ); if( v->getType()->isVectorTy() ) { - ty = VectorType::get(ty, v->getType()->getVectorNumElements()); + ty = FixedVectorType::get(ty, llvm::cast(v->getType())->getNumElements()); } return builder_->CreateZExtOrBitCast(v, ty); @@ -385,11 +392,11 @@ Value* cg_extension::call_external_2( Function* f, Value* v0, Value* v1 ) Value* cg_extension::cast_sv( Value* v, Type* ty, cast_ops::id op ) { - Type* elem_ty = ty->isVectorTy() ? ty->getVectorElementType() : ty; + Type* elem_ty = ty->isVectorTy() ? llvm::cast(v->getType())->getElementType() : ty; assert( !v->getType()->isAggregateType() ); Type* ret_ty = v->getType()->isVectorTy() - ? VectorType::get( elem_ty, v->getType()->getVectorNumElements() ) + ? FixedVectorType::get( elem_ty, llvm::cast(v->getType())->getNumElements() ) : elem_ty; Instruction::CastOps llvm_op = Instruction::BitCast; @@ -493,7 +500,7 @@ Type* cg_extension::extract_scalar_type( Type* ty ) { if( ty->isVectorTy() ) { - return ty->getVectorElementType(); + return llvm::cast(ty)->getElementType(); } else if( ty->isAggregateType() ) { @@ -528,8 +535,8 @@ Value* cg_extension::get_constant_by_scalar( Type* ty, Value* scalar ) if ( ty->isVectorTy() ) { // Vector - unsigned vector_size = ty->getVectorNumElements(); - assert( ty->getVectorElementType() == scalar_ty ); + unsigned vector_size = llvm::cast(ty)->getNumElements(); + assert( llvm::cast(ty)->getElementType() == scalar_ty ); vector scalar_values(vector_size, scalar); return get_vector( ArrayRef(scalar_values) ); @@ -561,7 +568,7 @@ Value* cg_extension::get_constant_by_scalar( Type* ty, Value* scalar ) Value* cg_extension::get_vector( ArrayRef const& elements ) { assert( !elements.empty() ); - Type* vector_ty = VectorType::get( + Type* vector_ty = FixedVectorType::get( elements.front()->getType(), static_cast( elements.size() ) ); Value* ret = UndefValue::get(vector_ty); @@ -634,7 +641,7 @@ Value* cg_extension::promote_to_binary_sv_impl(Value* lhs, Value* rhs, { if(vfn) { return vfn(lhs, rhs); } - unsigned elem_count = ty->getVectorNumElements(); + unsigned elem_count = llvm::cast(ty)->getNumElements(); // SIMD if( simd_fn && elem_count % SASL_SIMD_ELEMENT_COUNT == 0 ) @@ -648,9 +655,9 @@ Value* cg_extension::promote_to_binary_sv_impl(Value* lhs, Value* rhs, Value* ret_simd_elem = simd_fn( lhs_simd_elem, rhs_simd_elem ); if(!ret) { - Type* result_element_ty = ret_simd_elem->getType()->getVectorElementType(); - unsigned result_elements_count = lhs->getType()->getVectorNumElements(); - Type* result_ty = VectorType::get(result_element_ty, result_elements_count); + Type* result_element_ty = llvm::cast(ret_simd_elem->getType())->getElementType(); + unsigned result_elements_count = llvm::cast(lhs->getType())->getNumElements(); + Type* result_ty = FixedVectorType::get(result_element_ty, result_elements_count); ret = UndefValue::get(result_ty); } ret = insert_elements( ret, ret_simd_elem, i_batch*SASL_SIMD_ELEMENT_COUNT ); @@ -669,8 +676,8 @@ Value* cg_extension::promote_to_binary_sv_impl(Value* lhs, Value* rhs, Value* ret_elem = sfn(lhs_elem, rhs_elem); if(!ret) { - unsigned result_elements_count = lhs->getType()->getVectorNumElements(); - Type* result_ty = VectorType::get(ret_elem->getType(), result_elements_count); + unsigned result_elements_count = llvm::cast(lhs->getType())->getNumElements(); + Type* result_ty = FixedVectorType::get(ret_elem->getType(), result_elements_count); ret = UndefValue::get(result_ty); } ret = builder_->CreateInsertElement( ret, ret_elem, get_int(i) ); @@ -698,7 +705,7 @@ Value* cg_extension::promote_to_unary_sv_impl(Value* v, { if(vfn) { return vfn(v); } - unsigned elem_count = ty->getVectorNumElements(); + unsigned elem_count = llvm::cast(ty)->getNumElements(); // SIMD if( simd_fn && elem_count % SASL_SIMD_ELEMENT_COUNT == 0 ) @@ -711,9 +718,9 @@ Value* cg_extension::promote_to_unary_sv_impl(Value* v, Value* ret_simd_elem = simd_fn(v_simd_elem); if(!ret) { - Type* result_element_ty = ret_simd_elem->getType()->getVectorElementType(); - unsigned result_elements_count = v->getType()->getVectorNumElements(); - Type* result_ty = VectorType::get(result_element_ty, result_elements_count); + Type* result_element_ty = llvm::cast(ret_simd_elem->getType())->getElementType(); + unsigned result_elements_count = llvm::cast(v->getType())->getNumElements(); + Type* result_ty = FixedVectorType::get(result_element_ty, result_elements_count); ret = UndefValue::get(result_ty); } ret = insert_elements( ret, ret_simd_elem, i_batch*SASL_SIMD_ELEMENT_COUNT ); @@ -731,8 +738,8 @@ Value* cg_extension::promote_to_unary_sv_impl(Value* v, Value* ret_elem = sfn(v_elem); if(!ret) { - unsigned result_elements_count = v->getType()->getVectorNumElements(); - Type* result_ty = VectorType::get(ret_elem->getType(), result_elements_count); + unsigned result_elements_count = llvm::cast(v->getType())->getNumElements(); + Type* result_ty = FixedVectorType::get(ret_elem->getType(), result_elements_count); ret = UndefValue::get(result_ty); } ret = builder_->CreateInsertElement( ret, ret_elem, get_int(i) ); @@ -854,7 +861,11 @@ value_array cg_extension::call( { arg_values[i_arg] = args[i_arg][value_index]; } - ret[value_index] = builder_->CreateCall(fn[value_index], arg_values); + ret[value_index] = builder_->CreateCall( + llvm::cast(fn[value_index])->getFunctionType(), + fn[value_index], + arg_values + ); } return ret; } @@ -871,7 +882,10 @@ value_array cg_extension::call( { arg_values[i_arg] = (*args[i_arg])[value_index]; } - ret[value_index] = builder_->CreateCall(fn[value_index], arg_values); + ret[value_index] = builder_->CreateCall( + llvm::cast(fn[value_index])->getFunctionType(), + fn[value_index], + arg_values); } return ret; } diff --git a/sasl/src/codegen/cg_intrins.cpp b/sasl/src/codegen/cg_intrins.cpp index c301ca5c5..0ebe67e05 100644 --- a/sasl/src/codegen/cg_intrins.cpp +++ b/sasl/src/codegen/cg_intrins.cpp @@ -17,48 +17,49 @@ using std::vector; namespace Intrinsic = llvm::Intrinsic; -BEGIN_NS_SASL_CODEGEN(); - -llvm::Intrinsic::ID get_intrinsic_id( char const* Name ) +namespace sasl::codegen { - unsigned Len = static_cast( strlen(Name) ); - if (Len < 5 || Name[4] != '.' || Name[0] != 'l' || Name[1] != 'l' - || Name[2] != 'v' || Name[3] != 'm') - return llvm::Intrinsic::ID(0); // All intrinsics start with 'llvm.' + llvm::Intrinsic::ID get_intrinsic_id(char const* Name) + { + unsigned Len = static_cast(strlen(Name)); - return llvm::Intrinsic::ID(0); -} + if (Len < 5 || Name[4] != '.' || Name[0] != 'l' || Name[1] != 'l' + || Name[2] != 'v' || Name[3] != 'm') + return llvm::Intrinsic::ID(0); // All intrinsics start with 'llvm.' -llvm_intrin_cache::llvm_intrin_cache(): intrin_fns( Intrinsic::num_intrinsics ) -{ -} + return llvm::Intrinsic::ID(0); + } -Function* llvm_intrin_cache::get( char const* name, Module* mod ) -{ - return get( get_intrinsic_id( name ), mod ); -} + llvm_intrin_cache::llvm_intrin_cache() : intrin_fns(Intrinsic::num_intrinsics) + { + } -Function* llvm_intrin_cache::get( int id, Module* mod ) -{ - llvm::Intrinsic::ID IID = llvm::Intrinsic::ID( id ); - assert( !Intrinsic::isOverloaded(IID) ); + Function* llvm_intrin_cache::get(char const* name, Module* mod) + { + return get(get_intrinsic_id(name), mod); + } + + Function* llvm_intrin_cache::get(int id, Module* mod) + { + llvm::Intrinsic::ID IID = llvm::Intrinsic::ID(id); + assert(!Intrinsic::isOverloaded(IID)); - if( intrin_fns[IID] == NULL ){ - intrin_fns[IID] = ( (IID == 0) ? NULL : getDeclaration( mod, IID ) ); + if (intrin_fns[IID] == NULL) { + intrin_fns[IID] = ((IID == 0) ? NULL : Intrinsic::getDeclaration(mod, IID)); + } + return intrin_fns[IID]; } - return intrin_fns[IID]; -} -Function* llvm_intrin_cache::get( int id, Module* mod, llvm::FunctionType* fnty ) -{ - llvm::Intrinsic::ID IID = llvm::Intrinsic::ID( id ); - vector par_types; - for( unsigned i = 0; i < fnty->getNumParams(); ++i ) + Function* llvm_intrin_cache::get(int id, Module* mod, llvm::FunctionType* fnty) { - par_types.push_back( fnty->getParamType(i) ); + llvm::Intrinsic::ID IID = llvm::Intrinsic::ID(id); + vector par_types; + for (unsigned i = 0; i < fnty->getNumParams(); ++i) + { + par_types.push_back(fnty->getParamType(i)); + } + return llvm::cast(Intrinsic::getDeclaration(mod, IID, par_types)); } - return llvm::cast( getDeclaration(mod, IID, par_types) ); -} -END_NS_SASL_CODEGEN(); +} diff --git a/sasl/src/codegen/cg_simd.cpp b/sasl/src/codegen/cg_simd.cpp index 8b0294602..ff1417c97 100644 --- a/sasl/src/codegen/cg_simd.cpp +++ b/sasl/src/codegen/cg_simd.cpp @@ -381,7 +381,7 @@ SASL_SPECIFIC_VISIT_DEF( create_fnsig, function_def ) sem_->get_symbol(&v)->mangled_name().raw_string(), cg_impl::module() ); - fn->addFnAttr(llvm::Attribute::getWithStackAlignment(context(), 32).getAsString()); + fn->addFnAttr(llvm::Attribute::getWithStackAlignment(context(), llvm::Align(12)).getAsString()); fn->addFnAttr("stackrealign"); entry_fn = fn; // entry_sym = v.symbol().get(); diff --git a/sasl/src/codegen/cg_vs.cpp b/sasl/src/codegen/cg_vs.cpp index 09afeaeba..0e4d29fba 100644 --- a/sasl/src/codegen/cg_vs.cpp +++ b/sasl/src/codegen/cg_vs.cpp @@ -167,7 +167,7 @@ SASL_SPECIFIC_VISIT_DEF( create_fnsig, function_def ){ fntype, Function::ExternalLinkage, sem_->get_symbol(&v)->mangled_name().raw_string(), cg_impl::module() ); - fn->addFnAttr(Attribute::getWithStackAlignment(context(), 32).getAsString()); + fn->addFnAttr(Attribute::getWithStackAlignment(context(), llvm::Align(32)).getAsString()); fn->addFnAttr("stackrealign"); entry_fn = fn; entry_sym = sem_->get_symbol(&v); diff --git a/sasl/src/codegen/cgs.cpp b/sasl/src/codegen/cgs.cpp index 52224d041..2c9ecacdd 100644 --- a/sasl/src/codegen/cgs.cpp +++ b/sasl/src/codegen/cgs.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -205,7 +204,7 @@ cg_function* cg_service::fetch_function(function_def* fn_node){ fty, Function::ExternalLinkage, sem_->get_symbol(fn_node)->mangled_name().raw_string(), module() ); - ret->fn->addFnAttr(Attribute::getWithStackAlignment(context(), 32).getAsString()); + ret->fn->addFnAttr(Attribute::getWithStackAlignment(context(), Align(32)).getAsString()); ret->fn->addFnAttr("stackrealign"); return ret; } @@ -718,8 +717,8 @@ multi_value cg_service::emit_mul_comp( multi_value const& lhs, multi_value const else { assert(false); } } - binary_intrin_functor f_mul = std::bind( &DefaultIRBuilder::CreateFMul, builder(), _1, _2, "", (llvm::MDNode*)(NULL) ); - binary_intrin_functor i_mul = std::bind( &DefaultIRBuilder::CreateMul, builder(), _1, _2, "", false, false ); + binary_intrin_functor f_mul = std::bind( &DefaultIRBuilder::CreateFMul, std::ref(builder()), _1, _2, "", (llvm::MDNode*)(NULL) ); + binary_intrin_functor i_mul = std::bind( &DefaultIRBuilder::CreateMul, std::ref(builder()), _1, _2, "", false, false ); return emit_bin_ps_ta_sva( lv, rv, i_mul, i_mul, f_mul ); } @@ -730,16 +729,16 @@ bool bool_xor(bool l, bool r) multi_value cg_service::emit_add( multi_value const& lhs, multi_value const& rhs ) { - binary_intrin_functor f_add = std::bind( &DefaultIRBuilder::CreateFAdd, builder(), _1, _2, "", (llvm::MDNode*)(NULL) ); - binary_intrin_functor i_add = std::bind( &DefaultIRBuilder::CreateAdd, builder(), _1, _2, "", false, false ); + binary_intrin_functor f_add = std::bind( &DefaultIRBuilder::CreateFAdd, std::ref(builder()), _1, _2, "", (llvm::MDNode*)(NULL) ); + binary_intrin_functor i_add = std::bind( &DefaultIRBuilder::CreateAdd, std::ref(builder()), _1, _2, "", false, false ); return emit_bin_es_ta_sva( lhs, rhs, i_add, i_add, f_add ); } multi_value cg_service::emit_sub( multi_value const& lhs, multi_value const& rhs ) { - binary_intrin_functor f_sub = std::bind( &DefaultIRBuilder::CreateFSub, builder(), _1, _2, "", (llvm::MDNode*)(NULL) ); - binary_intrin_functor i_sub = std::bind( &DefaultIRBuilder::CreateSub, builder(), _1, _2, "", false, false ); + binary_intrin_functor f_sub = std::bind( &DefaultIRBuilder::CreateFSub, std::ref(builder()), _1, _2, "", (llvm::MDNode*)(NULL) ); + binary_intrin_functor i_sub = std::bind( &DefaultIRBuilder::CreateSub, std::ref(builder()), _1, _2, "", false, false ); return emit_bin_es_ta_sva( lhs, rhs, i_sub, i_sub, f_sub ); } @@ -781,9 +780,9 @@ multi_value cg_service::emit_mul_intrin( multi_value const& lhs, multi_value con multi_value cg_service::emit_div( multi_value const& lhs, multi_value const& rhs ) { - binary_intrin_functor f_div = std::bind( &DefaultIRBuilder::CreateFDiv, builder(), _1, _2, "", (llvm::MDNode*)(NULL) ); - binary_intrin_functor i_div = std::bind( &DefaultIRBuilder::CreateSDiv, builder(), _1, _2, "", false ); - binary_intrin_functor u_div = std::bind( &DefaultIRBuilder::CreateUDiv, builder(), _1, _2, "", false ); + binary_intrin_functor f_div = std::bind( &DefaultIRBuilder::CreateFDiv, std::ref(builder()), _1, _2, "", (llvm::MDNode*)(NULL) ); + binary_intrin_functor i_div = std::bind( &DefaultIRBuilder::CreateSDiv, std::ref(builder()), _1, _2, "", false ); + binary_intrin_functor u_div = std::bind( &DefaultIRBuilder::CreateUDiv, std::ref(builder()), _1, _2, "", false ); binary_intrin_functor i_safe_div = std::bind( &cg_extension::safe_idiv_imod_sv, ext_.get(), _1, _2, i_div ); binary_intrin_functor u_safe_div = std::bind( &cg_extension::safe_idiv_imod_sv, ext_.get(), _1, _2, u_div ); @@ -792,8 +791,8 @@ multi_value cg_service::emit_div( multi_value const& lhs, multi_value const& rhs multi_value cg_service::emit_mod( multi_value const& lhs, multi_value const& rhs ) { - binary_intrin_functor i_mod = std::bind( &DefaultIRBuilder::CreateSRem, builder(), _1, _2, "" ); - binary_intrin_functor u_mod = std::bind( &DefaultIRBuilder::CreateURem, builder(), _1, _2, "" ); + binary_intrin_functor i_mod = std::bind( &DefaultIRBuilder::CreateSRem, std::ref(builder()), _1, _2, "" ); + binary_intrin_functor u_mod = std::bind( &DefaultIRBuilder::CreateURem, std::ref(builder()), _1, _2, "" ); binary_intrin_functor i_safe_mod = std::bind( &cg_extension::safe_idiv_imod_sv, ext_.get(), _1, _2, i_mod ); binary_intrin_functor u_safe_mod = std::bind( &cg_extension::safe_idiv_imod_sv, ext_.get(), _1, _2, u_mod ); @@ -810,7 +809,7 @@ multi_value cg_service::emit_mod( multi_value const& lhs, multi_value const& rhs multi_value cg_service::emit_bitwise_bin_op(operators op, multi_value const& lhs, multi_value const& rhs) { auto vm_op = static_cast(conv_bin_op_to_vm_[op]); - auto bin_op = std::bind(&DefaultIRBuilder::CreateBinOp, builder(), vm_op, _1, _2, "", nullptr); + binary_intrin_functor bin_op = std::bind(&DefaultIRBuilder::CreateBinOp, std::ref(builder()), vm_op, _1, _2, "", nullptr); return emit_bin_es_ta_sva( lhs, rhs, bin_op, bin_op, bin_op); } @@ -1368,7 +1367,7 @@ multi_value cg_service::emit_call( cg_function const& fn, vector co } } - Value* ret_value = builder().CreateCall(fn.fn, physical_args); + Value* ret_value = builder().CreateCall(fn.fn->getFunctionType(), fn.fn, physical_args); // Parse result. if( fn.return_via_arg() ) @@ -1581,15 +1580,16 @@ multi_value cg_service::emit_cmp( multi_value const& lhs, multi_value const& rhs binary_intrin_functor cmp_fn; if( is_real(scalar_hint) ) { - cmp_fn = std::bind( - &DefaultIRBuilder::CreateFCmp, - builder(), (CmpInst::Predicate)pred_float, _1, _2, "", nullptr - ); + cmp_fn = [this, pred_float](llvm::Value* lhs, llvm::Value* rhs) { + return builder().CreateICmp(static_cast(pred_float), lhs, rhs, ""); + }; } else if ( is_integer(scalar_hint) ) { int pred = is_signed(scalar_hint) ? pred_signed : pred_unsigned; - cmp_fn = std::bind( &DefaultIRBuilder::CreateICmp, builder(), (CmpInst::Predicate)pred, _1, _2, "" ); + cmp_fn = [this, pred](llvm::Value* lhs, llvm::Value* rhs) { + return builder().CreateICmp(static_cast(pred), lhs, rhs, ""); + }; } unary_intrin_functor cast_fn = std::bind( &cg_extension::i1toi8_sv, ext_.get(), _1 ); diff --git a/sasl/src/codegen/ty_cache.cpp b/sasl/src/codegen/ty_cache.cpp index d9bb07924..1410c9b86 100644 --- a/sasl/src/codegen/ty_cache.cpp +++ b/sasl/src/codegen/ty_cache.cpp @@ -4,11 +4,11 @@ #include -int const PACKAGE_SIZE = 16; -int SIMD_WIDTH_IN_BYTES(){ +constexpr int PACKAGE_SIZE = 16; +constexpr int SIMD_WIDTH_IN_BYTES(){ return 16; } -int SIMD_FLOAT_SIZE(){ +constexpr int SIMD_FLOAT_SIZE(){ return SIMD_WIDTH_IN_BYTES() / sizeof(float); } @@ -181,7 +181,7 @@ Type* ty_cache_t::create_abi_ty(LLVMContext& ctxt, builtin_types bt, abis abi) return StructType::create(elem_tys, name(bt, abi), true); } case abis::llvm: - return VectorType::get( elem_ty, static_cast(vec_size) ); + return VectorType::get(elem_ty, static_cast(vec_size), false); case abis::regs: return ArrayType::get(elem_ty, vec_size); default: diff --git a/sasl/src/command/CMakeLists.txt b/sasl/src/command/CMakeLists.txt index 9c0847bc4..fb3c7b18d 100644 --- a/sasl/src/command/CMakeLists.txt +++ b/sasl/src/command/CMakeLists.txt @@ -3,8 +3,8 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() INCLUDE_DIRECTORIES( ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_LLVM_INCLUDE_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${LLVM_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ) LINK_DIRECTORIES( @@ -21,5 +21,4 @@ ADD_EXECUTABLE( sasl_command ${HEADER_FILES} ${SOURCE_FILES} ) TARGET_LINK_LIBRARIES( sasl_command sasl_common EFLIB ) SET_TARGET_PROPERTIES( sasl_command PROPERTIES FOLDER "Shader Drivers") -SALVIA_CONFIG_OUTPUT_PATHS( sasl_command ) diff --git a/sasl/src/drivers/CMakeLists.txt b/sasl/src/drivers/CMakeLists.txt index f048bb949..637a8d7e3 100644 --- a/sasl/src/drivers/CMakeLists.txt +++ b/sasl/src/drivers/CMakeLists.txt @@ -3,30 +3,56 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() INCLUDE_DIRECTORIES( ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_LLVM_INCLUDE_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${LLVM_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ) LINK_DIRECTORIES( - ${SALVIA_BOOST_LIB_DIR} - ${SALVIA_LLVM_LIB_DIR} + ${LLVM_LIBRARY_DIRS} + ${Boost_LIBRARY_DIRS} ) # Configure Dynamic Link Driver. -ADD_LIBRARY( sasl_drivers +add_library( sasl_drivers SHARED ${SASL_HOME_DIR}/sasl/include/drivers/drivers_api.h drivers_api.cpp ) -TARGET_LINK_LIBRARIES( sasl_drivers +target_link_libraries( sasl_drivers sasl_drivers_lib sasl_shims sasl_codegen sasl_semantic sasl_syntaxtree sasl_parser sasl_enums sasl_common EFLIB - ${SASL_LLVM_LIBS} - ${SALVIA_BOOST_LIBS} + ${Boost_LIBRARIES} + ${LLVM_LIBS} ) -SET_TARGET_PROPERTIES( sasl_drivers PROPERTIES FOLDER "Shader Drivers") -SALVIA_CONFIG_OUTPUT_PATHS( sasl_drivers ) +set_target_properties(sasl_drivers PROPERTIES FOLDER "Shader Drivers") +install(TARGETS sasl_drivers) +install( + CODE [[ + SET(EXCLUDE_PATTERNS + "^libcdpl.*" + "^.*linux.*" + "^libGL.*" + "^libX.*" + "^libpython.*" + "^.*system32.*" + "^.*ms-win-core.*" + ) + + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES $<$:$> + RESOLVED_DEPENDENCIES_VAR _r_deps + UNRESOLVED_DEPENDENCIES_VAR _u_deps + POST_EXCLUDE_REGEXES ${EXCLUDE_PATTERNS} + ) + file(INSTALL ${_r_deps} + DESTINATION ${CMAKE_INSTALL_PREFIX}/bin + FOLLOW_SYMLINK_CHAIN + ) + message(STATUS "Resolved deps: ${_r_deps}") + message(STATUS "Unresolved deps: ${_u_deps}") +]] +) # Configure Static Link Driver. set( HEADER_FILES ${SASL_HOME_DIR}/sasl/include/drivers/drivers_forward.h @@ -47,7 +73,6 @@ set( SOURCE_FILES code_sources.cpp ) -ADD_DEFINITIONS( -DSASL_STATIC_DRIVER ) -ADD_LIBRARY( sasl_drivers_lib STATIC ${HEADER_FILES} ${SOURCE_FILES} ) -SET_TARGET_PROPERTIES( sasl_drivers_lib PROPERTIES FOLDER "Shader Drivers") -SALVIA_CONFIG_OUTPUT_PATHS( sasl_drivers_lib ) \ No newline at end of file +add_definitions( -DSASL_STATIC_DRIVER ) +add_library(sasl_drivers_lib STATIC ${HEADER_FILES} ${SOURCE_FILES}) +set_target_properties( sasl_drivers_lib PROPERTIES FOLDER "Shader Drivers") \ No newline at end of file diff --git a/sasl/src/host/CMakeLists.txt b/sasl/src/host/CMakeLists.txt index 90bce0940..9c9543086 100644 --- a/sasl/src/host/CMakeLists.txt +++ b/sasl/src/host/CMakeLists.txt @@ -3,8 +3,8 @@ SALVIA_CHECK_BUILD_WITH_UNICODE() INCLUDE_DIRECTORIES( ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_LLVM_INCLUDE_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${LLVM_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ) LINK_DIRECTORIES( @@ -32,5 +32,4 @@ ADD_LIBRARY( sasl_host SHARED TARGET_LINK_LIBRARIES( sasl_host sasl_drivers EFLIB ) SET_TARGET_PROPERTIES( sasl_host PROPERTIES FOLDER "Shader Drivers") -SALVIA_CONFIG_OUTPUT_PATHS( sasl_host ) - +install(TARGETS sasl_host) diff --git a/sasl/test/abi_test/CMakeLists.txt b/sasl/test/abi_test/CMakeLists.txt index 86d661a31..455e1c2d8 100644 --- a/sasl/test/abi_test/CMakeLists.txt +++ b/sasl/test/abi_test/CMakeLists.txt @@ -4,11 +4,11 @@ include (tests.cmake) INCLUDE_DIRECTORIES( ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} ) LINK_DIRECTORIES( - ${SALVIA_BOOST_LIB_DIR} + ${Boost_LIBRARY_DIR} ) set( SASL_TEST_PROJECT_DIR abi_test ) @@ -29,10 +29,9 @@ TARGET_LINK_LIBRARIES( ${SASL_TEST_PROJECT_NAME} sasl_parser sasl_enums sasl_common ${SASL_ABI_TEST_LIBS} - ${SALVIA_BOOST_LIBS} + ${Boost_LIBRARIES} ) ADD_DEPENDENCIES(${SASL_TEST_PROJECT_NAME} sasl_test_shader_repo) SET_TARGET_PROPERTIES( ${SASL_TEST_PROJECT_NAME} PROPERTIES FOLDER "Shader Tests") -SALVIA_CONFIG_OUTPUT_PATHS( ${SASL_TEST_PROJECT_NAME} ) -SASL_TEST_CREATE_VCPROJ_USERFILE( ${SASL_TEST_PROJECT_NAME} ) +# SASL_TEST_CREATE_VCPROJ_USERFILE( ${SASL_TEST_PROJECT_NAME} ) diff --git a/sasl/test/death_test/CMakeLists.txt b/sasl/test/death_test/CMakeLists.txt index d90791789..09969573a 100644 --- a/sasl/test/death_test/CMakeLists.txt +++ b/sasl/test/death_test/CMakeLists.txt @@ -4,7 +4,7 @@ include (tests.cmake) INCLUDE_DIRECTORIES( ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} ) LINK_DIRECTORIES( @@ -32,5 +32,5 @@ TARGET_LINK_LIBRARIES( ${SASL_TEST_PROJECT_NAME} ) SET_TARGET_PROPERTIES( ${SASL_TEST_PROJECT_NAME} PROPERTIES FOLDER "Shader Tests") -SALVIA_CONFIG_OUTPUT_PATHS( ${SASL_TEST_PROJECT_NAME} ) +# SALVIA_CONFIG_OUTPUT_PATHS( ${SASL_TEST_PROJECT_NAME} ) SASL_TEST_CREATE_VCPROJ_USERFILE( ${SASL_TEST_PROJECT_NAME} ) diff --git a/sasl/test/jit_test/CMakeLists.txt b/sasl/test/jit_test/CMakeLists.txt index ba9c25715..7e10a0bea 100644 --- a/sasl/test/jit_test/CMakeLists.txt +++ b/sasl/test/jit_test/CMakeLists.txt @@ -4,13 +4,15 @@ include (tests.cmake) INCLUDE_DIRECTORIES( ${SASL_HOME_DIR} ${SALVIA_HOME_DIR} - ${SALVIA_LLVM_INCLUDE_DIR} - ${SALVIA_BOOST_INCLUDE_DIR} + ${LLVM_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ) +MESSAGE(STATUS ${LLVM_LIBRARY_DIRS}) + LINK_DIRECTORIES( - ${SALVIA_BOOST_LIB_DIR} - ${SALVIA_LLVM_LIB_DIR} + ${LLVM_LIBRARY_DIRS} + ${Boost_LIBRARY_DIRS} ) set( SASL_TEST_PROJECT_DIR jit_test ) @@ -31,11 +33,12 @@ TARGET_LINK_LIBRARIES( ${SASL_TEST_PROJECT_NAME} sasl_semantic sasl_syntaxtree sasl_parser sasl_enums sasl_common - ${SASL_JIT_TEST_LIBS} - ${SALVIA_BOOST_LIBS} + ${Boost_LIBRARIES} + ${LLVM_LIBS} ) ADD_DEPENDENCIES(${SASL_TEST_PROJECT_NAME} sasl_test_shader_repo) SET_TARGET_PROPERTIES( ${SASL_TEST_PROJECT_NAME} PROPERTIES FOLDER "Shader Tests") -SALVIA_CONFIG_OUTPUT_PATHS( ${SASL_TEST_PROJECT_NAME} ) SASL_TEST_CREATE_VCPROJ_USERFILE( ${SASL_TEST_PROJECT_NAME} ) + +install(TARGETS sasl_test_jit) \ No newline at end of file diff --git a/sasl/test/repo/CMakeLists.txt b/sasl/test/repo/CMakeLists.txt index b0dcdf5be..e2e4ddc05 100644 --- a/sasl/test/repo/CMakeLists.txt +++ b/sasl/test/repo/CMakeLists.txt @@ -1,5 +1,3 @@ -SALVIA_CHECK_BUILD_WITH_UNICODE() - set( TEST_SHADERS "auto_semantic.svs" "array.svs" @@ -77,4 +75,10 @@ ADD_CUSTOM_COMMAND( "${SASL_HOME_DIR}/sasl/test/repo/copy_tests.py" ${TEST_SHADERS} ) ADD_CUSTOM_TARGET( sasl_test_shader_repo ALL DEPENDS test_repo.deps ${TEST_SHADERS} SOURCES ${TEST_SHADERS} ) -SET_TARGET_PROPERTIES( sasl_test_shader_repo PROPERTIES FOLDER "Shader Tests") \ No newline at end of file +SET_TARGET_PROPERTIES( sasl_test_shader_repo PROPERTIES FOLDER "Shader Tests") + +install( + DIRECTORY ${SASL_HOME_DIR}/sasl/test/repo + DESTINATION . + FILES_MATCHING PATTERN "*.*" + ) \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 000000000..f5fb4cffb --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,33 @@ +{ + "name": "salvia-renderer", + "version-string": "22.02", + "dependencies": [ + "boost-algorithm", + "boost-assign", + "boost-bimap", + "boost-circular-buffer", + "boost-config", + "boost-format", + "boost-icl", + "boost-locale", + "boost-log", + "boost-math", + "boost-predef", + "boost-program-options", + "boost-property-tree", + "boost-range", + "boost-signals2", + "boost-spirit", + "boost-test", + "boost-utility", + "boost-uuid", + "boost-wave", + "freeimage", + "freetype", + { + "name": "llvm", + "default-features": false, + "features": ["utils", "target-aarch64", "target-amdgpu", "target-x86", "target-nvptx", "target-riscv", "target-webassembly"] + } + ] + } \ No newline at end of file