diff --git a/3rdparty/dark_style_sheet/qdarkstyle/style.qss b/3rdparty/dark_style_sheet/qdarkstyle/style.qss index 1642eca..fd9d694 100644 --- a/3rdparty/dark_style_sheet/qdarkstyle/style.qss +++ b/3rdparty/dark_style_sheet/qdarkstyle/style.qss @@ -949,6 +949,13 @@ QDockWidget { titlebar-normal-icon: url(:/qss_icons/rc/undock.png); } +QDockWidget::title{ + background: #383838; + padding-left: 5px; + margin-top: 4px; +} + + QDockWidget::close-button, QDockWidget::float-button { border: 1px solid transparent; border-radius: 2px; diff --git a/3rdparty/easyprofiler/.gitignore b/3rdparty/easyprofiler/.gitignore new file mode 100644 index 0000000..83ccc54 --- /dev/null +++ b/3rdparty/easyprofiler/.gitignore @@ -0,0 +1,2 @@ +/build/ +/bin/ diff --git a/3rdparty/easyprofiler/CMakeCache.txt b/3rdparty/easyprofiler/CMakeCache.txt new file mode 100644 index 0000000..b48fae6 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeCache.txt @@ -0,0 +1,322 @@ +# This is the CMakeCache file. +# For build in directory: /home/alex/Work/C++Projects/easyprofiler +# It was generated by CMake: /usr/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//Build easy_profiler as shared library. +BUILD_SHARED_LIBS:BOOL=ON + +//Use std::chrono::high_resolution_clock as a timer +BUILD_WITH_CHRONO_HIGH_RESOLUTION_CLOCK:BOOL=OFF + +//Use std::chrono::steady_clock as a timer +BUILD_WITH_CHRONO_STEADY_CLOCK:BOOL=OFF + +//Path to a program. +CMAKE_AR:FILEPATH=/usr/bin/ar + +//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or +// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel. +CMAKE_BUILD_TYPE:STRING=Release + +//Enable/Disable color output during build. +CMAKE_COLOR_MAKEFILE:BOOL=ON + +//CXX compiler +CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++ + +//Flags used by the compiler during all build types. +CMAKE_CXX_FLAGS:STRING= + +//Flags used by the compiler during debug builds. +CMAKE_CXX_FLAGS_DEBUG:STRING=-g + +//Flags used by the compiler during release builds for minimum +// size. +CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the compiler during release builds. +CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the compiler during release builds with debug info. +CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//Flags used by the linker. +CMAKE_EXE_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Enable/Disable output of compile commands during generation. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF + +//Install path prefix, prepended onto install directories. +CMAKE_INSTALL_PREFIX:PATH=/usr/local + +//Path to a program. +CMAKE_LINKER:FILEPATH=/usr/bin/ld + +//Path to a program. +CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make + +//Flags used by the linker during the creation of modules. +CMAKE_MODULE_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_NM:FILEPATH=/usr/bin/nm + +//Path to a program. +CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy + +//Path to a program. +CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump + +//Value Computed by CMake +CMAKE_PROJECT_NAME:STATIC=easy_profiler + +//Path to a program. +CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib + +//Flags used by the linker during the creation of dll's. +CMAKE_SHARED_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//If set, runtime paths are not added when installing shared libraries, +// but are added when building. +CMAKE_SKIP_INSTALL_RPATH:BOOL=NO + +//If set, runtime paths are not added when using shared libraries. +CMAKE_SKIP_RPATH:BOOL=NO + +//Flags used by the linker during the creation of static libraries. +CMAKE_STATIC_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_STRIP:FILEPATH=/usr/bin/strip + +//If this value is on, makefiles will be generated without the +// .SILENT directive, and all commands will be echoed to the console +// during the make. This is useful for debugging only. With Visual +// Studio IDE projects all commands are done without /nologo. +CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE + +//Default listening port +EASY_DEFAULT_PORT:STRING=28077 + +//Enable new threads registration when collecting context switch +// events +EASY_OPTION_IMPLICIT_THREAD_REGISTRATION:BOOL=ON + +//Enable automatic startListen on startup +EASY_OPTION_LISTEN:BOOL=OFF + +//Print errors to stderr +EASY_OPTION_LOG:BOOL=OFF + +//Use predefined set of colors (see profiler_colors.h). If you +// want to use your own colors palette you can turn this option +// OFF +EASY_OPTION_PREDEFINED_COLORS:BOOL=ON + +//Use pretty-printed function names with signature and argument +// types +EASY_OPTION_PRETTY_PRINT:BOOL=OFF + +//Enable self profiling (measure time for internal storage expand) +EASY_OPTION_PROFILE_SELF:BOOL=OFF + +//Storage expand default status (profiler::ON or profiler::OFF) +EASY_OPTION_PROFILE_SELF_BLOCKS_ON:BOOL=OFF + +//The directory containing a CMake configuration file for Qt5Core. +Qt5Core_DIR:PATH=/home/alex/Work/Qt/5.8/gcc_64/lib/cmake/Qt5Core + +//The directory containing a CMake configuration file for Qt5Gui. +Qt5Gui_DIR:PATH=/home/alex/Work/Qt/5.8/gcc_64/lib/cmake/Qt5Gui + +//The directory containing a CMake configuration file for Qt5Widgets. +Qt5Widgets_DIR:PATH=/home/alex/Work/Qt/5.8/gcc_64/lib/cmake/Qt5Widgets + +//Value Computed by CMake +easy_profiler_BINARY_DIR:STATIC=/home/alex/Work/C++Projects/easyprofiler + +//Dependencies for the target +easy_profiler_LIB_DEPENDS:STATIC=general;pthread; + +//Value Computed by CMake +easy_profiler_SOURCE_DIR:STATIC=/home/alex/Work/C++Projects/easyprofiler + + +######################## +# INTERNAL cache entries +######################## + +//ADVANCED property for variable: CMAKE_AR +CMAKE_AR-ADVANCED:INTERNAL=1 +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/home/alex/Work/C++Projects/easyprofiler +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=5 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=1 +//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE +CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/usr/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest +//ADVANCED property for variable: CMAKE_CXX_COMPILER +CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS +CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG +CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL +CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE +CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO +CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//Executable file format +CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS +CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG +CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE +CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS +CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1 +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Unix Makefiles +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/home/alex/Work/C++Projects/easyprofiler +//Install .so files without execute permission. +CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1 +//ADVANCED property for variable: CMAKE_LINKER +CMAKE_LINKER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MAKE_PROGRAM +CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS +CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG +CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE +CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_NM +CMAKE_NM-ADVANCED:INTERNAL=1 +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=5 +//ADVANCED property for variable: CMAKE_OBJCOPY +CMAKE_OBJCOPY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_OBJDUMP +CMAKE_OBJDUMP-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RANLIB +CMAKE_RANLIB-ADVANCED:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.5 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS +CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG +CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE +CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH +CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_RPATH +CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS +CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG +CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE +CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STRIP +CMAKE_STRIP-ADVANCED:INTERNAL=1 +//uname command +CMAKE_UNAME:INTERNAL=/bin/uname +//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE +CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 + diff --git a/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeCXXCompiler.cmake b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeCXXCompiler.cmake new file mode 100644 index 0000000..013ee92 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeCXXCompiler.cmake @@ -0,0 +1,68 @@ +set(CMAKE_CXX_COMPILER "/usr/bin/c++") +set(CMAKE_CXX_COMPILER_ARG1 "") +set(CMAKE_CXX_COMPILER_ID "GNU") +set(CMAKE_CXX_COMPILER_VERSION "5.4.0") +set(CMAKE_CXX_COMPILER_WRAPPER "") +set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "98") +set(CMAKE_CXX_COMPILE_FEATURES "cxx_template_template_parameters;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") +set(CMAKE_CXX98_COMPILE_FEATURES "cxx_template_template_parameters") +set(CMAKE_CXX11_COMPILE_FEATURES "cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") +set(CMAKE_CXX14_COMPILE_FEATURES "cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") + +set(CMAKE_CXX_PLATFORM_ID "Linux") +set(CMAKE_CXX_SIMULATE_ID "") +set(CMAKE_CXX_SIMULATE_VERSION "") + +set(CMAKE_AR "/usr/bin/ar") +set(CMAKE_RANLIB "/usr/bin/ranlib") +set(CMAKE_LINKER "/usr/bin/ld") +set(CMAKE_COMPILER_IS_GNUCXX 1) +set(CMAKE_CXX_COMPILER_LOADED 1) +set(CMAKE_CXX_COMPILER_WORKS TRUE) +set(CMAKE_CXX_ABI_COMPILED TRUE) +set(CMAKE_COMPILER_IS_MINGW ) +set(CMAKE_COMPILER_IS_CYGWIN ) +if(CMAKE_COMPILER_IS_CYGWIN) + set(CYGWIN 1) + set(UNIX 1) +endif() + +set(CMAKE_CXX_COMPILER_ENV_VAR "CXX") + +if(CMAKE_COMPILER_IS_MINGW) + set(MINGW 1) +endif() +set(CMAKE_CXX_COMPILER_ID_RUN 1) +set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) +set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;mm;CPP) +set(CMAKE_CXX_LINKER_PREFERENCE 30) +set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1) + +# Save compiler ABI information. +set(CMAKE_CXX_SIZEOF_DATA_PTR "8") +set(CMAKE_CXX_COMPILER_ABI "ELF") +set(CMAKE_CXX_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") + +if(CMAKE_CXX_SIZEOF_DATA_PTR) + set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}") +endif() + +if(CMAKE_CXX_COMPILER_ABI) + set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}") +endif() + +if(CMAKE_CXX_LIBRARY_ARCHITECTURE) + set(CMAKE_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") +endif() + +set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "") +if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX) + set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}") +endif() + + + + +set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;m;c") +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/5;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib") +set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "") diff --git a/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeDetermineCompilerABI_CXX.bin b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeDetermineCompilerABI_CXX.bin new file mode 100755 index 0000000..ccb7de9 Binary files /dev/null and b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeDetermineCompilerABI_CXX.bin differ diff --git a/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeSystem.cmake b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeSystem.cmake new file mode 100644 index 0000000..9b97c52 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CMakeSystem.cmake @@ -0,0 +1,15 @@ +set(CMAKE_HOST_SYSTEM "Linux-4.4.0-93-generic") +set(CMAKE_HOST_SYSTEM_NAME "Linux") +set(CMAKE_HOST_SYSTEM_VERSION "4.4.0-93-generic") +set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64") + + + +set(CMAKE_SYSTEM "Linux-4.4.0-93-generic") +set(CMAKE_SYSTEM_NAME "Linux") +set(CMAKE_SYSTEM_VERSION "4.4.0-93-generic") +set(CMAKE_SYSTEM_PROCESSOR "x86_64") + +set(CMAKE_CROSSCOMPILING "FALSE") + +set(CMAKE_SYSTEM_LOADED 1) diff --git a/3rdparty/easyprofiler/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp new file mode 100644 index 0000000..e6d8536 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CompilerIdCXX/CMakeCXXCompilerId.cpp @@ -0,0 +1,533 @@ +/* This source file must have a .cpp extension so that all C++ compilers + recognize the extension without flags. Borland does not know .cxx for + example. */ +#ifndef __cplusplus +# error "A C compiler has been selected for C++." +#endif + + +/* Version number components: V=Version, R=Revision, P=Patch + Version date components: YYYY=Year, MM=Month, DD=Day */ + +#if defined(__COMO__) +# define COMPILER_ID "Comeau" + /* __COMO_VERSION__ = VRR */ +# define COMPILER_VERSION_MAJOR DEC(__COMO_VERSION__ / 100) +# define COMPILER_VERSION_MINOR DEC(__COMO_VERSION__ % 100) + +#elif defined(__INTEL_COMPILER) || defined(__ICC) +# define COMPILER_ID "Intel" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif + /* __INTEL_COMPILER = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100) +# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10) +# if defined(__INTEL_COMPILER_UPDATE) +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE) +# else +# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10) +# endif +# if defined(__INTEL_COMPILER_BUILD_DATE) + /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */ +# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE) +# endif +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__PATHCC__) +# define COMPILER_ID "PathScale" +# define COMPILER_VERSION_MAJOR DEC(__PATHCC__) +# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__) +# if defined(__PATHCC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__) +# endif + +#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__) +# define COMPILER_ID "Embarcadero" +# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF) +# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF) +# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF) + +#elif defined(__BORLANDC__) +# define COMPILER_ID "Borland" + /* __BORLANDC__ = 0xVRR */ +# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8) +# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF) + +#elif defined(__WATCOMC__) && __WATCOMC__ < 1200 +# define COMPILER_ID "Watcom" + /* __WATCOMC__ = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__WATCOMC__) +# define COMPILER_ID "OpenWatcom" + /* __WATCOMC__ = VVRP + 1100 */ +# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100) +# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10) +# if (__WATCOMC__ % 10) > 0 +# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10) +# endif + +#elif defined(__SUNPRO_CC) +# define COMPILER_ID "SunPro" +# if __SUNPRO_CC >= 0x5100 + /* __SUNPRO_CC = 0xVRRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>12) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) +# else + /* __SUNPRO_CC = 0xVRP */ +# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>8) +# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xF) +# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF) +# endif + +#elif defined(__HP_aCC) +# define COMPILER_ID "HP" + /* __HP_aCC = VVRRPP */ +# define COMPILER_VERSION_MAJOR DEC(__HP_aCC/10000) +# define COMPILER_VERSION_MINOR DEC(__HP_aCC/100 % 100) +# define COMPILER_VERSION_PATCH DEC(__HP_aCC % 100) + +#elif defined(__DECCXX) +# define COMPILER_ID "Compaq" + /* __DECCXX_VER = VVRRTPPPP */ +# define COMPILER_VERSION_MAJOR DEC(__DECCXX_VER/10000000) +# define COMPILER_VERSION_MINOR DEC(__DECCXX_VER/100000 % 100) +# define COMPILER_VERSION_PATCH DEC(__DECCXX_VER % 10000) + +#elif defined(__IBMCPP__) && defined(__COMPILER_VER__) +# define COMPILER_ID "zOS" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ >= 800 +# define COMPILER_ID "XL" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ < 800 +# define COMPILER_ID "VisualAge" + /* __IBMCPP__ = VRP */ +# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100) +# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10) +# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10) + +#elif defined(__PGI) +# define COMPILER_ID "PGI" +# define COMPILER_VERSION_MAJOR DEC(__PGIC__) +# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__) +# if defined(__PGIC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__) +# endif + +#elif defined(_CRAYC) +# define COMPILER_ID "Cray" +# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR) +# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR) + +#elif defined(__TI_COMPILER_VERSION__) +# define COMPILER_ID "TI" + /* __TI_COMPILER_VERSION__ = VVVRRRPPP */ +# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000) +# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000) +# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000) + +#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version) +# define COMPILER_ID "Fujitsu" + +#elif defined(__SCO_VERSION__) +# define COMPILER_ID "SCO" + +#elif defined(__clang__) && defined(__apple_build_version__) +# define COMPILER_ID "AppleClang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif +# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__) + +#elif defined(__clang__) +# define COMPILER_ID "Clang" +# if defined(_MSC_VER) +# define SIMULATE_ID "MSVC" +# endif +# define COMPILER_VERSION_MAJOR DEC(__clang_major__) +# define COMPILER_VERSION_MINOR DEC(__clang_minor__) +# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__) +# if defined(_MSC_VER) + /* _MSC_VER = VVRR */ +# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100) +# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100) +# endif + +#elif defined(__GNUC__) +# define COMPILER_ID "GNU" +# define COMPILER_VERSION_MAJOR DEC(__GNUC__) +# if defined(__GNUC_MINOR__) +# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__) +# endif +# if defined(__GNUC_PATCHLEVEL__) +# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__) +# endif + +#elif defined(_MSC_VER) +# define COMPILER_ID "MSVC" + /* _MSC_VER = VVRR */ +# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100) +# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100) +# if defined(_MSC_FULL_VER) +# if _MSC_VER >= 1400 + /* _MSC_FULL_VER = VVRRPPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000) +# else + /* _MSC_FULL_VER = VVRRPPPP */ +# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000) +# endif +# endif +# if defined(_MSC_BUILD) +# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD) +# endif + +#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__) +# define COMPILER_ID "ADSP" +#if defined(__VISUALDSPVERSION__) + /* __VISUALDSPVERSION__ = 0xVVRRPP00 */ +# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24) +# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF) +# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF) +#endif + +#elif defined(__IAR_SYSTEMS_ICC__ ) || defined(__IAR_SYSTEMS_ICC) +# define COMPILER_ID "IAR" + +#elif defined(__ARMCC_VERSION) +# define COMPILER_ID "ARMCC" +#if __ARMCC_VERSION >= 1000000 + /* __ARMCC_VERSION = VRRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#else + /* __ARMCC_VERSION = VRPPPP */ + # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000) + # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10) + # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000) +#endif + + +#elif defined(_SGI_COMPILER_VERSION) || defined(_COMPILER_VERSION) +# define COMPILER_ID "MIPSpro" +# if defined(_SGI_COMPILER_VERSION) + /* _SGI_COMPILER_VERSION = VRP */ +# define COMPILER_VERSION_MAJOR DEC(_SGI_COMPILER_VERSION/100) +# define COMPILER_VERSION_MINOR DEC(_SGI_COMPILER_VERSION/10 % 10) +# define COMPILER_VERSION_PATCH DEC(_SGI_COMPILER_VERSION % 10) +# else + /* _COMPILER_VERSION = VRP */ +# define COMPILER_VERSION_MAJOR DEC(_COMPILER_VERSION/100) +# define COMPILER_VERSION_MINOR DEC(_COMPILER_VERSION/10 % 10) +# define COMPILER_VERSION_PATCH DEC(_COMPILER_VERSION % 10) +# endif + + +/* These compilers are either not known or too old to define an + identification macro. Try to identify the platform and guess that + it is the native compiler. */ +#elif defined(__sgi) +# define COMPILER_ID "MIPSpro" + +#elif defined(__hpux) || defined(__hpua) +# define COMPILER_ID "HP" + +#else /* unknown compiler */ +# define COMPILER_ID "" +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]"; +#ifdef SIMULATE_ID +char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; +#endif + +#ifdef __QNXNTO__ +char const* qnxnto = "INFO" ":" "qnxnto[]"; +#endif + +#if defined(__CRAYXE) || defined(__CRAYXC) +char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]"; +#endif + +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) + +/* Identify known platforms by name. */ +#if defined(__linux) || defined(__linux__) || defined(linux) +# define PLATFORM_ID "Linux" + +#elif defined(__CYGWIN__) +# define PLATFORM_ID "Cygwin" + +#elif defined(__MINGW32__) +# define PLATFORM_ID "MinGW" + +#elif defined(__APPLE__) +# define PLATFORM_ID "Darwin" + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define PLATFORM_ID "Windows" + +#elif defined(__FreeBSD__) || defined(__FreeBSD) +# define PLATFORM_ID "FreeBSD" + +#elif defined(__NetBSD__) || defined(__NetBSD) +# define PLATFORM_ID "NetBSD" + +#elif defined(__OpenBSD__) || defined(__OPENBSD) +# define PLATFORM_ID "OpenBSD" + +#elif defined(__sun) || defined(sun) +# define PLATFORM_ID "SunOS" + +#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__) +# define PLATFORM_ID "AIX" + +#elif defined(__sgi) || defined(__sgi__) || defined(_SGI) +# define PLATFORM_ID "IRIX" + +#elif defined(__hpux) || defined(__hpux__) +# define PLATFORM_ID "HP-UX" + +#elif defined(__HAIKU__) +# define PLATFORM_ID "Haiku" + +#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS) +# define PLATFORM_ID "BeOS" + +#elif defined(__QNX__) || defined(__QNXNTO__) +# define PLATFORM_ID "QNX" + +#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__) +# define PLATFORM_ID "Tru64" + +#elif defined(__riscos) || defined(__riscos__) +# define PLATFORM_ID "RISCos" + +#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__) +# define PLATFORM_ID "SINIX" + +#elif defined(__UNIX_SV__) +# define PLATFORM_ID "UNIX_SV" + +#elif defined(__bsdos__) +# define PLATFORM_ID "BSDOS" + +#elif defined(_MPRAS) || defined(MPRAS) +# define PLATFORM_ID "MP-RAS" + +#elif defined(__osf) || defined(__osf__) +# define PLATFORM_ID "OSF1" + +#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv) +# define PLATFORM_ID "SCO_SV" + +#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX) +# define PLATFORM_ID "ULTRIX" + +#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX) +# define PLATFORM_ID "Xenix" + +#elif defined(__WATCOMC__) +# if defined(__LINUX__) +# define PLATFORM_ID "Linux" + +# elif defined(__DOS__) +# define PLATFORM_ID "DOS" + +# elif defined(__OS2__) +# define PLATFORM_ID "OS2" + +# elif defined(__WINDOWS__) +# define PLATFORM_ID "Windows3x" + +# else /* unknown platform */ +# define PLATFORM_ID "" +# endif + +#else /* unknown platform */ +# define PLATFORM_ID "" + +#endif + +/* For windows compilers MSVC and Intel we can determine + the architecture of the compiler being used. This is because + the compilers do not have flags that can change the architecture, + but rather depend on which compiler is being used +*/ +#if defined(_WIN32) && defined(_MSC_VER) +# if defined(_M_IA64) +# define ARCHITECTURE_ID "IA64" + +# elif defined(_M_X64) || defined(_M_AMD64) +# define ARCHITECTURE_ID "x64" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# elif defined(_M_ARM) +# if _M_ARM == 4 +# define ARCHITECTURE_ID "ARMV4I" +# elif _M_ARM == 5 +# define ARCHITECTURE_ID "ARMV5I" +# else +# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM) +# endif + +# elif defined(_M_MIPS) +# define ARCHITECTURE_ID "MIPS" + +# elif defined(_M_SH) +# define ARCHITECTURE_ID "SHx" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#elif defined(__WATCOMC__) +# if defined(_M_I86) +# define ARCHITECTURE_ID "I86" + +# elif defined(_M_IX86) +# define ARCHITECTURE_ID "X86" + +# else /* unknown architecture */ +# define ARCHITECTURE_ID "" +# endif + +#else +# define ARCHITECTURE_ID "" +#endif + +/* Convert integer to decimal digit literals. */ +#define DEC(n) \ + ('0' + (((n) / 10000000)%10)), \ + ('0' + (((n) / 1000000)%10)), \ + ('0' + (((n) / 100000)%10)), \ + ('0' + (((n) / 10000)%10)), \ + ('0' + (((n) / 1000)%10)), \ + ('0' + (((n) / 100)%10)), \ + ('0' + (((n) / 10)%10)), \ + ('0' + ((n) % 10)) + +/* Convert integer to hex digit literals. */ +#define HEX(n) \ + ('0' + ((n)>>28 & 0xF)), \ + ('0' + ((n)>>24 & 0xF)), \ + ('0' + ((n)>>20 & 0xF)), \ + ('0' + ((n)>>16 & 0xF)), \ + ('0' + ((n)>>12 & 0xF)), \ + ('0' + ((n)>>8 & 0xF)), \ + ('0' + ((n)>>4 & 0xF)), \ + ('0' + ((n) & 0xF)) + +/* Construct a string literal encoding the version number components. */ +#ifdef COMPILER_VERSION_MAJOR +char const info_version[] = { + 'I', 'N', 'F', 'O', ':', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', + COMPILER_VERSION_MAJOR, +# ifdef COMPILER_VERSION_MINOR + '.', COMPILER_VERSION_MINOR, +# ifdef COMPILER_VERSION_PATCH + '.', COMPILER_VERSION_PATCH, +# ifdef COMPILER_VERSION_TWEAK + '.', COMPILER_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct a string literal encoding the version number components. */ +#ifdef SIMULATE_VERSION_MAJOR +char const info_simulate_version[] = { + 'I', 'N', 'F', 'O', ':', + 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[', + SIMULATE_VERSION_MAJOR, +# ifdef SIMULATE_VERSION_MINOR + '.', SIMULATE_VERSION_MINOR, +# ifdef SIMULATE_VERSION_PATCH + '.', SIMULATE_VERSION_PATCH, +# ifdef SIMULATE_VERSION_TWEAK + '.', SIMULATE_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + +/* Construct the string literal in pieces to prevent the source from + getting matched. Store it in a pointer rather than an array + because some compilers will just produce instructions to fill the + array rather than assigning a pointer to a static array. */ +char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]"; +char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]"; + + + + +const char* info_language_dialect_default = "INFO" ":" "dialect_default[" +#if __cplusplus >= 201402L + "14" +#elif __cplusplus >= 201103L + "11" +#else + "98" +#endif +"]"; + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char* argv[]) +{ + int require = 0; + require += info_compiler[argc]; + require += info_platform[argc]; +#ifdef COMPILER_VERSION_MAJOR + require += info_version[argc]; +#endif +#ifdef SIMULATE_ID + require += info_simulate[argc]; +#endif +#ifdef SIMULATE_VERSION_MAJOR + require += info_simulate_version[argc]; +#endif +#if defined(__CRAYXE) || defined(__CRAYXC) + require += info_cray[argc]; +#endif + require += info_language_dialect_default[argc]; + (void)argv; + return require; +} diff --git a/3rdparty/easyprofiler/CMakeFiles/3.5.1/CompilerIdCXX/a.out b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CompilerIdCXX/a.out new file mode 100755 index 0000000..b677508 Binary files /dev/null and b/3rdparty/easyprofiler/CMakeFiles/3.5.1/CompilerIdCXX/a.out differ diff --git a/3rdparty/easyprofiler/CMakeFiles/CMakeDirectoryInformation.cmake b/3rdparty/easyprofiler/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..9ecfcf5 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/alex/Work/C++Projects/easyprofiler") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/alex/Work/C++Projects/easyprofiler") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/3rdparty/easyprofiler/CMakeFiles/CMakeOutput.log b/3rdparty/easyprofiler/CMakeFiles/CMakeOutput.log new file mode 100644 index 0000000..d37f64b --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/CMakeOutput.log @@ -0,0 +1,356 @@ +The system is: Linux - 4.4.0-93-generic - x86_64 +Compiling the CXX compiler identification source file "CMakeCXXCompilerId.cpp" succeeded. +Compiler: /usr/bin/c++ +Build flags: +Id flags: + +The output was: +0 + + +Compilation of the CXX compiler identification source "CMakeCXXCompilerId.cpp" produced "a.out" + +The CXX compiler identification is GNU, found in "/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/3.5.1/CompilerIdCXX/a.out" + +Determining if the CXX compiler works passed with the following output: +Change Dir: /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_56b63/fast" +/usr/bin/make -f CMakeFiles/cmTC_56b63.dir/build.make CMakeFiles/cmTC_56b63.dir/build +make[1]: Entering directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_56b63.dir/testCXXCompiler.cxx.o +/usr/bin/c++ -o CMakeFiles/cmTC_56b63.dir/testCXXCompiler.cxx.o -c /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp/testCXXCompiler.cxx +Linking CXX executable cmTC_56b63 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_56b63.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_56b63.dir/testCXXCompiler.cxx.o -o cmTC_56b63 -rdynamic +make[1]: Leaving directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' + + +Detecting CXX compiler ABI info compiled with the following output: +Change Dir: /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_aa90b/fast" +/usr/bin/make -f CMakeFiles/cmTC_aa90b.dir/build.make CMakeFiles/cmTC_aa90b.dir/build +make[1]: Entering directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o +/usr/bin/c++ -o CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o -c /usr/share/cmake-3.5/Modules/CMakeCXXCompilerABI.cpp +Linking CXX executable cmTC_aa90b +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_aa90b.dir/link.txt --verbose=1 +/usr/bin/c++ -v CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_aa90b -rdynamic +Using built-in specs. +COLLECT_GCC=/usr/bin/c++ +COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.6' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu +Thread model: posix +gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.6) +COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/ +LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/ +COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_aa90b' '-rdynamic' '-shared-libgcc' '-mtune=generic' '-march=x86-64' + /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccL6oSX7.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o cmTC_aa90b /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o +make[1]: Leaving directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' + + +Parsed CXX implicit link information from above output: + link line regex: [^( *|.*[/\])(ld|([^/\]+-)?ld|collect2)[^/\]*( |$)] + ignore line: [Change Dir: /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp] + ignore line: [] + ignore line: [Run Build Command:"/usr/bin/make" "cmTC_aa90b/fast"] + ignore line: [/usr/bin/make -f CMakeFiles/cmTC_aa90b.dir/build.make CMakeFiles/cmTC_aa90b.dir/build] + ignore line: [make[1]: Entering directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp'] + ignore line: [Building CXX object CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o] + ignore line: [/usr/bin/c++ -o CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o -c /usr/share/cmake-3.5/Modules/CMakeCXXCompilerABI.cpp] + ignore line: [Linking CXX executable cmTC_aa90b] + ignore line: [/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_aa90b.dir/link.txt --verbose=1] + ignore line: [/usr/bin/c++ -v CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_aa90b -rdynamic ] + ignore line: [Using built-in specs.] + ignore line: [COLLECT_GCC=/usr/bin/c++] + ignore line: [COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper] + ignore line: [Target: x86_64-linux-gnu] + ignore line: [Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.6' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu] + ignore line: [Thread model: posix] + ignore line: [gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.6) ] + ignore line: [COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/] + ignore line: [LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/] + ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_aa90b' '-rdynamic' '-shared-libgcc' '-mtune=generic' '-march=x86-64'] + link line: [ /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccL6oSX7.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o cmTC_aa90b /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o] + arg [/usr/lib/gcc/x86_64-linux-gnu/5/collect2] ==> ignore + arg [-plugin] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so] ==> ignore + arg [-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper] ==> ignore + arg [-plugin-opt=-fresolution=/tmp/ccL6oSX7.res] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc] ==> ignore + arg [-plugin-opt=-pass-through=-lc] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore + arg [-plugin-opt=-pass-through=-lgcc] ==> ignore + arg [--sysroot=/] ==> ignore + arg [--build-id] ==> ignore + arg [--eh-frame-hdr] ==> ignore + arg [-m] ==> ignore + arg [elf_x86_64] ==> ignore + arg [--hash-style=gnu] ==> ignore + arg [--as-needed] ==> ignore + arg [-export-dynamic] ==> ignore + arg [-dynamic-linker] ==> ignore + arg [/lib64/ld-linux-x86-64.so.2] ==> ignore + arg [-zrelro] ==> ignore + arg [-o] ==> ignore + arg [cmTC_aa90b] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o] ==> ignore + arg [-L/usr/lib/gcc/x86_64-linux-gnu/5] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib] + arg [-L/lib/x86_64-linux-gnu] ==> dir [/lib/x86_64-linux-gnu] + arg [-L/lib/../lib] ==> dir [/lib/../lib] + arg [-L/usr/lib/x86_64-linux-gnu] ==> dir [/usr/lib/x86_64-linux-gnu] + arg [-L/usr/lib/../lib] ==> dir [/usr/lib/../lib] + arg [-L/usr/lib/gcc/x86_64-linux-gnu/5/../../..] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../..] + arg [CMakeFiles/cmTC_aa90b.dir/CMakeCXXCompilerABI.cpp.o] ==> ignore + arg [-lstdc++] ==> lib [stdc++] + arg [-lm] ==> lib [m] + arg [-lgcc_s] ==> lib [gcc_s] + arg [-lgcc] ==> lib [gcc] + arg [-lc] ==> lib [c] + arg [-lgcc_s] ==> lib [gcc_s] + arg [-lgcc] ==> lib [gcc] + arg [/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o] ==> ignore + arg [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o] ==> ignore + remove lib [gcc_s] + remove lib [gcc] + remove lib [gcc_s] + remove lib [gcc] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5] ==> [/usr/lib/gcc/x86_64-linux-gnu/5] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib] ==> [/usr/lib] + collapse library dir [/lib/x86_64-linux-gnu] ==> [/lib/x86_64-linux-gnu] + collapse library dir [/lib/../lib] ==> [/lib] + collapse library dir [/usr/lib/x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu] + collapse library dir [/usr/lib/../lib] ==> [/usr/lib] + collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/5/../../..] ==> [/usr/lib] + implicit libs: [stdc++;m;c] + implicit dirs: [/usr/lib/gcc/x86_64-linux-gnu/5;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib] + implicit fwks: [] + + + + +Detecting CXX [-std=c++14] compiler features compiled with the following output: +Change Dir: /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_46192/fast" +/usr/bin/make -f CMakeFiles/cmTC_46192.dir/build.make CMakeFiles/cmTC_46192.dir/build +make[1]: Entering directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_46192.dir/feature_tests.cxx.o +/usr/bin/c++ -std=c++14 -o CMakeFiles/cmTC_46192.dir/feature_tests.cxx.o -c /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/feature_tests.cxx +Linking CXX executable cmTC_46192 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_46192.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_46192.dir/feature_tests.cxx.o -o cmTC_46192 -rdynamic +make[1]: Leaving directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' + + + Feature record: CXX_FEATURE:1cxx_aggregate_default_initializers + Feature record: CXX_FEATURE:1cxx_alias_templates + Feature record: CXX_FEATURE:1cxx_alignas + Feature record: CXX_FEATURE:1cxx_alignof + Feature record: CXX_FEATURE:1cxx_attributes + Feature record: CXX_FEATURE:1cxx_attribute_deprecated + Feature record: CXX_FEATURE:1cxx_auto_type + Feature record: CXX_FEATURE:1cxx_binary_literals + Feature record: CXX_FEATURE:1cxx_constexpr + Feature record: CXX_FEATURE:1cxx_contextual_conversions + Feature record: CXX_FEATURE:1cxx_decltype + Feature record: CXX_FEATURE:1cxx_decltype_auto + Feature record: CXX_FEATURE:1cxx_decltype_incomplete_return_types + Feature record: CXX_FEATURE:1cxx_default_function_template_args + Feature record: CXX_FEATURE:1cxx_defaulted_functions + Feature record: CXX_FEATURE:1cxx_defaulted_move_initializers + Feature record: CXX_FEATURE:1cxx_delegating_constructors + Feature record: CXX_FEATURE:1cxx_deleted_functions + Feature record: CXX_FEATURE:1cxx_digit_separators + Feature record: CXX_FEATURE:1cxx_enum_forward_declarations + Feature record: CXX_FEATURE:1cxx_explicit_conversions + Feature record: CXX_FEATURE:1cxx_extended_friend_declarations + Feature record: CXX_FEATURE:1cxx_extern_templates + Feature record: CXX_FEATURE:1cxx_final + Feature record: CXX_FEATURE:1cxx_func_identifier + Feature record: CXX_FEATURE:1cxx_generalized_initializers + Feature record: CXX_FEATURE:1cxx_generic_lambdas + Feature record: CXX_FEATURE:1cxx_inheriting_constructors + Feature record: CXX_FEATURE:1cxx_inline_namespaces + Feature record: CXX_FEATURE:1cxx_lambdas + Feature record: CXX_FEATURE:1cxx_lambda_init_captures + Feature record: CXX_FEATURE:1cxx_local_type_template_args + Feature record: CXX_FEATURE:1cxx_long_long_type + Feature record: CXX_FEATURE:1cxx_noexcept + Feature record: CXX_FEATURE:1cxx_nonstatic_member_init + Feature record: CXX_FEATURE:1cxx_nullptr + Feature record: CXX_FEATURE:1cxx_override + Feature record: CXX_FEATURE:1cxx_range_for + Feature record: CXX_FEATURE:1cxx_raw_string_literals + Feature record: CXX_FEATURE:1cxx_reference_qualified_functions + Feature record: CXX_FEATURE:1cxx_relaxed_constexpr + Feature record: CXX_FEATURE:1cxx_return_type_deduction + Feature record: CXX_FEATURE:1cxx_right_angle_brackets + Feature record: CXX_FEATURE:1cxx_rvalue_references + Feature record: CXX_FEATURE:1cxx_sizeof_member + Feature record: CXX_FEATURE:1cxx_static_assert + Feature record: CXX_FEATURE:1cxx_strong_enums + Feature record: CXX_FEATURE:1cxx_template_template_parameters + Feature record: CXX_FEATURE:1cxx_thread_local + Feature record: CXX_FEATURE:1cxx_trailing_return_types + Feature record: CXX_FEATURE:1cxx_unicode_literals + Feature record: CXX_FEATURE:1cxx_uniform_initialization + Feature record: CXX_FEATURE:1cxx_unrestricted_unions + Feature record: CXX_FEATURE:1cxx_user_literals + Feature record: CXX_FEATURE:1cxx_variable_templates + Feature record: CXX_FEATURE:1cxx_variadic_macros + Feature record: CXX_FEATURE:1cxx_variadic_templates + + +Detecting CXX [-std=c++11] compiler features compiled with the following output: +Change Dir: /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_270c0/fast" +/usr/bin/make -f CMakeFiles/cmTC_270c0.dir/build.make CMakeFiles/cmTC_270c0.dir/build +make[1]: Entering directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_270c0.dir/feature_tests.cxx.o +/usr/bin/c++ -std=c++11 -o CMakeFiles/cmTC_270c0.dir/feature_tests.cxx.o -c /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/feature_tests.cxx +Linking CXX executable cmTC_270c0 +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_270c0.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_270c0.dir/feature_tests.cxx.o -o cmTC_270c0 -rdynamic +make[1]: Leaving directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' + + + Feature record: CXX_FEATURE:0cxx_aggregate_default_initializers + Feature record: CXX_FEATURE:1cxx_alias_templates + Feature record: CXX_FEATURE:1cxx_alignas + Feature record: CXX_FEATURE:1cxx_alignof + Feature record: CXX_FEATURE:1cxx_attributes + Feature record: CXX_FEATURE:0cxx_attribute_deprecated + Feature record: CXX_FEATURE:1cxx_auto_type + Feature record: CXX_FEATURE:0cxx_binary_literals + Feature record: CXX_FEATURE:1cxx_constexpr + Feature record: CXX_FEATURE:0cxx_contextual_conversions + Feature record: CXX_FEATURE:1cxx_decltype + Feature record: CXX_FEATURE:0cxx_decltype_auto + Feature record: CXX_FEATURE:1cxx_decltype_incomplete_return_types + Feature record: CXX_FEATURE:1cxx_default_function_template_args + Feature record: CXX_FEATURE:1cxx_defaulted_functions + Feature record: CXX_FEATURE:1cxx_defaulted_move_initializers + Feature record: CXX_FEATURE:1cxx_delegating_constructors + Feature record: CXX_FEATURE:1cxx_deleted_functions + Feature record: CXX_FEATURE:0cxx_digit_separators + Feature record: CXX_FEATURE:1cxx_enum_forward_declarations + Feature record: CXX_FEATURE:1cxx_explicit_conversions + Feature record: CXX_FEATURE:1cxx_extended_friend_declarations + Feature record: CXX_FEATURE:1cxx_extern_templates + Feature record: CXX_FEATURE:1cxx_final + Feature record: CXX_FEATURE:1cxx_func_identifier + Feature record: CXX_FEATURE:1cxx_generalized_initializers + Feature record: CXX_FEATURE:0cxx_generic_lambdas + Feature record: CXX_FEATURE:1cxx_inheriting_constructors + Feature record: CXX_FEATURE:1cxx_inline_namespaces + Feature record: CXX_FEATURE:1cxx_lambdas + Feature record: CXX_FEATURE:0cxx_lambda_init_captures + Feature record: CXX_FEATURE:1cxx_local_type_template_args + Feature record: CXX_FEATURE:1cxx_long_long_type + Feature record: CXX_FEATURE:1cxx_noexcept + Feature record: CXX_FEATURE:1cxx_nonstatic_member_init + Feature record: CXX_FEATURE:1cxx_nullptr + Feature record: CXX_FEATURE:1cxx_override + Feature record: CXX_FEATURE:1cxx_range_for + Feature record: CXX_FEATURE:1cxx_raw_string_literals + Feature record: CXX_FEATURE:1cxx_reference_qualified_functions + Feature record: CXX_FEATURE:0cxx_relaxed_constexpr + Feature record: CXX_FEATURE:0cxx_return_type_deduction + Feature record: CXX_FEATURE:1cxx_right_angle_brackets + Feature record: CXX_FEATURE:1cxx_rvalue_references + Feature record: CXX_FEATURE:1cxx_sizeof_member + Feature record: CXX_FEATURE:1cxx_static_assert + Feature record: CXX_FEATURE:1cxx_strong_enums + Feature record: CXX_FEATURE:1cxx_template_template_parameters + Feature record: CXX_FEATURE:1cxx_thread_local + Feature record: CXX_FEATURE:1cxx_trailing_return_types + Feature record: CXX_FEATURE:1cxx_unicode_literals + Feature record: CXX_FEATURE:1cxx_uniform_initialization + Feature record: CXX_FEATURE:1cxx_unrestricted_unions + Feature record: CXX_FEATURE:1cxx_user_literals + Feature record: CXX_FEATURE:0cxx_variable_templates + Feature record: CXX_FEATURE:1cxx_variadic_macros + Feature record: CXX_FEATURE:1cxx_variadic_templates + + +Detecting CXX [-std=c++98] compiler features compiled with the following output: +Change Dir: /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp + +Run Build Command:"/usr/bin/make" "cmTC_aba3e/fast" +/usr/bin/make -f CMakeFiles/cmTC_aba3e.dir/build.make CMakeFiles/cmTC_aba3e.dir/build +make[1]: Entering directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' +Building CXX object CMakeFiles/cmTC_aba3e.dir/feature_tests.cxx.o +/usr/bin/c++ -std=c++98 -o CMakeFiles/cmTC_aba3e.dir/feature_tests.cxx.o -c /home/alex/Work/C++Projects/easyprofiler/CMakeFiles/feature_tests.cxx +Linking CXX executable cmTC_aba3e +/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_aba3e.dir/link.txt --verbose=1 +/usr/bin/c++ CMakeFiles/cmTC_aba3e.dir/feature_tests.cxx.o -o cmTC_aba3e -rdynamic +make[1]: Leaving directory '/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/CMakeTmp' + + + Feature record: CXX_FEATURE:0cxx_aggregate_default_initializers + Feature record: CXX_FEATURE:0cxx_alias_templates + Feature record: CXX_FEATURE:0cxx_alignas + Feature record: CXX_FEATURE:0cxx_alignof + Feature record: CXX_FEATURE:0cxx_attributes + Feature record: CXX_FEATURE:0cxx_attribute_deprecated + Feature record: CXX_FEATURE:0cxx_auto_type + Feature record: CXX_FEATURE:0cxx_binary_literals + Feature record: CXX_FEATURE:0cxx_constexpr + Feature record: CXX_FEATURE:0cxx_contextual_conversions + Feature record: CXX_FEATURE:0cxx_decltype + Feature record: CXX_FEATURE:0cxx_decltype_auto + Feature record: CXX_FEATURE:0cxx_decltype_incomplete_return_types + Feature record: CXX_FEATURE:0cxx_default_function_template_args + Feature record: CXX_FEATURE:0cxx_defaulted_functions + Feature record: CXX_FEATURE:0cxx_defaulted_move_initializers + Feature record: CXX_FEATURE:0cxx_delegating_constructors + Feature record: CXX_FEATURE:0cxx_deleted_functions + Feature record: CXX_FEATURE:0cxx_digit_separators + Feature record: CXX_FEATURE:0cxx_enum_forward_declarations + Feature record: CXX_FEATURE:0cxx_explicit_conversions + Feature record: CXX_FEATURE:0cxx_extended_friend_declarations + Feature record: CXX_FEATURE:0cxx_extern_templates + Feature record: CXX_FEATURE:0cxx_final + Feature record: CXX_FEATURE:0cxx_func_identifier + Feature record: CXX_FEATURE:0cxx_generalized_initializers + Feature record: CXX_FEATURE:0cxx_generic_lambdas + Feature record: CXX_FEATURE:0cxx_inheriting_constructors + Feature record: CXX_FEATURE:0cxx_inline_namespaces + Feature record: CXX_FEATURE:0cxx_lambdas + Feature record: CXX_FEATURE:0cxx_lambda_init_captures + Feature record: CXX_FEATURE:0cxx_local_type_template_args + Feature record: CXX_FEATURE:0cxx_long_long_type + Feature record: CXX_FEATURE:0cxx_noexcept + Feature record: CXX_FEATURE:0cxx_nonstatic_member_init + Feature record: CXX_FEATURE:0cxx_nullptr + Feature record: CXX_FEATURE:0cxx_override + Feature record: CXX_FEATURE:0cxx_range_for + Feature record: CXX_FEATURE:0cxx_raw_string_literals + Feature record: CXX_FEATURE:0cxx_reference_qualified_functions + Feature record: CXX_FEATURE:0cxx_relaxed_constexpr + Feature record: CXX_FEATURE:0cxx_return_type_deduction + Feature record: CXX_FEATURE:0cxx_right_angle_brackets + Feature record: CXX_FEATURE:0cxx_rvalue_references + Feature record: CXX_FEATURE:0cxx_sizeof_member + Feature record: CXX_FEATURE:0cxx_static_assert + Feature record: CXX_FEATURE:0cxx_strong_enums + Feature record: CXX_FEATURE:1cxx_template_template_parameters + Feature record: CXX_FEATURE:0cxx_thread_local + Feature record: CXX_FEATURE:0cxx_trailing_return_types + Feature record: CXX_FEATURE:0cxx_unicode_literals + Feature record: CXX_FEATURE:0cxx_uniform_initialization + Feature record: CXX_FEATURE:0cxx_unrestricted_unions + Feature record: CXX_FEATURE:0cxx_user_literals + Feature record: CXX_FEATURE:0cxx_variable_templates + Feature record: CXX_FEATURE:0cxx_variadic_macros + Feature record: CXX_FEATURE:0cxx_variadic_templates diff --git a/3rdparty/easyprofiler/CMakeFiles/CMakeRuleHashes.txt b/3rdparty/easyprofiler/CMakeFiles/CMakeRuleHashes.txt new file mode 100644 index 0000000..5275219 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/CMakeRuleHashes.txt @@ -0,0 +1,2 @@ +# Hashes of file build rules. +74e55f76d310972c5aad534ac6b85d71 profiler_gui/CMakeFiles/profiler_gui_automoc diff --git a/3rdparty/easyprofiler/CMakeFiles/TargetDirectories.txt b/3rdparty/easyprofiler/CMakeFiles/TargetDirectories.txt new file mode 100644 index 0000000..afc5f57 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/TargetDirectories.txt @@ -0,0 +1,36 @@ +/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/install.dir +/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/list_install_components.dir +/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/install/strip.dir +/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/install/local.dir +/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/rebuild_cache.dir +/home/alex/Work/C++Projects/easyprofiler/CMakeFiles/edit_cache.dir +/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/install.dir +/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/list_install_components.dir +/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/rebuild_cache.dir +/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir +/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/install/strip.dir +/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/install/local.dir +/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/edit_cache.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/install.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/list_install_components.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/rebuild_cache.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/install/strip.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/install/local.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/edit_cache.dir +/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/install.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/list_install_components.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/rebuild_cache.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/edit_cache.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/profiler_sample.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/install/strip.dir +/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/install/local.dir +/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/install.dir +/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/list_install_components.dir +/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/rebuild_cache.dir +/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/profiler_reader.dir +/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/install/local.dir +/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/install/strip.dir +/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/edit_cache.dir diff --git a/3rdparty/easyprofiler/CMakeFiles/cmake.check_cache b/3rdparty/easyprofiler/CMakeFiles/cmake.check_cache new file mode 100644 index 0000000..3dccd73 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/cmake.check_cache @@ -0,0 +1 @@ +# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/3rdparty/easyprofiler/CMakeFiles/feature_tests.bin b/3rdparty/easyprofiler/CMakeFiles/feature_tests.bin new file mode 100755 index 0000000..9fe5cc8 Binary files /dev/null and b/3rdparty/easyprofiler/CMakeFiles/feature_tests.bin differ diff --git a/3rdparty/easyprofiler/CMakeFiles/feature_tests.cxx b/3rdparty/easyprofiler/CMakeFiles/feature_tests.cxx new file mode 100644 index 0000000..b93418c --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/feature_tests.cxx @@ -0,0 +1,405 @@ + + const char features[] = {"\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L +"1" +#else +"0" +#endif +"cxx_aggregate_default_initializers\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_alias_templates\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_alignas\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_alignof\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_attributes\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_attribute_deprecated\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_auto_type\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_binary_literals\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_constexpr\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_contextual_conversions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_decltype\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_decltype_auto\n" +"CXX_FEATURE:" +#if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_decltype_incomplete_return_types\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_default_function_template_args\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_defaulted_functions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_defaulted_move_initializers\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_delegating_constructors\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_deleted_functions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_digit_separators\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_enum_forward_declarations\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_explicit_conversions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_extended_friend_declarations\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_extern_templates\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_final\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_func_identifier\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_generalized_initializers\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_generic_lambdas\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_inheriting_constructors\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_inline_namespaces\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_lambdas\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_lambda_init_captures\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_local_type_template_args\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_long_long_type\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_noexcept\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_nonstatic_member_init\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_nullptr\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_override\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_range_for\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_raw_string_literals\n" +"CXX_FEATURE:" +#if ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_reference_qualified_functions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L +"1" +#else +"0" +#endif +"cxx_relaxed_constexpr\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L +"1" +#else +"0" +#endif +"cxx_return_type_deduction\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_right_angle_brackets\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_rvalue_references\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_sizeof_member\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_static_assert\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_strong_enums\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && __cplusplus +"1" +#else +"0" +#endif +"cxx_template_template_parameters\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_thread_local\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_trailing_return_types\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_unicode_literals\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_uniform_initialization\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_unrestricted_unions\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && __cplusplus >= 201103L +"1" +#else +"0" +#endif +"cxx_user_literals\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L +"1" +#else +"0" +#endif +"cxx_variable_templates\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_variadic_macros\n" +"CXX_FEATURE:" +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && (__cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)) +"1" +#else +"0" +#endif +"cxx_variadic_templates\n" + +}; + +int main(int argc, char** argv) { (void)argv; return features[argc]; } diff --git a/3rdparty/easyprofiler/CMakeFiles/progress.marks b/3rdparty/easyprofiler/CMakeFiles/progress.marks new file mode 100644 index 0000000..8f92bfd --- /dev/null +++ b/3rdparty/easyprofiler/CMakeFiles/progress.marks @@ -0,0 +1 @@ +35 diff --git a/3rdparty/easyprofiler/CMakeLists.txt b/3rdparty/easyprofiler/CMakeLists.txt new file mode 100644 index 0000000..4fe44a4 --- /dev/null +++ b/3rdparty/easyprofiler/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.0) +project(easy_profiler CXX) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +set(EASY_PROGRAM_VERSION_MAJOR 1) +set(EASY_PROGRAM_VERSION_MINOR 3) +set(EASY_PROGRAM_VERSION_PATCH 0) +set(EASY_PRODUCT_VERSION_STRING "${EASY_PROGRAM_VERSION_MAJOR}.${EASY_PROGRAM_VERSION_MINOR}.${EASY_PROGRAM_VERSION_PATCH}") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) + +# set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_LIST_DIR}/sdk) + +macro(easy_define_target_option TARGET SOURCE_OPTION TARGET_DEFINITION) + if (${SOURCE_OPTION}) + set(_VALUE 1) + else () + set(_VALUE 0) + endif () + target_compile_options(${TARGET} PUBLIC -D${TARGET_DEFINITION}=${_VALUE}) +endmacro() + +SET(CMAKE_INSTALL_RPATH "$ORIGIN") + +add_subdirectory(easy_profiler_core) +add_subdirectory(profiler_gui) + +if (NOT EASY_PROFILER_NO_SAMPLES) + add_subdirectory(sample) + add_subdirectory(reader) +endif () diff --git a/3rdparty/easyprofiler/LICENSE.APACHE b/3rdparty/easyprofiler/LICENSE.APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/3rdparty/easyprofiler/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/3rdparty/easyprofiler/LICENSE.MIT b/3rdparty/easyprofiler/LICENSE.MIT new file mode 100644 index 0000000..3840145 --- /dev/null +++ b/3rdparty/easyprofiler/LICENSE.MIT @@ -0,0 +1,18 @@ +Copyright (c) 2017 Sergey Yagovtsev, Victor Zarubkin + +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. \ No newline at end of file diff --git a/3rdparty/easyprofiler/README.md b/3rdparty/easyprofiler/README.md new file mode 100644 index 0000000..634e463 --- /dev/null +++ b/3rdparty/easyprofiler/README.md @@ -0,0 +1,224 @@ +# easy_profiler [![1.3.0](https://img.shields.io/badge/version-1.3.0-009688.svg)](https://github.com/yse/easy_profiler/releases) + +[![Build Status](https://travis-ci.org/yse/easy_profiler.svg?branch=develop)](https://travis-ci.org/yse/easy_profiler) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/yse/easy_profiler?branch=develop&svg=true)](https://ci.appveyor.com/project/yse/easy-profiler/branch/develop) + +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + + +1. [About](#about) +2. [Key features](#key-features) +3. [Usage](#usage) + - [Prepare build system](#prepare-build-system) + - [General build system](#general) + - [CMake](#build-with-cmake) + - [Add profiling blocks](#add-profiling-blocks) + - [Collect blocks](#collect-blocks) + - [Collect via network](#collect-via-network) + - [Collect via file](#collect-via-file) + - [Note about context-switch](#note-about-context-switch) +4. [Build](#build) + - [Linux](#linux) + - [Windows](#windows) +5. [License](#license) + +# About +Lightweight cross-platform profiler library for c++ + +You can profile any function in you code. Furthermore this library provide measuring time of any block of code. +For example, information for 12 millions of blocks is using less than 300Mb of memory. +Working profiler slows your application execution for only 1-2%. + +![Block time](https://hsto.org/files/3e4/afe/8b7/3e4afe8b77ac4ad3a6f8c805be4b7f13.png) +_Average overhead per block is about 15ns/block (tested on Intel Core i7-5930K 3.5GHz, Win7)_ + +Disabled profiler will not affect your application execution in any way. You can leave it in your Release build +and enable it at run-time at any moment during application launch to see what is happening at the moment. + +Also the library can capture system's context switch events between threads. Context switch information includes +duration, target thread id, thread owner process id, thread owner process name. + +You can see the results of measuring in simple GUI application which provides full statistics and renders beautiful time-line. + +![GUI screenshot](https://cloud.githubusercontent.com/assets/1775230/24852044/a0b1edd0-1dde-11e7-8736-7052b840ad06.png) +_Profiling CryEngine SDK example_ + +# Key features + +- Extremely low overhead +- Low additional memory usage +- Cross-platform +- Measuring over network +- Capture thread context-switch events +- Fully remove integration via defines +- GUI could be connected to an application which is already profiling (so you can profile initialization of your application) +- Monitor main thread fps at real-time in GUI even if profiling is disabled or draw your own HUD/fps-plot directly in your application using data provided by profiler +- Configurable timer type with CMakeLists or defines + +# Usage + +## Prepare build system + +### General + +First of all you can specify path to include directory which contains `include/profiler` directory and define macro `BUILD_WITH_EASY_PROFILER`. +For linking with easy_profiler you can specify path to library. + +### Build with cmake + +If you are using `cmake` set `CMAKE_PREFIX_PATH` to `lib/cmake/easy_profiler` directory (from [release](https://github.com/yse/easy_profiler/releases) package) and use function `find_package(easy_profiler)` with `target_link_libraries(... easy_profiler)`. Example: + +``` cmake +project(app_for_profiling) + +set(SOURCES + main.cpp +) + +#CMAKE_PREFIX_PATH should be set to /lib/cmake/easy_profiler +find_package(easy_profiler REQUIRED) + +add_executable(app_for_profiling ${SOURCES}) + +target_link_libraries(app_for_profiling easy_profiler) +``` + +## Add profiling blocks + +Example of usage. + +This code snippet will generate block with function name and Magenta color: +```cpp +#include + +void frame() { + EASY_FUNCTION(profiler::colors::Magenta); // Magenta block with name "frame" + prepareRender(); + calculatePhysics(); +} +``` + +To profile any block you may do this as following. +You can specify these blocks also with Google material design colors or just set name of the block +(in this case it will have default color which is `Amber100`): +```cpp +#include + +void foo() { + // some code + EASY_BLOCK("Calculating sum"); // Block with default color + int sum = 0; + for (int i = 0; i < 10; ++i) { + EASY_BLOCK("Addition", profiler::colors::Red); // Scoped red block (no EASY_END_BLOCK needed) + sum += i; + } + EASY_END_BLOCK; // This ends "Calculating sum" block + + EASY_BLOCK("Calculating multiplication", profiler::colors::Blue500); // Blue block + int mul = 1; + for (int i = 1; i < 11; ++i) + mul *= i; + //EASY_END_BLOCK; // This is not needed because all blocks are ended on destructor when closing braces met +} +``` + +You can also use your own colors. easy_profiler is using standard 32-bit ARGB color format. +Example: +```cpp +#include + +void bar() { + EASY_FUNCTION(0xfff080aa); // Function block with custom color + // some code +} +``` +## Collect blocks + +There are two ways to cature blocks + +### Collect via network + +It's most prefered and convenient approach in many case. + +1. Initialize listening by `profiler::startListen()`. It's start new thread to listen on `28077` port the start-capture-signal from gui-application. +2. To stop listening you can call `profiler::stopListen()` function. + +### Collect via file + +1. Enable profiler by `EASY_PROFILER_ENABLE` macro +2. Dump blocks to file in any place you want by `profiler::dumpBlocksToFile("test_profile.prof")` function + +Example: +```cpp +int main() +{ + EASY_PROFILER_ENABLE; + /* do work*/ + profiler::dumpBlocksToFile("test_profile.prof"); +} +``` + +### Note about context-switch + +To capture a thread context-switch event you need: + +- On Windows: run profiling application "as administrator" +- On linux: you can run special `systemtap` script with root privileges as follow (example on Fedora): +```bash +#stap -o /tmp/cs_profiling_info.log scripts/context_switch_logger.stp name APPLICATION_NAME +``` +APPLICATION_NAME - name of profiling application + +# Build + +## Prerequisites + +* CMake 3.0 or higher +* Compiler with c++11 support + * for Unix systems: compiler with `thread_local` support is **highly recommended**: _GCC >=4.8_, _Clang >=3.3_ + +Additional requirements for GUI: +* Qt 5.3.0 or higher + +## Linux + +```bash +$ mkdir build +$ cd build +$ cmake -DCMAKE_BUILD_TYPE="Release" .. +$ make +``` + +## Windows + +If you are using QtCreator IDE you can just open `CMakeLists.txt` file in root directory. +If you are using Visual Studio you can generate solution by cmake generator command. +Examples shows how to generate Win64 solution for Visual Studio 2013. To generate for another version use proper cmake generator (-G "name of generator"). + +### Way 1 +Specify path to cmake scripts in Qt5 dir (usually in lib/cmake subdir) and execute cmake generator command, +for example: +```batch +$ mkdir build +$ cd build +$ cmake -DCMAKE_PREFIX_PATH="C:\Qt\5.3\msvc2013_64\lib\cmake" .. -G "Visual Studio 12 2013 Win64" +``` + +### Way 2 +Create system variable "Qt5Widgets_DIR" and set it's value to "[path-to-Qt5-binaries]\lib\cmake\Qt5Widgets". +For example, "C:\Qt\5.3\msvc2013_64\lib\cmake\Qt5Widgets". +And then run cmake generator as follows: +```batch +$ mkdir build +$ cd build +$ cmake .. -G "Visual Studio 12 2013 Win64" +``` + +# License + +Licensed under either of +- MIT license ([LICENSE.MIT](LICENSE.MIT) or http://opensource.org/licenses/MIT) +- Apache License, Version 2.0, ([LICENSE.APACHE](LICENSE.APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + +at your option. diff --git a/3rdparty/easyprofiler/appveyor.bat b/3rdparty/easyprofiler/appveyor.bat new file mode 100644 index 0000000..5f78678 --- /dev/null +++ b/3rdparty/easyprofiler/appveyor.bat @@ -0,0 +1,6 @@ +mkdir build_msvc +cd build_msvc +cmake -G "%GENERATOR%" ../ +cmake --build . --config Release + +goto :EOF diff --git a/3rdparty/easyprofiler/appveyor.yml b/3rdparty/easyprofiler/appveyor.yml new file mode 100644 index 0000000..6a7708f --- /dev/null +++ b/3rdparty/easyprofiler/appveyor.yml @@ -0,0 +1,19 @@ +platform: + - Win64 + +configuration: + - Release + +environment: + matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + Qt5Widgets_DIR: "C:\\Qt\\5.5\\msvc2013_64\\lib\\cmake\\Qt5Widgets" + GENERATOR: "Visual Studio 12 2013 Win64" + +test: off + +build_script: + - CALL appveyor.bat + +skip_commits: + message: /.*\[skip appveyor\].*/ diff --git a/3rdparty/easyprofiler/cmake_install.cmake b/3rdparty/easyprofiler/cmake_install.cmake new file mode 100644 index 0000000..94c87df --- /dev/null +++ b/3rdparty/easyprofiler/cmake_install.cmake @@ -0,0 +1,53 @@ +# Install script for directory: /home/alex/Work/C++Projects/easyprofiler + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Release") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +if(NOT CMAKE_INSTALL_LOCAL_ONLY) + # Include the install script for each subdirectory. + include("/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/cmake_install.cmake") + include("/home/alex/Work/C++Projects/easyprofiler/profiler_gui/cmake_install.cmake") + include("/home/alex/Work/C++Projects/easyprofiler/sample/cmake_install.cmake") + include("/home/alex/Work/C++Projects/easyprofiler/reader/cmake_install.cmake") + +endif() + +if(CMAKE_INSTALL_COMPONENT) + set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") +else() + set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") +endif() + +string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT + "${CMAKE_INSTALL_MANIFEST_FILES}") +file(WRITE "/home/alex/Work/C++Projects/easyprofiler/${CMAKE_INSTALL_MANIFEST}" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/CMakeDirectoryInformation.cmake b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..9ecfcf5 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/alex/Work/C++Projects/easyprofiler") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/alex/Work/C++Projects/easyprofiler") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets-release.cmake b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets-release.cmake new file mode 100644 index 0000000..09cbb89 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets-release.cmake @@ -0,0 +1,19 @@ +#---------------------------------------------------------------- +# Generated CMake target import file for configuration "Release". +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Import target "easy_profiler" for configuration "Release" +set_property(TARGET easy_profiler APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(easy_profiler PROPERTIES + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/libeasy_profiler.so" + IMPORTED_SONAME_RELEASE "libeasy_profiler.so" + ) + +list(APPEND _IMPORT_CHECK_TARGETS easy_profiler ) +list(APPEND _IMPORT_CHECK_FILES_FOR_easy_profiler "${_IMPORT_PREFIX}/bin/libeasy_profiler.so" ) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets.cmake b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets.cmake new file mode 100644 index 0000000..5f95653 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets.cmake @@ -0,0 +1,95 @@ +# Generated by CMake 3.5.1 + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5) + message(FATAL_ERROR "CMake >= 2.6.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 2.6) +#---------------------------------------------------------------- +# Generated CMake target import file. +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_targetsDefined) +set(_targetsNotDefined) +set(_expectedTargets) +foreach(_expectedTarget easy_profiler) + list(APPEND _expectedTargets ${_expectedTarget}) + if(NOT TARGET ${_expectedTarget}) + list(APPEND _targetsNotDefined ${_expectedTarget}) + endif() + if(TARGET ${_expectedTarget}) + list(APPEND _targetsDefined ${_expectedTarget}) + endif() +endforeach() +if("${_targetsDefined}" STREQUAL "${_expectedTargets}") + set(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT "${_targetsDefined}" STREQUAL "") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n") +endif() +unset(_targetsDefined) +unset(_targetsNotDefined) +unset(_expectedTargets) + + +# Compute the installation prefix relative to this file. +get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) + +# Create imported target easy_profiler +add_library(easy_profiler SHARED IMPORTED) + +set_target_properties(easy_profiler PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "EASY_PROFILER_VERSION_MAJOR=1;EASY_PROFILER_VERSION_MINOR=3;EASY_PROFILER_VERSION_PATCH=0;EASY_DEFAULT_PORT=28077;BUILD_WITH_EASY_PROFILER=1" + INTERFACE_COMPILE_OPTIONS "-DEASY_CHRONO_STEADY_CLOCK=0;-DEASY_CHRONO_HIGHRES_CLOCK=0;-DEASY_OPTION_START_LISTEN_ON_STARTUP=0;-DEASY_OPTION_MEASURE_STORAGE_EXPAND=0;-DEASY_OPTION_STORAGE_EXPAND_BLOCKS_ON=0;-DEASY_OPTION_IMPLICIT_THREAD_REGISTRATION=1;-DEASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS=0;-DEASY_OPTION_LOG_ENABLED=0;-DEASY_OPTION_PRETTY_PRINT_FUNCTIONS=0;-DEASY_OPTION_BUILTIN_COLORS=1;-std=gnu++11" + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include;${_IMPORT_PREFIX}/include" + INTERFACE_LINK_LIBRARIES "pthread" +) + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "This file relies on consumers using CMake 2.8.12 or greater.") +endif() + +# Load information for each installed configuration. +get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +file(GLOB CONFIG_FILES "${_DIR}/easy_profilerTargets-*.cmake") +foreach(f ${CONFIG_FILES}) + include(${f}) +endforeach() + +# Cleanup temporary variables. +set(_IMPORT_PREFIX) + +# Loop over all imported files and verify that they actually exist +foreach(target ${_IMPORT_CHECK_TARGETS} ) + foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} ) + if(NOT EXISTS "${file}" ) + message(FATAL_ERROR "The imported target \"${target}\" references the file + \"${file}\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty and contained + \"${CMAKE_CURRENT_LIST_FILE}\" +but not all the files it references. +") + endif() + endforeach() + unset(_IMPORT_CHECK_FILES_FOR_${target}) +endforeach() +unset(_IMPORT_CHECK_TARGETS) + +# This file does not depend on other imported targets which have +# been exported from the same project but in a separate export set. + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake new file mode 100644 index 0000000..56e0950 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake @@ -0,0 +1,37 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/block.cpp" "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/easy_socket.cpp" "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/event_trace_win.cpp" "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/nonscoped_block.cpp" "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/profile_manager.cpp" "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/reader.cpp" "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/thread_storage.cpp" "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BUILD_WITH_EASY_PROFILER=1" + "EASY_DEFAULT_PORT=28077" + "EASY_PROFILER_VERSION_MAJOR=1" + "EASY_PROFILER_VERSION_MINOR=3" + "EASY_PROFILER_VERSION_PATCH=0" + "_BUILD_PROFILER=1" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "easy_profiler_core/include" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make new file mode 100644 index 0000000..2b3b823 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make @@ -0,0 +1,275 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/alex/Work/C++Projects/easyprofiler + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/alex/Work/C++Projects/easyprofiler + +# Include any dependencies generated for this target. +include easy_profiler_core/CMakeFiles/easy_profiler.dir/depend.make + +# Include the progress variables for this target. +include easy_profiler_core/CMakeFiles/easy_profiler.dir/progress.make + +# Include the compile flags for this target's objects. +include easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make + +easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o: easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make +easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o: easy_profiler_core/block.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/easy_profiler.dir/block.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/block.cpp + +easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/easy_profiler.dir/block.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/block.cpp > CMakeFiles/easy_profiler.dir/block.cpp.i + +easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/easy_profiler.dir/block.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/block.cpp -o CMakeFiles/easy_profiler.dir/block.cpp.s + +easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.requires: + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.provides: easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.requires + $(MAKE) -f easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.provides.build +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.provides + +easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.provides.build: easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o + + +easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o: easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make +easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o: easy_profiler_core/easy_socket.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building CXX object easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/easy_profiler.dir/easy_socket.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/easy_socket.cpp + +easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/easy_profiler.dir/easy_socket.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/easy_socket.cpp > CMakeFiles/easy_profiler.dir/easy_socket.cpp.i + +easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/easy_profiler.dir/easy_socket.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/easy_socket.cpp -o CMakeFiles/easy_profiler.dir/easy_socket.cpp.s + +easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.requires: + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.provides: easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.requires + $(MAKE) -f easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.provides.build +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.provides + +easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.provides.build: easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o + + +easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o: easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make +easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o: easy_profiler_core/event_trace_win.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building CXX object easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/event_trace_win.cpp + +easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/easy_profiler.dir/event_trace_win.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/event_trace_win.cpp > CMakeFiles/easy_profiler.dir/event_trace_win.cpp.i + +easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/easy_profiler.dir/event_trace_win.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/event_trace_win.cpp -o CMakeFiles/easy_profiler.dir/event_trace_win.cpp.s + +easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.requires: + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.provides: easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.requires + $(MAKE) -f easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.provides.build +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.provides + +easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.provides.build: easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o + + +easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o: easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make +easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o: easy_profiler_core/nonscoped_block.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Building CXX object easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/nonscoped_block.cpp + +easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/nonscoped_block.cpp > CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.i + +easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/nonscoped_block.cpp -o CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.s + +easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.requires: + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.provides: easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.requires + $(MAKE) -f easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.provides.build +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.provides + +easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.provides.build: easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o + + +easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o: easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make +easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o: easy_profiler_core/profile_manager.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_5) "Building CXX object easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/easy_profiler.dir/profile_manager.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/profile_manager.cpp + +easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/easy_profiler.dir/profile_manager.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/profile_manager.cpp > CMakeFiles/easy_profiler.dir/profile_manager.cpp.i + +easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/easy_profiler.dir/profile_manager.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/profile_manager.cpp -o CMakeFiles/easy_profiler.dir/profile_manager.cpp.s + +easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.requires: + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.provides: easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.requires + $(MAKE) -f easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.provides.build +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.provides + +easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.provides.build: easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o + + +easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o: easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make +easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o: easy_profiler_core/reader.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_6) "Building CXX object easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/easy_profiler.dir/reader.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/reader.cpp + +easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/easy_profiler.dir/reader.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/reader.cpp > CMakeFiles/easy_profiler.dir/reader.cpp.i + +easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/easy_profiler.dir/reader.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/reader.cpp -o CMakeFiles/easy_profiler.dir/reader.cpp.s + +easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.requires: + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.provides: easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.requires + $(MAKE) -f easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.provides.build +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.provides + +easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.provides.build: easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o + + +easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o: easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make +easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o: easy_profiler_core/thread_storage.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_7) "Building CXX object easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/easy_profiler.dir/thread_storage.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/thread_storage.cpp + +easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/easy_profiler.dir/thread_storage.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/thread_storage.cpp > CMakeFiles/easy_profiler.dir/thread_storage.cpp.i + +easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/easy_profiler.dir/thread_storage.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/thread_storage.cpp -o CMakeFiles/easy_profiler.dir/thread_storage.cpp.s + +easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.requires: + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.provides: easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.requires + $(MAKE) -f easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.provides.build +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.provides + +easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.provides.build: easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o + + +# Object files for target easy_profiler +easy_profiler_OBJECTS = \ +"CMakeFiles/easy_profiler.dir/block.cpp.o" \ +"CMakeFiles/easy_profiler.dir/easy_socket.cpp.o" \ +"CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o" \ +"CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o" \ +"CMakeFiles/easy_profiler.dir/profile_manager.cpp.o" \ +"CMakeFiles/easy_profiler.dir/reader.cpp.o" \ +"CMakeFiles/easy_profiler.dir/thread_storage.cpp.o" + +# External object files for target easy_profiler +easy_profiler_EXTERNAL_OBJECTS = + +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/build.make +bin/libeasy_profiler.so: easy_profiler_core/CMakeFiles/easy_profiler.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_8) "Linking CXX shared library ../bin/libeasy_profiler.so" + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/easy_profiler.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +easy_profiler_core/CMakeFiles/easy_profiler.dir/build: bin/libeasy_profiler.so + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/build + +easy_profiler_core/CMakeFiles/easy_profiler.dir/requires: easy_profiler_core/CMakeFiles/easy_profiler.dir/block.cpp.o.requires +easy_profiler_core/CMakeFiles/easy_profiler.dir/requires: easy_profiler_core/CMakeFiles/easy_profiler.dir/easy_socket.cpp.o.requires +easy_profiler_core/CMakeFiles/easy_profiler.dir/requires: easy_profiler_core/CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o.requires +easy_profiler_core/CMakeFiles/easy_profiler.dir/requires: easy_profiler_core/CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o.requires +easy_profiler_core/CMakeFiles/easy_profiler.dir/requires: easy_profiler_core/CMakeFiles/easy_profiler.dir/profile_manager.cpp.o.requires +easy_profiler_core/CMakeFiles/easy_profiler.dir/requires: easy_profiler_core/CMakeFiles/easy_profiler.dir/reader.cpp.o.requires +easy_profiler_core/CMakeFiles/easy_profiler.dir/requires: easy_profiler_core/CMakeFiles/easy_profiler.dir/thread_storage.cpp.o.requires + +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/requires + +easy_profiler_core/CMakeFiles/easy_profiler.dir/clean: + cd /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core && $(CMAKE_COMMAND) -P CMakeFiles/easy_profiler.dir/cmake_clean.cmake +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/clean + +easy_profiler_core/CMakeFiles/easy_profiler.dir/depend: + cd /home/alex/Work/C++Projects/easyprofiler && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : easy_profiler_core/CMakeFiles/easy_profiler.dir/depend + diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/cmake_clean.cmake b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/cmake_clean.cmake new file mode 100644 index 0000000..27eab83 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/cmake_clean.cmake @@ -0,0 +1,16 @@ +file(REMOVE_RECURSE + "CMakeFiles/easy_profiler.dir/block.cpp.o" + "CMakeFiles/easy_profiler.dir/easy_socket.cpp.o" + "CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o" + "CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o" + "CMakeFiles/easy_profiler.dir/profile_manager.cpp.o" + "CMakeFiles/easy_profiler.dir/reader.cpp.o" + "CMakeFiles/easy_profiler.dir/thread_storage.cpp.o" + "../bin/libeasy_profiler.pdb" + "../bin/libeasy_profiler.so" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/easy_profiler.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/depend.make b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/depend.make new file mode 100644 index 0000000..be24553 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/depend.make @@ -0,0 +1,2 @@ +# Empty dependencies file for easy_profiler. +# This may be replaced when dependencies are built. diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make new file mode 100644 index 0000000..a91ba6d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -O3 -DNDEBUG -fPIC -DEASY_CHRONO_STEADY_CLOCK=0 -DEASY_CHRONO_HIGHRES_CLOCK=0 -DEASY_OPTION_START_LISTEN_ON_STARTUP=0 -DEASY_OPTION_MEASURE_STORAGE_EXPAND=0 -DEASY_OPTION_STORAGE_EXPAND_BLOCKS_ON=0 -DEASY_OPTION_IMPLICIT_THREAD_REGISTRATION=1 -DEASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS=0 -DEASY_OPTION_LOG_ENABLED=0 -DEASY_OPTION_PRETTY_PRINT_FUNCTIONS=0 -DEASY_OPTION_BUILTIN_COLORS=1 -Wall -Wno-long-long -Wno-reorder -Wno-braced-scalar-init -pedantic -std=gnu++11 -std=gnu++11 + +CXX_DEFINES = -DBUILD_WITH_EASY_PROFILER=1 -DEASY_DEFAULT_PORT=28077 -DEASY_PROFILER_VERSION_MAJOR=1 -DEASY_PROFILER_VERSION_MINOR=3 -DEASY_PROFILER_VERSION_PATCH=0 -D_BUILD_PROFILER=1 -Deasy_profiler_EXPORTS + +CXX_INCLUDES = -I/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include + diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/link.txt b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/link.txt new file mode 100644 index 0000000..55b5e85 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/link.txt @@ -0,0 +1 @@ +/usr/bin/c++ -fPIC -O3 -DNDEBUG -shared -Wl,-soname,libeasy_profiler.so -o ../bin/libeasy_profiler.so CMakeFiles/easy_profiler.dir/block.cpp.o CMakeFiles/easy_profiler.dir/easy_socket.cpp.o CMakeFiles/easy_profiler.dir/event_trace_win.cpp.o CMakeFiles/easy_profiler.dir/nonscoped_block.cpp.o CMakeFiles/easy_profiler.dir/profile_manager.cpp.o CMakeFiles/easy_profiler.dir/reader.cpp.o CMakeFiles/easy_profiler.dir/thread_storage.cpp.o -lpthread -Wl,-rpath,::::::: diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/progress.make b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/progress.make new file mode 100644 index 0000000..5b29368 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/progress.make @@ -0,0 +1,9 @@ +CMAKE_PROGRESS_1 = 1 +CMAKE_PROGRESS_2 = 2 +CMAKE_PROGRESS_3 = 3 +CMAKE_PROGRESS_4 = 4 +CMAKE_PROGRESS_5 = 5 +CMAKE_PROGRESS_6 = 6 +CMAKE_PROGRESS_7 = 7 +CMAKE_PROGRESS_8 = 8 + diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/progress.marks b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/progress.marks new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeFiles/progress.marks @@ -0,0 +1 @@ +8 diff --git a/3rdparty/easyprofiler/easy_profiler_core/CMakeLists.txt b/3rdparty/easyprofiler/easy_profiler_core/CMakeLists.txt new file mode 100644 index 0000000..e41a7d9 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/CMakeLists.txt @@ -0,0 +1,309 @@ +message(STATUS "") +message(STATUS "EASY_PROFILER.Core version = ${EASY_PRODUCT_VERSION_STRING}") +message(STATUS "") + + + +##################################################################### +# Checking c++11 thread_local support +if (NOT WIN32) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8") + set(NO_CXX11_THREAD_LOCAL_SUPPORT TRUE) + endif () + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.3") + set(NO_CXX11_THREAD_LOCAL_SUPPORT TRUE) + endif () + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8.0") + set(NO_CXX11_THREAD_LOCAL_SUPPORT TRUE) + endif () + endif () + + # TODO: Check thread_local keyword support for other compilers for Unix + + if (NO_CXX11_THREAD_LOCAL_SUPPORT) + message(WARNING " Your compiler does not support C++11 thread_local feature.") + message(WARNING " Without C++11 thread_local feature you may face to possible memory leak or application crash if using implicit thread registration and using EASY_THREAD instead of EASY_THREAD_SCOPE.") + endif () +endif () +##################################################################### + + + +########################################################### +# EasyProfiler options: +set(EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT "Enable new threads registration when collecting context switch events") +set(EASY_DEFAULT_PORT 28077 CACHE STRING "Default listening port") +set(EASY_OPTION_LISTEN OFF CACHE BOOL "Enable automatic startListen on startup") +set(EASY_OPTION_PROFILE_SELF OFF CACHE BOOL "Enable self profiling (measure time for internal storage expand)") +set(EASY_OPTION_PROFILE_SELF_BLOCKS_ON OFF CACHE BOOL "Storage expand default status (profiler::ON or profiler::OFF)") +set(EASY_OPTION_LOG OFF CACHE BOOL "Print errors to stderr") +set(EASY_OPTION_PRETTY_PRINT OFF CACHE BOOL "Use pretty-printed function names with signature and argument types") +set(EASY_OPTION_PREDEFINED_COLORS ON CACHE BOOL "Use predefined set of colors (see profiler_colors.h). If you want to use your own colors palette you can turn this option OFF") +set(BUILD_SHARED_LIBS ON CACHE BOOL "Build easy_profiler as shared library.") +if (WIN32) + set(EASY_OPTION_IMPLICIT_THREAD_REGISTRATION ON CACHE BOOL ${EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT}) + set(EASY_OPTION_EVENT_TRACING ON CACHE BOOL "Enable event tracing by default") + set(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING ON CACHE BOOL "Set low priority for event tracing thread") +else () + if (NO_CXX11_THREAD_LOCAL_SUPPORT) + set(EASY_OPTION_IMPLICIT_THREAD_REGISTRATION OFF CACHE BOOL ${EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT}) + set(EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS OFF CACHE BOOL "Enable easy_profiler to remove empty unguarded threads. This fixes potential memory leak on Unix systems, but may lead to an application crash! This is used when C++11 thread_local is unavailable.") + else () + set(EASY_OPTION_IMPLICIT_THREAD_REGISTRATION ON CACHE BOOL ${EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT}) + endif () +endif () +set(BUILD_WITH_CHRONO_STEADY_CLOCK OFF CACHE BOOL "Use std::chrono::steady_clock as a timer" ) +set(BUILD_WITH_CHRONO_HIGH_RESOLUTION_CLOCK OFF CACHE BOOL "Use std::chrono::high_resolution_clock as a timer") +# End EasyProfiler options. +########################################################### + + + +##################################################################### +# Print EasyProfiler options status: +message(STATUS "-------- EASY_PROFILER OPTIONS: --------") +if (BUILD_WITH_CHRONO_STEADY_CLOCK) + message(STATUS " Use std::chrono::steady_clock as a timer") +elseif (BUILD_WITH_CHRONO_HIGH_RESOLUTION_CLOCK) + message(STATUS " Use std::chrono::high_resolution_clock as a timer") +else () + if (WIN32) + message(STATUS " Use QueryPerformanceCounter as a timer") + else () + message(STATUS " Use rtdsc as a timer") + endif () +endif () + +message(STATUS " Default listening port = ${EASY_DEFAULT_PORT}") +message(STATUS " Auto-start listening = ${EASY_OPTION_LISTEN}") +message(STATUS " Profile self = ${EASY_OPTION_PROFILE_SELF}") +message(STATUS " Profile self blocks initial status = ${EASY_OPTION_PROFILE_SELF_BLOCKS_ON}") +message(STATUS " Implicit thread registration = ${EASY_OPTION_IMPLICIT_THREAD_REGISTRATION}") +if (WIN32) + message(STATUS " Event tracing = ${EASY_OPTION_EVENT_TRACING}") + message(STATUS " Event tracing has low priority = ${EASY_OPTION_LOW_PRIORITY_EVENT_TRACING}") +elseif (NO_CXX11_THREAD_LOCAL_SUPPORT) + if (EASY_OPTION_IMPLICIT_THREAD_REGISTRATION) + message(STATUS " WARNING! Implicit thread registration for Unix systems can lead to memory leak") + message(STATUS " because there is no possibility to check if thread is alive and remove dead threads.") + endif () + message(STATUS " Removing empty unguarded threads = ${EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS}") + if (EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS) + message(STATUS " WARNING! Removing empty unguarded threads may lead to an application crash!") + message(STATUS " But fixes potential memory leak on Unix systems.") + else () + message(STATUS " WARNING! There is a possibility of memory leak without removing empty unguarded threads.") + endif () +endif () +message(STATUS " Log messages = ${EASY_OPTION_LOG}") +message(STATUS " Function names pretty-print = ${EASY_OPTION_PRETTY_PRINT}") +message(STATUS " Use EasyProfiler colors palette = ${EASY_OPTION_PREDEFINED_COLORS}") +message(STATUS " Shared library: ${BUILD_SHARED_LIBS}") +message(STATUS "------ END EASY_PROFILER OPTIONS -------") +message(STATUS "") +# End printing EasyProfiler options status. +##################################################################### + + + +################################################# +# Add source files: +set(CPP_FILES + block.cpp + easy_socket.cpp + event_trace_win.cpp + nonscoped_block.cpp + profile_manager.cpp + reader.cpp + thread_storage.cpp +) + +set(H_FILES + chunk_allocator.h + current_time.h + current_thread.h + event_trace_win.h + nonscoped_block.h + profile_manager.h + thread_storage.h + spin_lock.h + stack_buffer.h +) + +set(INCLUDE_FILES + include/easy/arbitrary_value.h + include/easy/easy_net.h + include/easy/easy_socket.h + include/easy/profiler.h + include/easy/reader.h + include/easy/serialized_block.h + include/easy/details/arbitrary_value_aux.h + include/easy/details/arbitrary_value_public_types.h + include/easy/details/easy_compiler_support.h + include/easy/details/profiler_aux.h + include/easy/details/profiler_colors.h + include/easy/details/profiler_public_types.h +) + +source_group(include FILES ${INCLUDE_FILES}) + +set(SOURCES + ${CPP_FILES} + ${H_FILES} + ${INCLUDE_FILES} +) + +add_library(easy_profiler ${SOURCES} resources.rc) +# End adding source files. +################################################# + + + +target_include_directories(easy_profiler PUBLIC + $ + $ # /include +) +target_compile_definitions(easy_profiler PRIVATE + -D_BUILD_PROFILER=1 + #-DEASY_PROFILER_API_DISABLED # uncomment this to disable profiler api only (you will have to rebuild only easy_profiler) +) +if (NOT BUILD_SHARED_LIBS) + target_compile_definitions(easy_profiler PUBLIC -DEASY_PROFILER_STATIC=1) +endif () +target_compile_definitions(easy_profiler PUBLIC + -DEASY_PROFILER_VERSION_MAJOR=${EASY_PROGRAM_VERSION_MAJOR} + -DEASY_PROFILER_VERSION_MINOR=${EASY_PROGRAM_VERSION_MINOR} + -DEASY_PROFILER_VERSION_PATCH=${EASY_PROGRAM_VERSION_PATCH} + -DEASY_DEFAULT_PORT=${EASY_DEFAULT_PORT} + -DBUILD_WITH_EASY_PROFILER=1 +) + + + +##################################################################### +# Add EasyProfiler options definitions: +easy_define_target_option(easy_profiler BUILD_WITH_CHRONO_STEADY_CLOCK EASY_CHRONO_STEADY_CLOCK) +easy_define_target_option(easy_profiler BUILD_WITH_CHRONO_HIGH_RESOLUTION_CLOCK EASY_CHRONO_HIGHRES_CLOCK) +easy_define_target_option(easy_profiler EASY_OPTION_LISTEN EASY_OPTION_START_LISTEN_ON_STARTUP) +easy_define_target_option(easy_profiler EASY_OPTION_PROFILE_SELF EASY_OPTION_MEASURE_STORAGE_EXPAND) +easy_define_target_option(easy_profiler EASY_OPTION_PROFILE_SELF_BLOCKS_ON EASY_OPTION_STORAGE_EXPAND_BLOCKS_ON) +easy_define_target_option(easy_profiler EASY_OPTION_IMPLICIT_THREAD_REGISTRATION EASY_OPTION_IMPLICIT_THREAD_REGISTRATION) +if (WIN32) + easy_define_target_option(easy_profiler EASY_OPTION_EVENT_TRACING EASY_OPTION_EVENT_TRACING_ENABLED) + easy_define_target_option(easy_profiler EASY_OPTION_LOW_PRIORITY_EVENT_TRACING EASY_OPTION_LOW_PRIORITY_EVENT_TRACING) +else () + easy_define_target_option(easy_profiler EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS) +endif () +easy_define_target_option(easy_profiler EASY_OPTION_LOG EASY_OPTION_LOG_ENABLED) +easy_define_target_option(easy_profiler EASY_OPTION_PRETTY_PRINT EASY_OPTION_PRETTY_PRINT_FUNCTIONS) +easy_define_target_option(easy_profiler EASY_OPTION_PREDEFINED_COLORS EASY_OPTION_BUILTIN_COLORS) +# End adding EasyProfiler options definitions. +##################################################################### + + + +############################################################################### +# Add platform specific compile options: +if (UNIX) + target_compile_options(easy_profiler PRIVATE -Wall -Wno-long-long -Wno-reorder -Wno-braced-scalar-init -pedantic) + target_link_libraries(easy_profiler pthread) +elseif (WIN32) + target_compile_definitions(easy_profiler PRIVATE -D_WIN32_WINNT=0x0600 -D_CRT_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS) + target_link_libraries(easy_profiler ws2_32 psapi) +endif () + +if (MINGW) + target_compile_definitions(easy_profiler PRIVATE -DSTRSAFE_NO_DEPRECATE) +endif () + +if (MSVC) + target_compile_options(easy_profiler PRIVATE /WX) +endif () + +if (APPLE) + target_compile_options(easy_profiler PUBLIC -std=gnu++11) +else () + if (CMAKE_VERSION VERSION_LESS "3.1") + if (NOT MSVC) + target_compile_options(easy_profiler PUBLIC $<$:-std=gnu++11>) + endif () + else () + if (NOT MSVC) + target_compile_options(easy_profiler PUBLIC -std=gnu++11) + endif () + set_target_properties(easy_profiler PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON) + endif () +endif () +# End adding platform specific compile options. +############################################################################### + + + +######################################################################################### +# Installation: +set(config_install_dir "lib/cmake/${PROJECT_NAME}") +set(include_install_dir "include") + +set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") + +# Configuration +set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") +set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") +set(targets_export_name "${PROJECT_NAME}Targets") + +include(CMakePackageConfigHelpers) +include(InstallRequiredSystemLibraries) + +write_basic_package_version_file( + "${version_config}" + VERSION + ${EASY_PRODUCT_VERSION_STRING} + COMPATIBILITY + SameMajorVersion +) + +configure_package_config_file( + "cmake/config.cmake.in" + "${project_config}" + INSTALL_DESTINATION "${config_install_dir}" +) + +install( + FILES "${project_config}" "${version_config}" + DESTINATION "${config_install_dir}" +) + +install( + FILES + ${INCLUDE_FILES} + DESTINATION + include/easy +) + +install( + FILES + LICENSE.MIT + LICENSE.APACHE + DESTINATION + . +) + +install( + TARGETS + easy_profiler + EXPORT + ${targets_export_name} + DESTINATION + bin + INCLUDES DESTINATION "${include_install_dir}" +) + +install( + EXPORT "${targets_export_name}" + DESTINATION "${config_install_dir}" +) + +target_compile_definitions(easy_profiler PUBLIC ) diff --git a/3rdparty/easyprofiler/easy_profiler_core/LICENSE.APACHE b/3rdparty/easyprofiler/easy_profiler_core/LICENSE.APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/3rdparty/easyprofiler/easy_profiler_core/LICENSE.MIT b/3rdparty/easyprofiler/easy_profiler_core/LICENSE.MIT new file mode 100644 index 0000000..3840145 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/LICENSE.MIT @@ -0,0 +1,18 @@ +Copyright (c) 2017 Sergey Yagovtsev, Victor Zarubkin + +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. \ No newline at end of file diff --git a/3rdparty/easyprofiler/easy_profiler_core/block.cpp b/3rdparty/easyprofiler/easy_profiler_core/block.cpp new file mode 100644 index 0000000..de0b7b7 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/block.cpp @@ -0,0 +1,241 @@ +/************************************************************************ +* file name : block.cpp +* ----------------- : +* creation time : 2016/02/16 +* authors : Sergey Yagovtsev, Victor Zarubkin +* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of profiling blocks +* : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include "profile_manager.h" +#include "current_time.h" + +using namespace profiler; + +#ifndef EASY_PROFILER_API_DISABLED +Event::Event(timestamp_t _begin_time) EASY_NOEXCEPT : m_begin(_begin_time), m_end(0) +{ + +} + +Event::Event(timestamp_t _begin_time, timestamp_t _end_time) EASY_NOEXCEPT : m_begin(_begin_time), m_end(_end_time) +{ + +} + +BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id) EASY_NOEXCEPT + : Event(_begin_time) + , m_id(_descriptor_id) +{ + +} + +BaseBlockData::BaseBlockData(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _descriptor_id) EASY_NOEXCEPT + : Event(_begin_time, _end_time) + , m_id(_descriptor_id) +{ + +} + +Block::Block(Block&& that) EASY_NOEXCEPT + : BaseBlockData(that.m_begin, that.m_id) + , m_name(that.m_name) + , m_status(that.m_status) + , m_isScoped(that.m_isScoped) +{ + m_end = that.m_end; + that.m_end = that.m_begin; +} + +Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName) EASY_NOEXCEPT + : BaseBlockData(_begin_time, _descriptor_id) + , m_name(_runtimeName) + , m_status(::profiler::ON) + , m_isScoped(true) +{ + +} + +Block::Block(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _descriptor_id, const char* _runtimeName) EASY_NOEXCEPT + : BaseBlockData(_begin_time, _end_time, _descriptor_id) + , m_name(_runtimeName) + , m_status(::profiler::ON) + , m_isScoped(true) +{ + +} + +Block::Block(const BaseBlockDescriptor* _descriptor, const char* _runtimeName, bool _scoped) EASY_NOEXCEPT + : BaseBlockData(1ULL, _descriptor->id()) + , m_name(_runtimeName) + , m_status(_descriptor->status()) + , m_isScoped(_scoped) +{ + +} + +void Block::start() +{ + m_begin = getCurrentTime(); +} + +void Block::start(timestamp_t _time) EASY_NOEXCEPT +{ + m_begin = _time; +} + +void Block::finish() +{ + m_end = getCurrentTime(); +} + +void Block::finish(timestamp_t _time) EASY_NOEXCEPT +{ + m_end = _time; +} + +Block::~Block() +{ + if (!finished()) + ::profiler::endBlock(); +} +#else +Event::Event(timestamp_t) EASY_NOEXCEPT : m_begin(0), m_end(0) +{ + +} + +Event::Event(timestamp_t, timestamp_t) EASY_NOEXCEPT : m_begin(0), m_end(0) +{ + +} + +BaseBlockData::BaseBlockData(timestamp_t, block_id_t) EASY_NOEXCEPT + : Event(0, 0) + , m_id(~0U) +{ + +} + +BaseBlockData::BaseBlockData(timestamp_t, timestamp_t, block_id_t) EASY_NOEXCEPT + : Event(0, 0) + , m_id(~0U) +{ + +} + +Block::Block(Block&& that) EASY_NOEXCEPT + : BaseBlockData(0, ~0U) + , m_name("") + , m_status(::profiler::OFF) + , m_isScoped(that.m_isScoped) +{ +} + +Block::Block(timestamp_t, block_id_t, const char*) EASY_NOEXCEPT + : BaseBlockData(0, ~0U) + , m_name("") + , m_status(::profiler::OFF) + , m_isScoped(true) +{ + +} + +Block::Block(timestamp_t, timestamp_t, block_id_t, const char*) EASY_NOEXCEPT + : BaseBlockData(0, ~0U) + , m_name("") + , m_status(::profiler::OFF) + , m_isScoped(true) +{ + +} + +Block::Block(const BaseBlockDescriptor*, const char*, bool _scoped) EASY_NOEXCEPT + : BaseBlockData(0, ~0U) + , m_name("") + , m_status(::profiler::OFF) + , m_isScoped(_scoped) +{ + +} + +void Block::start() +{ +} + +void Block::start(timestamp_t) EASY_NOEXCEPT +{ +} + +void Block::finish() +{ +} + +void Block::finish(timestamp_t) EASY_NOEXCEPT +{ +} + +Block::~Block() +{ +} +#endif + +////////////////////////////////////////////////////////////////////////// + +CSwitchEvent::CSwitchEvent(timestamp_t _begin_time, thread_id_t _tid) EASY_NOEXCEPT + : Event(_begin_time) + , m_thread_id(_tid) +{ + +} + +CSwitchBlock::CSwitchBlock(timestamp_t _begin_time, thread_id_t _tid, const char* _runtimeName) EASY_NOEXCEPT + : CSwitchEvent(_begin_time, _tid) + , m_name(_runtimeName) +{ + +} + +////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/easyprofiler/easy_profiler_core/chunk_allocator.h b/3rdparty/easyprofiler/easy_profiler_core/chunk_allocator.h new file mode 100644 index 0000000..c189cf3 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/chunk_allocator.h @@ -0,0 +1,509 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_CHUNK_ALLOCATOR_H +#define EASY_PROFILER_CHUNK_ALLOCATOR_H + +#include +#include +#include +#include +#include "outstream.h" + +////////////////////////////////////////////////////////////////////////// + +#ifndef EASY_ENABLE_ALIGNMENT +# define EASY_ENABLE_ALIGNMENT 0 +#endif + +#ifndef EASY_ALIGNMENT_SIZE +# define EASY_ALIGNMENT_SIZE alignof(std::max_align_t) +#endif + +#if EASY_ENABLE_ALIGNMENT == 0 +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR +# define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE) +# define EASY_FREE(MEMPTR) free(MEMPTR) +#else +// MSVC and GNUC aligned versions of malloc are defined in malloc.h +# include +# if defined(_MSC_VER) +# define EASY_ALIGNED(TYPE, VAR, A) __declspec(align(A)) TYPE VAR +# define EASY_MALLOC(MEMSIZE, A) _aligned_malloc(MEMSIZE, A) +# define EASY_FREE(MEMPTR) _aligned_free(MEMPTR) +# elif defined(__GNUC__) +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR __attribute__((aligned(A))) +# define EASY_MALLOC(MEMSIZE, A) memalign(A, MEMSIZE) +# define EASY_FREE(MEMPTR) free(MEMPTR) +# else +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR +# define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE) +# define EASY_FREE(MEMPTR) free(MEMPTR) +# endif +#endif + +////////////////////////////////////////////////////////////////////////// + +//! Checks if a pointer is aligned. +//! \param ptr The pointer to check. +//! \param alignment The alignement (must be a power of 2) +//! \returns true if the memory is aligned. +//! +template +EASY_FORCE_INLINE bool is_aligned(void* ptr) +{ + static_assert(ALIGNMENT % 2 == 0, "Alignment must be a power of two."); + return ((uintptr_t)ptr & (ALIGNMENT-1)) == 0; +} + +EASY_FORCE_INLINE void unaligned_zero16(void* ptr) +{ +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(uint16_t*)ptr = 0; +#else + ((char*)ptr)[0] = 0; + ((char*)ptr)[1] = 0; +#endif +} + +EASY_FORCE_INLINE void unaligned_zero32(void* ptr) +{ +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(uint32_t*)ptr = 0; +#else + ((char*)ptr)[0] = 0; + ((char*)ptr)[1] = 0; + ((char*)ptr)[2] = 0; + ((char*)ptr)[3] = 0; +#endif +} + +EASY_FORCE_INLINE void unaligned_zero64(void* ptr) +{ +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(uint64_t*)ptr = 0; +#else + // Assume unaligned is more common. + if (!is_aligned(ptr)) { + ((char*)ptr)[0] = 0; + ((char*)ptr)[1] = 0; + ((char*)ptr)[2] = 0; + ((char*)ptr)[3] = 0; + ((char*)ptr)[4] = 0; + ((char*)ptr)[5] = 0; + ((char*)ptr)[6] = 0; + ((char*)ptr)[7] = 0; + } + else { + *(uint64_t*)ptr = 0; + } +#endif +} + +template +EASY_FORCE_INLINE void unaligned_store16(void* ptr, T val) +{ + static_assert(sizeof(T) == 2, "16 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(T*)ptr = val; +#else + const char* const temp = (char*)&val; + ((char*)ptr)[0] = temp[0]; + ((char*)ptr)[1] = temp[1]; +#endif +} + +template +EASY_FORCE_INLINE void unaligned_store32(void* ptr, T val) +{ + static_assert(sizeof(T) == 4, "32 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(T*)ptr = val; +#else + const char* const temp = (char*)&val; + ((char*)ptr)[0] = temp[0]; + ((char*)ptr)[1] = temp[1]; + ((char*)ptr)[2] = temp[2]; + ((char*)ptr)[3] = temp[3]; +#endif +} + +template +EASY_FORCE_INLINE void unaligned_store64(void* ptr, T val) +{ + static_assert(sizeof(T) == 8, "64 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(T*)ptr = val; +#else + const char* const temp = (char*)&val; + // Assume unaligned is more common. + if (!is_aligned(ptr)) { + ((char*)ptr)[0] = temp[0]; + ((char*)ptr)[1] = temp[1]; + ((char*)ptr)[2] = temp[2]; + ((char*)ptr)[3] = temp[3]; + ((char*)ptr)[4] = temp[4]; + ((char*)ptr)[5] = temp[5]; + ((char*)ptr)[6] = temp[6]; + ((char*)ptr)[7] = temp[7]; + } + else { + *(T*)ptr = val; + } +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load16(const void* ptr) +{ + static_assert(sizeof(T) == 2, "16 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + return *(T*)ptr; +#else + T value; + ((char*)&value)[0] = ((char*)ptr)[0]; + ((char*)&value)[1] = ((char*)ptr)[1]; + return value; +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load16(const void* ptr, T* val) +{ + static_assert(sizeof(T) == 2, "16 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *val = *(T*)ptr; + return *val; +#else + ((char*)val)[0] = ((char*)ptr)[0]; + ((char*)val)[1] = ((char*)ptr)[1]; + return *val; +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load32(const void* ptr) +{ + static_assert(sizeof(T) == 4, "32 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + return *(T*)ptr; +#else + T value; + ((char*)&value)[0] = ((char*)ptr)[0]; + ((char*)&value)[1] = ((char*)ptr)[1]; + ((char*)&value)[2] = ((char*)ptr)[2]; + ((char*)&value)[3] = ((char*)ptr)[3]; + return value; +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load32(const void* ptr, T* val) +{ + static_assert(sizeof(T) == 4, "32 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *val = *(T*)ptr; +#else + ((char*)&val)[0] = ((char*)ptr)[0]; + ((char*)&val)[1] = ((char*)ptr)[1]; + ((char*)&val)[2] = ((char*)ptr)[2]; + ((char*)&val)[3] = ((char*)ptr)[3]; + return *val; +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load64(const void* ptr) +{ + static_assert(sizeof(T) == 8, "64 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + return *(T*)ptr; +#else + if (!is_aligned(ptr)) { + T value; + ((char*)&value)[0] = ((char*)ptr)[0]; + ((char*)&value)[1] = ((char*)ptr)[1]; + ((char*)&value)[2] = ((char*)ptr)[2]; + ((char*)&value)[3] = ((char*)ptr)[3]; + ((char*)&value)[4] = ((char*)ptr)[4]; + ((char*)&value)[5] = ((char*)ptr)[5]; + ((char*)&value)[6] = ((char*)ptr)[6]; + ((char*)&value)[7] = ((char*)ptr)[7]; + return value; + } + else { + return *(T*)ptr; + } +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val) +{ + static_assert(sizeof(T) == 8, "64 bit type required."); +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *val = *(T*)ptr; +#else + if (!is_aligned(ptr)) { + ((char*)&val)[0] = ((char*)ptr)[0]; + ((char*)&val)[1] = ((char*)ptr)[1]; + ((char*)&val)[2] = ((char*)ptr)[2]; + ((char*)&val)[3] = ((char*)ptr)[3]; + ((char*)&val)[4] = ((char*)ptr)[4]; + ((char*)&val)[5] = ((char*)ptr)[5]; + ((char*)&val)[6] = ((char*)ptr)[6]; + ((char*)&val)[7] = ((char*)ptr)[7]; + return *val; + } + else { + *val = *(T*)ptr; + return *val; + } +#endif +} + +////////////////////////////////////////////////////////////////////////// + +template +class chunk_allocator +{ + struct chunk { EASY_ALIGNED(char, data[N], EASY_ALIGNMENT_SIZE); chunk* prev = nullptr; }; + + struct chunk_list + { + chunk* last; + + chunk_list() : last(nullptr) + { + static_assert(sizeof(char) == 1, "easy_profiler logic error: sizeof(char) != 1 for this platform! Please, contact easy_profiler authors to resolve your problem."); + emplace_back(); + } + + ~chunk_list() + { + do free_last(); while (last != nullptr); + } + + void clear_all_except_last() + { + while (last->prev != nullptr) + free_last(); + zero_last_chunk_size(); + } + + void emplace_back() + { + auto prev = last; + last = ::new (EASY_MALLOC(sizeof(chunk), EASY_ALIGNMENT_SIZE)) chunk(); + last->prev = prev; + zero_last_chunk_size(); + } + + /** Invert current chunks list to enable to iterate over chunks list in direct order. + + This method is used by serialize(). + */ + void invert() + { + chunk* next = nullptr; + + while (last->prev != nullptr) { + auto p = last->prev; + last->prev = next; + next = last; + last = p; + } + + last->prev = next; + } + + private: + + chunk_list(const chunk_list&) = delete; + chunk_list(chunk_list&&) = delete; + + void free_last() + { + auto p = last; + last = last->prev; + EASY_FREE(p); + } + + void zero_last_chunk_size() + { + // Although there is no need for unaligned access stuff b/c a new chunk will + // usually be at least 8 byte aligned (and we only need 2 byte alignment), + // this is the only way I have been able to get rid of the GCC strict-aliasing warning + // without using std::memset. It's an extra line, but is just as fast as *(uint16_t*)last->data = 0; + char* const data = last->data; + *(uint16_t*)data = (uint16_t)0; + } + }; + + // Used in serialize(): workaround for no constexpr support in MSVC 2013. + static const int_fast32_t MAX_CHUNK_OFFSET = N - sizeof(uint16_t); + static const uint16_t N_MINUS_ONE = N - 1; + + chunk_list m_chunks; ///< List of chunks. + uint32_t m_size; ///< Number of elements stored(# of times allocate() has been called.) + uint16_t m_chunkOffset; ///< Number of bytes used in the current chunk. + +public: + + chunk_allocator() : m_size(0), m_chunkOffset(0) + { + } + + /** Allocate n bytes. + + Automatically checks if there is enough preserved memory to store additional n bytes + and allocates additional buffer if needed. + */ + void* allocate(uint16_t n) + { + ++m_size; + + if (!need_expand(n)) + { + // Temp to avoid extra load due to this* aliasing. + uint16_t chunkOffset = m_chunkOffset; + char* data = m_chunks.last->data + chunkOffset; + chunkOffset += n + sizeof(uint16_t); + m_chunkOffset = chunkOffset; + + unaligned_store16(data, n); + data += sizeof(uint16_t); + + // If there is enough space for at least another payload size, + // set it to zero. + if (chunkOffset < N_MINUS_ONE) + unaligned_zero16(data + n); + + return data; + } + + m_chunkOffset = n + sizeof(uint16_t); + m_chunks.emplace_back(); + + char* data = m_chunks.last->data; + unaligned_store16(data, n); + data += sizeof(uint16_t); + + // We assume here that it takes more than one element to fill a chunk. + unaligned_zero16(data + n); + + return data; + } + + /** Check if current storage is not enough to store additional n bytes. + */ + bool need_expand(uint16_t n) const + { + return (m_chunkOffset + n + sizeof(uint16_t)) > N; + } + + uint32_t size() const + { + return m_size; + } + + bool empty() const + { + return m_size == 0; + } + + void clear() + { + m_size = 0; + m_chunkOffset = 0; + m_chunks.clear_all_except_last(); // There is always at least one chunk + } + + /** Serialize data to stream. + + \warning Data will be cleared after serialization. + */ + void serialize(profiler::OStream& _outputStream) + { + // Chunks are stored in reversed order (stack). + // To be able to iterate them in direct order we have to invert the chunks list. + m_chunks.invert(); + + // Each chunk is an array of N bytes that can hold between + // 1(if the list isn't empty) and however many elements can fit in a chunk, + // where an element consists of a payload size + a payload as follows: + // elementStart[0..1]: size as a uint16_t + // elementStart[2..size-1]: payload. + + // The maximum chunk offset is N-sizeof(uint16_t) b/c, if we hit that (or go past), + // there is either no space left, 1 byte left, or 2 bytes left, all of which are + // too small to cary more than a zero-sized element. + + chunk* current = m_chunks.last; + do { + const char* data = current->data; + int_fast32_t chunkOffset = 0; // signed int so overflow is not checked. + uint16_t payloadSize = unaligned_load16(data); + while (chunkOffset < MAX_CHUNK_OFFSET && payloadSize != 0) { + const uint16_t chunkSize = sizeof(uint16_t) + payloadSize; + _outputStream.write(data, chunkSize); + data += chunkSize; + chunkOffset += chunkSize; + unaligned_load16(data, &payloadSize); + } + + current = current->prev; + } while (current != nullptr); + + clear(); + } + +private: + + chunk_allocator(const chunk_allocator&) = delete; + chunk_allocator(chunk_allocator&&) = delete; + +}; // END of class chunk_allocator. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_CHUNK_ALLOCATOR_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/cmake/config.cmake.in b/3rdparty/easyprofiler/easy_profiler_core/cmake/config.cmake.in new file mode 100644 index 0000000..9b4c9ee --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/cmake/config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/3rdparty/easyprofiler/easy_profiler_core/cmake_install.cmake b/3rdparty/easyprofiler/easy_profiler_core/cmake_install.cmake new file mode 100644 index 0000000..89b2d40 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/cmake_install.cmake @@ -0,0 +1,104 @@ +# Install script for directory: /home/alex/Work/C++Projects/easyprofiler/easy_profiler_core + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Release") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified") + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/easy_profiler" TYPE FILE FILES + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/generated/easy_profilerConfig.cmake" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/generated/easy_profilerConfigVersion.cmake" + ) +endif() + +if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified") + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/include/easy" TYPE FILE FILES + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/arbitrary_value.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/easy_net.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/easy_socket.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/profiler.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/reader.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/serialized_block.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_aux.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/details/easy_compiler_support.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/details/profiler_aux.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/details/profiler_colors.h" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include/easy/details/profiler_public_types.h" + ) +endif() + +if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified") + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/." TYPE FILE FILES + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/LICENSE.MIT" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/LICENSE.APACHE" + ) +endif() + +if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified") + if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/libeasy_profiler.so" AND + NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/libeasy_profiler.so") + file(RPATH_CHECK + FILE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/libeasy_profiler.so" + RPATH "$ORIGIN") + endif() + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" TYPE SHARED_LIBRARY FILES "/home/alex/Work/C++Projects/easyprofiler/bin/libeasy_profiler.so") + if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/libeasy_profiler.so" AND + NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/libeasy_profiler.so") + file(RPATH_CHANGE + FILE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/libeasy_profiler.so" + OLD_RPATH ":::::::" + NEW_RPATH "$ORIGIN") + if(CMAKE_INSTALL_DO_STRIP) + execute_process(COMMAND "/usr/bin/strip" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/libeasy_profiler.so") + endif() + endif() +endif() + +if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified") + if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/lib/cmake/easy_profiler/easy_profilerTargets.cmake") + file(DIFFERENT EXPORT_FILE_CHANGED FILES + "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/lib/cmake/easy_profiler/easy_profilerTargets.cmake" + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets.cmake") + if(EXPORT_FILE_CHANGED) + file(GLOB OLD_CONFIG_FILES "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/lib/cmake/easy_profiler/easy_profilerTargets-*.cmake") + if(OLD_CONFIG_FILES) + message(STATUS "Old export file \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/lib/cmake/easy_profiler/easy_profilerTargets.cmake\" will be replaced. Removing files [${OLD_CONFIG_FILES}].") + file(REMOVE ${OLD_CONFIG_FILES}) + endif() + endif() + endif() + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/easy_profiler" TYPE FILE FILES "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets.cmake") + if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/easy_profiler" TYPE FILE FILES "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/Export/lib/cmake/easy_profiler/easy_profilerTargets-release.cmake") + endif() +endif() + diff --git a/3rdparty/easyprofiler/easy_profiler_core/current_thread.h b/3rdparty/easyprofiler/easy_profiler_core/current_thread.h new file mode 100644 index 0000000..816979c --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/current_thread.h @@ -0,0 +1,79 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_CURRENT_THREAD_H +#define EASY_PROFILER_CURRENT_THREAD_H + +#include + +#ifdef _WIN32 +# include +#elif defined(__APPLE__) +# include +# include +#else +# include +# include +# include +#endif + +inline profiler::thread_id_t getCurrentThreadId() +{ +#ifdef _WIN32 + return (profiler::thread_id_t)::GetCurrentThreadId(); +#elif defined(__APPLE__) +# if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6) || \ + (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0) + EASY_THREAD_LOCAL static uint64_t _id = 0; + if (!_id) + pthread_threadid_np(NULL, &_id); + return (profiler::thread_id_t)_id; +# else + return (profiler::thread_id_t)pthread_self(); +# endif +#else + EASY_THREAD_LOCAL static const profiler::thread_id_t _id = (profiler::thread_id_t)syscall(__NR_gettid); + return _id; +#endif +} + +#endif // EASY_PROFILER_CURRENT_THREAD_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/current_time.h b/3rdparty/easyprofiler/easy_profiler_core/current_time.h new file mode 100644 index 0000000..b08224e --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/current_time.h @@ -0,0 +1,174 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_CURRENT_TIME_H +#define EASY_PROFILER_CURRENT_TIME_H + +#include + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +// std::chrono for MSVC2013 is broken - it has very low resolution of 16ms +// restrict usage of std::chrono +# if EASY_CHRONO_HIGHRES_CLOCK +# undef EASY_CHRONO_HIGHRES_CLOCK +# endif +# if EASY_CHRONO_STEADY_CLOCK +# undef EASY_CHRONO_STEADY_CLOCK +# endif +#endif + +#if EASY_CHRONO_HIGHRES_CLOCK +# include +# define EASY_CHRONO_CLOCK std::chrono::high_resolution_clock +#elif EASY_CHRONO_STEADY_CLOCK +# include +# define EASY_CHRONO_CLOCK std::chrono::steady_clock +#elif defined(_WIN32) +# include +#else +# include +# include +# ifdef __ARM_ARCH +# include +# endif//__ARM_ARCH +#endif + +static inline profiler::timestamp_t getCurrentTime() +{ +#if EASY_CHRONO_HIGHRES_CLOCK || EASY_CHRONO_STEADY_CLOCK + return (profiler::timestamp_t)EASY_CHRONO_CLOCK::now().time_since_epoch().count(); +#elif defined(_WIN32) + //see https://msdn.microsoft.com/library/windows/desktop/dn553408(v=vs.85).aspx + LARGE_INTEGER elapsedMicroseconds; + if (!QueryPerformanceCounter(&elapsedMicroseconds)) + return 0; + return (profiler::timestamp_t)elapsedMicroseconds.QuadPart; +#else// not _WIN32 + +#if (defined(__GNUC__) || defined(__ICC)) + + // part of code from google/benchmark library (Licensed under the Apache License, Version 2.0) + // see https://github.com/google/benchmark/blob/master/src/cycleclock.h#L111 + #if defined(__i386__) + int64_t ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + return ret; + #elif defined(__x86_64__) || defined(__amd64__) + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return (high << 32) | low; + #elif defined(__powerpc__) || defined(__ppc__) + // This returns a time-base, which is not always precisely a cycle-count. + int64_t tbl, tbu0, tbu1; + asm("mftbu %0" : "=r"(tbu0)); + asm("mftb %0" : "=r"(tbl)); + asm("mftbu %0" : "=r"(tbu1)); + tbl &= -static_cast(tbu0 == tbu1); + // high 32 bits in tbu1; low 32 bits in tbl (tbu0 is garbage) + return (tbu1 << 32) | tbl; + #elif defined(__sparc__) + int64_t tick; + asm(".byte 0x83, 0x41, 0x00, 0x00"); + asm("mov %%g1, %0" : "=r"(tick)); + return tick; + #elif defined(__ia64__) + int64_t itc; + asm("mov %0 = ar.itc" : "=r"(itc)); + return itc; + #elif defined(COMPILER_MSVC) && defined(_M_IX86) + // Older MSVC compilers (like 7.x) don't seem to support the + // __rdtsc intrinsic properly, so I prefer to use _asm instead + // when I know it will work. Otherwise, I'll use __rdtsc and hope + // the code is being compiled with a non-ancient compiler. + _asm rdtsc + #elif defined(COMPILER_MSVC) + return __rdtsc(); + #elif defined(__aarch64__) + // System timer of ARMv8 runs at a different frequency than the CPU's. + // The frequency is fixed, typically in the range 1-50MHz. It can be + // read at CNTFRQ special register. We assume the OS has set up + // the virtual timer properly. + int64_t virtual_timer_value; + asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value)); + return virtual_timer_value; + #elif defined(__ARM_ARCH) + #if (__ARM_ARCH >= 6) // V6 is the earliest arch that has a standard cyclecount + uint32_t pmccntr; + uint32_t pmuseren; + uint32_t pmcntenset; + // Read the user mode perf monitor counter access permissions. + asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren)); + if (pmuseren & 1) { // Allows reading perfmon counters for user mode code. + asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset)); + if (pmcntenset & 0x80000000ul) { // Is it counting? + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr)); + // The counter is set up to count every 64th cycle + return static_cast(pmccntr) * 64; // Should optimize to << 6 + } + } + #endif + struct timeval tv; + gettimeofday(&tv, nullptr); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + #elif defined(__mips__) + // mips apparently only allows rdtsc for superusers, so we fall + // back to gettimeofday. It's possible clock_gettime would be better. + struct timeval tv; + gettimeofday(&tv, nullptr); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + #else + #warning You need to define fast getCurrentTime() for your OS and CPU + return std::chrono::high_resolution_clock::now().time_since_epoch().count(); + #define EASY_CHRONO_CLOCK std::chrono::high_resolution_clock + #endif + +#else // not _WIN32, __GNUC__, __ICC + #warning You need to define fast getCurrentTime() for your OS and CPU + return std::chrono::high_resolution_clock::now().time_since_epoch().count(); + #define EASY_CHRONO_CLOCK std::chrono::high_resolution_clock +#endif + +#endif +} + + +#endif // EASY_PROFILER_CURRENT_TIME_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/easy_socket.cpp b/3rdparty/easyprofiler/easy_profiler_core/easy_socket.cpp new file mode 100644 index 0000000..05f6308 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/easy_socket.cpp @@ -0,0 +1,431 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of +* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License +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. + + +The Apache License, Version 2.0 (the "License"); +You may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +**/ + +#include +#include +#include +#include + +#if defined(_WIN32) +# pragma comment (lib, "Ws2_32.lib") +# pragma comment (lib, "Mswsock.lib") +# pragma comment (lib, "AdvApi32.lib") +# ifdef max +# undef max +# endif +#else +# include +# include +#endif + +///////////////////////////////////////////////////////////////// + +#if defined(_WIN32) +const int SOCK_ABORTED = WSAECONNABORTED; +const int SOCK_RESET = WSAECONNRESET; +const int SOCK_IN_PROGRESS = WSAEINPROGRESS; +#else +const int SOCK_ABORTED = ECONNABORTED; +const int SOCK_RESET = ECONNRESET; +const int SOCK_IN_PROGRESS = EINPROGRESS; +const int SOCK_BROKEN_PIPE = EPIPE; +const int SOCK_ENOENT = ENOENT; +#endif + +const int SEND_BUFFER_SIZE = 64 * 1024 * 1024; + +///////////////////////////////////////////////////////////////// + +static int closeSocket(EasySocket::socket_t s) +{ +#if defined(_WIN32) + return ::closesocket(s); +#else + return ::close(s); +#endif +} + +///////////////////////////////////////////////////////////////// + +bool EasySocket::checkSocket(socket_t s) const +{ + return s > 0; +} + +void EasySocket::setBlocking(EasySocket::socket_t s, bool blocking) +{ +#if defined(_WIN32) + u_long iMode = blocking ? 0 : 1;//0 - blocking, 1 - non blocking + ::ioctlsocket(s, FIONBIO, &iMode); +#else + int iMode = blocking ? 0 : 1;//0 - blocking, 1 - non blocking + ::ioctl(s, FIONBIO, (char*)&iMode); +#endif +} + +int EasySocket::bind(uint16_t port) +{ + if (!checkSocket(m_socket)) + return -1; + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + auto res = ::bind(m_socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + + return res; +} + +void EasySocket::flush() +{ + if (m_socket) + closeSocket(m_socket); + + if (m_replySocket != m_socket) + closeSocket(m_replySocket); + +#if defined(_WIN32) + m_socket = 0; + m_replySocket = 0; +#else + m_wsaret = 0; +#endif +} + +void EasySocket::checkResult(int result) +{ + if (result >= 0) + { + m_state = ConnectionState::Connected; + return; + } + + if (result == -1) // is this check necessary? + { +#if defined(_WIN32) + const int error_code = WSAGetLastError(); +#else + const int error_code = errno; +#endif + + switch (error_code) + { + case SOCK_ABORTED: + case SOCK_RESET: +#if !defined(_WIN32) + case SOCK_BROKEN_PIPE: + case SOCK_ENOENT: +#endif + m_state = ConnectionState::Disconnected; + break; + + case SOCK_IN_PROGRESS: + m_state = ConnectionState::Connecting; + break; + + default: + break; + } + } +} + +void EasySocket::init() +{ + if (m_wsaret != 0) + return; + +#if !defined(_WIN32) + const int protocol = 0; +#else + const int protocol = IPPROTO_TCP; +#endif + + m_socket = ::socket(AF_INET, SOCK_STREAM, protocol); + if (!checkSocket(m_socket)) + return; + + setBlocking(m_socket, true); + +#if !defined(_WIN32) + m_wsaret = 1; +#endif + + const int opt = 1; + ::setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(int)); +} + +EasySocket::ConnectionState EasySocket::state() const +{ + return m_state; +} + +bool EasySocket::isDisconnected() const +{ + return static_cast(m_state) <= 0; +} + +bool EasySocket::isConnected() const +{ + return m_state == ConnectionState::Connected; +} + +EasySocket::EasySocket() +{ +#if defined(_WIN32) + WSADATA wsaData; + m_wsaret = WSAStartup(0x101, &wsaData); +#else + m_wsaret = 0; +#endif + + init(); +} + +EasySocket::~EasySocket() +{ + flush(); + +#if defined(_WIN32) + if (m_wsaret == 0) + WSACleanup(); +#endif +} + +void EasySocket::setReceiveTimeout(int milliseconds) +{ + if (!isConnected() || !checkSocket(m_replySocket)) + return; + + if (milliseconds <= 0) + milliseconds = std::numeric_limits::max() / 1000; + +#if defined(_WIN32) + const DWORD timeout = static_cast(milliseconds); + ::setsockopt(m_replySocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD)); +#else + struct timeval tv; + if (milliseconds >= 1000) + { + const int s = milliseconds / 1000; + const int us = (milliseconds - s * 1000) * 1000; + tv.tv_sec = s; + tv.tv_usec = us; + } + else + { + tv.tv_sec = 0; + tv.tv_usec = milliseconds * 1000; + } + + ::setsockopt(m_replySocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof (struct timeval)); +#endif +} + +int EasySocket::send(const void* buffer, size_t nbytes) +{ + if (!checkSocket(m_replySocket)) + return -1; + +#if defined(_WIN32) || defined(__APPLE__) + const int res = ::send(m_replySocket, (const char*)buffer, (int)nbytes, 0); +#else + const int res = (int)::send(m_replySocket, buffer, nbytes, MSG_NOSIGNAL); +#endif + + checkResult(res); + + return res; +} + +int EasySocket::receive(void* buffer, size_t nbytes) +{ + if (!checkSocket(m_replySocket)) + return -1; + +#if defined(_WIN32) + const int res = ::recv(m_replySocket, (char*)buffer, (int)nbytes, 0); +#else + const int res = (int)::read(m_replySocket, buffer, nbytes); +#endif + + checkResult(res); + if (res == 0) + m_state = ConnectionState::Disconnected; + + return res; +} + +int EasySocket::listen(int count) +{ + if (!checkSocket(m_socket)) + return -1; + + const int res = ::listen(m_socket, count); + checkResult(res); + + return res; +} + +int EasySocket::accept() +{ + if (!checkSocket(m_socket)) + return -1; + + fd_set fdread; + FD_ZERO (&fdread); + FD_SET (m_socket, &fdread); + + fd_set fdwrite = fdread; + fd_set fdexcl = fdread; + + struct timeval tv { 0, 500 }; + const int rc = ::select((int)m_socket + 1, &fdread, &fdwrite, &fdexcl, &tv); + if (rc <= 0) + return -1; // there is no connection for accept + + m_replySocket = ::accept(m_socket, nullptr, nullptr); + checkResult((int)m_replySocket); + + if (checkSocket(m_replySocket)) + { + ::setsockopt(m_replySocket, SOL_SOCKET, SO_SNDBUF, (char*)&SEND_BUFFER_SIZE, sizeof(int)); + + //const int flag = 1; + //const int result = setsockopt(m_replySocket,IPPROTO_TCP,TCP_NODELAY,(char *)&flag,sizeof(int)); + +#if defined(__APPLE__) + // Apple doesn't have MSG_NOSIGNAL, work around it + const int value = 1; + ::setsockopt(m_replySocket, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)); +#endif + + //setBlocking(m_replySocket,true); + } + + return (int)m_replySocket; +} + +bool EasySocket::setAddress(const char* address, uint16_t port) +{ + m_server = ::gethostbyname(address); + if (m_server == nullptr) + return false; + + memset((char*)&m_serverAddress, 0, sizeof(m_serverAddress)); + m_serverAddress.sin_family = AF_INET; + memcpy((char*)&m_serverAddress.sin_addr.s_addr, (char*)m_server->h_addr, (size_t)m_server->h_length); + + m_serverAddress.sin_port = htons(port); + + return true; +} + +int EasySocket::connect() +{ + if (m_server == nullptr || m_socket <= 0) + return -1; + + int res = 0; + //TODO: more intelligence +#if !defined(_WIN32) + setBlocking(m_socket, false); + + const int sleepMs = 20; + const int waitSec = 1; + const int waitMs = waitSec * 1000 / sleepMs; + + for (int counter = 0; counter++ < waitMs;) + { + res = ::connect(m_socket, (struct sockaddr*)&m_serverAddress, sizeof(m_serverAddress)); + +#if defined(__APPLE__) + // on Apple, treat EISCONN error as success + if (res == -1 && errno == EISCONN) + { + res = 0; + break; + } +#endif + + checkResult(res); + if (res == 0) + { + break; + } + + if (m_state == ConnectionState::Connecting) + { + std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs)); + continue; + } + + if (isDisconnected()) + { + break; + } + } + + setBlocking(m_socket, true); +#else + res = ::connect(m_socket, (struct sockaddr*)&m_serverAddress, sizeof(m_serverAddress)); + checkResult(res); +#endif + + if (res == 0) + { + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + ::setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval)); + +#if defined(__APPLE__) + // Apple doesn't have MSG_NOSIGNAL, work around it + const int value = 1; + setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)); +#endif + + m_replySocket = m_socket; + } + + return res; +} diff --git a/3rdparty/easyprofiler/easy_profiler_core/event_trace_status.h b/3rdparty/easyprofiler/easy_profiler_core/event_trace_status.h new file mode 100644 index 0000000..12c4476 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/event_trace_status.h @@ -0,0 +1,27 @@ + + + +#ifndef EASY_PROFILER__EVENT_TRACE_STATUS__H_ +#define EASY_PROFILER__EVENT_TRACE_STATUS__H_ + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + enum EventTracingEnableStatus : unsigned char + { + EVENT_TRACING_LAUNCHED_SUCCESSFULLY = 0, + EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS, + EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE, + EVENT_TRACING_BAD_PROPERTIES_SIZE, + EVENT_TRACING_OPEN_TRACE_ERROR, + EVENT_TRACING_MISTERIOUS_ERROR, + }; + +} // END of namespace profiler. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__EVENT_TRACE_STATUS__H_ diff --git a/3rdparty/easyprofiler/easy_profiler_core/event_trace_win.cpp b/3rdparty/easyprofiler/easy_profiler_core/event_trace_win.cpp new file mode 100644 index 0000000..871a797 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/event_trace_win.cpp @@ -0,0 +1,550 @@ +/************************************************************************ +* file name : event_trace_win.cpp +* ----------------- : +* creation time : 2016/09/04 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of EasyEventTracer class used for tracing +* : Windows system events to get context switches. +* ----------------- : +* change log : * 2016/09/04 Victor Zarubkin: initial commit. +* : +* : * 2016/09/13 Victor Zarubkin: get process id and process name +* : of the owner of thread with id == CSwitch::NewThreadId. +* : +* : * 2016/09/17 Victor Zarubkin: added log messages printing. +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifdef _WIN32 +#include +#include +#include +#include +#include "profile_manager.h" +#include "current_time.h" + +#include "event_trace_win.h" +#include + +#ifdef __MINGW32__ +#include +#endif + +//#include + +#if EASY_OPTION_LOG_ENABLED != 0 +# include + +# ifndef EASY_ERRORLOG +# define EASY_ERRORLOG ::std::cerr +# endif + +# ifndef EASY_LOG +# define EASY_LOG ::std::cerr +# endif + +# ifndef EASY_ERROR +# define EASY_ERROR(LOG_MSG) EASY_ERRORLOG << "EasyProfiler ERROR: " << LOG_MSG +# endif + +# ifndef EASY_WARNING +# define EASY_WARNING(LOG_MSG) EASY_ERRORLOG << "EasyProfiler WARNING: " << LOG_MSG +# endif + +# ifndef EASY_LOGMSG +# define EASY_LOGMSG(LOG_MSG) EASY_LOG << "EasyProfiler INFO: " << LOG_MSG +# endif + +# ifndef EASY_LOG_ONLY +# define EASY_LOG_ONLY(CODE) CODE +# endif + +#else + +# ifndef EASY_ERROR +# define EASY_ERROR(LOG_MSG) +# endif + +# ifndef EASY_WARNING +# define EASY_WARNING(LOG_MSG) +# endif + +# ifndef EASY_LOGMSG +# define EASY_LOGMSG(LOG_MSG) +# endif + +# ifndef EASY_LOG_ONLY +# define EASY_LOG_ONLY(CODE) +# endif + +#endif + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +//extern ProfileManager& MANAGER; +#define MANAGER ProfileManager::instance() + +extern const ::profiler::color_t EASY_COLOR_INTERNAL_EVENT; + +#ifdef __MINGW32__ +::std::atomic TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL); +char KERNEL_LOGGER[] = KERNEL_LOGGER_NAME; +#else +::std::atomic_uint64_t TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL); +#endif + +namespace profiler { + + const decltype(EVENT_DESCRIPTOR::Opcode) SWITCH_CONTEXT_OPCODE = 36; + const int RAW_TIMESTAMP_TIME_TYPE = 1; + + ////////////////////////////////////////////////////////////////////////// + + struct ProcessInfo { + std::string name; + processid_t id = 0; + int8_t valid = 0; + }; + + ////////////////////////////////////////////////////////////////////////// + + // CSwitch class + // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx + // EventType = 36 + struct CSwitch + { + uint32_t NewThreadId; + uint32_t OldThreadId; + int8_t NewThreadPriority; + int8_t OldThreadPriority; + uint8_t PreviousCState; + int8_t SpareByte; + int8_t OldThreadWaitReason; + int8_t OldThreadWaitMode; + int8_t OldThreadState; + int8_t OldThreadWaitIdealProcessor; + uint32_t NewThreadWaitTime; + uint32_t Reserved; + }; + + ////////////////////////////////////////////////////////////////////////// + + struct do_not_calc_hash { + template inline size_t operator()(T _value) const { + return static_cast(_value); + } + }; + + typedef ::std::unordered_map thread_process_info_map; + typedef ::std::unordered_map process_info_map; + + // Using static is safe because processTraceEvent() is called from one thread + process_info_map PROCESS_INFO_TABLE; + thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })(); + + ////////////////////////////////////////////////////////////////////////// + + void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent) + { + if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE) + return; + + if (sizeof(CSwitch) != _traceEvent->UserDataLength) + return; + + EASY_FUNCTION(EASY_COLOR_INTERNAL_EVENT, ::profiler::OFF); + + auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData); + const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); + if (time > TRACING_END_TIME.load(::std::memory_order_acquire)) + return; + + DWORD pid = 0; + const char* process_name = ""; + + // Trying to get target process name and id + auto it = THREAD_PROCESS_INFO_TABLE.find(_contextSwitchEvent->NewThreadId); + if (it == THREAD_PROCESS_INFO_TABLE.end()) + { + auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId); + if (hThread != nullptr) + { + pid = GetProcessIdOfThread(hThread); + auto pinfo = &PROCESS_INFO_TABLE[pid]; + + if (pinfo->valid == 0) + { + if (pinfo->name.empty()) + { + static char numbuf[128] = {}; + sprintf(numbuf, "%u", pid); + pinfo->name = numbuf; + pinfo->id = pid; + } + + /* + According to documentation, using GetModuleBaseName() requires + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ access rights. + But it works fine with PROCESS_QUERY_LIMITED_INFORMATION instead of PROCESS_QUERY_INFORMATION. + + See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx + */ + + //auto hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + //if (hProc == nullptr) + auto hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (hProc != nullptr) + { + static TCHAR buf[MAX_PATH] = {}; // Using static is safe because processTraceEvent() is called from one thread + auto len = GetModuleBaseName(hProc, 0, buf, MAX_PATH); + + if (len != 0) + { + pinfo->name.reserve(pinfo->name.size() + 2 + len); + pinfo->name.append(" ", 1); + pinfo->name.append(buf, len); + pinfo->valid = 1; + } + + CloseHandle(hProc); + } + else + { + //auto err = GetLastError(); + //printf("OpenProcess(%u) fail: GetLastError() == %u\n", pid, err); + pinfo->valid = -1; + + if (pid == 4) { + pinfo->name.reserve(pinfo->name.size() + 8); + pinfo->name.append(" System", 7); + } + } + } + + process_name = pinfo->name.c_str(); + THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = pinfo; + + CloseHandle(hThread); + } + else + { + //printf("Can not OpenThread(%u);\n", _contextSwitchEvent->NewThreadId); + THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr; + } + } + else + { + auto pinfo = it->second; + if (pinfo != nullptr) + process_name = pinfo->name.c_str(); + else if (it->first == 0) + process_name = "System Idle"; + else if (it->first == 4) + process_name = "System"; + } + + MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, process_name); + MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, pid, time); + } + + ////////////////////////////////////////////////////////////////////////// + +#ifndef EASY_MAGIC_STATIC_AVAILABLE + class EasyEventTracerInstance { + friend EasyEventTracer; + EasyEventTracer instance; + } EASY_EVENT_TRACER; +#endif + + EasyEventTracer& EasyEventTracer::instance() + { +#ifndef EASY_MAGIC_STATIC_AVAILABLE + return EASY_EVENT_TRACER.instance; +#else + static EasyEventTracer tracer; + return tracer; +#endif + } + + EasyEventTracer::EasyEventTracer() + { + m_lowPriority = ATOMIC_VAR_INIT(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING); + } + + EasyEventTracer::~EasyEventTracer() + { + disable(); + } + + bool EasyEventTracer::isLowPriority() const + { + return m_lowPriority.load(::std::memory_order_acquire); + } + + void EasyEventTracer::setLowPriority(bool _value) + { + m_lowPriority.store(_value, ::std::memory_order_release); + } + + bool setPrivilege(HANDLE hToken, LPCSTR _privelegeName) + { + bool success = false; + + if (hToken) + { + LUID privilegyId; + if (LookupPrivilegeValue(NULL, _privelegeName, &privilegyId)) + { + TOKEN_PRIVILEGES tokenPrivilege; + tokenPrivilege.PrivilegeCount = 1; + tokenPrivilege.Privileges[0].Luid = privilegyId; + tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + success = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL) != FALSE; + } + } + + EASY_LOG_ONLY( + if (!success) + EASY_WARNING("Failed to set " << _privelegeName << " privelege for the application.\n"); + ) + + return success; + } + + void EasyEventTracer::setProcessPrivileges() + { + static bool alreadySet = false; + if (alreadySet) + return; + + alreadySet = true; + + HANDLE hToken = nullptr; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + { +#if EASY_OPTION_LOG_ENABLED != 0 + const bool success = setPrivilege(hToken, SE_DEBUG_NAME); + if (!success) + EASY_WARNING("Some context switch events could not get process name.\n"); +#else + setPrivilege(hToken, SE_DEBUG_NAME); +#endif + + CloseHandle(hToken); + } + EASY_LOG_ONLY( + else { + EASY_WARNING("Failed to open process to adjust priveleges.\n"); + } + ) + } + + ::profiler::EventTracingEnableStatus EasyEventTracer::startTrace(bool _force, int _step) + { + auto startTraceResult = StartTrace(&m_sessionHandle, KERNEL_LOGGER_NAME, props()); + switch (startTraceResult) + { + case ERROR_SUCCESS: + return EVENT_TRACING_LAUNCHED_SUCCESSFULLY; + + case ERROR_ALREADY_EXISTS: + { + if (_force) + { + // Try to stop another event tracing session to force launch self session. + + if (_step == 0) + { + /* + According to https://msdn.microsoft.com/en-us/library/windows/desktop/aa363696(v=vs.85).aspx + SessionHandle is ignored (and could be NULL) if SessionName is not NULL, + and you only need to set the Wnode.BufferSize, Wnode.Guid, LoggerNameOffset, and LogFileNameOffset + in EVENT_TRACE_PROPERTIES structure if ControlCode is EVENT_TRACE_CONTROL_STOP. + All data is already set for m_properties to the moment. Simply copy m_properties and use the copy. + + This method supposed to be faster than launching console window and executing shell command, + but if that would not work, return to using shell command "logman stop". + */ + + // static is safe because we are guarded by spin-lock m_spin + static Properties p = ([]{ Properties prp; strncpy(prp.sessionName, KERNEL_LOGGER_NAME, sizeof(prp.sessionName)); return prp; })(); + p.base = m_properties.base; // Use copy of m_properties to make sure m_properties will not be changed + + // Stop another session + ControlTrace((TRACEHANDLE)NULL, KERNEL_LOGGER_NAME, reinterpret_cast(&p), EVENT_TRACE_CONTROL_STOP); + + // Console window variant: + //if (32 >= (int)ShellExecute(NULL, NULL, "logman", "stop \"" KERNEL_LOGGER_NAME "\" -ets", NULL, SW_HIDE)) + // return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE; + } + + if (_step < 4) + { + // Command executed successfully. Wait for a few time until tracing session finish. + ::std::this_thread::sleep_for(::std::chrono::milliseconds(500)); + return startTrace(true, ++_step); + } + } + + EASY_ERROR("Event tracing not launched: ERROR_ALREADY_EXISTS. To stop another session execute cmd: logman stop \"" << KERNEL_LOGGER_NAME << "\" -ets\n"); + return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE; + } + + case ERROR_ACCESS_DENIED: + EASY_ERROR("Event tracing not launched: ERROR_ACCESS_DENIED. Try to launch your application as Administrator.\n"); + return EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS; + + case ERROR_BAD_LENGTH: + EASY_ERROR("Event tracing not launched: ERROR_BAD_LENGTH. It seems that your KERNEL_LOGGER_NAME differs from \"" << m_properties.sessionName << "\". Try to re-compile easy_profiler or contact EasyProfiler developers.\n"); + return EVENT_TRACING_BAD_PROPERTIES_SIZE; + } + + EASY_ERROR("Event tracing not launched: StartTrace() returned " << startTraceResult << ::std::endl); + + return EVENT_TRACING_MISTERIOUS_ERROR; + } + + ::profiler::EventTracingEnableStatus EasyEventTracer::enable(bool _force) + { + ::profiler::guard_lock<::profiler::spin_lock> lock(m_spin); + if (m_bEnabled) + return EVENT_TRACING_LAUNCHED_SUCCESSFULLY; + + /* + Trying to set debug privilege for current process + to be able to get other process information (process name). + */ + EasyEventTracer::setProcessPrivileges(); + + // Clear properties + memset(&m_properties, 0, sizeof(m_properties)); + m_properties.base.Wnode.BufferSize = sizeof(m_properties); + m_properties.base.Wnode.Flags = WNODE_FLAG_TRACED_GUID; + m_properties.base.Wnode.ClientContext = RAW_TIMESTAMP_TIME_TYPE; + m_properties.base.Wnode.Guid = SystemTraceControlGuid; + m_properties.base.LoggerNameOffset = sizeof(m_properties.base); + m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH; + m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + + // Start event tracing + auto res = startTrace(_force); + if (res != EVENT_TRACING_LAUNCHED_SUCCESSFULLY) + return res; + + memset(&m_trace, 0, sizeof(m_trace)); +#ifdef __MINGW32__ + m_trace.LoggerName = KERNEL_LOGGER; +#else + m_trace.LoggerName = KERNEL_LOGGER_NAME; +#endif + m_trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP; + m_trace.EventRecordCallback = ::profiler::processTraceEvent; + + m_openedHandle = OpenTrace(&m_trace); + if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE) + { + EASY_ERROR("Event tracing not launched: OpenTrace() returned invalid handle.\n"); + return EVENT_TRACING_OPEN_TRACE_ERROR; + } + + /* + Have to launch a thread to process events because according to MSDN documentation: + + The ProcessTrace function blocks the thread until it delivers all events, the BufferCallback function returns FALSE, + or you call CloseTrace. If the consumer is consuming events in real time, the ProcessTrace function returns after + the controller stops the trace session. (Note that there may be a several-second delay before the function returns.) + + https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx + */ + m_processThread = ::std::thread([this](bool _lowPriority) + { + if (_lowPriority) // Set low priority for event tracing thread + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); + EASY_THREAD_SCOPE("EasyProfiler.ETW"); + ProcessTrace(&m_openedHandle, 1, 0, 0); + + }, m_lowPriority.load(::std::memory_order_acquire)); + + m_bEnabled = true; + + EASY_LOGMSG("Event tracing launched\n"); + return EVENT_TRACING_LAUNCHED_SUCCESSFULLY; + } + + void EasyEventTracer::disable() + { + ::profiler::guard_lock<::profiler::spin_lock> lock(m_spin); + if (!m_bEnabled) + return; + + EASY_LOGMSG("Event tracing is stopping...\n"); + + TRACING_END_TIME.store(getCurrentTime(), ::std::memory_order_release); + + ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP); + CloseTrace(m_openedHandle); + + // Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later. + if (m_processThread.joinable()) + m_processThread.join(); + + m_bEnabled = false; + + // processTraceEvent() is not called anymore. Clean static maps is safe. + PROCESS_INFO_TABLE.clear(); + THREAD_PROCESS_INFO_TABLE.clear(); + THREAD_PROCESS_INFO_TABLE[0U] = nullptr; + + TRACING_END_TIME.store(~0ULL, ::std::memory_order_release); + + EASY_LOGMSG("Event tracing stopped\n"); + } + +} // END of namespace profiler. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // _WIN32 diff --git a/3rdparty/easyprofiler/easy_profiler_core/event_trace_win.h b/3rdparty/easyprofiler/easy_profiler_core/event_trace_win.h new file mode 100644 index 0000000..cb3c01b --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/event_trace_win.h @@ -0,0 +1,129 @@ +/************************************************************************ +* file name : event_trace_win.h +* ----------------- : +* creation time : 2016/09/04 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of EasyEventTracer class used for tracing +* : Windows system events to get context switches. +* ----------------- : +* change log : * 2016/09/04 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER_EVENT_TRACE_WINDOWS_H +#define EASY_PROFILER_EVENT_TRACE_WINDOWS_H +#ifdef _WIN32 + +#define INITGUID // This is to enable using SystemTraceControlGuid in evntrace.h. +#include +#include +#include +#include +#include +#include +#include +#include "event_trace_status.h" +#include "spin_lock.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + class EasyEventTracer EASY_FINAL + { +#ifndef EASY_MAGIC_STATIC_AVAILABLE + friend class EasyEventTracerInstance; +#endif + +#pragma pack(push, 1) + struct Properties { + EVENT_TRACE_PROPERTIES base; + char sessionName[sizeof(KERNEL_LOGGER_NAME)]; + }; +#pragma pack(pop) + + ::std::thread m_processThread; + Properties m_properties; + EVENT_TRACE_LOGFILE m_trace; + ::profiler::spin_lock m_spin; + ::std::atomic_bool m_lowPriority; + TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE; + TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE; + bool m_bEnabled = false; + + public: + + static EasyEventTracer& instance(); + ~EasyEventTracer(); + + bool isLowPriority() const; + + ::profiler::EventTracingEnableStatus enable(bool _force = false); + void disable(); + void setLowPriority(bool _value); + static void setProcessPrivileges(); + + private: + + EasyEventTracer(); + + inline EVENT_TRACE_PROPERTIES* props() + { + return reinterpret_cast(&m_properties); + } + + ::profiler::EventTracingEnableStatus startTrace(bool _force, int _step = 0); + + }; // END of class EasyEventTracer. + +} // END of namespace profiler. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // _WIN32 +#endif // EASY_PROFILER_EVENT_TRACE_WINDOWS_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/generated/easy_profilerConfig.cmake b/3rdparty/easyprofiler/easy_profiler_core/generated/easy_profilerConfig.cmake new file mode 100644 index 0000000..75ba62d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/generated/easy_profilerConfig.cmake @@ -0,0 +1,28 @@ + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was config.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +include("${CMAKE_CURRENT_LIST_DIR}/easy_profilerTargets.cmake") +check_required_components("easy_profiler") diff --git a/3rdparty/easyprofiler/easy_profiler_core/generated/easy_profilerConfigVersion.cmake b/3rdparty/easyprofiler/easy_profiler_core/generated/easy_profilerConfigVersion.cmake new file mode 100644 index 0000000..4f2f941 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/generated/easy_profilerConfigVersion.cmake @@ -0,0 +1,46 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version, +# but only if the requested major version is the same as the current one. +# The variable CVF_VERSION must be set before calling configure_file(). + + +set(PACKAGE_VERSION "1.3.0") + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + + if("1.3.0" MATCHES "^([0-9]+)\\.") + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + else() + set(CVF_VERSION_MAJOR "1.3.0") + endif() + + if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() + + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/3rdparty/easyprofiler/easy_profiler_core/hashed_cstr.h b/3rdparty/easyprofiler/easy_profiler_core/hashed_cstr.h new file mode 100644 index 0000000..075f2b1 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/hashed_cstr.h @@ -0,0 +1,298 @@ +/************************************************************************ +* file name : hashed_str.h +* ----------------- : +* creation time : 2016/09/11 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains definition of C-strings with calculated hash-code. +* : These strings may be used as optimized keys for std::unordered_map. +* ----------------- : +* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from reader.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER__HASHED_CSTR__H_ +#define EASY_PROFILER__HASHED_CSTR__H_ + +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#if 0 == 1//defined(_MSC_VER)// && _MSC_VER >= 1800 +# define EASY_PROFILER_HASHED_CSTR_DEFINED + +namespace profiler { + + /** \brief Simple C-string pointer with length. + + It is used as base class for a key in std::unordered_map. + It is used to get better performance than std::string. + It simply stores a pointer and a length, there is no + any memory allocation and copy. + + \warning Make sure you know what you are doing. You have to be sure that + pointed C-string will exist until you finish using this cstring. + + \ingroup profiler + */ + class cstring + { + protected: + + const char* m_str; + size_t m_len; + + public: + + cstring(const char* _str) : m_str(_str), m_len(strlen(_str)) + { + } + + cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len) + { + } + + cstring(const cstring&) = default; + cstring& operator = (const cstring&) = default; + + inline bool operator == (const cstring& _other) const + { + return m_len == _other.m_len && !strncmp(m_str, _other.m_str, m_len); + } + + inline bool operator != (const cstring& _other) const + { + return !operator == (_other); + } + + inline bool operator < (const cstring& _other) const + { + if (m_len == _other.m_len) + { + return strncmp(m_str, _other.m_str, m_len) < 0; + } + + return m_len < _other.m_len; + } + + inline const char* c_str() const + { + return m_str; + } + + inline size_t size() const + { + return m_len; + } + + }; // END of class cstring. + + /** \brief cstring with precalculated hash. + + This is used to calculate hash for C-string and to cache it + to be used in the future without recurring hash calculatoin. + + \note This class is used as a key in std::unordered_map. + + \ingroup profiler + */ + class hashed_cstr : public cstring + { + typedef cstring Parent; + + size_t m_hash; + + public: + + hashed_cstr(const char* _str) : Parent(_str), m_hash(0) + { + m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len); + } + + hashed_cstr(const char* _str, size_t _hash_code) : Parent(_str), m_hash(_hash_code) + { + } + + hashed_cstr(const char* _str, size_t _len, size_t _hash_code) : Parent(_str, _len), m_hash(_hash_code) + { + } + + hashed_cstr(const hashed_cstr&) = default; + hashed_cstr& operator = (const hashed_cstr&) = default; + + inline bool operator == (const hashed_cstr& _other) const + { + return m_hash == _other.m_hash && Parent::operator == (_other); + } + + inline bool operator != (const hashed_cstr& _other) const + { + return !operator == (_other); + } + + inline size_t hcode() const + { + return m_hash; + } + + }; // END of class hashed_cstr. + +} // END of namespace profiler. + +namespace std { + + /** \brief Simply returns precalculated hash of a C-string. */ + template <> struct hash<::profiler::hashed_cstr> { + typedef ::profiler::hashed_cstr argument_type; + typedef size_t result_type; + inline size_t operator () (const ::profiler::hashed_cstr& _str) const { + return _str.hcode(); + } + }; + +} // END of namespace std. + +#else //////////////////////////////////////////////////////////////////// + +// TODO: Create hashed_cstr for Linux (need to use Linux version of std::_Hash_seq) + +#endif + +namespace profiler { + + class hashed_stdstring + { + ::std::string m_str; + size_t m_hash; + + public: + + hashed_stdstring(const char* _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(const ::std::string& _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(::std::string&& _str) : m_str(::std::forward<::std::string&&>(_str)), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(hashed_stdstring&& _other) : m_str(::std::move(_other.m_str)), m_hash(_other.m_hash) + { + } + + hashed_stdstring(const char* _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code) + { + } + + hashed_stdstring(const ::std::string& _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code) + { + } + + hashed_stdstring(::std::string&& _str, size_t _hash_code) : m_str(::std::forward<::std::string&&>(_str)), m_hash(_hash_code) + { + } + + hashed_stdstring(const hashed_stdstring&) = default; + hashed_stdstring& operator = (const hashed_stdstring&) = default; + + hashed_stdstring& operator = (hashed_stdstring&& _other) + { + m_str = ::std::move(_other.m_str); + m_hash = _other.m_hash; + return *this; + } + + inline bool operator == (const hashed_stdstring& _other) const + { + return m_hash == _other.m_hash && m_str == _other.m_str; + } + + inline bool operator != (const hashed_stdstring& _other) const + { + return !operator == (_other); + } + + inline size_t hcode() const + { + return m_hash; + } + + inline const char* c_str() const + { + return m_str.c_str(); + } + + inline size_t size() const + { + return m_str.size(); + } + + }; // END of class hashed_stdstring. + +} // END of namespace profiler. + +namespace std { + + /** \brief Simply returns precalculated hash of a std::string. */ + template <> struct hash<::profiler::hashed_stdstring> { + typedef ::profiler::hashed_stdstring argument_type; + typedef size_t result_type; + inline size_t operator () (const ::profiler::hashed_stdstring& _str) const { + return _str.hcode(); + } + }; + +} // END of namespace std. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__HASHED_CSTR__H_ diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/arbitrary_value.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/arbitrary_value.h new file mode 100644 index 0000000..9e1305b --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/arbitrary_value.h @@ -0,0 +1,308 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_ARBITRARY_VALUE_H +#define EASY_PROFILER_ARBITRARY_VALUE_H + +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#endif + +#ifdef USING_EASY_PROFILER + +/** Macro used to create a unique Value Identification Number. + +Use this if you want to change the same value from different places in your code. +Otherwise do not mind. + +\code +struct A { + int someCount; +}; + +void foo(const A& a) { + EASY_VALUE("foo count", a.someCount); // Value ID is automatically set to (uint64_t)&a.someCount + + // This notation is completely the same as EASY_VALUE("foo count", a.someCount, EASY_VIN(a.someCount)); +} + +void bar(const A& b) { + EASY_VALUE("bar count", b.someCount); // Same ID as for "foo count" if &b == &a (and different ID otherwise) +} + +void baz(const A& c) { + EASY_VALUE("baz count", c.someCount, EASY_VIN(EASY_FUNC_NAME)); // Different ID from "foo count" and "bar count" + EASY_VALUE("qux count", 100500, EASY_VIN(EASY_FUNC_NAME)); // Same ID as for "baz count" +} +\endcode + +\ingroup profiler +*/ +# define EASY_VIN(member) ::profiler::ValueId(member) + +/** Macro used to identify global value which would be recognized by it's name in GUI. + +\code +struct A { + int someCount; +}; + +struct B { + int someOtherCount; +}; + +void foo(const A& a) { + EASY_VALUE("Count", a.someCount, EASY_GLOBAL_VIN); +} + +void bar(const B& b) { + EASY_VALUE("Count", b.someOtherCount, EASY_GLOBAL_VIN); // Same ID as for foo::"Count" despite of &b != &a +} +\endcode + +\ingroup profiler +*/ +# define EASY_GLOBAL_VIN ::profiler::ValueId() + +/** Macro used to identify unique value. + +\code +struct A { + int someCount; +}; + +void foo(const A& a) { + // All these values would have different IDs despite of names, pointers and values are the same + EASY_VALUE("foo count", a.someCount, EASY_UNIQUE_VIN); + EASY_VALUE("foo count", a.someCount, EASY_UNIQUE_VIN); + EASY_VALUE("foo count", a.someCount, EASY_UNIQUE_VIN); +} +\endcode + +\ingroup profiler +*/ +# define EASY_UNIQUE_VIN ::profiler::ValueId(EASY_UNIQUE_DESC(__LINE__)) + +/** Macro used to store single arbitrary value. + +\note Also stores a time-stamp of it's occurrence like an Event. + +\note To store an array, please, use EASY_ARRAY macro. + +\note Currently arbitrary values support only compile-time names. + +\sa EASY_ARRAY, EASY_TEXT, EASY_STRING + +\ingroup profiler +*/ +# define EASY_VALUE(name, value, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Value, ::profiler::extract_color(__VA_ARGS__), false));\ + ::profiler::setValue(EASY_UNIQUE_DESC(__LINE__), value, ::profiler::extract_value_id(value, ## __VA_ARGS__)); + +/** Macro used to store an array of arbitrary values. + +\note Also stores a time-stamp of it's occurrence like an Event. + +\note Currently arbitrary values support only compile-time names. + +\sa EASY_VALUE, EASY_TEXT, EASY_STRING + +\ingroup profiler +*/ +# define EASY_ARRAY(name, value, size, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Value, ::profiler::extract_color(__VA_ARGS__), false));\ + ::profiler::setValue(EASY_UNIQUE_DESC(__LINE__), value, ::profiler::extract_value_id(value, ## __VA_ARGS__), size); + +/** Macro used to store custom text. + +Could be C-string or std::string. + +\note Also stores a time-stamp of it's occurrence like an Event. + +\note Currently arbitrary values support only compile-time names. + +\sa EASY_VALUE, EASY_ARRAY, EASY_STRING + +\ingroup profiler +*/ +# define EASY_TEXT(name, text, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Value, ::profiler::extract_color(__VA_ARGS__), false));\ + ::profiler::setText(EASY_UNIQUE_DESC(__LINE__), text, ::profiler::extract_value_id(text , ## __VA_ARGS__)); + +/** Macro used to store custom text of specified length. + +Same as EASY_TEXT, but with explicitly specified length. +Use this for C-strings of known length (compile-time or run-time). + +\note Recommendation (not a requirement): Take into account a zero-terminator '\0' symbol (e.g. strlen("BlaBla") + 1). + +\note Also stores a time-stamp of it's occurrence like an Event. + +\note Currently arbitrary values support only compile-time names. + +\sa EASY_VALUE, EASY_ARRAY, EASY_TEXT + +\ingroup profiler +*/ +# define EASY_STRING(name, text, size, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Value, ::profiler::extract_color(__VA_ARGS__), false));\ + ::profiler::setText(EASY_UNIQUE_DESC(__LINE__), text, ::profiler::extract_value_id(text, ## __VA_ARGS__), size); + +namespace profiler +{ + + EASY_CONSTEXPR uint16_t MaxArbitraryValuesArraySize = 65535; + + extern "C" PROFILER_API void storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, + size_t _size, bool _isArray, ValueId _vin); + + template + inline void setValue(const BaseBlockDescriptor* _desc, T _value, ValueId _vin) + { + static_assert(!::std::is_pointer::value, + "You should not pass pointers into EASY_VALUE. Pass a reference instead."); + + using Type = typename ::std::remove_reference::type>::type; + + static_assert(StdToDataType::data_type != DataType::TypesCount, + "You should use standard builtin scalar types as profiler::Value type!"); + + storeValue(_desc, StdToDataType::data_type, &_value, sizeof(Type), false, _vin); + } + + template + inline void setValue(const BaseBlockDescriptor* _desc, const T* _valueArray, ValueId _vin, uint16_t _arraySize) + { + static_assert(StdToDataType::data_type != DataType::TypesCount, + "You should use standard builtin scalar types as profiler::Value type!"); + + storeValue(_desc, StdToDataType::data_type, _valueArray, sizeof(T) * _arraySize, true, _vin); + } + + template + inline void setValue(const BaseBlockDescriptor* _desc, const T (&_value)[N], ValueId _vin) + { + static_assert(StdToDataType::data_type != DataType::TypesCount, + "You should use standard builtin scalar types as profiler::Value type!"); + + static_assert(N <= MaxArbitraryValuesArraySize, "Maximum arbitrary values array size is 65535."); + + storeValue(_desc, StdToDataType::data_type, _value, sizeof(_value), true, _vin); + } + + inline void setText(const BaseBlockDescriptor* _desc, const char* _text, ValueId _vin, uint16_t _textLength) + { + storeValue(_desc, DataType::String, _text, _textLength, true, _vin); + } + + inline void setText(const BaseBlockDescriptor* _desc, const char* _text, ValueId _vin) + { + storeValue(_desc, DataType::String, _text, strlen(_text) + 1, true, _vin); + } + + inline void setText(const BaseBlockDescriptor* _desc, const ::std::string& _text, ValueId _vin) + { + storeValue(_desc, DataType::String, _text.c_str(), _text.size() + 1, true, _vin); + } + + template + inline void setText(const BaseBlockDescriptor* _desc, const char (&_text)[N], ValueId _vin) + { + static_assert(N <= MaxArbitraryValuesArraySize, "Maximum arbitrary values array size is 65535."); + storeValue(_desc, DataType::String, &_text[0], N, true, _vin); + } + +} // end of namespace profiler. + +#else + +# define EASY_GLOBAL_VIN +# define EASY_UNIQUE_VIN +# define EASY_VIN(member) +# define EASY_VALUE(...) +# define EASY_ARRAY(...) +# define EASY_TEXT(...) +# define EASY_STRING(...) + +namespace profiler +{ + + inline void storeValue(const BaseBlockDescriptor*, DataType, const void*, size_t, bool, ValueId) {} + + template + inline void setValue(const BaseBlockDescriptor*, T, ValueId) {} + + template + inline void setValue(const BaseBlockDescriptor*, const T*, ValueId, uint16_t) {} + + template + inline void setValue(const BaseBlockDescriptor*, const T (&)[N], ValueId) {} + + inline void setText(const BaseBlockDescriptor*, const char*, ValueId, uint16_t) {} + + inline void setText(const BaseBlockDescriptor*, const char*, ValueId) {} + + inline void setText(const BaseBlockDescriptor*, const ::std::string&, ValueId) {} + + template + inline void setText(const BaseBlockDescriptor*, const char (&)[N], ValueId) {} + +} // end of namespace profiler. + +#endif // USING_EASY_PROFILER + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +#endif // EASY_PROFILER_ARBITRARY_VALUE_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_aux.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_aux.h new file mode 100644 index 0000000..2f8506e --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_aux.h @@ -0,0 +1,130 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_ARBITRARY_VALUE_AUX_H +#define EASY_PROFILER_ARBITRARY_VALUE_AUX_H + +#include +#include + +namespace profiler +{ + + using vin_t = uint64_t; + + class ValueId EASY_FINAL + { + friend ::ThreadStorage; + vin_t m_id; + + public: + +#if defined(_MSC_VER) && _MSC_VER <= 1800 + inline EASY_CONSTEXPR_FCN ValueId(const ValueId& _another) : m_id(_another.m_id) {} + inline EASY_CONSTEXPR_FCN ValueId(ValueId&& _another) : m_id(_another.m_id) {} +#else + inline EASY_CONSTEXPR_FCN ValueId(const ValueId&) = default; + inline EASY_CONSTEXPR_FCN ValueId(ValueId&&) = default; +#endif + + explicit inline EASY_CONSTEXPR_FCN ValueId() : m_id(0) {} + explicit inline EASY_CONSTEXPR_FCN ValueId(const void* _member) : m_id(reinterpret_cast(_member)) {} + + template + explicit inline EASY_CONSTEXPR_FCN ValueId(const T& _member) : m_id(reinterpret_cast(&_member)) {} + + template + explicit inline EASY_CONSTEXPR_FCN ValueId(const T (&_member)[N]) : m_id(reinterpret_cast((void*)_member)) {} + + ValueId& operator = (const ValueId&) = delete; + ValueId& operator = (ValueId&&) = delete; + }; + + namespace { + template + inline EASY_CONSTEXPR_FCN bool subextract_value_id(TArgs...); + + template <> + inline EASY_CONSTEXPR_FCN bool subextract_value_id<>() { return false; } + + template + inline EASY_CONSTEXPR_FCN bool subextract_value_id(T) { return false; } + + inline EASY_CONSTEXPR_FCN ValueId subextract_value_id(ValueId _value) { return _value; } + + template + inline EASY_CONSTEXPR_FCN ValueId subextract_value_id(ValueId _value, TArgs...) { return _value; } + + template + inline EASY_CONSTEXPR_FCN auto subextract_value_id(T, TArgs... _args) -> decltype(subextract_value_id(_args...)) { + return subextract_value_id(_args...); + } + + struct GetFirst { + template + static EASY_CONSTEXPR_FCN ValueId get(const T& _first, TArgs...) { return ValueId(_first); } + + template + static EASY_CONSTEXPR_FCN ValueId get(const T (&_first)[N], TArgs...) { return ValueId(_first); } + }; + + struct GetRest { + template + static EASY_CONSTEXPR_FCN ValueId get(const T&, TArgs... _args) { return subextract_value_id(_args...); } + }; + } // end of noname namespace. + + template + inline EASY_CONSTEXPR_FCN ValueId extract_value_id(const T& _first, TArgs... _args) { + return ::std::conditional<::std::is_same::value, GetRest, GetFirst> + ::type::get(_first, _args...); + } + + template + inline EASY_CONSTEXPR_FCN ValueId extract_value_id(const T (&_first)[N], TArgs... _args) { + return ::std::conditional<::std::is_same::value, GetRest, GetFirst> + ::type::get(_first, _args...); + } + +} // end of namespace profiler. + +#endif // EASY_PROFILER_ARBITRARY_VALUE_AUX_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h new file mode 100644 index 0000000..c2851cb --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/arbitrary_value_public_types.h @@ -0,0 +1,102 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_ARBITRARY_VALUE_PUBLIC_TYPES_H +#define EASY_PROFILER_ARBITRARY_VALUE_PUBLIC_TYPES_H + +#include +#include +#include + +namespace profiler +{ + + enum class DataType : uint8_t + { + Bool = 0, + Char, + Int8, + Uint8, + Int16, + Uint16, + Int32, + Uint32, + Int64, + Uint64, + Float, + Double, + String, + + TypesCount + }; // end of enum class DataType. + + template struct StdType; + + template struct StdToDataType EASY_FINAL { + EASY_STATIC_CONSTEXPR auto data_type = DataType::TypesCount; + }; + +# define EASY_DATATYPE_CONVERSION(DataTypeName, StdTypeName)\ + template <> struct StdType EASY_FINAL { using value_type = StdTypeName; };\ + template <> struct StdToDataType EASY_FINAL { EASY_STATIC_CONSTEXPR auto data_type = DataTypeName; } + + EASY_DATATYPE_CONVERSION(DataType::Bool , bool ); + EASY_DATATYPE_CONVERSION(DataType::Char , char ); + EASY_DATATYPE_CONVERSION(DataType::Int8 , int8_t ); + EASY_DATATYPE_CONVERSION(DataType::Uint8 , uint8_t ); + EASY_DATATYPE_CONVERSION(DataType::Int16 , int16_t ); + EASY_DATATYPE_CONVERSION(DataType::Uint16, uint16_t); + EASY_DATATYPE_CONVERSION(DataType::Int32 , int32_t ); + EASY_DATATYPE_CONVERSION(DataType::Uint32, uint32_t); + EASY_DATATYPE_CONVERSION(DataType::Int64 , int64_t ); + EASY_DATATYPE_CONVERSION(DataType::Uint64, uint64_t); + EASY_DATATYPE_CONVERSION(DataType::Float , float ); + EASY_DATATYPE_CONVERSION(DataType::Double, double ); + +# undef EASY_DATATYPE_CONVERSION + + template <> struct StdType EASY_FINAL { using value_type = char; }; + template <> struct StdToDataType EASY_FINAL { EASY_STATIC_CONSTEXPR auto data_type = DataType::String; }; + +} // end of namespace profiler. + +#endif //EASY_PROFILER_ARBITRARY_VALUE_PUBLIC_TYPES_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/easy_compiler_support.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/easy_compiler_support.h new file mode 100644 index 0000000..6d9fe8d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/easy_compiler_support.h @@ -0,0 +1,224 @@ +/************************************************************************ +* file name : easy_compiler_support.h +* ----------------- : +* creation time : 2016/09/22 +* authors : Victor Zarubkin, Sergey Yagovtsev +* emails : v.s.zarubkin@gmail.com, yse.sey@gmail.com +* ----------------- : +* description : This file contains auxiliary profiler macros for different compiler support. +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER_COMPILER_SUPPORT_H +#define EASY_PROFILER_COMPILER_SUPPORT_H + +#include + +#if defined(_WIN32) && !defined(EASY_PROFILER_STATIC) +// Visual Studio and MinGW +# ifdef _BUILD_PROFILER +# define PROFILER_API __declspec(dllexport) +# else +# define PROFILER_API __declspec(dllimport) +# endif +#endif + + + +#if defined(_MSC_VER) +////////////////////////////////////////////////////////////////////////// +// Visual Studio + +# if defined(EASY_OPTION_PRETTY_PRINT_FUNCTIONS) && EASY_OPTION_PRETTY_PRINT_FUNCTIONS != 0 +# define EASY_FUNC_NAME __FUNCSIG__ +# else +# define EASY_FUNC_NAME __FUNCTION__ +# endif + +# if _MSC_VER <= 1800 +// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead. +// There is also no support for C++11 magic statics feature :( So it becomes slightly harder to initialize static vars - additional "if" for each profiler block. +# define EASY_THREAD_LOCAL __declspec(thread) +# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer)\ + __declspec(thread) static VarType VarName = 0;\ + if (!VarName)\ + VarName = VarInitializer + +// No constexpr support before Visual Studio 2015 +# define EASY_CONSTEXPR const +# define EASY_STATIC_CONSTEXPR static const +# define EASY_CONSTEXPR_FCN + +// No noexcept support before Visual Studio 2015 +# define EASY_NOEXCEPT throw() +# endif + +# define EASY_FORCE_INLINE __forceinline + +#elif defined(__clang__) +////////////////////////////////////////////////////////////////////////// +// Clang Compiler + +# define EASY_COMPILER_VERSION (__clang_major__ * 10 + __clang_minor__) + +# if EASY_COMPILER_VERSION < 33 || (defined(__APPLE_CC__) && __APPLE_CC__ < 8000) +// There is no support for C++11 thread_local keyword prior to Clang v3.3 and Apple LLVM clang 8.0. Use __thread instead. +# define EASY_THREAD_LOCAL __thread +# endif + +# if EASY_COMPILER_VERSION < 31 +// No constexpr support before Clang v3.1 +# define EASY_CONSTEXPR const +# define EASY_STATIC_CONSTEXPR static const +# define EASY_CONSTEXPR_FCN +# endif + +# if EASY_COMPILER_VERSION < 29 +// There is no support for C++11 magic statics feature prior to clang 2.9. It becomes slightly harder to initialize static vars - additional "if" for each profiler block. +# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer)\ + EASY_THREAD_LOCAL static VarType VarName = 0;\ + if (!VarName)\ + VarName = VarInitializer + +// There is no support for C++11 final keyword prior to Clang v2.9 +# define EASY_FINAL +# endif + +# define EASY_FORCE_INLINE inline __attribute__((always_inline)) +# undef EASY_COMPILER_VERSION + +#elif defined(__GNUC__) +////////////////////////////////////////////////////////////////////////// +// GNU Compiler + +# define EASY_COMPILER_VERSION (__GNUC__ * 10 + __GNUC_MINOR__) + +# if EASY_COMPILER_VERSION < 48 +// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead. +# define EASY_THREAD_LOCAL __thread +# endif + +# if EASY_COMPILER_VERSION < 46 +// No constexpr support before GCC v4.6 +# define EASY_CONSTEXPR const +# define EASY_STATIC_CONSTEXPR static const +# define EASY_CONSTEXPR_FCN + +// No noexcept support before GCC v4.6 +# define EASY_NOEXCEPT throw() +# endif + +# if EASY_COMPILER_VERSION < 43 +// There is no support for C++11 magic statics feature prior to gcc 4.3. It becomes slightly harder to initialize static vars - additional "if" for each profiler block. +# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer)\ + EASY_THREAD_LOCAL static VarType VarName = 0;\ + if (!VarName)\ + VarName = VarInitializer +# endif + +# if EASY_COMPILER_VERSION < 47 +// There is no support for C++11 final keyword prior to gcc 4.7 +# define EASY_FINAL +# endif + +# define EASY_FORCE_INLINE inline __attribute__((always_inline)) +# undef EASY_COMPILER_VERSION + +#else +////////////////////////////////////////////////////////////////////////// +// TODO: Add other compilers support + +static_assert(false, "EasyProfiler is not configured for using your compiler type. Please, contact developers."); +#endif +// END +////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////// +// Default values + +#ifndef EASY_FUNC_NAME +# if defined(EASY_OPTION_PRETTY_PRINT_FUNCTIONS) && EASY_OPTION_PRETTY_PRINT_FUNCTIONS != 0 +# define EASY_FUNC_NAME __PRETTY_FUNCTION__ +# else +# define EASY_FUNC_NAME __func__ +# endif +#endif + +#ifndef EASY_THREAD_LOCAL +# define EASY_THREAD_LOCAL thread_local +# define EASY_CXX11_TLS_AVAILABLE +#endif + +#ifndef EASY_LOCAL_STATIC_PTR +# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer) static VarType VarName = VarInitializer +# define EASY_MAGIC_STATIC_AVAILABLE +#endif + +#ifndef EASY_FINAL +# define EASY_FINAL final +#endif + +#ifndef EASY_FORCE_INLINE +# define EASY_FORCE_INLINE inline +#endif + +#ifndef EASY_CONSTEXPR +# define EASY_CONSTEXPR constexpr +# define EASY_STATIC_CONSTEXPR static constexpr +# define EASY_CONSTEXPR_FCN constexpr +# define EASY_CONSTEXPR_AVAILABLE +#endif + +#ifndef EASY_NOEXCEPT +# define EASY_NOEXCEPT noexcept +# define EASY_NOEXCEPT_AVAILABLE +#endif + +#ifndef PROFILER_API +# define PROFILER_API +#endif + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_COMPILER_SUPPORT_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_aux.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_aux.h new file mode 100644 index 0000000..9b8da6c --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_aux.h @@ -0,0 +1,214 @@ +/************************************************************************ +* file name : profiler_aux.h +* ----------------- : +* creation time : 2016/06/11 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains auxiliary profiler macros and funcitons. +* ----------------- : +* change log : * 2016/06/11 Victor Zarubkin: Moved sources from profiler.h +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER_AUX_H +#define EASY_PROFILER_AUX_H + +#include + +#include +#include + +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + enum EasyBlockStatus : uint8_t { + OFF = 0, ///< The block is OFF + ON = 1, ///< The block is ON (but if it's parent block is off recursively then this block will be off too) + FORCE_ON = ON | 2, ///< The block is ALWAYS ON (even if it's parent has turned off all children) + OFF_RECURSIVE = 4, ///< The block is OFF and all of it's children by call-stack are also OFF. + ON_WITHOUT_CHILDREN = ON | OFF_RECURSIVE, ///< The block is ON but all of it's children are OFF. + FORCE_ON_WITHOUT_CHILDREN = FORCE_ON | OFF_RECURSIVE, ///< The block is ALWAYS ON but all of it's children are OFF. + }; + +} + +////////////////////////////////////////////////////////////////////////// + +#include +#include + +# define EASY_STRINGIFY(a) #a +# define EASY_STRINGIFICATION(a) EASY_STRINGIFY(a) +# define EASY_TOKEN_JOIN(x, y) x ## y +# define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(x, y) +# define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x) +# define EASY_UNIQUE_FRAME_COUNTER(x) EASY_TOKEN_CONCATENATE(unique_profiler_frame_mark_name_, x) +# define EASY_UNIQUE_DESC(x) EASY_TOKEN_CONCATENATE(unique_profiler_descriptor_, x) + +#ifdef BUILD_WITH_EASY_PROFILER + +namespace profiler { + + template struct NameSwitch; + + class ForceConstStr EASY_FINAL { + friend NameSwitch; + friend NameSwitch; + + const char* c_str; + + public: + + ForceConstStr() = delete; + ForceConstStr(const ForceConstStr&) = delete; + ForceConstStr(ForceConstStr&&) = delete; + ForceConstStr& operator = (const ForceConstStr&) = delete; + ForceConstStr& operator = (ForceConstStr&&) = delete; + + explicit EASY_CONSTEXPR_FCN ForceConstStr(const char* _str) : c_str(_str) {} + explicit ForceConstStr(const ::std::string& _str) : c_str(_str.c_str()) {} + }; + + template struct NameSwitch EASY_FINAL { + static const char* runtime_name(const char* name) { return name; } + static const char* runtime_name(const ::std::string& name) { return name.c_str(); } + static EASY_CONSTEXPR_FCN const char* runtime_name(const ForceConstStr&) { return ""; } + + template + static EASY_CONSTEXPR_FCN const char* compiletime_name(const T&, const char* autoGeneratedName) { return autoGeneratedName; } + static EASY_CONSTEXPR_FCN const char* compiletime_name(const char*, const char* autoGeneratedName) { return autoGeneratedName; } + static EASY_CONSTEXPR_FCN const char* compiletime_name(const ForceConstStr& name, const char*) { return name.c_str; } + }; + + template <> struct NameSwitch EASY_FINAL { + static EASY_CONSTEXPR_FCN const char* runtime_name(const char*) { return ""; } + static EASY_CONSTEXPR_FCN const char* runtime_name(const ForceConstStr&) { return ""; } + static const char* runtime_name(const ::std::string& name) { return name.c_str(); } + + template + static EASY_CONSTEXPR_FCN const char* compiletime_name(const T&, const char* autoGeneratedName) { return autoGeneratedName; } + static EASY_CONSTEXPR_FCN const char* compiletime_name(const char* name, const char*) { return name; } + static EASY_CONSTEXPR_FCN const char* compiletime_name(const ForceConstStr& name, const char*) { return name.c_str; } + }; + + //*********************************************** + + template + inline EASY_CONSTEXPR_FCN color_t extract_color(TArgs...); + + template <> + inline EASY_CONSTEXPR_FCN color_t extract_color<>() { + return ::profiler::colors::Default; + } + + template + inline EASY_CONSTEXPR_FCN color_t extract_color(T) { + return ::profiler::colors::Default; + } + + template <> + inline EASY_CONSTEXPR_FCN color_t extract_color(color_t _color) { + return _color; + } + + template + inline EASY_CONSTEXPR_FCN color_t extract_color(color_t _color, TArgs...) { + return _color; + } + + template + inline EASY_CONSTEXPR_FCN color_t extract_color(T, TArgs... _args) { + return extract_color(_args...); + } + + //*********************************************** + + template + inline EASY_CONSTEXPR_FCN EasyBlockStatus extract_enable_flag(TArgs...); + + template <> + inline EASY_CONSTEXPR_FCN EasyBlockStatus extract_enable_flag<>() { + return ::profiler::ON; + } + + template + inline EASY_CONSTEXPR_FCN EasyBlockStatus extract_enable_flag(T) { + return ::profiler::ON; + } + + template <> + inline EASY_CONSTEXPR_FCN EasyBlockStatus extract_enable_flag(EasyBlockStatus _flag) { + return _flag; + } + + template + inline EASY_CONSTEXPR_FCN EasyBlockStatus extract_enable_flag(EasyBlockStatus _flag, TArgs...) { + return _flag; + } + + template + inline EASY_CONSTEXPR_FCN EasyBlockStatus extract_enable_flag(T, TArgs... _args) { + return extract_enable_flag(_args...); + } + + //*********************************************** + +} // END of namespace profiler. + +# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFICATION(__LINE__) +# define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID) +# define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) +# define EASY_CONST_NAME(name) ::profiler::ForceConstStr(name) + +#else + +# define EASY_CONST_NAME(name) + +#endif // BUILD_WITH_EASY_PROFILER + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_AUX_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_colors.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_colors.h new file mode 100644 index 0000000..61e8b5b --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_colors.h @@ -0,0 +1,413 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_COLORS_H +#define EASY_PROFILER_COLORS_H + +#include +#include + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +namespace profiler { + + using color_t = uint32_t; // Standard four-byte ARGB color format + + namespace colors { + + ///< Change alpha for color. Only 8 major bytes (0xff000000) used from alpha. + inline EASY_CONSTEXPR_FCN color_t modify_alpha32(color_t _color, color_t _alpha) { + return (_alpha & 0xff000000) | (_color & 0x00ffffff); + } + + ///< Change alpha for color. + inline EASY_CONSTEXPR_FCN color_t modify_alpha8(color_t _color, uint8_t _alpha) { + return (static_cast(_alpha) << 24) | (_color & 0x00ffffff); + } + + ///< Create color from ARGB components. + inline EASY_CONSTEXPR_FCN color_t color(uint8_t _red, uint8_t _green, uint8_t _blue, uint8_t _alpha = 0xff) { + return (static_cast(_alpha) << 24) | (static_cast(_red) << 16) | (static_cast(_green) << 8) | static_cast(_blue); + } + +#if !defined(EASY_OPTION_BUILTIN_COLORS) || EASY_OPTION_BUILTIN_COLORS != 0 + // Google Material Design colors + // See https://material.google.com/style/color.html + + EASY_CONSTEXPR color_t Red50 = 0xffffebee; + EASY_CONSTEXPR color_t Red100 = 0xffffcdd2; + EASY_CONSTEXPR color_t Red200 = 0xffef9a9a; + EASY_CONSTEXPR color_t Red300 = 0xffe57373; + EASY_CONSTEXPR color_t Red400 = 0xffef5350; + EASY_CONSTEXPR color_t Red500 = 0xfff44336; + EASY_CONSTEXPR color_t Red600 = 0xffe53935; + EASY_CONSTEXPR color_t Red700 = 0xffd32f2f; + EASY_CONSTEXPR color_t Red800 = 0xffc62828; + EASY_CONSTEXPR color_t Red900 = 0xffb71c1c; + EASY_CONSTEXPR color_t RedA100 = 0xffff8a80; + EASY_CONSTEXPR color_t RedA200 = 0xffff5252; + EASY_CONSTEXPR color_t RedA400 = 0xffff1744; + EASY_CONSTEXPR color_t RedA700 = 0xffd50000; + + EASY_CONSTEXPR color_t Pink50 = 0xfffce4ec; + EASY_CONSTEXPR color_t Pink100 = 0xfff8bbd0; + EASY_CONSTEXPR color_t Pink200 = 0xfff48fb1; + EASY_CONSTEXPR color_t Pink300 = 0xfff06292; + EASY_CONSTEXPR color_t Pink400 = 0xffec407a; + EASY_CONSTEXPR color_t Pink500 = 0xffe91e63; + EASY_CONSTEXPR color_t Pink600 = 0xffd81b60; + EASY_CONSTEXPR color_t Pink700 = 0xffc2185b; + EASY_CONSTEXPR color_t Pink800 = 0xffad1457; + EASY_CONSTEXPR color_t Pink900 = 0xff880e4f; + EASY_CONSTEXPR color_t PinkA100 = 0xffff80ab; + EASY_CONSTEXPR color_t PinkA200 = 0xffff4081; + EASY_CONSTEXPR color_t PinkA400 = 0xfff50057; + EASY_CONSTEXPR color_t PinkA700 = 0xffc51162; + + EASY_CONSTEXPR color_t Purple50 = 0xfff3e5f5; + EASY_CONSTEXPR color_t Purple100 = 0xffe1bee7; + EASY_CONSTEXPR color_t Purple200 = 0xffce93d8; + EASY_CONSTEXPR color_t Purple300 = 0xffba68c8; + EASY_CONSTEXPR color_t Purple400 = 0xffab47bc; + EASY_CONSTEXPR color_t Purple500 = 0xff9c27b0; + EASY_CONSTEXPR color_t Purple600 = 0xff8e24aa; + EASY_CONSTEXPR color_t Purple700 = 0xff7b1fa2; + EASY_CONSTEXPR color_t Purple800 = 0xff6a1b9a; + EASY_CONSTEXPR color_t Purple900 = 0xff4a148c; + EASY_CONSTEXPR color_t PurpleA100 = 0xffea80fc; + EASY_CONSTEXPR color_t PurpleA200 = 0xffe040fb; + EASY_CONSTEXPR color_t PurpleA400 = 0xffd500f9; + EASY_CONSTEXPR color_t PurpleA700 = 0xffaa00ff; + + EASY_CONSTEXPR color_t DeepPurple50 = 0xffede7f6; + EASY_CONSTEXPR color_t DeepPurple100 = 0xffd1c4e9; + EASY_CONSTEXPR color_t DeepPurple200 = 0xffb39ddb; + EASY_CONSTEXPR color_t DeepPurple300 = 0xff9575cd; + EASY_CONSTEXPR color_t DeepPurple400 = 0xff7e57c2; + EASY_CONSTEXPR color_t DeepPurple500 = 0xff673ab7; + EASY_CONSTEXPR color_t DeepPurple600 = 0xff5e35b1; + EASY_CONSTEXPR color_t DeepPurple700 = 0xff512da8; + EASY_CONSTEXPR color_t DeepPurple800 = 0xff4527a0; + EASY_CONSTEXPR color_t DeepPurple900 = 0xff311b92; + EASY_CONSTEXPR color_t DeepPurpleA100 = 0xffb388ff; + EASY_CONSTEXPR color_t DeepPurpleA200 = 0xff7c4dff; + EASY_CONSTEXPR color_t DeepPurpleA400 = 0xff651fff; + EASY_CONSTEXPR color_t DeepPurpleA700 = 0xff6200ea; + + EASY_CONSTEXPR color_t Indigo50 = 0xffe8eaf6; + EASY_CONSTEXPR color_t Indigo100 = 0xffc5cae9; + EASY_CONSTEXPR color_t Indigo200 = 0xff9fa8da; + EASY_CONSTEXPR color_t Indigo300 = 0xff7986cb; + EASY_CONSTEXPR color_t Indigo400 = 0xff5c6bc0; + EASY_CONSTEXPR color_t Indigo500 = 0xff3f51b5; + EASY_CONSTEXPR color_t Indigo600 = 0xff3949ab; + EASY_CONSTEXPR color_t Indigo700 = 0xff303f9f; + EASY_CONSTEXPR color_t Indigo800 = 0xff283593; + EASY_CONSTEXPR color_t Indigo900 = 0xff1a237e; + EASY_CONSTEXPR color_t IndigoA100 = 0xff8c9eff; + EASY_CONSTEXPR color_t IndigoA200 = 0xff536dfe; + EASY_CONSTEXPR color_t IndigoA400 = 0xff3d5afe; + EASY_CONSTEXPR color_t IndigoA700 = 0xff304ffe; + + EASY_CONSTEXPR color_t Blue50 = 0xffe3f2fd; + EASY_CONSTEXPR color_t Blue100 = 0xffbbdefb; + EASY_CONSTEXPR color_t Blue200 = 0xff90caf9; + EASY_CONSTEXPR color_t Blue300 = 0xff64b5f6; + EASY_CONSTEXPR color_t Blue400 = 0xff42a5f5; + EASY_CONSTEXPR color_t Blue500 = 0xff2196f3; + EASY_CONSTEXPR color_t Blue600 = 0xff1e88e5; + EASY_CONSTEXPR color_t Blue700 = 0xff1976d2; + EASY_CONSTEXPR color_t Blue800 = 0xff1565c0; + EASY_CONSTEXPR color_t Blue900 = 0xff0d47a1; + EASY_CONSTEXPR color_t BlueA100 = 0xff82b1ff; + EASY_CONSTEXPR color_t BlueA200 = 0xff448aff; + EASY_CONSTEXPR color_t BlueA400 = 0xff2979ff; + EASY_CONSTEXPR color_t BlueA700 = 0xff2962ff; + + EASY_CONSTEXPR color_t LightBlue50 = 0xffe1f5fe; + EASY_CONSTEXPR color_t LightBlue100 = 0xffb3e5fc; + EASY_CONSTEXPR color_t LightBlue200 = 0xff81d4fa; + EASY_CONSTEXPR color_t LightBlue300 = 0xff4fc3f7; + EASY_CONSTEXPR color_t LightBlue400 = 0xff29b6f6; + EASY_CONSTEXPR color_t LightBlue500 = 0xff03a9f4; + EASY_CONSTEXPR color_t LightBlue600 = 0xff039be5; + EASY_CONSTEXPR color_t LightBlue700 = 0xff0288d1; + EASY_CONSTEXPR color_t LightBlue800 = 0xff0277bd; + EASY_CONSTEXPR color_t LightBlue900 = 0xff01579b; + EASY_CONSTEXPR color_t LightBlueA100 = 0xff80d8ff; + EASY_CONSTEXPR color_t LightBlueA200 = 0xff40c4ff; + EASY_CONSTEXPR color_t LightBlueA400 = 0xff00b0ff; + EASY_CONSTEXPR color_t LightBlueA700 = 0xff0091ea; + + EASY_CONSTEXPR color_t Cyan50 = 0xffe0f7fa; + EASY_CONSTEXPR color_t Cyan100 = 0xffb2ebf2; + EASY_CONSTEXPR color_t Cyan200 = 0xff80deea; + EASY_CONSTEXPR color_t Cyan300 = 0xff4dd0e1; + EASY_CONSTEXPR color_t Cyan400 = 0xff26c6da; + EASY_CONSTEXPR color_t Cyan500 = 0xff00bcd4; + EASY_CONSTEXPR color_t Cyan600 = 0xff00acc1; + EASY_CONSTEXPR color_t Cyan700 = 0xff0097a7; + EASY_CONSTEXPR color_t Cyan800 = 0xff00838f; + EASY_CONSTEXPR color_t Cyan900 = 0xff006064; + EASY_CONSTEXPR color_t CyanA100 = 0xff84ffff; + EASY_CONSTEXPR color_t CyanA200 = 0xff18ffff; + EASY_CONSTEXPR color_t CyanA400 = 0xff00e5ff; + EASY_CONSTEXPR color_t CyanA700 = 0xff00b8d4; + + EASY_CONSTEXPR color_t Teal50 = 0xffe0f2f1; + EASY_CONSTEXPR color_t Teal100 = 0xffb2dfdb; + EASY_CONSTEXPR color_t Teal200 = 0xff80cbc4; + EASY_CONSTEXPR color_t Teal300 = 0xff4db6ac; + EASY_CONSTEXPR color_t Teal400 = 0xff26a69a; + EASY_CONSTEXPR color_t Teal500 = 0xff009688; + EASY_CONSTEXPR color_t Teal600 = 0xff00897b; + EASY_CONSTEXPR color_t Teal700 = 0xff00796b; + EASY_CONSTEXPR color_t Teal800 = 0xff00695c; + EASY_CONSTEXPR color_t Teal900 = 0xff004d40; + EASY_CONSTEXPR color_t TealA100 = 0xffa7ffeb; + EASY_CONSTEXPR color_t TealA200 = 0xff64ffda; + EASY_CONSTEXPR color_t TealA400 = 0xff1de9b6; + EASY_CONSTEXPR color_t TealA700 = 0xff00bfa5; + + EASY_CONSTEXPR color_t Green50 = 0xffe8f5e9; + EASY_CONSTEXPR color_t Green100 = 0xffc8e6c9; + EASY_CONSTEXPR color_t Green200 = 0xffa5d6a7; + EASY_CONSTEXPR color_t Green300 = 0xff81c784; + EASY_CONSTEXPR color_t Green400 = 0xff66bb6a; + EASY_CONSTEXPR color_t Green500 = 0xff4caf50; + EASY_CONSTEXPR color_t Green600 = 0xff43a047; + EASY_CONSTEXPR color_t Green700 = 0xff388e3c; + EASY_CONSTEXPR color_t Green800 = 0xff2e7d32; + EASY_CONSTEXPR color_t Green900 = 0xff1b5e20; + EASY_CONSTEXPR color_t GreenA100 = 0xffb9f6ca; + EASY_CONSTEXPR color_t GreenA200 = 0xff69f0ae; + EASY_CONSTEXPR color_t GreenA400 = 0xff00e676; + EASY_CONSTEXPR color_t GreenA700 = 0xff00c853; + + EASY_CONSTEXPR color_t LightGreen50 = 0xfff1f8e9; + EASY_CONSTEXPR color_t LightGreen100 = 0xffdcedc8; + EASY_CONSTEXPR color_t LightGreen200 = 0xffc5e1a5; + EASY_CONSTEXPR color_t LightGreen300 = 0xffaed581; + EASY_CONSTEXPR color_t LightGreen400 = 0xff9ccc65; + EASY_CONSTEXPR color_t LightGreen500 = 0xff8bc34a; + EASY_CONSTEXPR color_t LightGreen600 = 0xff7cb342; + EASY_CONSTEXPR color_t LightGreen700 = 0xff689f38; + EASY_CONSTEXPR color_t LightGreen800 = 0xff558b2f; + EASY_CONSTEXPR color_t LightGreen900 = 0xff33691e; + EASY_CONSTEXPR color_t LightGreenA100 = 0xffccff90; + EASY_CONSTEXPR color_t LightGreenA200 = 0xffb2ff59; + EASY_CONSTEXPR color_t LightGreenA400 = 0xff76ff03; + EASY_CONSTEXPR color_t LightGreenA700 = 0xff64dd17; + + EASY_CONSTEXPR color_t Lime50 = 0xfff9ebe7; + EASY_CONSTEXPR color_t Lime100 = 0xfff0f4c3; + EASY_CONSTEXPR color_t Lime200 = 0xffe6ee9c; + EASY_CONSTEXPR color_t Lime300 = 0xffdce775; + EASY_CONSTEXPR color_t Lime400 = 0xffd4e157; + EASY_CONSTEXPR color_t Lime500 = 0xffcddc39; + EASY_CONSTEXPR color_t Lime600 = 0xffc0ca33; + EASY_CONSTEXPR color_t Lime700 = 0xffafb42b; + EASY_CONSTEXPR color_t Lime800 = 0xff9e9d24; + EASY_CONSTEXPR color_t Lime900 = 0xff827717; + EASY_CONSTEXPR color_t LimeA100 = 0xfff4ff81; + EASY_CONSTEXPR color_t LimeA200 = 0xffeeff41; + EASY_CONSTEXPR color_t LimeA400 = 0xffc6ff00; + EASY_CONSTEXPR color_t LimeA700 = 0xffaeea00; + + EASY_CONSTEXPR color_t Yellow50 = 0xfffffde7; + EASY_CONSTEXPR color_t Yellow100 = 0xfffff9c4; + EASY_CONSTEXPR color_t Yellow200 = 0xfffff59d; + EASY_CONSTEXPR color_t Yellow300 = 0xfffff176; + EASY_CONSTEXPR color_t Yellow400 = 0xffffee58; + EASY_CONSTEXPR color_t Yellow500 = 0xffffeb3b; + EASY_CONSTEXPR color_t Yellow600 = 0xfffdd835; + EASY_CONSTEXPR color_t Yellow700 = 0xfffbc02d; + EASY_CONSTEXPR color_t Yellow800 = 0xfff9a825; + EASY_CONSTEXPR color_t Yellow900 = 0xfff57f17; + EASY_CONSTEXPR color_t YellowA100 = 0xffffff8d; + EASY_CONSTEXPR color_t YellowA200 = 0xffffff00; + EASY_CONSTEXPR color_t YellowA400 = 0xffffea00; + EASY_CONSTEXPR color_t YellowA700 = 0xffffd600; + + EASY_CONSTEXPR color_t Amber50 = 0xfffff8e1; + EASY_CONSTEXPR color_t Amber100 = 0xffffecb3; + EASY_CONSTEXPR color_t Amber200 = 0xffffe082; + EASY_CONSTEXPR color_t Amber300 = 0xffffd54f; + EASY_CONSTEXPR color_t Amber400 = 0xffffca28; + EASY_CONSTEXPR color_t Amber500 = 0xffffc107; + EASY_CONSTEXPR color_t Amber600 = 0xffffb300; + EASY_CONSTEXPR color_t Amber700 = 0xffffa000; + EASY_CONSTEXPR color_t Amber800 = 0xffff8f00; + EASY_CONSTEXPR color_t Amber900 = 0xffff6f00; + EASY_CONSTEXPR color_t AmberA100 = 0xffffe57f; + EASY_CONSTEXPR color_t AmberA200 = 0xffffd740; + EASY_CONSTEXPR color_t AmberA400 = 0xffffc400; + EASY_CONSTEXPR color_t AmberA700 = 0xffffab00; + + EASY_CONSTEXPR color_t Orange50 = 0xfffff3e0; + EASY_CONSTEXPR color_t Orange100 = 0xffffe0b2; + EASY_CONSTEXPR color_t Orange200 = 0xffffcc80; + EASY_CONSTEXPR color_t Orange300 = 0xffffb74d; + EASY_CONSTEXPR color_t Orange400 = 0xffffa726; + EASY_CONSTEXPR color_t Orange500 = 0xffff9800; + EASY_CONSTEXPR color_t Orange600 = 0xfffb8c00; + EASY_CONSTEXPR color_t Orange700 = 0xfff57c00; + EASY_CONSTEXPR color_t Orange800 = 0xffef6c00; + EASY_CONSTEXPR color_t Orange900 = 0xffe65100; + EASY_CONSTEXPR color_t OrangeA100 = 0xffffd180; + EASY_CONSTEXPR color_t OrangeA200 = 0xffffab40; + EASY_CONSTEXPR color_t OrangeA400 = 0xffff9100; + EASY_CONSTEXPR color_t OrangeA700 = 0xffff6d00; + + EASY_CONSTEXPR color_t DeepOrange50 = 0xfffbe9e7; + EASY_CONSTEXPR color_t DeepOrange100 = 0xffffccbc; + EASY_CONSTEXPR color_t DeepOrange200 = 0xffffab91; + EASY_CONSTEXPR color_t DeepOrange300 = 0xffff8a65; + EASY_CONSTEXPR color_t DeepOrange400 = 0xffff7043; + EASY_CONSTEXPR color_t DeepOrange500 = 0xffff5722; + EASY_CONSTEXPR color_t DeepOrange600 = 0xfff4511e; + EASY_CONSTEXPR color_t DeepOrange700 = 0xffe64a19; + EASY_CONSTEXPR color_t DeepOrange800 = 0xffd84315; + EASY_CONSTEXPR color_t DeepOrange900 = 0xffbf360c; + EASY_CONSTEXPR color_t DeepOrangeA100 = 0xffff9e80; + EASY_CONSTEXPR color_t DeepOrangeA200 = 0xffff6e40; + EASY_CONSTEXPR color_t DeepOrangeA400 = 0xffff3d00; + EASY_CONSTEXPR color_t DeepOrangeA700 = 0xffdd2c00; + + EASY_CONSTEXPR color_t Brown50 = 0xffefebe9; + EASY_CONSTEXPR color_t Brown100 = 0xffd7ccc8; + EASY_CONSTEXPR color_t Brown200 = 0xffbcaaa4; + EASY_CONSTEXPR color_t Brown300 = 0xffa1887f; + EASY_CONSTEXPR color_t Brown400 = 0xff8d6e63; + EASY_CONSTEXPR color_t Brown500 = 0xff795548; + EASY_CONSTEXPR color_t Brown600 = 0xff6d4c41; + EASY_CONSTEXPR color_t Brown700 = 0xff5d4037; + EASY_CONSTEXPR color_t Brown800 = 0xff4e342e; + EASY_CONSTEXPR color_t Brown900 = 0xff3e2723; + + EASY_CONSTEXPR color_t Grey50 = 0xfffafafa; + EASY_CONSTEXPR color_t Grey100 = 0xfff5f5f5; + EASY_CONSTEXPR color_t Grey200 = 0xffeeeeee; + EASY_CONSTEXPR color_t Grey300 = 0xffe0e0e0; + EASY_CONSTEXPR color_t Grey400 = 0xffbdbdbd; + EASY_CONSTEXPR color_t Grey500 = 0xff9e9e9e; + EASY_CONSTEXPR color_t Grey600 = 0xff757575; + EASY_CONSTEXPR color_t Grey700 = 0xff616161; + EASY_CONSTEXPR color_t Grey800 = 0xff424242; + EASY_CONSTEXPR color_t Grey900 = 0xff212121; + + EASY_CONSTEXPR color_t BlueGrey50 = 0xffeceff1; + EASY_CONSTEXPR color_t BlueGrey100 = 0xffcfd8dc; + EASY_CONSTEXPR color_t BlueGrey200 = 0xffb0bec5; + EASY_CONSTEXPR color_t BlueGrey300 = 0xff90a4ae; + EASY_CONSTEXPR color_t BlueGrey400 = 0xff78909c; + EASY_CONSTEXPR color_t BlueGrey500 = 0xff607d8b; + EASY_CONSTEXPR color_t BlueGrey600 = 0xff546e7a; + EASY_CONSTEXPR color_t BlueGrey700 = 0xff455a64; + EASY_CONSTEXPR color_t BlueGrey800 = 0xff37474f; + EASY_CONSTEXPR color_t BlueGrey900 = 0xff263238; + + EASY_CONSTEXPR color_t Black = 0xff000000; + EASY_CONSTEXPR color_t White = 0xffffffff; + EASY_CONSTEXPR color_t Null = 0x00000000; + + + EASY_CONSTEXPR color_t Red = Red500; + EASY_CONSTEXPR color_t DarkRed = Red900; + EASY_CONSTEXPR color_t Coral = Red200; + EASY_CONSTEXPR color_t RichRed = 0xffff0000; + EASY_CONSTEXPR color_t Pink = Pink500; + EASY_CONSTEXPR color_t Rose = PinkA100; + EASY_CONSTEXPR color_t Purple = Purple500; + EASY_CONSTEXPR color_t Magenta = PurpleA200; + EASY_CONSTEXPR color_t DarkMagenta = PurpleA700; + EASY_CONSTEXPR color_t DeepPurple = DeepPurple500; + EASY_CONSTEXPR color_t Indigo = Indigo500; + EASY_CONSTEXPR color_t Blue = Blue500; + EASY_CONSTEXPR color_t DarkBlue = Blue900; + EASY_CONSTEXPR color_t RichBlue = 0xff0000ff; + EASY_CONSTEXPR color_t LightBlue = LightBlue500; + EASY_CONSTEXPR color_t SkyBlue = LightBlueA100; + EASY_CONSTEXPR color_t Navy = LightBlue800; + EASY_CONSTEXPR color_t Cyan = Cyan500; + EASY_CONSTEXPR color_t DarkCyan = Cyan900; + EASY_CONSTEXPR color_t Teal = Teal500; + EASY_CONSTEXPR color_t DarkTeal = Teal900; + EASY_CONSTEXPR color_t Green = Green500; + EASY_CONSTEXPR color_t DarkGreen = Green900; + EASY_CONSTEXPR color_t RichGreen = 0xff00ff00; + EASY_CONSTEXPR color_t LightGreen = LightGreen500; + EASY_CONSTEXPR color_t Mint = LightGreen900; + EASY_CONSTEXPR color_t Lime = Lime500; + EASY_CONSTEXPR color_t Olive = Lime900; + EASY_CONSTEXPR color_t Yellow = Yellow500; + EASY_CONSTEXPR color_t RichYellow = YellowA200; + EASY_CONSTEXPR color_t Amber = Amber500; + EASY_CONSTEXPR color_t Gold = Amber300; + EASY_CONSTEXPR color_t PaleGold = AmberA100; + EASY_CONSTEXPR color_t Orange = Orange500; + EASY_CONSTEXPR color_t Skin = Orange100; + EASY_CONSTEXPR color_t DeepOrange = DeepOrange500; + EASY_CONSTEXPR color_t Brick = DeepOrange900; + EASY_CONSTEXPR color_t Brown = Brown500; + EASY_CONSTEXPR color_t DarkBrown = Brown900; + EASY_CONSTEXPR color_t CreamWhite = Orange50; + EASY_CONSTEXPR color_t Wheat = Amber100; + EASY_CONSTEXPR color_t Grey = Grey500; + EASY_CONSTEXPR color_t Dark = Grey900; + EASY_CONSTEXPR color_t Silver = Grey300; + EASY_CONSTEXPR color_t BlueGrey = BlueGrey500; + + EASY_CONSTEXPR color_t Default = Wheat; +#else + EASY_CONSTEXPR color_t Default = 0xffffecb3; +#endif // #if !defined(EASY_OPTION_BUILTIN_COLORS) || EASY_OPTION_BUILTIN_COLORS == 0 + + } // END of namespace colors. + +} // END of namespace profiler. + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_COLORS_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_public_types.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_public_types.h new file mode 100644 index 0000000..d19af70 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/details/profiler_public_types.h @@ -0,0 +1,203 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_PUBLIC_TYPES_H +#define EASY_PROFILER_PUBLIC_TYPES_H + +#include + +class NonscopedBlock; +class ProfileManager; +struct ThreadStorage; + +namespace profiler { + + using timestamp_t = uint64_t; + using thread_id_t = uint64_t; + using block_id_t = uint32_t; + + enum class BlockType : uint8_t + { + Event = 0, + Block, + Value, + + TypesCount + }; + using block_type_t = BlockType; + + enum Duration : uint8_t + { + TICKS = 0, ///< CPU ticks + MICROSECONDS ///< Microseconds + }; + + //*********************************************** + +#pragma pack(push,1) + class PROFILER_API BaseBlockDescriptor + { + friend ::ProfileManager; + friend ::ThreadStorage; + + protected: + + block_id_t m_id; ///< This descriptor id (We can afford this spending because there are much more blocks than descriptors) + int32_t m_line; ///< Line number in the source file + color_t m_color; ///< Color of the block packed into 1-byte structure + block_type_t m_type; ///< Type of the block (See BlockType) + EasyBlockStatus m_status; ///< If false then blocks with such id() will not be stored by profiler during profile session + + explicit BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status, int _line, block_type_t _block_type, color_t _color) EASY_NOEXCEPT; + + public: + + BaseBlockDescriptor() = delete; + + inline block_id_t id() const EASY_NOEXCEPT { return m_id; } + inline int32_t line() const EASY_NOEXCEPT { return m_line; } + inline color_t color() const EASY_NOEXCEPT { return m_color; } + inline block_type_t type() const EASY_NOEXCEPT { return m_type; } + inline EasyBlockStatus status() const EASY_NOEXCEPT { return m_status; } + + }; // END of class BaseBlockDescriptor. + + //*********************************************** + + class PROFILER_API Event + { + friend ::ProfileManager; + + protected: + + timestamp_t m_begin; + timestamp_t m_end; + + public: + + Event() = delete; + + Event(const Event&) = default; + explicit Event(timestamp_t _begin_time) EASY_NOEXCEPT; + explicit Event(timestamp_t _begin_time, timestamp_t _end_time) EASY_NOEXCEPT; + + inline timestamp_t begin() const EASY_NOEXCEPT { return m_begin; } + inline timestamp_t end() const EASY_NOEXCEPT { return m_end; } + inline timestamp_t duration() const EASY_NOEXCEPT { return m_end - m_begin; } + + }; // END class Event. + + class PROFILER_API BaseBlockData : public Event + { + friend ::ProfileManager; + + protected: + + block_id_t m_id; + + public: + + BaseBlockData() = delete; + + BaseBlockData(const BaseBlockData&) = default; + explicit BaseBlockData(timestamp_t _begin_time, block_id_t _id) EASY_NOEXCEPT; + explicit BaseBlockData(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _id) EASY_NOEXCEPT; + + inline block_id_t id() const EASY_NOEXCEPT { return m_id; } + inline void setId(block_id_t _id) EASY_NOEXCEPT { m_id = _id; } + + }; // END of class BaseBlockData. +#pragma pack(pop) + + //*********************************************** + + class PROFILER_API Block : public BaseBlockData + { + friend ::ProfileManager; + friend ::ThreadStorage; + friend ::NonscopedBlock; + + const char* m_name; + EasyBlockStatus m_status; + bool m_isScoped; + + private: + + void start(); + void start(timestamp_t _time) EASY_NOEXCEPT; + void finish(); + void finish(timestamp_t _time) EASY_NOEXCEPT; + inline bool finished() const EASY_NOEXCEPT { return m_end >= m_begin; } + inline EasyBlockStatus status() const EASY_NOEXCEPT { return m_status; } + inline void setStatus(EasyBlockStatus _status) EASY_NOEXCEPT { m_status = _status; } + + public: + + Block(const Block&) = delete; + Block& operator = (const Block&) = delete; + + Block(Block&& that) EASY_NOEXCEPT; + Block(const BaseBlockDescriptor* _desc, const char* _runtimeName, bool _scoped = true) EASY_NOEXCEPT; + Block(timestamp_t _begin_time, block_id_t _id, const char* _runtimeName) EASY_NOEXCEPT; + Block(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _id, const char* _runtimeName) EASY_NOEXCEPT; + ~Block(); + + inline const char* name() const EASY_NOEXCEPT { return m_name; } + + }; // END of class Block. + + //*********************************************** + + class PROFILER_API ThreadGuard EASY_FINAL + { + friend ::ProfileManager; + thread_id_t m_id = 0; + + public: + + ~ThreadGuard(); + + }; // END of class ThreadGuard. + +} // END of namespace profiler. + +#endif // EASY_PROFILER_PUBLIC_TYPES_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/easy_net.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/easy_net.h new file mode 100644 index 0000000..cf2d092 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/easy_net.h @@ -0,0 +1,161 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_NET_H +#define EASY_NET_H + +#include +#include + +namespace profiler { namespace net { + +EASY_CONSTEXPR uint32_t EASY_MESSAGE_SIGN = 20160909; + +#pragma pack(push,1) + +enum class MessageType : uint8_t +{ + Undefined = 0, + + Request_Start_Capture, + Reply_Capturing_Started, + Request_Stop_Capture, + + Reply_Blocks, + Reply_Blocks_End, + + Connection_Accepted, + + Request_Blocks_Description, + Reply_Blocks_Description, + Reply_Blocks_Description_End, + + Change_Block_Status, + Change_Event_Tracing_Status, + Change_Event_Tracing_Priority, + + Ping, + + Request_MainThread_FPS, + Reply_MainThread_FPS, +}; + +struct Message +{ + uint32_t magic_number = EASY_MESSAGE_SIGN; + MessageType type = MessageType::Undefined; + + bool isEasyNetMessage() const EASY_NOEXCEPT { + return EASY_MESSAGE_SIGN == magic_number; + } + + explicit Message(MessageType _t) EASY_NOEXCEPT : type(_t) { } + + Message() = default; +}; + +struct DataMessage : public Message +{ + uint32_t size = 0; // bytes + + explicit DataMessage(MessageType _t = MessageType::Reply_Blocks) : Message(_t) {} + explicit DataMessage(uint32_t _s, MessageType _t = MessageType::Reply_Blocks) : Message(_t), size(_s) {} + + const char* data() const { return reinterpret_cast(this) + sizeof(DataMessage); } +}; + +struct BlockStatusMessage : public Message +{ + uint32_t id; + uint8_t status; + + explicit BlockStatusMessage(uint32_t _id, uint8_t _status) + : Message(MessageType::Change_Block_Status), id(_id), status(_status) { } + + BlockStatusMessage() = delete; +}; + +struct EasyProfilerStatus : public Message +{ + bool isProfilerEnabled; + bool isEventTracingEnabled; + bool isLowPriorityEventTracing; + + explicit EasyProfilerStatus(bool _enabled, bool _ETenabled, bool _ETlowp) + : Message(MessageType::Connection_Accepted) + , isProfilerEnabled(_enabled) + , isEventTracingEnabled(_ETenabled) + , isLowPriorityEventTracing(_ETlowp) + { + } + + EasyProfilerStatus() = delete; +}; + +struct BoolMessage : public Message +{ + bool flag = false; + + explicit BoolMessage(MessageType _t, bool _flag = false) + : Message(_t), flag(_flag) { } + + BoolMessage() = default; +}; + +struct TimestampMessage : public Message +{ + uint32_t maxValue = 0; + uint32_t avgValue = 0; + + explicit TimestampMessage(MessageType _t, uint32_t _maxValue, uint32_t _avgValue) + : Message(_t), maxValue(_maxValue), avgValue(_avgValue) { } + + TimestampMessage() = default; +}; + +#pragma pack(pop) + +}//net + +}//profiler + +#endif // EASY_NET_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/easy_socket.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/easy_socket.h new file mode 100644 index 0000000..064ba6a --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/easy_socket.h @@ -0,0 +1,130 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ +#ifndef EASY_PROFILER_SOCKET_H +#define EASY_PROFILER_SOCKET_H + +#include +#include + +#ifndef _WIN32 + +// Unix +# include +# include +# include +# include +# include +# include +# include //for android-build + +#else + +// Windows +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# include + +#endif + +class PROFILER_API EasySocket EASY_FINAL +{ +public: + +#ifdef _WIN32 + typedef SOCKET socket_t; +#else + typedef int socket_t; +#endif + + enum class ConnectionState : int8_t + { + Disconnected = -1, + Unknown, + Connected, + Connecting + }; + +private: + + socket_t m_socket = 0; + socket_t m_replySocket = 0; + int m_wsaret = -1; + + struct hostent* m_server = nullptr; + struct sockaddr_in m_serverAddress; + + ConnectionState m_state = ConnectionState::Unknown; + +public: + + EasySocket(); + ~EasySocket(); + + void setReceiveTimeout(int milliseconds); + + int send(const void* buf, size_t nbyte); + int receive(void* buf, size_t nbyte); + int listen(int count = 5); + int accept(); + int bind(uint16_t portno); + + bool setAddress(const char* serv, uint16_t port); + int connect(); + + void flush(); + void init(); + + ConnectionState state() const; + bool isDisconnected() const; + bool isConnected() const; + +private: + + void checkResult(int result); + bool checkSocket(socket_t s) const; + void setBlocking(socket_t s, bool blocking); + +}; // end of class EasySocket. + +#endif // EASY_PROFILER_SOCKET_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/profiler.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/profiler.h new file mode 100644 index 0000000..dffc525 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/profiler.h @@ -0,0 +1,910 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_H +#define EASY_PROFILER_H + +#include + +#if defined ( __clang__ ) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#endif + +// +// BUILD_WITH_EASY_PROFILER is defined in CMakeLists.txt if your project is linked to easy_profiler. +// + +// +// DISABLE_EASY_PROFILER may be defined manually in source-file before #include +// to disable profiler for certain source-file or project. +// + +#if defined(BUILD_WITH_EASY_PROFILER) && !defined(DISABLE_EASY_PROFILER) + +/** +\defgroup profiler EasyProfiler +*/ + + +/** Indicates that EasyProfiler is used. + +\ingroup profiler +*/ +#define USING_EASY_PROFILER + + +// EasyProfiler core API: + +/** Macro for beginning of a scoped block with custom name and color. + +\code + #include + void foo() + { + // some code ... + + EASY_BLOCK("Check something", profiler::OFF); // Disabled block (There is possibility to enable this block later via GUI) + if(something){ + EASY_BLOCK("Calling bar()"); // Block with default color + bar(); + } + else{ + EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block + baz(); + } + EASY_END_BLOCK; // End of "Check something" block (Even if "Check something" is disabled, this EASY_END_BLOCK will not end any other block). + + EASY_BLOCK("Some another block", profiler::colors::Blue, profiler::ON_WITHOUT_CHILDREN); // Block with Blue color without + // some another code... + EASY_BLOCK("Calculate sum"); // This block will not be profiled because it's parent is ON_WITHOUT_CHILDREN + int sum = 0; + for (int i = 0; i < 10; ++i) + sum += i; + EASY_END_BLOCK; // End of "Calculate sum" block + } +\endcode + +Block will be automatically completed by destructor. + +\ingroup profiler +*/ +# define EASY_BLOCK(name, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BlockType::Block, ::profiler::extract_color(__VA_ARGS__),\ + ::std::is_base_of<::profiler::ForceConstStr, decltype(name)>::value));\ + ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ + ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); + +/** Macro for beginning of a non-scoped block with custom name and color. + +You must end such block manually with EASY_END_BLOCK. + +\code + #include + void foo() { + EASY_NONSCOPED_BLOCK("Callback"); // Begin block which would not be finished when function returns. + + // some code ... + } + + void bar() { + // some another code... + + EASY_END_BLOCK; // This, as always, ends last opened block. You have to take care about blocks order by yourself. + } + + void baz() { + foo(); // non-scoped block begins here + + // some code... + + bar(); // non-scoped block ends here + } +\endcode + +Block will be automatically completed by destructor. + +\ingroup profiler +*/ +#define EASY_NONSCOPED_BLOCK(name, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BlockType::Block, ::profiler::extract_color(__VA_ARGS__),\ + ::std::is_base_of<::profiler::ForceConstStr, decltype(name)>::value));\ + ::profiler::beginNonScopedBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); + +/** Macro for beginning of a block with function name and custom color. + +\code + #include + void foo(){ + EASY_FUNCTION(); // Block with name="foo" and default color + //some code... + } + + void bar(){ + EASY_FUNCTION(profiler::colors::Green); // Green block with name="bar" + //some code... + } + + void baz(){ + EASY_FUNCTION(profiler::FORCE_ON); // Force enabled block with name="baz" and default color (This block will be profiled even if it's parent is OFF_RECURSIVE) + // som code... + } +\endcode + +Name of the block automatically created with function name. + +\ingroup profiler +*/ +# define EASY_FUNCTION(...) EASY_BLOCK(EASY_FUNC_NAME, ## __VA_ARGS__) + +/** Macro for completion of last opened block explicitly. + +\code +#include +int foo() +{ + // some code ... + + int sum = 0; + EASY_BLOCK("Calculating sum"); + for (int i = 0; i < 10; ++i){ + sum += i; + } + EASY_END_BLOCK; + + // some antoher code here ... + + return sum; +} +\endcode + +\ingroup profiler +*/ +# define EASY_END_BLOCK ::profiler::endBlock(); + +/** Macro for creating event marker with custom name and color. + +Event marker is a block with zero duration and special type. + +\warning Event marker ends immidiately and calling EASY_END_BLOCK after EASY_EVENT +will end previously opened EASY_BLOCK or EASY_FUNCTION. + +\ingroup profiler +*/ +# define EASY_EVENT(name, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__),\ + ::std::is_base_of<::profiler::ForceConstStr, decltype(name)>::value));\ + ::profiler::storeEvent(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); + +/** Macro for enabling profiler. + +\ingroup profiler +*/ +# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); + +/** Macro for disabling profiler. + +\ingroup profiler +*/ +# define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); + +/** Macro for current thread registration. + +\note If this thread has been already registered then nothing happens. + +\ingroup profiler +*/ +# define EASY_THREAD(name)\ + EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = 0;\ + if (!EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__))\ + EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::registerThread(name); + +/** Macro for current thread registration and creating a thread guard object. + +\note If this thread has been already registered then nothing happens. + +\note Also creates thread guard which marks thread as "expired" on it's destructor +and creates "ThreadFinished" profiler event. + +\ingroup profiler +*/ +# define EASY_THREAD_SCOPE(name)\ + EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = 0;\ + ::profiler::ThreadGuard EASY_TOKEN_CONCATENATE(unique_profiler_thread_guard, __LINE__);\ + if (!EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__))\ + EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::registerThreadScoped(name,\ + EASY_TOKEN_CONCATENATE(unique_profiler_thread_guard, __LINE__)); + +/** Macro for main thread registration. + +This is just for user's comfort. There is no difference for EasyProfiler GUI between different threads. + +\ingroup profiler +*/ +# define EASY_MAIN_THREAD EASY_THREAD("Main") + +/** Enable or disable event tracing (context switch events). + +\note Default value is controlled by EASY_OPTION_EVENT_TRACING_ENABLED macro. + +\note Change will take effect on the next call to EASY_PROFILER_ENABLE. + +\sa EASY_PROFILER_ENABLE, EASY_OPTION_EVENT_TRACING_ENABLED + +\ingroup profiler +*/ +# define EASY_SET_EVENT_TRACING_ENABLED(isEnabled) ::profiler::setEventTracingEnabled(isEnabled); + +/** Set event tracing thread priority (low or normal). + +Event tracing with low priority will affect your application performance much more less, but +it can be late to gather information about thread/process (thread could be finished to the moment +when event tracing thread will be awaken) and you will not see process name and process id +information in GUI for such threads. You will still be able to see all context switch events. + +Event tracing with normal priority could gather more information about processes but potentially +it could affect performance as it has more work to do. Usually you will not notice any performance +breakdown, but if you care about that then you change set event tracing priority level to low. + +\sa EASY_OPTION_LOW_PRIORITY_EVENT_TRACING + +\ingroup profiler +*/ +# define EASY_SET_LOW_PRIORITY_EVENT_TRACING(isLowPriority) ::profiler::setLowPriorityEventTracing(isLowPriority); + +/** Macro for setting temporary log-file path for Unix event tracing system. + +\note Default value is "/tmp/cs_profiling_info.log". + +\ingroup profiler +*/ +# define EASY_EVENT_TRACING_SET_LOG(filename) ::profiler::setContextSwitchLogFilename(filename); + +/** Macro returning current path to the temporary log-file for Unix event tracing system. + +\ingroup profiler +*/ +# define EASY_EVENT_TRACING_LOG ::profiler::getContextSwitchLogFilename(); + +// EasyProfiler settings: + +/** If != 0 then EasyProfiler will measure time for blocks storage expansion. +If 0 then EasyProfiler will be compiled without blocks of code responsible +for measuring these events. + +These are "EasyProfiler.ExpandStorage" blocks on a diagram. + +\ingroup profiler +*/ +# ifndef EASY_OPTION_MEASURE_STORAGE_EXPAND +# define EASY_OPTION_MEASURE_STORAGE_EXPAND 0 +# endif + +# if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 +/** If true then "EasyProfiler.ExpandStorage" blocks are enabled by default and will be +writed to output file or translated over the net. +If false then you need to enable these blocks via GUI if you want to see them. + +\ingroup profiler +*/ +# ifndef EASY_OPTION_STORAGE_EXPAND_BLOCKS_ON +# define EASY_OPTION_STORAGE_EXPAND_BLOCKS_ON true +# endif + +# endif // EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 + +/** If true then EasyProfiler event tracing is enabled by default +and will be turned on and off when you call profiler::setEnabled(). +Otherwise, it have to be turned on via GUI and then it will be +turned on/off with next calls of profiler::setEnabled(). + +\ingroup profiler +*/ +# ifndef EASY_OPTION_EVENT_TRACING_ENABLED +# define EASY_OPTION_EVENT_TRACING_ENABLED true +# endif + +/** If true then EasyProfiler.ETW thread (Event tracing for Windows) will have low priority by default. + +\sa EASY_SET_LOW_PRIORITY_EVENT_TRACING + +\note You can always change priority level via GUI or API while profiling session is not launched. +You don't need to rebuild or restart your application for that. + +\ingroup profiler +*/ +# ifndef EASY_OPTION_LOW_PRIORITY_EVENT_TRACING +# define EASY_OPTION_LOW_PRIORITY_EVENT_TRACING true +# endif + + +/** If != 0 then EasyProfiler will print error messages into stderr. +Otherwise, no log messages will be printed. + +\ingroup profiler +*/ +# ifndef EASY_OPTION_LOG_ENABLED +# define EASY_OPTION_LOG_ENABLED 0 +# endif + +/** If != 0 then EasyProfiler will start listening thread immidiately on ProfileManager initialization. + +\sa startListen + +\ingroup profiler +*/ +# ifndef EASY_OPTION_START_LISTEN_ON_STARTUP +# define EASY_OPTION_START_LISTEN_ON_STARTUP 0 +# endif + +#else // #ifdef BUILD_WITH_EASY_PROFILER + +# define EASY_BLOCK(...) +# define EASY_NONSCOPED_BLOCK(...) +# define EASY_FUNCTION(...) +# define EASY_END_BLOCK +# define EASY_PROFILER_ENABLE +# define EASY_PROFILER_DISABLE +# define EASY_EVENT(...) +# define EASY_THREAD(...) +# define EASY_THREAD_SCOPE(...) +# define EASY_MAIN_THREAD +# define EASY_SET_EVENT_TRACING_ENABLED(isEnabled) +# define EASY_SET_LOW_PRIORITY_EVENT_TRACING(isLowPriority) + +# ifndef _WIN32 +# define EASY_EVENT_TRACING_SET_LOG(filename) +# define EASY_EVENT_TRACING_LOG "" +# endif + +# ifndef EASY_OPTION_MEASURE_STORAGE_EXPAND +# define EASY_OPTION_MEASURE_STORAGE_EXPAND 0 +# endif + +# ifndef EASY_OPTION_EVENT_TRACING_ENABLED +# define EASY_OPTION_EVENT_TRACING_ENABLED false +# endif + +# ifndef EASY_OPTION_LOW_PRIORITY_EVENT_TRACING +# define EASY_OPTION_LOW_PRIORITY_EVENT_TRACING true +# endif + +# ifndef EASY_OPTION_LOG_ENABLED +# define EASY_OPTION_LOG_ENABLED 0 +# endif + +# ifndef EASY_OPTION_START_LISTEN_ON_STARTUP +# define EASY_OPTION_START_LISTEN_ON_STARTUP 0 +# endif + +#endif // #ifndef BUILD_WITH_EASY_PROFILER + +# ifndef EASY_DEFAULT_PORT +# define EASY_DEFAULT_PORT 28077 +# endif + +/** Alias for EASY_PROFILER_ENABLE. + +Added for clarification. + +\sa EASY_PROFILER_ENABLE + +\ingroup profiler +*/ +#define EASY_START_CAPTURE EASY_PROFILER_ENABLE + +/** Alias for EASY_PROFILER_DISABLE. + +Added for clarification. + +\sa EASY_PROFILER_DISABLE + +\ingroup profiler +*/ +#define EASY_STOP_CAPTURE EASY_PROFILER_DISABLE + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + EASY_CONSTEXPR uint16_t DEFAULT_PORT = EASY_DEFAULT_PORT; + + ////////////////////////////////////////////////////////////////////// + // Core API + // Note: It is better to use macros defined above than a direct calls to API. + // But some API functions does not have macro wrappers... + +#ifdef USING_EASY_PROFILER + extern "C" { + + /** Returns current time in ticks. + + You can use it if you want to store block explicitly. + + \retval Current CPU time in ticks. + + \ingroup profiler + */ + PROFILER_API timestamp_t currentTime(); + + /** Convert ticks to nanoseconds. + + \retval _ticks converted to nanoseconds. + + \ingroup profiler + */ + PROFILER_API timestamp_t toNanoseconds(timestamp_t _ticks); + + /** Convert ticks to microseconds. + + \retval _ticks converted to microseconds. + + \ingroup profiler + */ + PROFILER_API timestamp_t toMicroseconds(timestamp_t _ticks); + + /** Registers static description of a block. + + It is general information which is common for all such blocks. + Includes color, block type (see BlockType), file-name, line-number, compile-time name of a block and enable-flag. + + \note This API function is used by EASY_EVENT, EASY_BLOCK, EASY_FUNCTION macros. + There is no need to invoke this function explicitly. + + \retval Pointer to registered block description. + + \ingroup profiler + */ + PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color, bool _copyName = false); + + /** Stores event in the blocks list. + + An event ends instantly and has zero duration. + + \note There is no need to invoke this function explicitly - use EASY_EVENT macro instead. + + \param _desc Reference to the previously registered description. + \param _runtimeName Standard zero-terminated string which will be copied to the events buffer. + + \note _runtimeName must be an empty string ("") if you do not want to set name to the event at run-time. + + \ingroup profiler + */ + PROFILER_API void storeEvent(const BaseBlockDescriptor* _desc, const char* _runtimeName = ""); + + /** Stores block explicitly in the blocks list. + + Use this function for additional flexibility if you want to set block duration manually. + + \param _desc Reference to the previously registered description. + \param _runtimeName Standard zero-terminated string which will be copied to the events buffer. + \param _beginTime begin time of the block + \param _endTime end time of the block + + \note _runtimeName must be an empty string ("") if you do not want to set name to the block at run-time. + + \ingroup profiler + */ + PROFILER_API void storeBlock(const BaseBlockDescriptor* _desc, const char* _runtimeName, timestamp_t _beginTime, timestamp_t _endTime); + + /** Begins scoped block. + + \ingroup profiler + */ + PROFILER_API void beginBlock(Block& _block); + + /** Begins non-scoped block. + + \param _desc Reference to the previously registered description (see registerDescription). + \param _runtimeName Standard zero-terminated string which will be copied to the block buffer when block will end. + + \note There is no need to invoke this function explicitly - use EASY_NONSCOPED_BLOCK macro instead. + EASY_NONSCOPED_BLOCK macro could be used for higher flexibility if you have to begin block in one + function and end it in another one. + + \note _runtimeName must be an empty string ("") if you do not want to set name to the block at run-time. + \note _runtimeName is copied only when block ends so you must ensure it's validity until block end. + + \warning You have to end this block explicitly. + + \ingroup profiler + */ + PROFILER_API void beginNonScopedBlock(const BaseBlockDescriptor* _desc, const char* _runtimeName = ""); + + /** Ends last started block. + + Use this only if you want to finish block explicitly. + + \ingroup profiler + */ + PROFILER_API void endBlock(); + + /** Enable or disable profiler. + + AKA start or stop profiling (capturing blocks). + + \ingroup profiler + */ + PROFILER_API void setEnabled(bool _isEnable); + PROFILER_API bool isEnabled(); + + /** Save all gathered blocks into file. + + \note This also disables profiler. + + \retval Number of saved blocks. If 0 then nothing was profiled or an error occured. + + \ingroup profiler + */ + PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); + + /** Register current thread and give it a name. + + Also creates a scoped ThreadGuard which would unregister thread on it's destructor. + This helps for memory management while using an old compiler whitout thread_local support. + + \note Only first call of registerThread() for the current thread will have an effect. + + \note Use this function if you want to build your source code with an old compiler (MSVC < 2013, GCC < 4.8, Clang < 3.3). + Otherwise there is no need in this function because a thread_local ThreadGuard created inside. + + \retval Registered name of the thread. It may differ from _name if the thread was registered before. + + \sa registerThread, ThreadGuard + + \ingroup profiler + */ + PROFILER_API const char* registerThreadScoped(const char* _name, ThreadGuard&); + + /** Register current thread and give it a name. + + \note Only first call of registerThread() for the current thread will have an effect. + + \retval Registered name of the thread. It may differ from _name if the thread was registered before. + + \ingroup profiler + */ + PROFILER_API const char* registerThread(const char* _name); + + /** Enable or disable event tracing. + + \note This change will take an effect on the next call of setEnabled(true); + + \sa setEnabled, EASY_SET_EVENT_TRACING_ENABLED + + \ingroup profiler + */ + PROFILER_API void setEventTracingEnabled(bool _isEnable); + PROFILER_API bool isEventTracingEnabled(); + + /** Set event tracing thread priority (low or normal). + + \note This change will take effect on the next call of setEnabled(true); + + \sa setEnabled, EASY_SET_LOW_PRIORITY_EVENT_TRACING + + \ingroup profiler + */ + PROFILER_API void setLowPriorityEventTracing(bool _isLowPriority); + PROFILER_API bool isLowPriorityEventTracing(); + + /** Set temporary log-file path for Unix event tracing system. + + \note Default value is "/tmp/cs_profiling_info.log". + + \ingroup profiler + */ + PROFILER_API void setContextSwitchLogFilename(const char* _name); + + /** Returns current path to the temporary log-file for Unix event tracing system. + + \ingroup profiler + */ + PROFILER_API const char* getContextSwitchLogFilename(); + + /** Start listening for network commands. + + Launches a separate listening thread which would listen to the network commands (start, stop, etc.). + The listening thread sends all profiled blocks via network after receiving network command 'stop'. + + \ingroup profiler + */ + PROFILER_API void startListen(uint16_t _port = ::profiler::DEFAULT_PORT); + + /** Stops listening thread. + + \note This would be invoked automatically on application exit. + + \note Does not send any messages to the network, just stops thread. + + \ingroup profiler + */ + PROFILER_API void stopListen(); + + /** Check if listening thread launched. + + \ingroup profiler + */ + PROFILER_API bool isListening(); + + /** Returns current major version. + + \ingroup profiler + */ + PROFILER_API uint8_t versionMajor(); + + /** Returns current minor version. + + \ingroup profiler + */ + PROFILER_API uint8_t versionMinor(); + + /** Returns current version patch. + + \ingroup profiler + */ + PROFILER_API uint16_t versionPatch(); + + /** Returns current version in 32-bit integer format. + + \note Format is: 0x MAJ-MAJ MIN-MIN PATCH-PATCH-PATCH-PATCH + For example v1.3.0 is: 0x01030000 + + \ingroup profiler + */ + PROFILER_API uint32_t version(); + + /** Returns current version string. + + Example: "v1.3.0" + + \ingroup profiler + */ + PROFILER_API const char* versionName(); + + /** Returns true if current thread has been marked as Main. + Otherwise, returns false. + + \ingroup profiler + */ + PROFILER_API bool isMainThread(); + + /** Returns last frame duration for current thread. + + \param _durationCast desired duration units (could be cpu-ticks or microseconds) + + \ingroup profiler + */ + PROFILER_API timestamp_t this_thread_frameTime(Duration _durationCast = ::profiler::MICROSECONDS); + + /** Returns local max of frame duration for current thread. + + Local max is maximum frame duration since last frameTimeLocalMax() call. + + \param _durationCast desired duration units (could be cpu-ticks or microseconds) + + \ingroup profiler + */ + PROFILER_API timestamp_t this_thread_frameTimeLocalMax(Duration _durationCast = ::profiler::MICROSECONDS); + + /** Returns local average of frame duration for current thread. + + Local average is average frame duration since last frameTimeLocalAvg() call. + + \param _durationCast desired duration units (could be cpu-ticks or microseconds) + + \ingroup profiler + */ + PROFILER_API timestamp_t this_thread_frameTimeLocalAvg(Duration _durationCast = ::profiler::MICROSECONDS); + + /** Returns last frame duration for main thread. + + \param _durationCast desired duration units (could be cpu-ticks or microseconds) + + \ingroup profiler + */ + PROFILER_API timestamp_t main_thread_frameTime(Duration _durationCast = ::profiler::MICROSECONDS); + + /** Returns local max of frame duration for main thread. + + Local max is maximum frame duration since last frameTimeLocalMax() call. + + \param _durationCast desired duration units (could be cpu-ticks or microseconds) + + \ingroup profiler + */ + PROFILER_API timestamp_t main_thread_frameTimeLocalMax(Duration _durationCast = ::profiler::MICROSECONDS); + + /** Returns local average of frame duration for main thread. + + Local average is average frame duration since last frameTimeLocalAvg() call. + + \param _durationCast desired duration units (could be cpu-ticks or microseconds) + + \ingroup profiler + */ + PROFILER_API timestamp_t main_thread_frameTimeLocalAvg(Duration _durationCast = ::profiler::MICROSECONDS); + + } +#else + inline timestamp_t currentTime() { return 0; } + inline timestamp_t toNanoseconds(timestamp_t) { return 0; } + inline timestamp_t toMicroseconds(timestamp_t) { return 0; } + inline const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t, bool = false) + { return reinterpret_cast(0xbad); } + inline void endBlock() { } + inline void setEnabled(bool) { } + inline bool isEnabled() { return false; } + inline void storeEvent(const BaseBlockDescriptor*, const char* = "") { } + inline void storeBlock(const BaseBlockDescriptor*, const char*, timestamp_t, timestamp_t) { } + inline void beginBlock(Block&) { } + inline void beginNonScopedBlock(const BaseBlockDescriptor*, const char* = "") { } + inline uint32_t dumpBlocksToFile(const char*) { return 0; } + inline const char* registerThreadScoped(const char*, ThreadGuard&) { return ""; } + inline const char* registerThread(const char*) { return ""; } + inline void setEventTracingEnabled(bool) { } + inline bool isEventTracingEnabled() { return false; } + inline void setLowPriorityEventTracing(bool) { } + inline bool isLowPriorityEventTracing() { return false; } + inline void setContextSwitchLogFilename(const char*) { } + inline const char* getContextSwitchLogFilename() { return ""; } + inline void startListen(uint16_t = ::profiler::DEFAULT_PORT) { } + inline void stopListen() { } + inline bool isListening() { return false; } + inline uint8_t versionMajor() { return 0; } + inline uint8_t versionMinor() { return 0; } + inline uint16_t versionPatch() { return 0; } + inline uint32_t version() { return 0; } + inline const char* versionName() { return "v0.0.0_disabled"; } + inline bool isMainThread() { return false; } + inline timestamp_t this_thread_frameTime(Duration = ::profiler::MICROSECONDS) { return 0; } + inline timestamp_t this_thread_frameTimeLocalMax(Duration = ::profiler::MICROSECONDS) { return 0; } + inline timestamp_t this_thread_frameTimeLocalAvg(Duration = ::profiler::MICROSECONDS) { return 0; } + inline timestamp_t main_thread_frameTime(Duration = ::profiler::MICROSECONDS) { return 0; } + inline timestamp_t main_thread_frameTimeLocalMax(Duration = ::profiler::MICROSECONDS) { return 0; } + inline timestamp_t main_thread_frameTimeLocalAvg(Duration = ::profiler::MICROSECONDS) { return 0; } +#endif + + /** API functions binded to current thread. + + \ingroup profiler + */ + namespace this_thread { + + inline const char* registrate(const char* _name) { + return ::profiler::registerThread(_name); + } + + inline const char* registrate(const char* _name, ThreadGuard& _threadGuard) { + return ::profiler::registerThreadScoped(_name, _threadGuard); + } + + inline timestamp_t frameTime(Duration _durationCast = ::profiler::MICROSECONDS) { + return ::profiler::this_thread_frameTime(_durationCast); + } + + inline timestamp_t frameTimeLocalMax(Duration _durationCast = ::profiler::MICROSECONDS) { + return ::profiler::this_thread_frameTimeLocalMax(_durationCast); + } + + inline timestamp_t frameTimeLocalAvg(Duration _durationCast = ::profiler::MICROSECONDS) { + return ::profiler::this_thread_frameTimeLocalAvg(_durationCast); + } + + inline bool isMain() { + return ::profiler::isMainThread(); + } + + } // END of namespace this_thread. + + /** API functions binded to main thread. + + Could be called from any thread. + + \ingroup profiler + */ + namespace main_thread { + + inline timestamp_t frameTime(Duration _durationCast = ::profiler::MICROSECONDS) { + return ::profiler::main_thread_frameTime(_durationCast); + } + + inline timestamp_t frameTimeLocalMax(Duration _durationCast = ::profiler::MICROSECONDS) { + return ::profiler::main_thread_frameTimeLocalMax(_durationCast); + } + + inline timestamp_t frameTimeLocalAvg(Duration _durationCast = ::profiler::MICROSECONDS) { + return ::profiler::main_thread_frameTimeLocalAvg(_durationCast); + } + + /** Always returns true. + */ + inline EASY_CONSTEXPR_FCN bool isMain() { + return true; + } + + } // END of namespace main_thread. + + /** Alias for isEnabled(). + + Added for clarification. + + \sa isEnabled + + \ingroup profiler + */ + EASY_FORCE_INLINE bool isCapturing() { return isEnabled(); } + + /** Alias for EASY_PROFILER_ENABLE. + + Added for clarification. + + \sa EASY_PROFILER_ENABLE + + \ingroup profiler + */ + EASY_FORCE_INLINE void startCapture() { EASY_PROFILER_ENABLE; } + + /** Alias for EASY_PROFILER_DISABLE. + + Added for clarification. + + \sa EASY_PROFILER_DISABLE + + \ingroup profiler + */ + EASY_FORCE_INLINE void stopCapture() { EASY_PROFILER_DISABLE; } + + ////////////////////////////////////////////////////////////////////// + +} // END of namespace profiler. + +#if defined ( __clang__ ) +# pragma clang diagnostic pop +#endif + +#endif // EASY_PROFILER_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/reader.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/reader.h new file mode 100644 index 0000000..cbfe414 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/reader.h @@ -0,0 +1,428 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef PROFILER_READER____H +#define PROFILER_READER____H + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + using calls_number_t = uint32_t; + using block_index_t = uint32_t; + +#pragma pack(push, 1) + struct BlockStatistics EASY_FINAL + { + ::profiler::timestamp_t total_duration; ///< Total duration of all block calls + ::profiler::timestamp_t total_children_duration; ///< Total duration of all children of all block calls + ::profiler::block_index_t min_duration_block; ///< Will be used in GUI to jump to the block with min duration + ::profiler::block_index_t max_duration_block; ///< Will be used in GUI to jump to the block with max duration + ::profiler::block_index_t parent_block; ///< Index of block which is "parent" for "per_parent_stats" or "frame" for "per_frame_stats" or thread-id for "per_thread_stats" + ::profiler::calls_number_t calls_number; ///< Block calls number + + explicit BlockStatistics(::profiler::timestamp_t _duration, ::profiler::block_index_t _block_index, ::profiler::block_index_t _parent_index) + : total_duration(_duration) + , total_children_duration(0) + , min_duration_block(_block_index) + , max_duration_block(_block_index) + , parent_block(_parent_index) + , calls_number(1) + { + } + + //BlockStatistics() = default; + + inline ::profiler::timestamp_t average_duration() const + { + return total_duration / calls_number; + } + + }; // END of struct BlockStatistics. +#pragma pack(pop) + + extern "C" PROFILER_API void release_stats(BlockStatistics*& _stats); + + ////////////////////////////////////////////////////////////////////////// + + class BlocksTree EASY_FINAL + { + using This = BlocksTree; + + public: + + using blocks_t = ::std::vector; + using children_t = ::std::vector<::profiler::block_index_t>; + + children_t children; ///< List of children blocks. May be empty. + + union { + ::profiler::SerializedBlock* node; ///< Pointer to serialized data for regular block (id, name, begin, end etc.) + ::profiler::SerializedCSwitch* cs; ///< Pointer to serialized data for context switch (thread_id, name, begin, end etc.) + ::profiler::ArbitraryValue* value; ///< Pointer to serialized data for arbitrary value + }; + + ::profiler::BlockStatistics* per_parent_stats; ///< Pointer to statistics for this block within the parent (may be nullptr for top-level blocks) + ::profiler::BlockStatistics* per_frame_stats; ///< Pointer to statistics for this block within the frame (may be nullptr for top-level blocks) + ::profiler::BlockStatistics* per_thread_stats; ///< Pointer to statistics for this block within the bounds of all frames per current thread + uint8_t depth; ///< Maximum number of sublevels (maximum children depth) + + BlocksTree(const This&) = delete; + This& operator = (const This&) = delete; + + BlocksTree() EASY_NOEXCEPT + : node(nullptr) + , per_parent_stats(nullptr) + , per_frame_stats(nullptr) + , per_thread_stats(nullptr) + , depth(0) + { + + } + + BlocksTree(This&& that) EASY_NOEXCEPT + : BlocksTree() + { + make_move(::std::forward(that)); + } + + This& operator = (This&& that) EASY_NOEXCEPT + { + make_move(::std::forward(that)); + return *this; + } + + ~BlocksTree() EASY_NOEXCEPT + { + release_stats(per_thread_stats); + release_stats(per_parent_stats); + release_stats(per_frame_stats); + } + + bool operator < (const This& other) const EASY_NOEXCEPT + { + if (node == nullptr || other.node == nullptr) + return false; + return node->begin() < other.node->begin(); + } + + void shrink_to_fit() EASY_NOEXCEPT + { + //for (auto& child : children) + // child.shrink_to_fit(); + + // shrink version 1: + //children.shrink_to_fit(); + + // shrink version 2: + //children_t new_children; + //new_children.reserve(children.size()); + //::std::move(children.begin(), children.end(), ::std::back_inserter(new_children)); + //new_children.swap(children); + } + + private: + + void make_move(This&& that) EASY_NOEXCEPT + { + if (per_thread_stats != that.per_thread_stats) + release_stats(per_thread_stats); + + if (per_parent_stats != that.per_parent_stats) + release_stats(per_parent_stats); + + if (per_frame_stats != that.per_frame_stats) + release_stats(per_frame_stats); + + children = ::std::move(that.children); + node = that.node; + per_parent_stats = that.per_parent_stats; + per_frame_stats = that.per_frame_stats; + per_thread_stats = that.per_thread_stats; + depth = that.depth; + + that.node = nullptr; + that.per_parent_stats = nullptr; + that.per_frame_stats = nullptr; + that.per_thread_stats = nullptr; + } + + }; // END of class BlocksTree. + + ////////////////////////////////////////////////////////////////////////// + + class BlocksTreeRoot EASY_FINAL + { + using This = BlocksTreeRoot; + + public: + + BlocksTree::children_t children; ///< List of children indexes + BlocksTree::children_t sync; ///< List of context-switch events + BlocksTree::children_t events; ///< List of events indexes + std::string thread_name; ///< Name of this thread + ::profiler::timestamp_t profiled_time; ///< Profiled time of this thread (sum of all children duration) + ::profiler::timestamp_t wait_time; ///< Wait time of this thread (sum of all context switches) + ::profiler::thread_id_t thread_id; ///< System Id of this thread + ::profiler::block_index_t frames_number; ///< Total frames number (top-level blocks) + ::profiler::block_index_t blocks_number; ///< Total blocks number including their children + uint8_t depth; ///< Maximum stack depth (number of levels) + + BlocksTreeRoot(const This&) = delete; + This& operator = (const This&) = delete; + + BlocksTreeRoot() EASY_NOEXCEPT + : profiled_time(0), wait_time(0), thread_id(0), frames_number(0), blocks_number(0), depth(0) + { + } + + BlocksTreeRoot(This&& that) EASY_NOEXCEPT + : children(::std::move(that.children)) + , sync(::std::move(that.sync)) + , events(::std::move(that.events)) + , thread_name(::std::move(that.thread_name)) + , profiled_time(that.profiled_time) + , wait_time(that.wait_time) + , thread_id(that.thread_id) + , frames_number(that.frames_number) + , blocks_number(that.blocks_number) + , depth(that.depth) + { + } + + This& operator = (This&& that) EASY_NOEXCEPT + { + children = ::std::move(that.children); + sync = ::std::move(that.sync); + events = ::std::move(that.events); + thread_name = ::std::move(that.thread_name); + profiled_time = that.profiled_time; + wait_time = that.wait_time; + thread_id = that.thread_id; + frames_number = that.frames_number; + blocks_number = that.blocks_number; + depth = that.depth; + return *this; + } + + inline bool got_name() const EASY_NOEXCEPT + { + return !thread_name.empty(); + } + + inline const char* name() const EASY_NOEXCEPT + { + return thread_name.c_str(); + } + + bool operator < (const This& other) const EASY_NOEXCEPT + { + return thread_id < other.thread_id; + } + + }; // END of class BlocksTreeRoot. + + using blocks_t = ::profiler::BlocksTree::blocks_t; + using thread_blocks_tree_t = ::std::unordered_map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot, ::estd::hash<::profiler::thread_id_t> >; + + ////////////////////////////////////////////////////////////////////////// + + class PROFILER_API SerializedData EASY_FINAL + { + char* m_data; + size_t m_size; + + public: + + SerializedData(const SerializedData&) = delete; + SerializedData& operator = (const SerializedData&) = delete; + + SerializedData() : m_data(nullptr), m_size(0) + { + } + + SerializedData(SerializedData&& that) : m_data(that.m_data), m_size(that.m_size) + { + that.m_data = nullptr; + that.m_size = 0; + } + + ~SerializedData() + { + clear(); + } + + void set(uint64_t _size); + void extend(uint64_t _size); + + SerializedData& operator = (SerializedData&& that) + { + set(that.m_data, that.m_size); + that.m_data = nullptr; + that.m_size = 0; + return *this; + } + + char* operator [] (uint64_t i) + { + return m_data + i; + } + + const char* operator [] (uint64_t i) const + { + return m_data + i; + } + + bool empty() const + { + return m_size == 0; + } + + uint64_t size() const + { + return m_size; + } + + char* data() + { + return m_data; + } + + const char* data() const + { + return m_data; + } + + void clear() + { + set(nullptr, 0); + } + + void swap(SerializedData& other) + { + char* d = other.m_data; + uint64_t sz = other.m_size; + + other.m_data = m_data; + other.m_size = m_size; + + m_data = d; + m_size = (size_t)sz; + } + + private: + + void set(char* _data, uint64_t _size); + + }; // END of class SerializedData. + + ////////////////////////////////////////////////////////////////////////// + + using descriptors_list_t = ::std::vector; + +} // END of namespace profiler. + +extern "C" { + + PROFILER_API ::profiler::block_index_t fillTreesFromFile(::std::atomic& progress, const char* filename, + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + uint32_t& version, + bool gather_statistics, + ::std::stringstream& _log); + + PROFILER_API ::profiler::block_index_t fillTreesFromStream(::std::atomic& progress, ::std::stringstream& str, + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + uint32_t& version, + bool gather_statistics, + ::std::stringstream& _log); + + PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& str, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::std::stringstream& _log); +} + +inline ::profiler::block_index_t fillTreesFromFile(const char* filename, ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + uint32_t& version, + bool gather_statistics, + ::std::stringstream& _log) +{ + ::std::atomic progress = ATOMIC_VAR_INIT(0); + return fillTreesFromFile(progress, filename, serialized_blocks, serialized_descriptors, descriptors, _blocks, threaded_trees, total_descriptors_number, version, gather_statistics, _log); +} + +inline bool readDescriptionsFromStream(::std::stringstream& str, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::std::stringstream& _log) +{ + ::std::atomic progress = ATOMIC_VAR_INIT(0); + return readDescriptionsFromStream(progress, str, serialized_descriptors, descriptors, _log); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif // PROFILER_READER____H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/serialized_block.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/serialized_block.h new file mode 100644 index 0000000..dd07711 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/serialized_block.h @@ -0,0 +1,289 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_SERIALIZED_BLOCK_H +#define EASY_PROFILER_SERIALIZED_BLOCK_H + +#include +#include + +class CSwitchBlock; + +namespace profiler { + + template + struct Value; + + template + struct Value; + + ////////////////////////////////////////////////////////////////////////// + + class PROFILER_API SerializedBlock EASY_FINAL : public BaseBlockData + { + friend ::ProfileManager; + friend ::ThreadStorage; + + public: + + inline const char* data() const { return reinterpret_cast(this); } + + ///< Run-time block name is stored right after main BaseBlockData data + inline const char* name() const { return data() + sizeof(BaseBlockData); } + + SerializedBlock(const SerializedBlock&) = delete; + SerializedBlock& operator = (const SerializedBlock&) = delete; + SerializedBlock(SerializedBlock&&) = delete; + SerializedBlock& operator = (SerializedBlock&&) = delete; + ~SerializedBlock() = delete; + + private: + + explicit SerializedBlock(const Block& block, uint16_t name_length); + + }; // END of SerializedBlock. + + ////////////////////////////////////////////////////////////////////////// + +#pragma pack(push, 1) + class PROFILER_API CSwitchEvent : public Event + { + thread_id_t m_thread_id; + + public: + + CSwitchEvent() = default; + CSwitchEvent(const CSwitchEvent&) = default; + explicit CSwitchEvent(timestamp_t _begin_time, thread_id_t _tid) EASY_NOEXCEPT; + + inline thread_id_t tid() const EASY_NOEXCEPT { return m_thread_id; } + + }; // END of class CSwitchEvent. +#pragma pack(pop) + + class PROFILER_API SerializedCSwitch EASY_FINAL : public CSwitchEvent + { + friend ::ProfileManager; + friend ::ThreadStorage; + + public: + + inline const char* data() const { return reinterpret_cast(this); } + + ///< Run-time block name is stored right after main CSwitchEvent data + inline const char* name() const { return data() + sizeof(CSwitchEvent); } + + SerializedCSwitch(const SerializedCSwitch&) = delete; + SerializedCSwitch& operator = (const SerializedCSwitch&) = delete; + SerializedCSwitch(SerializedCSwitch&&) = delete; + SerializedCSwitch& operator = (SerializedCSwitch&&) = delete; + ~SerializedCSwitch() = delete; + + private: + + explicit SerializedCSwitch(const CSwitchBlock& block, uint16_t name_length); + + }; // END of SerializedCSwitch. + + ////////////////////////////////////////////////////////////////////////// + +#pragma pack(push, 1) + class PROFILER_API SerializedBlockDescriptor EASY_FINAL : public BaseBlockDescriptor + { + uint16_t m_nameLength; ///< Length of the name including trailing '\0' sybmol + + public: + + inline const char* data() const { + return reinterpret_cast(this); + } + + ///< Name is stored right after m_nameLength + inline const char* name() const { + static const auto shift = sizeof(BaseBlockDescriptor) + sizeof(decltype(m_nameLength)); + return data() + shift; + } + + ///< File name is stored right after the name + inline const char* file() const { + return name() + m_nameLength; + } + + inline void setStatus(EasyBlockStatus _status) EASY_NOEXCEPT { + m_status = _status; + } + + // Instances of this class can not be created or destroyed directly + SerializedBlockDescriptor() = delete; + SerializedBlockDescriptor(const SerializedBlockDescriptor&) = delete; + SerializedBlockDescriptor& operator = (const SerializedBlockDescriptor&) = delete; + SerializedBlockDescriptor(SerializedBlockDescriptor&&) = delete; + SerializedBlockDescriptor& operator = (SerializedBlockDescriptor&&) = delete; + ~SerializedBlockDescriptor() = delete; + + }; // END of SerializedBlockDescriptor. +//#pragma pack(pop) + + ////////////////////////////////////////////////////////////////////////// + +//#pragma pack(push, 1) + class PROFILER_API ArbitraryValue : protected BaseBlockData + { + friend ::ThreadStorage; + + protected: + + char m_nameStub; ///< Artificial padding which is used to imitate SerializedBlock::name() == 0 behavior + char m_padding; ///< Padding to the bound of 2 bytes + uint16_t m_size; + DataType m_type; + bool m_isArray; + vin_t m_value_id; + + explicit ArbitraryValue(timestamp_t _timestamp, vin_t _vin, block_id_t _id, + uint16_t _size, DataType _type, bool _isArray) + : BaseBlockData(_timestamp, _timestamp, _id) + , m_nameStub(0) + , m_padding(0) + , m_size(_size) + , m_type(_type) + , m_isArray(_isArray) + , m_value_id(_vin) + { + } + + public: + + using BaseBlockData::id; + using Event::begin; + + ~ArbitraryValue() = delete; + + const char* data() const { + return reinterpret_cast(this) + sizeof(ArbitraryValue); + } + + vin_t value_id() const { + return m_value_id; + } + + DataType type() const { + return m_type; + } + + bool isArray() const { + return m_isArray; + } + + template + const Value* toValue() const { + return m_type == dataType ? static_cast*>(this) : nullptr; + } + + template + const Value::data_type, false>* toValue() const { + static_assert(StdToDataType::data_type != DataType::TypesCount, + "You should use standard builtin scalar types as profiler::Value type!"); + return toValue::data_type>(); + } + + template + const Value* toArray() const { + return m_isArray && m_type == dataType ? static_cast*>(this) : nullptr; + } + + template + const Value::data_type, true>* toArray() const { + static_assert(StdToDataType::data_type != DataType::TypesCount, + "You should use standard builtin scalar types as profiler::Value type!"); + return toArray::data_type>(); + } + }; // end of class ArbitraryValue. +#pragma pack(pop) + + ////////////////////////////////////////////////////////////////////////// + + template + struct Value EASY_FINAL : public ArbitraryValue { + using value_type = typename StdType::value_type; + value_type value() const { return *reinterpret_cast(data()); } + ~Value() = delete; + }; + + + template + struct Value EASY_FINAL : public ArbitraryValue { + using value_type = typename StdType::value_type; + const value_type* value() const { return reinterpret_cast(data()); } + uint16_t size() const { return m_size / sizeof(value_type); } + value_type operator [] (int i) const { return value()[i]; } + value_type at(int i) const { return value()[i]; } + ~Value() = delete; + }; + + + template <> + struct Value EASY_FINAL : public ArbitraryValue { + using value_type = char; + const char* value() const { return data(); } + uint16_t size() const { return m_size; } + char operator [] (int i) const { return data()[i]; } + char at(int i) const { return data()[i]; } + const char* c_str() const { return data(); } + ~Value() = delete; + }; + + ////////////////////////////////////////////////////////////////////////// + + template + using SingleValue = Value; + + template + using ArrayValue = Value; + + using StringValue = Value; + + ////////////////////////////////////////////////////////////////////////// + +} // END of namespace profiler. + +#endif // EASY_PROFILER_SERIALIZED_BLOCK_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/include/easy/utility.h b/3rdparty/easyprofiler/easy_profiler_core/include/easy/utility.h new file mode 100644 index 0000000..167e17d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/include/easy/utility.h @@ -0,0 +1,63 @@ + + +#ifndef EASY_PROFILER_UTILITY_H +#define EASY_PROFILER_UTILITY_H + +#include +#include +#include +#include + +namespace estd { + +////////////////////////////////////////////////////////////////////////// + + namespace detail { + template struct hasher { + using type = const T&; + EASY_FORCE_INLINE size_t operator () (type value) const { return ::std::hash {}(value); } }; + + template struct hasher { + using type = T; + EASY_FORCE_INLINE size_t operator () (type value) const { return static_cast(value); } }; + } + + template struct hash EASY_FINAL : public ::estd::detail::hasher sizeof(void*))> { + using ::estd::detail::hasher sizeof(void*))>::operator(); + }; + + template struct hash EASY_FINAL { + EASY_FORCE_INLINE size_t operator () (const T* value) const { return reinterpret_cast(value); } }; + + template struct hash EASY_FINAL { + EASY_FORCE_INLINE size_t operator () (const T* value) const { return reinterpret_cast(value); } }; + +////////////////////////////////////////////////////////////////////////// + + template + inline EASY_CONSTEXPR_FCN Q clamp(T min_value, Q value, W max_value) { + return static_cast(min_value < value ? (value < max_value ? value : max_value) : min_value); + } + + template + EASY_FORCE_INLINE EASY_CONSTEXPR_FCN T sqr(T value) { + return value * value; + } + + template + EASY_FORCE_INLINE EASY_CONSTEXPR_FCN int sign(T value) { return value < 0 ? -1 : 1; } + + template + inline EASY_CONSTEXPR_FCN T absmin(T a, T b) { return abs(a) < abs(b) ? a : b; } + + template + inline T logn(T value) { + EASY_STATIC_CONSTEXPR double div = 1.0 / log2((double)N); + return log2(value) * div; + } + +////////////////////////////////////////////////////////////////////////// + +} // end of namespace estd. + +#endif // EASY_PROFILER_UTILITY_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/nonscoped_block.cpp b/3rdparty/easyprofiler/easy_profiler_core/nonscoped_block.cpp new file mode 100644 index 0000000..6fa5d77 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/nonscoped_block.cpp @@ -0,0 +1,92 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#include "nonscoped_block.h" +#include +#include + +NonscopedBlock::NonscopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, bool) + : profiler::Block(_desc, _runtimeName, false), m_runtimeName(nullptr) +{ + +} + +NonscopedBlock::~NonscopedBlock() +{ + // Actually destructor should not be invoked because StackBuffer do manual memory management + + m_end = m_begin; // to restrict profiler::Block to invoke profiler::endBlock() on destructor. + free(m_runtimeName); +} + +void NonscopedBlock::copyname() +{ + // Here we need to copy m_name to m_runtimeName to ensure that + // it would be alive to the moment we will serialize the block + + if ((m_status & profiler::ON) == 0) + return; + + if (*m_name != 0) + { + auto len = strlen(m_name); + m_runtimeName = static_cast(malloc(len + 1)); + + // memcpy should be faster than strncpy because we know + // actual bytes number and both strings have the same size + memcpy(m_runtimeName, m_name, len); + + m_runtimeName[len] = 0; + m_name = m_runtimeName; + } + else + { + m_name = ""; + } +} + +void NonscopedBlock::destroy() +{ + // free memory used by m_runtimeName + free(m_runtimeName); + m_name = ""; +} diff --git a/3rdparty/easyprofiler/easy_profiler_core/nonscoped_block.h b/3rdparty/easyprofiler/easy_profiler_core/nonscoped_block.h new file mode 100644 index 0000000..5655f5a --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/nonscoped_block.h @@ -0,0 +1,73 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_NONSCOPED_BLOCK_H +#define EASY_PROFILER_NONSCOPED_BLOCK_H + +#include + +class NonscopedBlock : public profiler::Block +{ + char* m_runtimeName; ///< A copy of _runtimeName to make it safe to begin block in one function and end it in another + +public: + + NonscopedBlock() = delete; + NonscopedBlock(const NonscopedBlock&) = delete; + NonscopedBlock(NonscopedBlock&&) = delete; + NonscopedBlock& operator = (const NonscopedBlock&) = delete; + NonscopedBlock& operator = (NonscopedBlock&&) = delete; + + NonscopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, bool = false); + ~NonscopedBlock(); + + /** Copy string from m_name to m_runtimeName to make it safe to end block in another function. + + Performs any work if block is ON and m_name != "" + */ + void copyname(); + + void destroy(); + +}; // END of class NonscopedBlock. + +#endif // EASY_PROFILER_NONSCOPED_BLOCK_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/outstream.h b/3rdparty/easyprofiler/easy_profiler_core/outstream.h new file mode 100644 index 0000000..f27762d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/outstream.h @@ -0,0 +1,115 @@ +/************************************************************************ +* file name : outstream.h +* ----------------- : +* creation time : 2016/09/11 +* authors : Sergey Yagovtsev, Victor Zarubkin +* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains definition of output stream helpers. +* ----------------- : +* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from profiler_manager.h/.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER__OUTPUT_STREAM__H_ +#define EASY_PROFILER__OUTPUT_STREAM__H_ + +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + class OStream + { + ::std::stringstream m_stream; + + public: + + explicit OStream() : m_stream(std::ios_base::out | std::ios_base::binary) + { + + } + + template void write(const char* _data, T _size) + { + m_stream.write(_data, _size); + } + + template void write(const T& _data) + { + m_stream.write((const char*)&_data, sizeof(T)); + } + + ::std::stringstream& stream() + { + return m_stream; + } + + const ::std::stringstream& stream() const + { + return m_stream; + } + + void clear() + { +#if defined(__GNUC__) && __GNUC__ < 5 + // gcc 4 has a known bug which has been solved in gcc 5: + // std::stringstream has no swap() method :( + m_stream.str(::std::string()); +#else + ::std::stringstream().swap(m_stream); +#endif + } + + }; // END of class OStream. + +} // END of namespace profiler. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__OUTPUT_STREAM__H_ diff --git a/3rdparty/easyprofiler/easy_profiler_core/profile_manager.cpp b/3rdparty/easyprofiler/easy_profiler_core/profile_manager.cpp new file mode 100644 index 0000000..d61246d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/profile_manager.cpp @@ -0,0 +1,2015 @@ +/************************************************************************ +* file name : profile_manager.cpp +* ----------------- : +* creation time : 2016/02/16 +* authors : Sergey Yagovtsev, Victor Zarubkin +* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of Profile manager and implement access c-function +* : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include "profile_manager.h" + +#include +#include +#include +#include + +#ifndef _WIN32 +# include +#endif + +#include "event_trace_win.h" +#include "current_time.h" +#include "current_thread.h" + +#ifdef __APPLE__ +#include +#include +#endif + +#if EASY_OPTION_LOG_ENABLED != 0 +# include + +# ifndef EASY_ERRORLOG +# define EASY_ERRORLOG ::std::cerr +# endif + +# ifndef EASY_LOG +# define EASY_LOG ::std::cerr +# endif + +# ifndef EASY_ERROR +# define EASY_ERROR(LOG_MSG) EASY_ERRORLOG << "EasyProfiler ERROR: " << LOG_MSG +# endif + +# ifndef EASY_WARNING +# define EASY_WARNING(LOG_MSG) EASY_ERRORLOG << "EasyProfiler WARNING: " << LOG_MSG +# endif + +# ifndef EASY_LOGMSG +# define EASY_LOGMSG(LOG_MSG) EASY_LOG << "EasyProfiler INFO: " << LOG_MSG +# endif + +# ifndef EASY_LOG_ONLY +# define EASY_LOG_ONLY(CODE) CODE +# endif + +#else + +# ifndef EASY_ERROR +# define EASY_ERROR(LOG_MSG) +# endif + +# ifndef EASY_WARNING +# define EASY_WARNING(LOG_MSG) +# endif + +# ifndef EASY_LOGMSG +# define EASY_LOGMSG(LOG_MSG) +# endif + +# ifndef EASY_LOG_ONLY +# define EASY_LOG_ONLY(CODE) +# endif + +#endif + +#ifdef min +# undef min +#endif + +#ifndef EASY_ENABLE_BLOCK_STATUS +# define EASY_ENABLE_BLOCK_STATUS 1 +#endif + +#if !defined(_WIN32) && !defined(EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS) +# define EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS 0 +#endif + +#ifndef EASY_OPTION_IMPLICIT_THREAD_REGISTRATION +# define EASY_OPTION_IMPLICIT_THREAD_REGISTRATION 0 +#endif + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +using namespace profiler; + +////////////////////////////////////////////////////////////////////////// + +#if !defined(EASY_PROFILER_VERSION_MAJOR) || !defined(EASY_PROFILER_VERSION_MINOR) || !defined(EASY_PROFILER_VERSION_PATCH) +# ifdef _WIN32 +# error EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined +# else +# error "EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined" +# endif +#endif + +# define EASY_PROFILER_PRODUCT_VERSION "v" EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MAJOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MINOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_PATCH) + +# define EASY_VERSION_INT(v_major, v_minor, v_patch) ((static_cast(v_major) << 24) | (static_cast(v_minor) << 16) | static_cast(v_patch)) +extern const uint32_t PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y'; +extern const uint32_t EASY_CURRENT_VERSION = EASY_VERSION_INT(EASY_PROFILER_VERSION_MAJOR, EASY_PROFILER_VERSION_MINOR, EASY_PROFILER_VERSION_PATCH); +# undef EASY_VERSION_INT + +////////////////////////////////////////////////////////////////////////// + +# define EASY_PROF_DISABLED 0 +# define EASY_PROF_ENABLED 1 +# define EASY_PROF_DUMP 2 + +////////////////////////////////////////////////////////////////////////// + +//auto& MANAGER = ProfileManager::instance(); +# define MANAGER ProfileManager::instance() +EASY_CONSTEXPR uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON; + +#if defined(EASY_CHRONO_CLOCK) +#include +const int64_t CPU_FREQUENCY = EASY_CHRONO_CLOCK::period::den / EASY_CHRONO_CLOCK::period::num; +# define TICKS_TO_US(ticks) ticks * 1000000LL / CPU_FREQUENCY +#elif defined(_WIN32) +const decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY = ([](){ LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return freq.QuadPart; })(); +# define TICKS_TO_US(ticks) ticks * 1000000LL / CPU_FREQUENCY +#else +# ifndef __APPLE__ +# include +# endif +int64_t calculate_cpu_frequency() +{ + double g_TicksPerNanoSec; + uint64_t begin = 0, end = 0; +#ifdef __APPLE__ + clock_serv_t cclock; + mach_timespec_t begints, endts; + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &begints); +#else + struct timespec begints, endts; + clock_gettime(CLOCK_MONOTONIC, &begints); +#endif + begin = getCurrentTime(); + volatile uint64_t i; + for (i = 0; i < 100000000; i++); /* must be CPU intensive */ + end = getCurrentTime(); +#ifdef __APPLE__ + clock_get_time(cclock, &endts); + mach_port_deallocate(mach_task_self(), cclock); +#else + clock_gettime(CLOCK_MONOTONIC, &endts); +#endif + struct timespec tmpts; + const int NANO_SECONDS_IN_SEC = 1000000000; + tmpts.tv_sec = endts.tv_sec - begints.tv_sec; + tmpts.tv_nsec = endts.tv_nsec - begints.tv_nsec; + if (tmpts.tv_nsec < 0) + { + tmpts.tv_sec--; + tmpts.tv_nsec += NANO_SECONDS_IN_SEC; + } + + uint64_t nsecElapsed = tmpts.tv_sec * 1000000000LL + tmpts.tv_nsec; + g_TicksPerNanoSec = (double)(end - begin) / (double)nsecElapsed; + + int64_t cpu_frequency = int(g_TicksPerNanoSec * 1000000); + + return cpu_frequency; +} + +static std::atomic CPU_FREQUENCY = ATOMIC_VAR_INIT(1); +# define TICKS_TO_US(ticks) ticks * 1000 / CPU_FREQUENCY.load(std::memory_order_acquire) +#endif + +extern const profiler::color_t EASY_COLOR_INTERNAL_EVENT = 0xffffffff; // profiler::colors::White +EASY_CONSTEXPR profiler::color_t EASY_COLOR_THREAD_END = 0xff212121; // profiler::colors::Dark +EASY_CONSTEXPR profiler::color_t EASY_COLOR_START = 0xff4caf50; // profiler::colors::Green +EASY_CONSTEXPR profiler::color_t EASY_COLOR_END = 0xfff44336; // profiler::colors::Red + +////////////////////////////////////////////////////////////////////////// + +EASY_THREAD_LOCAL static ::ThreadStorage* THIS_THREAD = nullptr; +EASY_THREAD_LOCAL static bool THIS_THREAD_IS_MAIN = false; + +EASY_THREAD_LOCAL static profiler::timestamp_t THIS_THREAD_FRAME_T_MAX = 0ULL; +EASY_THREAD_LOCAL static profiler::timestamp_t THIS_THREAD_FRAME_T_CUR = 0ULL; +EASY_THREAD_LOCAL static profiler::timestamp_t THIS_THREAD_FRAME_T_ACC = 0ULL; +EASY_THREAD_LOCAL static uint32_t THIS_THREAD_N_FRAMES = 0; +EASY_THREAD_LOCAL static bool THIS_THREAD_FRAME_T_RESET_MAX = false; +EASY_THREAD_LOCAL static bool THIS_THREAD_FRAME_T_RESET_AVG = false; + +#ifdef EASY_CXX11_TLS_AVAILABLE +thread_local static profiler::ThreadGuard THIS_THREAD_GUARD; // thread guard for monitoring thread life time +#endif + +////////////////////////////////////////////////////////////////////////// + +#ifdef BUILD_WITH_EASY_PROFILER +# define EASY_EVENT_RES(res, name, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), MANAGER.addBlockDescriptor(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\ + res = MANAGER.storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)) + +# define EASY_FORCE_EVENT(timestamp, name, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), addBlockDescriptor(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\ + storeBlockForce(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp) + +# define EASY_FORCE_EVENT2(timestamp, name, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), addBlockDescriptor(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\ + storeBlockForce2(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp) + +# define EASY_FORCE_EVENT3(ts, timestamp, name, ...)\ + EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), addBlockDescriptor(\ + ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\ + storeBlockForce2(ts, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp) +#else +# ifndef EASY_PROFILER_API_DISABLED +# define EASY_PROFILER_API_DISABLED +# endif +# define EASY_EVENT_RES(res, name, ...) +# define EASY_FORCE_EVENT(timestamp, name, ...) +# define EASY_FORCE_EVENT2(timestamp, name, ...) +# define EASY_FORCE_EVENT3(ts, timestamp, name, ...) +#endif + +////////////////////////////////////////////////////////////////////////// + +extern "C" { + +#if !defined(EASY_PROFILER_API_DISABLED) + PROFILER_API timestamp_t currentTime() + { + return getCurrentTime(); + } + + PROFILER_API timestamp_t toNanoseconds(timestamp_t _ticks) + { +#if defined(EASY_CHRONO_CLOCK) || defined(_WIN32) + return _ticks * 1000000000LL / CPU_FREQUENCY; +#else + return _ticks / CPU_FREQUENCY.load(std::memory_order_acquire); +#endif + } + + PROFILER_API timestamp_t toMicroseconds(timestamp_t _ticks) + { + return TICKS_TO_US(_ticks); + } + + PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color, bool _copyName) + { + return MANAGER.addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line, _block_type, _color, _copyName); + } + + PROFILER_API void endBlock() + { + MANAGER.endBlock(); + } + + PROFILER_API void setEnabled(bool isEnable) + { + MANAGER.setEnabled(isEnable); + } + + PROFILER_API bool isEnabled() + { + return MANAGER.isEnabled(); + } + + PROFILER_API void storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, size_t _size, bool _isArray, ValueId _vin) + { + MANAGER.storeValue(_desc, _type, _data, _size, _isArray, _vin); + } + + PROFILER_API void storeEvent(const BaseBlockDescriptor* _desc, const char* _runtimeName) + { + MANAGER.storeBlock(_desc, _runtimeName); + } + + PROFILER_API void storeBlock(const BaseBlockDescriptor* _desc, const char* _runtimeName, timestamp_t _beginTime, timestamp_t _endTime) + { + MANAGER.storeBlock(_desc, _runtimeName, _beginTime, _endTime); + } + + PROFILER_API void beginBlock(Block& _block) + { + MANAGER.beginBlock(_block); + } + + PROFILER_API void beginNonScopedBlock(const BaseBlockDescriptor* _desc, const char* _runtimeName) + { + MANAGER.beginNonScopedBlock(_desc, _runtimeName); + } + + PROFILER_API uint32_t dumpBlocksToFile(const char* filename) + { + return MANAGER.dumpBlocksToFile(filename); + } + + PROFILER_API const char* registerThreadScoped(const char* name, ThreadGuard& threadGuard) + { + return MANAGER.registerThread(name, threadGuard); + } + + PROFILER_API const char* registerThread(const char* name) + { + return MANAGER.registerThread(name); + } + + PROFILER_API void setEventTracingEnabled(bool _isEnable) + { + MANAGER.setEventTracingEnabled(_isEnable); + } + + PROFILER_API bool isEventTracingEnabled() + { + return MANAGER.isEventTracingEnabled(); + } + +# ifdef _WIN32 + PROFILER_API void setLowPriorityEventTracing(bool _isLowPriority) + { + EasyEventTracer::instance().setLowPriority(_isLowPriority); + } + + PROFILER_API bool isLowPriorityEventTracing() + { + return EasyEventTracer::instance().isLowPriority(); + } +# else + PROFILER_API void setLowPriorityEventTracing(bool) { } + PROFILER_API bool isLowPriorityEventTracing() { return false; } +# endif + + PROFILER_API void setContextSwitchLogFilename(const char* name) + { + return MANAGER.setContextSwitchLogFilename(name); + } + + PROFILER_API const char* getContextSwitchLogFilename() + { + return MANAGER.getContextSwitchLogFilename(); + } + + PROFILER_API void startListen(uint16_t _port) + { + return MANAGER.startListen(_port); + } + + PROFILER_API void stopListen() + { + return MANAGER.stopListen(); + } + + PROFILER_API bool isListening() + { + return MANAGER.isListening(); + } + + PROFILER_API bool isMainThread() + { + return THIS_THREAD_IS_MAIN; + } + + PROFILER_API timestamp_t this_thread_frameTime(Duration _durationCast) + { + if (_durationCast == profiler::TICKS) + return THIS_THREAD_FRAME_T_CUR; + return TICKS_TO_US(THIS_THREAD_FRAME_T_CUR); + } + + PROFILER_API timestamp_t this_thread_frameTimeLocalMax(Duration _durationCast) + { + THIS_THREAD_FRAME_T_RESET_MAX = true; + if (_durationCast == profiler::TICKS) + return THIS_THREAD_FRAME_T_MAX; + return TICKS_TO_US(THIS_THREAD_FRAME_T_MAX); + } + + PROFILER_API timestamp_t this_thread_frameTimeLocalAvg(Duration _durationCast) + { + THIS_THREAD_FRAME_T_RESET_AVG = true; + auto avgDuration = THIS_THREAD_N_FRAMES > 0 ? THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES : 0; + if (_durationCast == profiler::TICKS) + return avgDuration; + return TICKS_TO_US(avgDuration); + } + + PROFILER_API timestamp_t main_thread_frameTime(Duration _durationCast) + { + const auto ticks = THIS_THREAD_IS_MAIN ? THIS_THREAD_FRAME_T_CUR : MANAGER.curFrameDuration(); + if (_durationCast == profiler::TICKS) + return ticks; + return TICKS_TO_US(ticks); + } + + PROFILER_API timestamp_t main_thread_frameTimeLocalMax(Duration _durationCast) + { + if (THIS_THREAD_IS_MAIN) + { + THIS_THREAD_FRAME_T_RESET_MAX = true; + if (_durationCast == profiler::TICKS) + return THIS_THREAD_FRAME_T_MAX; + return TICKS_TO_US(THIS_THREAD_FRAME_T_MAX); + } + + if (_durationCast == profiler::TICKS) + return MANAGER.maxFrameDuration(); + return TICKS_TO_US(MANAGER.maxFrameDuration()); + } + + PROFILER_API timestamp_t main_thread_frameTimeLocalAvg(Duration _durationCast) + { + if (THIS_THREAD_IS_MAIN) + { + THIS_THREAD_FRAME_T_RESET_AVG = true; + auto avgDuration = THIS_THREAD_N_FRAMES > 0 ? THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES : 0; + if (_durationCast == profiler::TICKS) + return avgDuration; + return TICKS_TO_US(avgDuration); + } + + if (_durationCast == profiler::TICKS) + return MANAGER.avgFrameDuration(); + return TICKS_TO_US(MANAGER.avgFrameDuration()); + } + +#else + PROFILER_API timestamp_t currentTime() { return 0; } + PROFILER_API timestamp_t toNanoseconds(timestamp_t) { return 0; } + PROFILER_API timestamp_t toMicroseconds(timestamp_t) { return 0; } + PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t, bool) { return reinterpret_cast(0xbad); } + PROFILER_API void endBlock() { } + PROFILER_API void setEnabled(bool) { } + PROFILER_API bool isEnabled() { return false; } + PROFILER_API void storeValue(const BaseBlockDescriptor*, DataType, const void*, size_t, bool, ValueId) {} + PROFILER_API void storeEvent(const BaseBlockDescriptor*, const char*) { } + PROFILER_API void storeBlock(const BaseBlockDescriptor*, const char*, timestamp_t, timestamp_t) { } + PROFILER_API void beginBlock(Block&) { } + PROFILER_API void beginNonScopedBlock(const BaseBlockDescriptor*, const char*) { } + PROFILER_API uint32_t dumpBlocksToFile(const char*) { return 0; } + PROFILER_API const char* registerThreadScoped(const char*, ThreadGuard&) { return ""; } + PROFILER_API const char* registerThread(const char*) { return ""; } + PROFILER_API void setEventTracingEnabled(bool) { } + PROFILER_API bool isEventTracingEnabled() { return false; } + PROFILER_API void setLowPriorityEventTracing(bool) { } + PROFILER_API bool isLowPriorityEventTracing(bool) { return false; } + PROFILER_API void setContextSwitchLogFilename(const char*) { } + PROFILER_API const char* getContextSwitchLogFilename() { return ""; } + PROFILER_API void startListen(uint16_t) { } + PROFILER_API void stopListen() { } + PROFILER_API bool isListening() { return false; } + + PROFILER_API bool isMainThread() { return false; } + PROFILER_API timestamp_t this_thread_frameTime(Duration) { return 0; } + PROFILER_API timestamp_t this_thread_frameTimeLocalMax(Duration) { return 0; } + PROFILER_API timestamp_t this_thread_frameTimeLocalAvg(Duration) { return 0; } + PROFILER_API timestamp_t main_thread_frameTime(Duration) { return 0; } + PROFILER_API timestamp_t main_thread_frameTimeLocalMax(Duration) { return 0; } + PROFILER_API timestamp_t main_thread_frameTimeLocalAvg(Duration) { return 0; } +#endif + + PROFILER_API uint8_t versionMajor() + { + static_assert(0 <= EASY_PROFILER_VERSION_MAJOR && EASY_PROFILER_VERSION_MAJOR <= 255, "EASY_PROFILER_VERSION_MAJOR must be defined in range [0, 255]"); + return EASY_PROFILER_VERSION_MAJOR; + } + + PROFILER_API uint8_t versionMinor() + { + static_assert(0 <= EASY_PROFILER_VERSION_MINOR && EASY_PROFILER_VERSION_MINOR <= 255, "EASY_PROFILER_VERSION_MINOR must be defined in range [0, 255]"); + return EASY_PROFILER_VERSION_MINOR; + } + + PROFILER_API uint16_t versionPatch() + { + static_assert(0 <= EASY_PROFILER_VERSION_PATCH && EASY_PROFILER_VERSION_PATCH <= 65535, "EASY_PROFILER_VERSION_PATCH must be defined in range [0, 65535]"); + return EASY_PROFILER_VERSION_PATCH; + } + + PROFILER_API uint32_t version() + { + return EASY_CURRENT_VERSION; + } + + PROFILER_API const char* versionName() + { + return EASY_PROFILER_PRODUCT_VERSION +#ifdef EASY_PROFILER_API_DISABLED + "_disabled" +#endif + ; + } + +} + +////////////////////////////////////////////////////////////////////////// + +SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length) + : BaseBlockData(block) +{ + char* pName = const_cast(name()); + if (name_length) strncpy(pName, block.name(), name_length); + pName[name_length] = 0; +} + +SerializedCSwitch::SerializedCSwitch(const CSwitchBlock& block, uint16_t name_length) + : CSwitchEvent(block) +{ + char* pName = const_cast(name()); + if (name_length) strncpy(pName, block.name(), name_length); + pName[name_length] = 0; +} + +////////////////////////////////////////////////////////////////////////// + +BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status, int _line, + block_type_t _block_type, color_t _color) EASY_NOEXCEPT + : m_id(_id) + , m_line(_line) + , m_type(_block_type) + , m_color(_color) + , m_status(_status) +{ + +} + +////////////////////////////////////////////////////////////////////////// + +#ifndef EASY_BLOCK_DESC_FULL_COPY +# define EASY_BLOCK_DESC_FULL_COPY 1 +#endif + +#if EASY_BLOCK_DESC_FULL_COPY == 0 +# define EASY_BLOCK_DESC_STRING const char* +# define EASY_BLOCK_DESC_STRING_LEN(s) static_cast(strlen(s) + 1) +# define EASY_BLOCK_DESC_STRING_VAL(s) s +#else +# define EASY_BLOCK_DESC_STRING std::string +# define EASY_BLOCK_DESC_STRING_LEN(s) static_cast(s.size() + 1) +# define EASY_BLOCK_DESC_STRING_VAL(s) s.c_str() +#endif + +class BlockDescriptor : public BaseBlockDescriptor +{ + friend ProfileManager; + + EASY_BLOCK_DESC_STRING m_filename; ///< Source file name where this block is declared + EASY_BLOCK_DESC_STRING m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier + +public: + + BlockDescriptor(block_id_t _id, EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + : BaseBlockDescriptor(_id, _status, _line, _block_type, _color) + , m_filename(_filename) + , m_name(_name) + { + } + + const char* name() const { + return EASY_BLOCK_DESC_STRING_VAL(m_name); + } + + const char* filename() const { + return EASY_BLOCK_DESC_STRING_VAL(m_filename); + } + + uint16_t nameSize() const { + return EASY_BLOCK_DESC_STRING_LEN(m_name); + } + + uint16_t filenameSize() const { + return EASY_BLOCK_DESC_STRING_LEN(m_filename); + } + +}; // END of class BlockDescriptor. + +////////////////////////////////////////////////////////////////////////// + +ThreadGuard::~ThreadGuard() +{ +#ifndef EASY_PROFILER_API_DISABLED + if (m_id != 0 && THIS_THREAD != nullptr && THIS_THREAD->id == m_id) + { + bool isMarked = false; + EASY_EVENT_RES(isMarked, "ThreadFinished", EASY_COLOR_THREAD_END, ::profiler::FORCE_ON); + THIS_THREAD->profiledFrameOpened.store(false, std::memory_order_release); + THIS_THREAD->expired.store(isMarked ? 2 : 1, std::memory_order_release); + THIS_THREAD = nullptr; + } +#endif +} + +////////////////////////////////////////////////////////////////////////// + +ProfileManager::ProfileManager() : +#ifdef _WIN32 + m_processId(GetProcessId(GetCurrentProcess())) +#else + m_processId((processid_t)getpid()) +#endif + , m_usedMemorySize(0) + , m_beginTime(0) + , m_endTime(0) +{ + m_profilerStatus = ATOMIC_VAR_INIT(EASY_PROF_DISABLED); + m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_OPTION_EVENT_TRACING_ENABLED); + m_isAlreadyListening = ATOMIC_VAR_INIT(false); + m_stopDumping = ATOMIC_VAR_INIT(false); + m_stopListen = ATOMIC_VAR_INIT(false); + + m_mainThreadId = ATOMIC_VAR_INIT(0); + m_frameMax = ATOMIC_VAR_INIT(0); + m_frameAvg = ATOMIC_VAR_INIT(0); + m_frameCur = ATOMIC_VAR_INIT(0); + m_frameMaxReset = ATOMIC_VAR_INIT(false); + m_frameAvgReset = ATOMIC_VAR_INIT(false); + +#if !defined(EASY_PROFILER_API_DISABLED) && EASY_OPTION_START_LISTEN_ON_STARTUP != 0 + startListen(profiler::DEFAULT_PORT); +#endif + +#if !defined(EASY_PROFILER_API_DISABLED) && !defined(EASY_CHRONO_CLOCK) && !defined(_WIN32) + const int64_t cpu_frequency = calculate_cpu_frequency(); + CPU_FREQUENCY.store(cpu_frequency, std::memory_order_release); +#endif +} + +ProfileManager::~ProfileManager() +{ +#ifndef EASY_PROFILER_API_DISABLED + stopListen(); +#endif + + for (auto desc : m_descriptors) { +#if EASY_BLOCK_DESC_FULL_COPY == 0 + if (desc) + desc->~BlockDescriptor(); + free(desc); +#else + delete desc; +#endif + } +} + +#ifndef EASY_MAGIC_STATIC_AVAILABLE +class ProfileManagerInstance { + friend ProfileManager; + ProfileManager instance; +} PROFILE_MANAGER; +#endif + +////////////////////////////////////////////////////////////////////////// + +ProfileManager& ProfileManager::instance() +{ +#ifndef EASY_MAGIC_STATIC_AVAILABLE + return PROFILE_MANAGER.instance; +#else + ///C++11 makes possible to create Singleton without any warry about thread-safeness + ///http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/ + static ProfileManager profileManager; + return profileManager; +#endif +} + +////////////////////////////////////////////////////////////////////////// + +ThreadStorage& ProfileManager::_threadStorage(profiler::thread_id_t _thread_id) +{ + return m_threads[_thread_id]; +} + +ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_id) +{ + auto it = m_threads.find(_thread_id); + return it != m_threads.end() ? &it->second : nullptr; +} + +////////////////////////////////////////////////////////////////////////// + +const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _defaultStatus, + const char* _autogenUniqueId, + const char* _name, + const char* _filename, + int _line, + block_type_t _block_type, + color_t _color, + bool _copyName) +{ + guard_lock_t lock(m_storedSpin); + + descriptors_map_t::key_type key(_autogenUniqueId); + auto it = m_descriptorsMap.find(key); + if (it != m_descriptorsMap.end()) + return m_descriptors[it->second]; + + const auto nameLen = strlen(_name); + m_usedMemorySize += sizeof(profiler::SerializedBlockDescriptor) + nameLen + strlen(_filename) + 2; + +#if EASY_BLOCK_DESC_FULL_COPY == 0 + BlockDescriptor* desc = nullptr; + + if (_copyName) + { + void* data = malloc(sizeof(BlockDescriptor) + nameLen + 1); + char* name = reinterpret_cast(data) + sizeof(BlockDescriptor); + strncpy(name, _name, nameLen); + desc = ::new (data)BlockDescriptor(static_cast(m_descriptors.size()), _defaultStatus, name, _filename, _line, _block_type, _color); + } + else + { + void* data = malloc(sizeof(BlockDescriptor)); + desc = ::new (data)BlockDescriptor(static_cast(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color); + } +#else + auto desc = new BlockDescriptor(static_cast(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color); +#endif + + m_descriptors.emplace_back(desc); + m_descriptorsMap.emplace(key, desc->id()); + + return desc; +} + +////////////////////////////////////////////////////////////////////////// + +void ProfileManager::storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, size_t _size, bool _isArray, ValueId _vin) +{ + const auto state = m_profilerStatus.load(std::memory_order_acquire); + if (state != EASY_PROF_ENABLED || (_desc->m_status & profiler::ON) == 0) + return; + + if (THIS_THREAD == nullptr) + registerThread(); + +#if EASY_ENABLE_BLOCK_STATUS != 0 + if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + return; +#endif + + THIS_THREAD->storeValue(getCurrentTime(), _desc->id(), _type, _data, _size, _isArray, _vin); +} + +////////////////////////////////////////////////////////////////////////// + +bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName) +{ + const auto state = m_profilerStatus.load(std::memory_order_acquire); + if (state == EASY_PROF_DISABLED || (_desc->m_status & profiler::ON) == 0) + return false; + + if (state == EASY_PROF_DUMP) + { + if (THIS_THREAD == nullptr || THIS_THREAD->halt) + return false; + } + else if (THIS_THREAD == nullptr) + { + registerThread(); + } + +#if EASY_ENABLE_BLOCK_STATUS != 0 + if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + return false; +#endif + + const auto time = getCurrentTime(); + THIS_THREAD->storeBlock(profiler::Block(time, time, _desc->id(), _runtimeName)); + + return true; +} + +bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime) +{ + const auto state = m_profilerStatus.load(std::memory_order_acquire); + if (state == EASY_PROF_DISABLED || (_desc->m_status & profiler::ON) == 0) + return false; + + if (state == EASY_PROF_DUMP) + { + if (THIS_THREAD == nullptr || THIS_THREAD->halt) + return false; + } + else if (THIS_THREAD == nullptr) + { + registerThread(); + } + +#if EASY_ENABLE_BLOCK_STATUS != 0 + if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + return false; +#endif + + profiler::Block b(_beginTime, _endTime, _desc->id(), _runtimeName); + THIS_THREAD->storeBlock(b); + b.m_end = b.m_begin; + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +void ProfileManager::storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp) +{ + if ((_desc->m_status & profiler::ON) == 0) + return; + + if (THIS_THREAD == nullptr) + registerThread(); + +#if EASY_ENABLE_BLOCK_STATUS != 0 + if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + return; +#endif + + _timestamp = getCurrentTime(); + THIS_THREAD->storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName)); +} + +void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp) +{ + if ((_desc->m_status & profiler::ON) == 0) + return; + + if (THIS_THREAD == nullptr) + registerThread(); + +#if EASY_ENABLE_BLOCK_STATUS != 0 + if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + return; +#endif + + THIS_THREAD->storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName)); +} + +void ProfileManager::storeBlockForce2(ThreadStorage& _registeredThread, const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp) +{ + _registeredThread.storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName)); +} + +////////////////////////////////////////////////////////////////////////// + +void ProfileManager::beginBlock(Block& _block) +{ + if (THIS_THREAD == nullptr) + registerThread(); + + if (++THIS_THREAD->stackSize > 1) + { + _block.m_status = profiler::OFF; + THIS_THREAD->blocks.openedList.emplace_back(_block); + return; + } + + bool empty = true; + const auto state = m_profilerStatus.load(std::memory_order_acquire); + switch (state) + { + case EASY_PROF_DISABLED: + { + _block.m_status = profiler::OFF; + THIS_THREAD->halt = false; + THIS_THREAD->blocks.openedList.emplace_back(_block); + beginFrame(); + return; + } + + case EASY_PROF_DUMP: + { + const bool halt = THIS_THREAD->halt; + if (halt || THIS_THREAD->blocks.openedList.empty()) + { + _block.m_status = profiler::OFF; + THIS_THREAD->blocks.openedList.emplace_back(_block); + + if (!halt) + { + THIS_THREAD->halt = true; + beginFrame(); + } + + return; + } + + empty = false; + break; + } + + default: + { + empty = THIS_THREAD->blocks.openedList.empty(); + break; + } + } + + THIS_THREAD->stackSize = 0; + THIS_THREAD->halt = false; + + auto blockStatus = _block.m_status; +#if EASY_ENABLE_BLOCK_STATUS != 0 + if (THIS_THREAD->allowChildren) + { +#endif + if (blockStatus & profiler::ON) + _block.start(); +#if EASY_ENABLE_BLOCK_STATUS != 0 + THIS_THREAD->allowChildren = ((blockStatus & profiler::OFF_RECURSIVE) == 0); + } + else if (blockStatus & FORCE_ON_FLAG) + { + _block.start(); + _block.m_status = profiler::FORCE_ON_WITHOUT_CHILDREN; + } + else + { + _block.m_status = profiler::OFF_RECURSIVE; + } +#endif + + if (empty) + { + beginFrame(); + THIS_THREAD->profiledFrameOpened.store(true, std::memory_order_release); + } + + THIS_THREAD->blocks.openedList.emplace_back(_block); +} + +void ProfileManager::beginNonScopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName) +{ + if (THIS_THREAD == nullptr) + registerThread(); + + NonscopedBlock& b = THIS_THREAD->nonscopedBlocks.push(_desc, _runtimeName, false); + beginBlock(b); + b.copyname(); +} + +void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin) +{ + auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); + if (ts != nullptr) + // Dirty hack: _target_thread_id will be written to the field "block_id_t m_id" + // and will be available calling method id(). + ts->sync.openedList.emplace_back(_time, _target_thread_id, _target_process); +} + +////////////////////////////////////////////////////////////////////////// + +void ProfileManager::endBlock() +{ + if (--THIS_THREAD->stackSize > 0) + { + THIS_THREAD->popSilent(); + return; + } + + THIS_THREAD->stackSize = 0; + if (THIS_THREAD->halt || m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_DISABLED) + { + THIS_THREAD->popSilent(); + endFrame(); + return; + } + + if (THIS_THREAD->blocks.openedList.empty()) + return; + + Block& top = THIS_THREAD->blocks.openedList.back(); + if (top.m_status & profiler::ON) + { + if (!top.finished()) + top.finish(); + THIS_THREAD->storeBlock(top); + } + else + { + top.m_end = top.m_begin; // this is to restrict endBlock() call inside ~Block() + } + + if (!top.m_isScoped) + THIS_THREAD->nonscopedBlocks.pop(); + + THIS_THREAD->blocks.openedList.pop_back(); + const bool empty = THIS_THREAD->blocks.openedList.empty(); + if (empty) + { + THIS_THREAD->profiledFrameOpened.store(false, std::memory_order_release); + endFrame(); +#if EASY_ENABLE_BLOCK_STATUS != 0 + THIS_THREAD->allowChildren = true; + } + else + { + THIS_THREAD->allowChildren = + ((THIS_THREAD->blocks.openedList.back().get().m_status & profiler::OFF_RECURSIVE) == 0); + } +#else + } +#endif +} + +void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin) +{ + ThreadStorage* ts = nullptr; + if (_process_id == m_processId) + { + // Implicit thread registration. + // If thread owned by current process then create new ThreadStorage if there is no one +#if EASY_OPTION_IMPLICIT_THREAD_REGISTRATION != 0 + ts = _lockSpin ? &threadStorage(_thread_id) : &_threadStorage(_thread_id); +# if !defined(_WIN32) && !defined(EASY_CXX11_TLS_AVAILABLE) +# if EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS != 0 +# pragma message "Warning: Implicit thread registration together with removing empty unguarded threads may cause application crash because there is no possibility to check thread state (dead or alive) for pthreads and removed ThreadStorage may be reused if thread is still alive." +# else +# pragma message "Warning: Implicit thread registration without removing empty unguarded threads may lead to memory leak because there is no possibility to check thread state (dead or alive) for pthreads." +# endif +# endif +#endif + } + else + { + // If thread owned by another process OR _process_id IS UNKNOWN then do not create ThreadStorage for this + ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); + } + + if (ts == nullptr || ts->sync.openedList.empty()) + return; + + CSwitchBlock& lastBlock = ts->sync.openedList.back(); + lastBlock.m_end = _endtime; + + ts->storeCSwitch(lastBlock); + ts->sync.openedList.pop_back(); +} + +////////////////////////////////////////////////////////////////////////// + +void ProfileManager::beginFrame() +{ + THIS_THREAD->beginFrame(); +} + +void ProfileManager::endFrame() +{ + if (!THIS_THREAD->frameOpened) + return; + + const profiler::timestamp_t duration = THIS_THREAD->endFrame(); + + if (THIS_THREAD_FRAME_T_RESET_MAX) THIS_THREAD_FRAME_T_MAX = 0; + THIS_THREAD_FRAME_T_RESET_MAX = false; + + THIS_THREAD_FRAME_T_CUR = duration; + if (duration > THIS_THREAD_FRAME_T_MAX) + THIS_THREAD_FRAME_T_MAX = duration; + + THIS_THREAD_FRAME_T_RESET_AVG = THIS_THREAD_FRAME_T_RESET_AVG || THIS_THREAD_N_FRAMES > 10000; + + if (THIS_THREAD_IS_MAIN) + { + if (m_frameAvgReset.exchange(false, std::memory_order_release) || THIS_THREAD_FRAME_T_RESET_AVG) + { + if (THIS_THREAD_N_FRAMES > 0) + m_frameAvg.store(THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES, std::memory_order_release); + THIS_THREAD_FRAME_T_RESET_AVG = false; + THIS_THREAD_FRAME_T_ACC = duration; + THIS_THREAD_N_FRAMES = 1; + } + else + { + THIS_THREAD_FRAME_T_ACC += duration; + ++THIS_THREAD_N_FRAMES; + m_frameAvg.store(THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES, std::memory_order_release); + } + + const auto maxDuration = m_frameMax.load(std::memory_order_acquire); + if (m_frameMaxReset.exchange(false, std::memory_order_release) || duration > maxDuration) + m_frameMax.store(duration, std::memory_order_release); + + m_frameCur.store(duration, std::memory_order_release); + + return; + } + + const auto reset = (uint32_t)!THIS_THREAD_FRAME_T_RESET_AVG; + THIS_THREAD_FRAME_T_RESET_AVG = false; + THIS_THREAD_N_FRAMES = 1 + reset * THIS_THREAD_N_FRAMES; + THIS_THREAD_FRAME_T_ACC = duration + reset * THIS_THREAD_FRAME_T_ACC; +} + +profiler::timestamp_t ProfileManager::maxFrameDuration() +{ + auto duration = m_frameMax.load(std::memory_order_acquire); + m_frameMaxReset.store(true, std::memory_order_release); + return duration; +} + +profiler::timestamp_t ProfileManager::avgFrameDuration() +{ + auto duration = m_frameAvg.load(std::memory_order_acquire); + m_frameAvgReset.store(true, std::memory_order_release); + return duration; +} + +profiler::timestamp_t ProfileManager::curFrameDuration() const +{ + return m_frameCur.load(std::memory_order_acquire); +} + +////////////////////////////////////////////////////////////////////////// + +void ProfileManager::enableEventTracer() +{ +#ifdef _WIN32 + if (m_isEventTracingEnabled.load(std::memory_order_acquire)) + EasyEventTracer::instance().enable(true); +#endif +} + +void ProfileManager::disableEventTracer() +{ +#ifdef _WIN32 + EasyEventTracer::instance().disable(); +#endif +} + +void ProfileManager::setEnabled(bool isEnable) +{ + guard_lock_t lock(m_dumpSpin); + + auto time = getCurrentTime(); + const auto status = isEnable ? EASY_PROF_ENABLED : EASY_PROF_DISABLED; + const auto prev = m_profilerStatus.exchange(status, std::memory_order_release); + if (prev == status) + return; + + if (isEnable) + { + EASY_LOGMSG("Enabled profiling\n"); + enableEventTracer(); + m_beginTime = time; + } + else + { + EASY_LOGMSG("Disabled profiling\n"); + disableEventTracer(); + m_endTime = time; + } +} + +bool ProfileManager::isEnabled() const +{ + return m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_ENABLED; +} + +void ProfileManager::setEventTracingEnabled(bool _isEnable) +{ + m_isEventTracingEnabled.store(_isEnable, std::memory_order_release); +} + +bool ProfileManager::isEventTracingEnabled() const +{ + return m_isEventTracingEnabled.load(std::memory_order_acquire); +} + +////////////////////////////////////////////////////////////////////////// + +char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread) +{ + const char val = _registeredThread.expired.load(std::memory_order_acquire); + if (val != 0) + return val; + + if (_registeredThread.guarded) + return 0; + +#ifdef _WIN32 + // Check thread state for Windows + + DWORD exitCode = 0; + auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)_registeredThread.id); + if (hThread == nullptr || GetExitCodeThread(hThread, &exitCode) == FALSE || exitCode != STILL_ACTIVE) + { + // Thread has been expired + _registeredThread.expired.store(1, std::memory_order_release); + if (hThread != nullptr) + CloseHandle(hThread); + return 1; + } + + if (hThread != nullptr) + CloseHandle(hThread); + + return 0; +#else + // Check thread state for Linux and MacOS/iOS + + // This would drop the application if pthread already died + //return pthread_kill(_registeredThread.pthread_id, 0) != 0 ? 1 : 0; + + // There is no function to check external pthread state in Linux! :(( + +#ifndef EASY_CXX11_TLS_AVAILABLE +#pragma message "Warning: Your compiler does not support thread_local C++11 feature. Please use EASY_THREAD_SCOPE as much as possible. Otherwise, there is a possibility of memory leak if there are a lot of rapidly created and destroyed threads." +#endif + + return 0; +#endif +} + +////////////////////////////////////////////////////////////////////////// + +uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bool _lockSpin, bool _async) +{ + EASY_LOGMSG("dumpBlocksToStream(_lockSpin = " << _lockSpin << ")...\n"); + + if (_lockSpin) + m_dumpSpin.lock(); + + const auto state = m_profilerStatus.load(std::memory_order_acquire); + +#ifndef _WIN32 + const bool eventTracingEnabled = m_isEventTracingEnabled.load(std::memory_order_acquire); +#endif + + if (state == EASY_PROF_ENABLED) { + m_profilerStatus.store(EASY_PROF_DUMP, std::memory_order_release); + disableEventTracer(); + m_endTime = getCurrentTime(); + } + + + // This is to make sure that no new descriptors or new threads will be + // added until we finish sending data. + //m_spin.lock(); + // This is the only place using both spins, so no dead-lock will occur + + if (_async && m_stopDumping.load(std::memory_order_acquire)) + { + if (_lockSpin) + m_dumpSpin.unlock(); + return 0; + } + + // Wait for some time to be sure that all operations which began before setEnabled(false) will be finished. + // This is much better than inserting spin-lock or atomic variable check into each store operation. + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + + // wait for all threads finish opened frames + EASY_LOG_ONLY(bool logged = false); + for (auto thread_it = m_threads.begin(), end = m_threads.end(); thread_it != end;) + { + if (_async && m_stopDumping.load(std::memory_order_acquire)) + { + if (_lockSpin) + m_dumpSpin.unlock(); + return 0; + } + + if (!thread_it->second.profiledFrameOpened.load(std::memory_order_acquire)) + { + ++thread_it; + EASY_LOG_ONLY(logged = false); + } + else + { + EASY_LOG_ONLY( + if (!logged) + { + logged = true; + if (thread_it->second.named) + EASY_WARNING("Waiting for thread \"" << thread_it->second.name << "\" finish opened frame (which is top EASY_BLOCK for this thread)...\n"); + else + EASY_WARNING("Waiting for thread " << thread_it->first << " finish opened frame (which is top EASY_BLOCK for this thread)...\n"); + } + ); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + + m_profilerStatus.store(EASY_PROF_DISABLED, std::memory_order_release); + + EASY_LOGMSG("All threads have closed frames\n"); + EASY_LOGMSG("Disabled profiling\n"); + + m_spin.lock(); + m_storedSpin.lock(); + // TODO: think about better solution because this one is not 100% safe... + + const profiler::timestamp_t now = getCurrentTime(); + const profiler::timestamp_t endtime = m_endTime == 0 ? now : std::min(now, m_endTime); + +#ifndef _WIN32 + if (eventTracingEnabled) + { + // Read thread context switch events from temporary file + + if (_async && m_stopDumping.load(std::memory_order_acquire)) + { + m_spin.unlock(); + m_storedSpin.unlock(); + if (_lockSpin) + m_dumpSpin.unlock(); + return 0; + } + + EASY_LOGMSG("Writing context switch events...\n"); + + uint64_t timestamp = 0; + profiler::thread_id_t thread_from = 0, thread_to = 0; + + std::ifstream infile(m_csInfoFilename.c_str()); + if(infile.is_open()) + { + EASY_LOG_ONLY(uint32_t num = 0); + std::string next_task_name; + pid_t process_to = 0; + while (infile >> timestamp >> thread_from >> thread_to >> next_task_name >> process_to) + { + if (_async && m_stopDumping.load(std::memory_order_acquire)) + { + m_spin.unlock(); + m_storedSpin.unlock(); + if (_lockSpin) + m_dumpSpin.unlock(); + return 0; + } + + beginContextSwitch(thread_from, timestamp, thread_to, next_task_name.c_str(), false); + endContextSwitch(thread_to, (processid_t)process_to, timestamp, false); + EASY_LOG_ONLY(++num); + } + + EASY_LOGMSG("Done, " << num << " context switch events wrote\n"); + } + EASY_LOG_ONLY( + else { + EASY_ERROR("Can not open context switch log-file \"" << m_csInfoFilename << "\"\n"); + } + ) + } +#endif + + bool mainThreadExpired = false; + + // Calculate used memory total size and total blocks number + uint64_t usedMemorySize = 0; + uint32_t blocks_number = 0; + for (auto thread_it = m_threads.begin(), end = m_threads.end(); thread_it != end;) + { + if (_async && m_stopDumping.load(std::memory_order_acquire)) + { + m_spin.unlock(); + m_storedSpin.unlock(); + if (_lockSpin) + m_dumpSpin.unlock(); + return 0; + } + + auto& thread = thread_it->second; + uint32_t num = static_cast(thread.blocks.closedList.size()) + static_cast(thread.sync.closedList.size()); + const char expired = ProfileManager::checkThreadExpired(thread); + +#ifdef _WIN32 + if (num == 0 && expired != 0) +#elif defined(EASY_CXX11_TLS_AVAILABLE) + // Removing !guarded thread when thread_local feature is supported is safe. + if (num == 0 && (expired != 0 || !thread.guarded)) +#elif EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS != 0 +# pragma message "Warning: Removing !guarded thread without thread_local support may cause an application crash, but fixes potential memory leak when using pthreads." + // Removing !guarded thread may cause an application crash if a thread would start to write blocks after ThreadStorage remove. + // TODO: Find solution to check thread state for pthread or to nullify THIS_THREAD pointer for removed ThreadStorage + if (num == 0 && (expired != 0 || !t.guarded)) +#else +# pragma message "Warning: Can not check pthread state (dead or alive). This may cause memory leak because ThreadStorage-s would not be removed ever during an application launched." + if (num == 0 && expired != 0) +#endif + { + // Remove thread if it contains no profiled information and has been finished (or is not guarded --deprecated). + profiler::thread_id_t id = thread_it->first; + if (!mainThreadExpired && m_mainThreadId.compare_exchange_weak(id, 0, std::memory_order_release, std::memory_order_acquire)) + mainThreadExpired = true; + m_threads.erase(thread_it++); + continue; + } + + if (expired == 1) + { + EASY_FORCE_EVENT3(thread, endtime, "ThreadExpired", EASY_COLOR_THREAD_END); + ++num; + } + + usedMemorySize += thread.blocks.usedMemorySize + thread.sync.usedMemorySize; + blocks_number += num; + ++thread_it; + } + + // Write profiler signature and version + _outputStream.write(PROFILER_SIGNATURE); + _outputStream.write(EASY_CURRENT_VERSION); + _outputStream.write(m_processId); + + // Write CPU frequency to let GUI calculate real time value from CPU clocks +#if defined(EASY_CHRONO_CLOCK) || defined(_WIN32) + _outputStream.write(CPU_FREQUENCY); +#else + EASY_LOGMSG("Calculating CPU frequency\n"); + const int64_t cpu_frequency = calculate_cpu_frequency(); + _outputStream.write(cpu_frequency * 1000LL); + EASY_LOGMSG("Done calculating CPU frequency\n"); + + CPU_FREQUENCY.store(cpu_frequency, std::memory_order_release); +#endif + + // Write begin and end time + _outputStream.write(m_beginTime); + _outputStream.write(m_endTime); + + // Write blocks number and used memory size + _outputStream.write(blocks_number); + _outputStream.write(usedMemorySize); + _outputStream.write(static_cast(m_descriptors.size())); + _outputStream.write(m_usedMemorySize); + + // Write block descriptors + for (const auto descriptor : m_descriptors) + { + const auto name_size = descriptor->nameSize(); + const auto filename_size = descriptor->filenameSize(); + const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); + + _outputStream.write(size); + _outputStream.write(*descriptor); + _outputStream.write(name_size); + _outputStream.write(descriptor->name(), name_size); + _outputStream.write(descriptor->filename(), filename_size); + } + + // Write blocks and context switch events for each thread + for (auto thread_it = m_threads.begin(), end = m_threads.end(); thread_it != end;) + { + if (_async && m_stopDumping.load(std::memory_order_acquire)) + { + m_spin.unlock(); + m_storedSpin.unlock(); + if (_lockSpin) + m_dumpSpin.unlock(); + return 0; + } + + auto& thread = thread_it->second; + + _outputStream.write(thread_it->first); + + const auto name_size = static_cast(thread.name.size() + 1); + _outputStream.write(name_size); + _outputStream.write(name_size > 1 ? thread.name.c_str() : "", name_size); + + _outputStream.write(thread.sync.closedList.size()); + if (!thread.sync.closedList.empty()) + thread.sync.closedList.serialize(_outputStream); + + _outputStream.write(thread.blocks.closedList.size()); + if (!thread.blocks.closedList.empty()) + thread.blocks.closedList.serialize(_outputStream); + + thread.clearClosed(); + //t.blocks.openedList.clear(); + thread.sync.openedList.clear(); + + if (thread.expired.load(std::memory_order_acquire) != 0) + { + // Remove expired thread after writing all profiled information + profiler::thread_id_t id = thread_it->first; + if (!mainThreadExpired && m_mainThreadId.compare_exchange_weak(id, 0, std::memory_order_release, std::memory_order_acquire)) + mainThreadExpired = true; + m_threads.erase(thread_it++); + } + else + { + ++thread_it; + } + } + + m_storedSpin.unlock(); + m_spin.unlock(); + + if (_lockSpin) + m_dumpSpin.unlock(); + + EASY_LOGMSG("Done dumpBlocksToStream(). Dumped " << blocks_number << " blocks\n"); + + return blocks_number; +} + +uint32_t ProfileManager::dumpBlocksToFile(const char* _filename) +{ + EASY_LOGMSG("dumpBlocksToFile(\"" << _filename << "\")...\n"); + + std::ofstream outputFile(_filename, std::fstream::binary); + if (!outputFile.is_open()) + { + EASY_ERROR("Can not open \"" << _filename << "\" for writing\n"); + return 0; + } + + profiler::OStream outputStream; + + // Replace outputStream buffer to outputFile buffer to avoid redundant copying + typedef ::std::basic_iostream stringstream_parent; + stringstream_parent& s = outputStream.stream(); + auto oldbuf = s.rdbuf(outputFile.rdbuf()); + + // Write data directly to file + const auto blocksNumber = dumpBlocksToStream(outputStream, true, false); + + // Restore old outputStream buffer to avoid possible second memory free on stringstream destructor + s.rdbuf(oldbuf); + + EASY_LOGMSG("Done dumpBlocksToFile()\n"); + + return blocksNumber; +} + +void ProfileManager::registerThread() +{ + THIS_THREAD = &threadStorage(getCurrentThreadId()); + +#ifdef EASY_CXX11_TLS_AVAILABLE + THIS_THREAD->guarded = true; + THIS_THREAD_GUARD.m_id = THIS_THREAD->id; +#endif +} + +const char* ProfileManager::registerThread(const char* name, ThreadGuard& threadGuard) +{ + if (THIS_THREAD == nullptr) + THIS_THREAD = &threadStorage(getCurrentThreadId()); + + THIS_THREAD->guarded = true; + if (!THIS_THREAD->named) + { + THIS_THREAD->named = true; + THIS_THREAD->name = name; + + if (THIS_THREAD->name == "Main") + { + profiler::thread_id_t id = 0; + THIS_THREAD_IS_MAIN = m_mainThreadId.compare_exchange_weak(id, THIS_THREAD->id, std::memory_order_release, std::memory_order_acquire); + } + +#ifdef EASY_CXX11_TLS_AVAILABLE + THIS_THREAD_GUARD.m_id = THIS_THREAD->id; + } + + (void)threadGuard; // this is just to prevent from warning about unused variable +#else + } + + threadGuard.m_id = THIS_THREAD->id; +#endif + + return THIS_THREAD->name.c_str(); +} + +const char* ProfileManager::registerThread(const char* name) +{ + if (THIS_THREAD == nullptr) + THIS_THREAD = &threadStorage(getCurrentThreadId()); + + if (!THIS_THREAD->named) + { + THIS_THREAD->named = true; + THIS_THREAD->name = name; + + if (THIS_THREAD->name == "Main") + { + profiler::thread_id_t id = 0; + THIS_THREAD_IS_MAIN = m_mainThreadId.compare_exchange_weak(id, THIS_THREAD->id, std::memory_order_release, std::memory_order_acquire); + } + +#ifdef EASY_CXX11_TLS_AVAILABLE + THIS_THREAD->guarded = true; + THIS_THREAD_GUARD.m_id = THIS_THREAD->id; +#endif + } + + return THIS_THREAD->name.c_str(); +} + +void ProfileManager::setBlockStatus(block_id_t _id, EasyBlockStatus _status) +{ + if (m_profilerStatus.load(std::memory_order_acquire) != EASY_PROF_DISABLED) + return; // Changing blocks statuses is restricted while profile session is active + + guard_lock_t lock(m_storedSpin); + if (_id < m_descriptors.size()) + { + auto desc = m_descriptors[_id]; + lock.unlock(); + desc->m_status = _status; + } +} + +void ProfileManager::startListen(uint16_t _port) +{ + if (!m_isAlreadyListening.exchange(true, std::memory_order_release)) + { + m_stopListen.store(false, std::memory_order_release); + m_listenThread = std::thread(&ProfileManager::listen, this, _port); + } +} + +void ProfileManager::stopListen() +{ + m_stopListen.store(true, std::memory_order_release); + if (m_listenThread.joinable()) + m_listenThread.join(); + m_isAlreadyListening.store(false, std::memory_order_release); + + EASY_LOGMSG("Listening stopped\n"); +} + +bool ProfileManager::isListening() const +{ + return m_isAlreadyListening.load(std::memory_order_acquire); +} + +////////////////////////////////////////////////////////////////////////// + +template +inline void join(std::future& futureResult) +{ + if (futureResult.valid()) + futureResult.get(); +} + +void ProfileManager::listen(uint16_t _port) +{ + EASY_THREAD_SCOPE("EasyProfiler.Listen"); + + EASY_LOGMSG("Listening started\n"); + + profiler::OStream os; + std::future dumpingResult; + bool dumping = false; + + const auto stopDumping = [this, &dumping, &dumpingResult, &os] + { + dumping = false; + m_stopDumping.store(true, std::memory_order_release); + join(dumpingResult); + os.clear(); + }; + + EasySocket socket; + profiler::net::Message replyMessage(profiler::net::MessageType::Reply_Capturing_Started); + + socket.bind(_port); + int bytes = 0; + while (!m_stopListen.load(std::memory_order_acquire)) + { + if (dumping) + stopDumping(); + + socket.listen(); + socket.accept(); + + bool hasConnect = true; + + // Send reply + { + const bool wasLowPriorityET = +#ifdef _WIN32 + EasyEventTracer::instance().isLowPriority(); +#else + false; +#endif + const profiler::net::EasyProfilerStatus connectionReply( + m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_ENABLED, + m_isEventTracingEnabled.load(std::memory_order_acquire), wasLowPriorityET); + + bytes = socket.send(&connectionReply, sizeof(profiler::net::EasyProfilerStatus)); + hasConnect = bytes > 0; + } + + while (hasConnect && !m_stopListen.load(std::memory_order_acquire)) + { + if (dumping) + { + if (!dumpingResult.valid()) + { + dumping = false; + socket.setReceiveTimeout(0); + os.clear(); + } + else if (dumpingResult.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) + { + dumping = false; + dumpingResult.get(); + + const auto size = os.stream().tellp(); + static const decltype(size) badSize = -1; + if (size != badSize) + { + const profiler::net::DataMessage dm(static_cast(size), + profiler::net::MessageType::Reply_Blocks); + + const size_t packet_size = sizeof(dm) + dm.size; + std::string sendbuf; + sendbuf.reserve(packet_size + 1); + + if (sendbuf.capacity() >= packet_size) // check if there is enough memory + { + sendbuf.append((const char*) &dm, sizeof(dm)); + sendbuf += os.stream().str(); // TODO: Avoid double-coping data from stringstream! + os.clear(); + + bytes = socket.send(sendbuf.c_str(), packet_size); + hasConnect = bytes > 0; + if (!hasConnect) + break; + } + else + { + EASY_ERROR("Can not send blocks. Not enough memory for allocating " << packet_size + << " bytes"); + os.clear(); + } + } + else + { + EASY_ERROR("Can not send blocks. Bad std::stringstream.tellp() == -1"); + os.clear(); + } + + replyMessage.type = profiler::net::MessageType::Reply_Blocks_End; + bytes = socket.send(&replyMessage, sizeof(replyMessage)); + hasConnect = bytes > 0; + if (!hasConnect) + break; + + socket.setReceiveTimeout(0); + } + } + + char buffer[256] = {}; + bytes = socket.receive(buffer, 255); + + hasConnect = socket.isConnected(); + if (!hasConnect || bytes < static_cast(sizeof(profiler::net::Message))) + continue; + + auto message = (const profiler::net::Message*)buffer; + if (!message->isEasyNetMessage()) + continue; + + switch (message->type) + { + case profiler::net::MessageType::Ping: + { + EASY_LOGMSG("receive MessageType::Ping\n"); + break; + } + + case profiler::net::MessageType::Request_MainThread_FPS: + { + profiler::timestamp_t maxDuration = maxFrameDuration(), avgDuration = avgFrameDuration(); + + maxDuration = TICKS_TO_US(maxDuration); + avgDuration = TICKS_TO_US(avgDuration); + + const profiler::net::TimestampMessage reply(profiler::net::MessageType::Reply_MainThread_FPS, + (uint32_t)maxDuration, (uint32_t)avgDuration); + + bytes = socket.send(&reply, sizeof(profiler::net::TimestampMessage)); + hasConnect = bytes > 0; + + break; + } + + case profiler::net::MessageType::Request_Start_Capture: + { + EASY_LOGMSG("receive MessageType::Request_Start_Capture\n"); + + ::profiler::timestamp_t t = 0; + EASY_FORCE_EVENT(t, "StartCapture", EASY_COLOR_START, profiler::OFF); + + m_dumpSpin.lock(); + const auto prev = m_profilerStatus.exchange(EASY_PROF_ENABLED, std::memory_order_release); + if (prev != EASY_PROF_ENABLED) { + enableEventTracer(); + m_beginTime = t; + } + m_dumpSpin.unlock(); + + replyMessage.type = profiler::net::MessageType::Reply_Capturing_Started; + bytes = socket.send(&replyMessage, sizeof(replyMessage)); + hasConnect = bytes > 0; + + break; + } + + case profiler::net::MessageType::Request_Stop_Capture: + { + EASY_LOGMSG("receive MessageType::Request_Stop_Capture\n"); + + if (dumping) + break; + + m_dumpSpin.lock(); + auto time = getCurrentTime(); + const auto prev = m_profilerStatus.exchange(EASY_PROF_DUMP, std::memory_order_release); + if (prev == EASY_PROF_ENABLED) { + disableEventTracer(); + m_endTime = time; + } + EASY_FORCE_EVENT2(m_endTime, "StopCapture", EASY_COLOR_END, profiler::OFF); + + dumping = true; + socket.setReceiveTimeout(500); // We have to check if dumping ready or not + + m_stopDumping.store(false, std::memory_order_release); + dumpingResult = std::async(std::launch::async, [this, &os] + { + auto result = dumpBlocksToStream(os, false, true); + m_dumpSpin.unlock(); + return result; + }); + + break; + } + + case profiler::net::MessageType::Request_Blocks_Description: + { + EASY_LOGMSG("receive MessageType::Request_Blocks_Description\n"); + + if (dumping) + stopDumping(); + + // Write profiler signature and version + os.write(PROFILER_SIGNATURE); + os.write(EASY_CURRENT_VERSION); + + // Write block descriptors + m_storedSpin.lock(); + os.write(static_cast(m_descriptors.size())); + os.write(m_usedMemorySize); + for (const auto descriptor : m_descriptors) + { + const auto name_size = descriptor->nameSize(); + const auto filename_size = descriptor->filenameSize(); + const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + + name_size + filename_size); + + os.write(size); + os.write(*descriptor); + os.write(name_size); + os.write(descriptor->name(), name_size); + os.write(descriptor->filename(), filename_size); + } + m_storedSpin.unlock(); + // END of Write block descriptors. + + const auto size = os.stream().tellp(); + static const decltype(size) badSize = -1; + if (size != badSize) + { + const profiler::net::DataMessage dm(static_cast(size), + profiler::net::MessageType::Reply_Blocks_Description); + + const size_t packet_size = sizeof(dm) + dm.size; + std::string sendbuf; + sendbuf.reserve(packet_size + 1); + + if (sendbuf.capacity() >= packet_size) // check if there is enough memory + { + sendbuf.append((const char*)&dm, sizeof(dm)); + sendbuf += os.stream().str(); // TODO: Avoid double-coping data from stringstream! + os.clear(); + + bytes = socket.send(sendbuf.c_str(), packet_size); + //hasConnect = bytes > 0; + } + else + { + EASY_ERROR("Can not send block descriptions. Not enough memory for allocating " << packet_size << " bytes"); + } + } + else + { + EASY_ERROR("Can not send block descriptions. Bad std::stringstream.tellp() == -1"); + } + + replyMessage.type = profiler::net::MessageType::Reply_Blocks_Description_End + ; + bytes = socket.send(&replyMessage, sizeof(replyMessage)); + hasConnect = bytes > 0; + + break; + } + + case profiler::net::MessageType::Change_Block_Status: + { + auto data = reinterpret_cast(message); + + EASY_LOGMSG("receive MessageType::ChangeBLock_Status id=" << data->id << " status=" << data->status << std::endl); + + setBlockStatus(data->id, static_cast<::profiler::EasyBlockStatus>(data->status)); + + break; + } + + case profiler::net::MessageType::Change_Event_Tracing_Status: + { + auto data = reinterpret_cast(message); + + EASY_LOGMSG("receive MessageType::Change_Event_Tracing_Status on=" << data->flag << std::endl); + + m_isEventTracingEnabled.store(data->flag, std::memory_order_release); + break; + } + + case profiler::net::MessageType::Change_Event_Tracing_Priority: + { +#if defined(_WIN32) || EASY_OPTION_LOG_ENABLED != 0 + auto data = reinterpret_cast(message); +#endif + + EASY_LOGMSG("receive MessageType::Change_Event_Tracing_Priority low=" << data->flag << std::endl); + +#if defined(_WIN32) + EasyEventTracer::instance().setLowPriority(data->flag); +#endif + break; + } + + default: + break; + } + } + } + + if (dumping) + { + m_stopDumping.store(true, std::memory_order_release); + join(dumpingResult); + } +} + +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/easy_profiler_core/profile_manager.h b/3rdparty/easyprofiler/easy_profiler_core/profile_manager.h new file mode 100644 index 0000000..963f9da --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/profile_manager.h @@ -0,0 +1,211 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_MANAGER_H +#define EASY_PROFILER_MANAGER_H + +#include + +#ifdef _WIN32 +// Do not move this include to other place! +// It should be included before Windows.h which is included in spin_lock.h +# include +#endif // _WIN32 + +#include "spin_lock.h" +#include "outstream.h" +#include "hashed_cstr.h" +#include "thread_storage.h" + +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// + +typedef uint64_t processid_t; + +class BlockDescriptor; + +namespace profiler { class ValueId; } + +class ProfileManager +{ +#ifndef EASY_MAGIC_STATIC_AVAILABLE + friend class ProfileManagerInstance; +#endif + + ProfileManager(); + + typedef profiler::guard_lock guard_lock_t; + typedef std::map map_of_threads_stacks; + typedef std::vector block_descriptors_t; + +#ifdef EASY_PROFILER_HASHED_CSTR_DEFINED + typedef std::unordered_map descriptors_map_t; +#else + typedef std::unordered_map descriptors_map_t; +#endif + + const processid_t m_processId; + + map_of_threads_stacks m_threads; + block_descriptors_t m_descriptors; + descriptors_map_t m_descriptorsMap; + uint64_t m_usedMemorySize; + profiler::timestamp_t m_beginTime; + profiler::timestamp_t m_endTime; + std::atomic m_frameMax; + std::atomic m_frameAvg; + std::atomic m_frameCur; + profiler::spin_lock m_spin; + profiler::spin_lock m_storedSpin; + profiler::spin_lock m_dumpSpin; + std::atomic m_mainThreadId; + std::atomic m_profilerStatus; + std::atomic_bool m_isEventTracingEnabled; + std::atomic_bool m_isAlreadyListening; + std::atomic_bool m_frameMaxReset; + std::atomic_bool m_frameAvgReset; + std::atomic_bool m_stopDumping; + + std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; + + uint32_t dumpBlocksToStream(profiler::OStream& _outputStream, bool _lockSpin, bool _async); + void setBlockStatus(profiler::block_id_t _id, profiler::EasyBlockStatus _status); + + std::thread m_listenThread; + void listen(uint16_t _port); + + std::atomic_bool m_stopListen; + +public: + + ProfileManager(const ProfileManager&) = delete; + ProfileManager(ProfileManager&&) = delete; + ProfileManager& operator = (const ProfileManager&) = delete; + ProfileManager& operator = (ProfileManager&&) = delete; + + static ProfileManager& instance(); + ~ProfileManager(); + + const profiler::BaseBlockDescriptor* addBlockDescriptor(profiler::EasyBlockStatus _defaultStatus, + const char* _autogenUniqueId, + const char* _name, + const char* _filename, + int _line, + profiler::block_type_t _block_type, + profiler::color_t _color, + bool _copyName = false); + + void storeValue(const profiler::BaseBlockDescriptor* _desc, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin); + bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName); + bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime); + void beginBlock(profiler::Block& _block); + void beginNonScopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName); + void endBlock(); + profiler::timestamp_t maxFrameDuration(); + profiler::timestamp_t avgFrameDuration(); + profiler::timestamp_t curFrameDuration() const; + void setEnabled(bool isEnable); + bool isEnabled() const; + void setEventTracingEnabled(bool _isEnable); + bool isEventTracingEnabled() const; + uint32_t dumpBlocksToFile(const char* filename); + const char* registerThread(const char* name, profiler::ThreadGuard& threadGuard); + const char* registerThread(const char* name); + + void setContextSwitchLogFilename(const char* name) + { + m_csInfoFilename = name; + } + + const char* getContextSwitchLogFilename() const + { + return m_csInfoFilename.c_str(); + } + + void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin = true); + void endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin = true); + void startListen(uint16_t _port); + void stopListen(); + bool isListening() const; + +private: + + void registerThread(); + + void beginFrame(); + void endFrame(); + + void enableEventTracer(); + void disableEventTracer(); + + static char checkThreadExpired(ThreadStorage& _registeredThread); + + void storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp); + void storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp); + void storeBlockForce2(ThreadStorage& _registeredThread, const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp); + + ThreadStorage& _threadStorage(profiler::thread_id_t _thread_id); + ThreadStorage* _findThreadStorage(profiler::thread_id_t _thread_id); + + inline ThreadStorage& threadStorage(profiler::thread_id_t _thread_id) + { + guard_lock_t lock(m_spin); + return _threadStorage(_thread_id); + } + + inline ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id) + { + guard_lock_t lock(m_spin); + return _findThreadStorage(_thread_id); + } + +}; // END of class ProfileManager. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_MANAGER_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/reader.cpp b/3rdparty/easyprofiler/easy_profiler_core/reader.cpp new file mode 100644 index 0000000..4676374 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/reader.cpp @@ -0,0 +1,1030 @@ +/************************************************************************ +* file name : reader.cpp +* ----------------- : +* creation time : 2016/06/19 +* authors : Sergey Yagovtsev, Victor Zarubkin +* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of fillTreesFromFile function +* : which reads profiler file and fill profiler blocks tree. +* ----------------- : +* change log : * 2016/06/19 Sergey Yagovtsev: First fillTreesFromFile implementation. +* : +* : * 2016/06/25 Victor Zarubkin: Removed unnecessary memory allocation and copy +* : when creating and inserting blocks into the tree. +* : +* : * 2016/06/26 Victor Zarubkin: Added statistics gathering (min, max, average duration, +* : number of block calls). +* : * 2016/06/26 Victor Zarubkin, Sergey Yagovtsev: Added statistics gathering for root +* : blocks in the tree. +* : +* : * 2016/06/29 Victor Zarubkin: Added calculaton of total children number for blocks. +* : +* : * 2016/06/30 Victor Zarubkin: Added this header. +* : Added tree depth calculation. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include + +#include "hashed_cstr.h" + +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// + +typedef uint64_t processid_t; + +extern const uint32_t PROFILER_SIGNATURE; +extern const uint32_t EASY_CURRENT_VERSION; + +# define EASY_VERSION_INT(v_major, v_minor, v_patch) ((static_cast(v_major) << 24) | (static_cast(v_minor) << 16) | static_cast(v_patch)) +const uint32_t MIN_COMPATIBLE_VERSION = EASY_VERSION_INT(0, 1, 0); ///< minimal compatible version (.prof file format was not changed seriously since this version) +const uint32_t EASY_V_100 = EASY_VERSION_INT(1, 0, 0); ///< in v1.0.0 some additional data were added into .prof file +const uint32_t EASY_V_130 = EASY_VERSION_INT(1, 3, 0); ///< in v1.3.0 changed sizeof(thread_id_t) uint32_t -> uint64_t +# undef EASY_VERSION_INT + +const uint64_t TIME_FACTOR = 1000000000ULL; + +// TODO: use 128 bit integer operations for better accuracy +#define EASY_USE_FLOATING_POINT_CONVERSION + +#ifdef EASY_USE_FLOATING_POINT_CONVERSION + +// Suppress warnings about double to uint64 conversion +# ifdef _MSC_VER +# pragma warning(disable:4244) +# elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wsign-conversion" +# endif + +# define EASY_CONVERT_TO_NANO(t, freq, factor) t *= factor + +#else + +# define EASY_CONVERT_TO_NANO(t, freq, factor) t *= TIME_FACTOR; t /= freq + +#endif + +////////////////////////////////////////////////////////////////////////// + +inline bool isCompatibleVersion(uint32_t _version) +{ + return _version >= MIN_COMPATIBLE_VERSION; +} + +inline void write(::std::stringstream& _stream, const char* _value, size_t _size) +{ + _stream.write(_value, _size); +} + +template +inline void write(::std::stringstream& _stream, const T& _value) +{ + _stream.write((const char*)&_value, sizeof(T)); +} + +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + void SerializedData::set(char* _data, uint64_t _size) + { + delete [] m_data; + m_data = _data; + m_size = _size; + } + + void SerializedData::set(uint64_t _size) + { + if (_size != 0) + set(new char[_size], _size); + else + set(nullptr, 0); + } + + void SerializedData::extend(uint64_t _size) + { + auto olddata = m_data; + auto oldsize = m_size; + + m_size = oldsize + _size; + m_data = new char[m_size]; + + if (olddata != nullptr) { + memcpy(m_data, olddata, oldsize); + delete [] olddata; + } + } + + extern "C" PROFILER_API void release_stats(BlockStatistics*& _stats) + { + if (_stats == nullptr) + return; + + if (--_stats->calls_number == 0) + delete _stats; + + _stats = nullptr; + } + +} + +////////////////////////////////////////////////////////////////////////// + +#ifdef EASY_PROFILER_HASHED_CSTR_DEFINED + +using StatsMap = ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::estd::hash<::profiler::block_id_t> >; + +/** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map, +which uses it as a key, exists only inside fillTreesFromFile function. */ +using IdMap = ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t>; + +using CsStatsMap = ::std::unordered_map<::profiler::hashed_cstr, ::profiler::BlockStatistics*>; + +#else + +// TODO: Create optimized version of profiler::hashed_cstr for Linux too. +using StatsMap = ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::estd::hash<::profiler::block_id_t> >; +using IdMap = ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t>; +using CsStatsMap = ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::BlockStatistics*>; + +#endif + +////////////////////////////////////////////////////////////////////////// + +/** \brief Updates statistics for a profiler block. + +\param _stats_map Storage of statistics for blocks. +\param _current Pointer to the current block. +\param _stats Reference to the variable where pointer to the block statistics must be written. + +\note All blocks with similar name have the same pointer to statistics information. + +\note As all profiler block keeps a pointer to it's statistics, all similar blocks +automatically receive statistics update. + +*/ +::profiler::BlockStatistics* update_statistics(StatsMap& _stats_map, const ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, const ::profiler::blocks_t& _blocks, bool _calculate_children = true) +{ + auto duration = _current.node->duration(); + //StatsMap::key_type key(_current.node->name()); + //auto it = _stats_map.find(key); + auto it = _stats_map.find(_current.node->id()); + if (it != _stats_map.end()) + { + // Update already existing statistics + + auto stats = it->second; // write pointer to statistics into output (this is BlocksTree:: per_thread_stats or per_parent_stats or per_frame_stats) + + ++stats->calls_number; // update calls number of this block + stats->total_duration += duration; // update summary duration of all block calls + + if (_calculate_children) + { + for (auto i : _current.children) + stats->total_children_duration += _blocks[i].node->duration(); + } + + if (duration > _blocks[stats->max_duration_block].node->duration()) + { + // update max duration + stats->max_duration_block = _current_index; + //stats->max_duration = duration; + } + + if (duration < _blocks[stats->min_duration_block].node->duration()) + { + // update min duraton + stats->min_duration_block = _current_index; + //stats->min_duration = duration; + } + + // average duration is calculated inside average_duration() method by dividing total_duration to the calls_number + + return stats; + } + + // This is first time the block appear in the file. + // Create new statistics. + auto stats = new ::profiler::BlockStatistics(duration, _current_index, _parent_index); + //_stats_map.emplace(key, stats); + _stats_map.emplace(_current.node->id(), stats); + + if (_calculate_children) + { + for (auto i : _current.children) + stats->total_children_duration += _blocks[i].node->duration(); + } + + return stats; +} + +::profiler::BlockStatistics* update_statistics(CsStatsMap& _stats_map, const ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, const ::profiler::blocks_t& _blocks, bool _calculate_children = true) +{ + auto duration = _current.node->duration(); + CsStatsMap::key_type key(_current.node->name()); + auto it = _stats_map.find(key); + if (it != _stats_map.end()) + { + // Update already existing statistics + + auto stats = it->second; // write pointer to statistics into output (this is BlocksTree:: per_thread_stats or per_parent_stats or per_frame_stats) + + ++stats->calls_number; // update calls number of this block + stats->total_duration += duration; // update summary duration of all block calls + + if (_calculate_children) + { + for (auto i : _current.children) + stats->total_children_duration += _blocks[i].node->duration(); + } + + if (duration > _blocks[stats->max_duration_block].node->duration()) + { + // update max duration + stats->max_duration_block = _current_index; + //stats->max_duration = duration; + } + + if (duration < _blocks[stats->min_duration_block].node->duration()) + { + // update min duraton + stats->min_duration_block = _current_index; + //stats->min_duration = duration; + } + + // average duration is calculated inside average_duration() method by dividing total_duration to the calls_number + + return stats; + } + + // This is first time the block appear in the file. + // Create new statistics. + auto stats = new ::profiler::BlockStatistics(duration, _current_index, _parent_index); + _stats_map.emplace(key, stats); + + if (_calculate_children) + { + for (auto i : _current.children) + stats->total_children_duration += _blocks[i].node->duration(); + } + + return stats; +} + +////////////////////////////////////////////////////////////////////////// + +void update_statistics_recursive(StatsMap& _stats_map, ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, ::profiler::blocks_t& _blocks) +{ + _current.per_frame_stats = update_statistics(_stats_map, _current, _current_index, _parent_index, _blocks, false); + for (auto i : _current.children) + { + _current.per_frame_stats->total_children_duration += _blocks[i].node->duration(); + update_statistics_recursive(_stats_map, _blocks[i], i, _parent_index, _blocks); + } +} + +////////////////////////////////////////////////////////////////////////// + +/*void validate_pointers(::std::atomic& _progress, const char* _oldbase, ::profiler::SerializedData& _serialized_blocks, ::profiler::blocks_t& _blocks, size_t _size) +{ + if (_oldbase == nullptr) + { + _progress.store(25, ::std::memory_order_release); + return; + } + + for (size_t i = 0; i < _size; ++i) + { + auto& tree = _blocks[i]; + auto dist = ::std::distance(_oldbase, reinterpret_cast(tree.node)); + tree.node = reinterpret_cast<::profiler::SerializedBlock*>(_serialized_blocks.data() + dist); + _progress.store(20 + static_cast(5 * i / _size), ::std::memory_order_release); + } +} + +void validate_pointers(::std::atomic& _progress, const char* _oldbase, ::profiler::SerializedData& _serialized_descriptors, ::profiler::descriptors_list_t& _descriptors, size_t _size) +{ + if (_oldbase == nullptr) + { + _progress.store(5, ::std::memory_order_release); + return; + } + + for (size_t i = 0; i < _size; ++i) + { + auto dist = ::std::distance(_oldbase, reinterpret_cast(_descriptors[i])); + _descriptors[i] = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(_serialized_descriptors.data() + dist); + _progress.store(static_cast(5 * i / _size)); + } +}*/ + +////////////////////////////////////////////////////////////////////////// + +extern "C" { + + PROFILER_API ::profiler::block_index_t fillTreesFromFile(::std::atomic& progress, const char* filename, + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + uint32_t& version, + bool gather_statistics, + ::std::stringstream& _log) + { + auto oldprogress = progress.exchange(0, ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; + } + + ::std::ifstream inFile(filename, ::std::fstream::binary); + if (!inFile.is_open()) + { + _log << "Can not open file " << filename; + return 0; + } + + ::std::stringstream str; + + // Replace str buffer to inFile buffer to avoid redundant copying + typedef ::std::basic_iostream<::std::stringstream::char_type, ::std::stringstream::traits_type> stringstream_parent; + stringstream_parent& s = str; + auto oldbuf = s.rdbuf(inFile.rdbuf()); + + // Read data from file + auto result = fillTreesFromStream(progress, str, serialized_blocks, serialized_descriptors, descriptors, blocks, + threaded_trees, total_descriptors_number, version, gather_statistics, _log); + + // Restore old str buffer to avoid possible second memory free on stringstream destructor + s.rdbuf(oldbuf); + + return result; + } + + ////////////////////////////////////////////////////////////////////////// + + PROFILER_API ::profiler::block_index_t fillTreesFromStream(::std::atomic& progress, ::std::stringstream& inFile, + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + uint32_t& version, + bool gather_statistics, + ::std::stringstream& _log) + { + EASY_FUNCTION(::profiler::colors::Cyan); + + auto oldprogress = progress.exchange(0, ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; + } + + uint32_t signature = 0; + inFile.read((char*)&signature, sizeof(uint32_t)); + if (signature != PROFILER_SIGNATURE) + { + _log << "Wrong signature " << signature << "\nThis is not EasyProfiler file/stream."; + return 0; + } + + version = 0; + inFile.read((char*)&version, sizeof(uint32_t)); + if (!isCompatibleVersion(version)) + { + _log << "Incompatible version: v" << (version >> 24) << "." << ((version & 0x00ff0000) >> 16) << "." << (version & 0x0000ffff); + return 0; + } + + processid_t pid = 0; + if (version > EASY_V_100) + { + if (version < EASY_V_130) + { + uint32_t old_pid = 0; + inFile.read((char*)&old_pid, sizeof(uint32_t)); + pid = old_pid; + } + else + { + inFile.read((char*)&pid, sizeof(processid_t)); + } + } + + int64_t file_cpu_frequency = 0LL; + inFile.read((char*)&file_cpu_frequency, sizeof(int64_t)); + uint64_t cpu_frequency = file_cpu_frequency; + const double conversion_factor = static_cast(TIME_FACTOR) / static_cast(cpu_frequency); + + ::profiler::timestamp_t begin_time = 0ULL; + ::profiler::timestamp_t end_time = 0ULL; + inFile.read((char*)&begin_time, sizeof(::profiler::timestamp_t)); + inFile.read((char*)&end_time, sizeof(::profiler::timestamp_t)); + if (cpu_frequency != 0) + { + EASY_CONVERT_TO_NANO(begin_time, cpu_frequency, conversion_factor); + EASY_CONVERT_TO_NANO(end_time, cpu_frequency, conversion_factor); + } + + uint32_t total_blocks_number = 0; + inFile.read((char*)&total_blocks_number, sizeof(uint32_t)); + if (total_blocks_number == 0) + { + _log << "Profiled blocks number == 0"; + return 0; + } + + uint64_t memory_size = 0; + inFile.read((char*)&memory_size, sizeof(decltype(memory_size))); + if (memory_size == 0) + { + _log << "Wrong memory size == 0 for " << total_blocks_number << " blocks"; + return 0; + } + + total_descriptors_number = 0; + inFile.read((char*)&total_descriptors_number, sizeof(uint32_t)); + if (total_descriptors_number == 0) + { + _log << "Blocks description number == 0"; + return 0; + } + + uint64_t descriptors_memory_size = 0; + inFile.read((char*)&descriptors_memory_size, sizeof(decltype(descriptors_memory_size))); + if (descriptors_memory_size == 0) + { + _log << "Wrong memory size == 0 for " << total_descriptors_number << " blocks descriptions"; + return 0; + } + + descriptors.reserve(total_descriptors_number); + //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; + serialized_descriptors.set(descriptors_memory_size); + //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); + + uint64_t i = 0; + while (!inFile.eof() && descriptors.size() < total_descriptors_number) + { + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + { + descriptors.push_back(nullptr); + continue; + } + + //if (i + sz > descriptors_memory_size) { + // printf("FILE CORRUPTED\n"); + // return 0; + //} + + char* data = serialized_descriptors[i]; + inFile.read(data, sz); + auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); + descriptors.push_back(descriptor); + + i += sz; + oldprogress = progress.exchange(static_cast(15 * i / descriptors_memory_size), ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; // Loading interrupted + } + } + + using PerThreadStats = ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::estd::hash<::profiler::thread_id_t> >; + PerThreadStats parent_statistics, frame_statistics; + IdMap identification_table; + + blocks.reserve(total_blocks_number); + //olddata = append_regime ? serialized_blocks.data() : nullptr; + serialized_blocks.set(memory_size); + //validate_pointers(progress, olddata, serialized_blocks, blocks, blocks.size()); + + i = 0; + uint32_t read_number = 0; + ::profiler::block_index_t blocks_counter = 0; + ::std::vector name; + + const size_t thread_id_t_size = version < EASY_V_130 ? sizeof(uint32_t) : sizeof(::profiler::thread_id_t); + + while (!inFile.eof()) + { + EASY_BLOCK("Read thread data", ::profiler::colors::DarkGreen); + + ::profiler::thread_id_t thread_id = 0; + inFile.read((char*)&thread_id, thread_id_t_size); + if (inFile.eof()) + break; + + auto& root = threaded_trees[thread_id]; + + uint16_t name_size = 0; + inFile.read((char*)&name_size, sizeof(uint16_t)); + if (name_size != 0) + { + name.resize(name_size); + inFile.read(name.data(), name_size); + root.thread_name = name.data(); + } + + CsStatsMap per_thread_statistics_cs; + + uint32_t blocks_number_in_thread = 0; + inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); + auto threshold = read_number + blocks_number_in_thread; + while (!inFile.eof() && read_number < threshold) + { + EASY_BLOCK("Read context switch", ::profiler::colors::Green); + + ++read_number; + + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + { + _log << "Bad CSwitch block size == 0"; + return 0; + } + + char* data = serialized_blocks[i]; + inFile.read(data, sz); + i += sz; + auto baseData = reinterpret_cast<::profiler::SerializedCSwitch*>(data); + auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); + auto t_end = t_begin + 1; + + if (cpu_frequency != 0) + { + EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor); + EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor); + } + + if (*t_end > begin_time) + { + if (*t_begin < begin_time) + *t_begin = begin_time; + + blocks.emplace_back(); + ::profiler::BlocksTree& tree = blocks.back(); + tree.cs = baseData; + const auto block_index = blocks_counter++; + + root.wait_time += baseData->duration(); + root.sync.emplace_back(block_index); + + if (gather_statistics) + { + EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral); + tree.per_thread_stats = update_statistics(per_thread_statistics_cs, tree, block_index, ~0U, blocks);//, thread_id, blocks); + } + } + + oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; // Loading interrupted + } + } + + if (inFile.eof()) + break; + + StatsMap per_thread_statistics; + + blocks_number_in_thread = 0; + inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); + threshold = read_number + blocks_number_in_thread; + while (!inFile.eof() && read_number < threshold) + { + EASY_BLOCK("Read block", ::profiler::colors::Green); + + ++read_number; + + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + { + _log << "Bad block size == 0"; + return 0; + } + + char* data = serialized_blocks[i]; + inFile.read(data, sz); + i += sz; + auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); + if (baseData->id() >= total_descriptors_number) + { + _log << "Bad block id == " << baseData->id(); + return 0; + } + + auto desc = descriptors[baseData->id()]; + if (desc == nullptr) + { + _log << "Bad block id == " << baseData->id() << ". Description is null."; + return 0; + } + + auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); + auto t_end = t_begin + 1; + + if (cpu_frequency != 0) + { + EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor); + EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor); + } + + if (*t_end >= begin_time) + { + if (*t_begin < begin_time) + *t_begin = begin_time; + + blocks.emplace_back(); + ::profiler::BlocksTree& tree = blocks.back(); + tree.node = baseData; + const auto block_index = blocks_counter++; + + if (*tree.node->name() != 0) + { + // If block has runtime name then generate new id for such block. + // Blocks with the same name will have same id. + + IdMap::key_type key(tree.node->name()); + auto it = identification_table.find(key); + if (it != identification_table.end()) + { + // There is already block with such name, use it's id + baseData->setId(it->second); + } + else + { + // There were no blocks with such name, generate new id and save it in the table for further usage. + auto id = static_cast<::profiler::block_id_t>(descriptors.size()); + identification_table.emplace(key, id); + if (descriptors.capacity() == descriptors.size()) + descriptors.reserve((descriptors.size() * 3) >> 1); + descriptors.push_back(descriptors[baseData->id()]); + baseData->setId(id); + } + } + + if (!root.children.empty()) + { + auto& back = blocks[root.children.back()]; + auto t1 = back.node->end(); + auto mt0 = tree.node->begin(); + if (mt0 < t1)//parent - starts earlier than last ends + { + //auto lower = ::std::lower_bound(root.children.begin(), root.children.end(), tree); + /**/ + EASY_BLOCK("Find children", ::profiler::colors::Blue); + auto rlower1 = ++root.children.rbegin(); + for (; rlower1 != root.children.rend() && !(mt0 > blocks[*rlower1].node->begin()); ++rlower1); + auto lower = rlower1.base(); + ::std::move(lower, root.children.end(), ::std::back_inserter(tree.children)); + + root.children.erase(lower, root.children.end()); + EASY_END_BLOCK; + + if (gather_statistics) + { + EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta); + auto& per_parent_statistics = parent_statistics[thread_id]; + per_parent_statistics.clear(); + + //per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows + //per_parent_statistics.reserve(tree.children.size() * 2); // this gives no speed-up on Windows + // TODO: check this behavior on Linux + + for (auto i : tree.children) + { + auto& child = blocks[i]; + child.per_parent_stats = update_statistics(per_parent_statistics, child, i, block_index, blocks); + if (tree.depth < child.depth) + tree.depth = child.depth; + } + } + else + { + for (auto i : tree.children) + { + const auto& child = blocks[i]; + if (tree.depth < child.depth) + tree.depth = child.depth; + } + } + + if (tree.depth == 254) + { + // 254 because we need 1 additional level for root (thread). + // In other words: real stack depth = 1 root block + 254 children + + if (*tree.node->name() != 0) + _log << "Stack depth exceeded value of 254\nfor block \"" << desc->name() << "\""; + else + _log << "Stack depth exceeded value of 254\nfor block \"" << desc->name() << "\"\nfrom file \"" << desc->file() << "\":" << desc->line(); + + return 0; + } + + ++tree.depth; + } + } + + ++root.blocks_number; + root.children.emplace_back(block_index);// ::std::move(tree)); + if (desc->type() != ::profiler::BlockType::Block) + root.events.emplace_back(block_index); + + + if (gather_statistics) + { + EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral); + tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, ~0U, blocks);//, thread_id, blocks); + } + } + + oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; // Loading interrupted + } + } + } + + oldprogress = progress.exchange(90, ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; // Loading interrupted + } + + EASY_BLOCK("Gather statistics for roots", ::profiler::colors::Purple); + if (gather_statistics) + { + ::std::vector<::std::thread> statistics_threads; + statistics_threads.reserve(threaded_trees.size()); + + for (auto& it : threaded_trees) + { + auto& root = it.second; + root.thread_id = it.first; + //root.tree.shrink_to_fit(); + + auto& per_frame_statistics = frame_statistics[root.thread_id]; + auto& per_parent_statistics = parent_statistics[it.first]; + per_parent_statistics.clear(); + + statistics_threads.emplace_back(::std::thread([&per_parent_statistics, &per_frame_statistics, &blocks, &descriptors](::profiler::BlocksTreeRoot& root) + { + //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) + //{ + // return blocks[left].node->begin() < blocks[right].node->begin(); + //}); + + ::profiler::block_index_t cs_index = 0; + for (auto i : root.children) + { + auto& frame = blocks[i]; + + if (descriptors[frame.node->id()]->type() == ::profiler::BlockType::Block) + ++root.frames_number; + + frame.per_parent_stats = update_statistics(per_parent_statistics, frame, i, ~0U, blocks);//, root.thread_id, blocks); + + per_frame_statistics.clear(); + update_statistics_recursive(per_frame_statistics, frame, i, i, blocks); + + if (cs_index < root.sync.size()) + { + CsStatsMap frame_stats_cs; + do { + + auto j = root.sync[cs_index]; + auto& cs = blocks[j]; + if (cs.node->end() < frame.node->begin()) + continue; + if (cs.node->begin() > frame.node->end()) + break; + cs.per_frame_stats = update_statistics(frame_stats_cs, cs, cs_index, i, blocks); + + } while (++cs_index < root.sync.size()); + } + + if (root.depth < frame.depth) + root.depth = frame.depth; + + root.profiled_time += frame.node->duration(); + } + + ++root.depth; + }, ::std::ref(root))); + } + + int j = 0, n = static_cast(statistics_threads.size()); + for (auto& t : statistics_threads) + { + t.join(); + progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); + } + } + else + { + int j = 0, n = static_cast(threaded_trees.size()); + for (auto& it : threaded_trees) + { + auto& root = it.second; + root.thread_id = it.first; + + //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) + //{ + // return blocks[left].node->begin() < blocks[right].node->begin(); + //}); + + //root.tree.shrink_to_fit(); + for (auto i : root.children) + { + auto& frame = blocks[i]; + + if (descriptors[frame.node->id()]->type() == ::profiler::BlockType::Block) + ++root.frames_number; + + if (root.depth < frame.depth) + root.depth = frame.depth; + + root.profiled_time += frame.node->duration(); + } + + ++root.depth; + + progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); + } + } + // No need to delete BlockStatistics instances - they will be deleted inside BlocksTree destructors + + progress.store(100, ::std::memory_order_release); + return blocks_counter; + } + + ////////////////////////////////////////////////////////////////////////// + + PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& inFile, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::std::stringstream& _log) + { + EASY_FUNCTION(::profiler::colors::Cyan); + + progress.store(0); + + uint32_t signature = 0; + inFile.read((char*)&signature, sizeof(uint32_t)); + if (signature != PROFILER_SIGNATURE) + { + _log << "Wrong file signature.\nThis is not EasyProfiler file/stream."; + return false; + } + + uint32_t version = 0; + inFile.read((char*)&version, sizeof(uint32_t)); + if (!isCompatibleVersion(version)) + { + _log << "Incompatible version: v" << (version >> 24) << "." << ((version & 0x00ff0000) >> 16) << "." << (version & 0x0000ffff); + return false; + } + + uint32_t total_descriptors_number = 0; + inFile.read((char*)&total_descriptors_number, sizeof(decltype(total_descriptors_number))); + if (total_descriptors_number == 0) + { + _log << "Blocks description number == 0"; + return false; + } + + uint64_t descriptors_memory_size = 0; + inFile.read((char*)&descriptors_memory_size, sizeof(decltype(descriptors_memory_size))); + if (descriptors_memory_size == 0) + { + _log << "Wrong memory size == 0 for " << total_descriptors_number << " blocks descriptions"; + return false; + } + + descriptors.reserve(total_descriptors_number); + //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; + serialized_descriptors.set(descriptors_memory_size); + //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); + + uint64_t i = 0; + while (!inFile.eof() && descriptors.size() < total_descriptors_number) + { + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + { + descriptors.push_back(nullptr); + continue; + } + + //if (i + sz > descriptors_memory_size) { + // printf("FILE CORRUPTED\n"); + // return 0; + //} + + char* data = serialized_descriptors[i]; + inFile.read(data, sz); + auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); + descriptors.push_back(descriptor); + + i += sz; + auto oldprogress = progress.exchange(static_cast(100 * i / descriptors_memory_size), ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return false; // Loading interrupted + } + } + + return !descriptors.empty(); + } + + ////////////////////////////////////////////////////////////////////////// + +} + +#undef EASY_CONVERT_TO_NANO + +#ifdef EASY_USE_FLOATING_POINT_CONVERSION +# ifdef _MSC_VER +# pragma warning(default:4244) +# elif defined(__GNUC__) +# pragma GCC diagnostic pop +# elif defined(__clang__) +# pragma clang diagnostic pop +# endif +# undef EASY_USE_FLOATING_POINT_CONVERSION +#endif diff --git a/3rdparty/easyprofiler/easy_profiler_core/resources.rc b/3rdparty/easyprofiler/easy_profiler_core/resources.rc new file mode 100644 index 0000000..e68eb4d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/resources.rc @@ -0,0 +1,30 @@ +1 VERSIONINFO + +# define EASY_STRINGIFY(a) #a +# define EASY_STRINGIFICATION(a) EASY_STRINGIFY(a) + +#define EASY_PROFILER_PRODUCT_VERSION "v" EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MAJOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MINOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_PATCH) + +FILEVERSION EASY_PROFILER_VERSION_MAJOR, EASY_PROFILER_VERSION_MINOR, EASY_PROFILER_VERSION_PATCH +PRODUCTVERSION EASY_PROFILER_VERSION_MAJOR, EASY_PROFILER_VERSION_MINOR, EASY_PROFILER_VERSION_PATCH +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "CompanyName", "EasySolutions" + VALUE "FileDescription", "Lightweight profiler library for c++" + VALUE "LegalCopyright", "Copyright (C) 2016-2017 Victor Zarubkin, Sergey Yagovtsev" + VALUE "LegalTrademarks1", "All Rights Reserved" + VALUE "LegalTrademarks2", "All Rights Reserved" + VALUE "ProductName", "easy_profiler lib" + VALUE "ProductVersion", EASY_PROFILER_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END diff --git a/3rdparty/easyprofiler/easy_profiler_core/spin_lock.h b/3rdparty/easyprofiler/easy_profiler_core/spin_lock.h new file mode 100644 index 0000000..705540d --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/spin_lock.h @@ -0,0 +1,126 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_SPIN_LOCK_H +#define EASY_PROFILER_SPIN_LOCK_H + +#define EASY_USE_CRITICAL_SECTION // Use CRITICAL_SECTION instead of std::atomic_flag + +#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION) +#include +#else +#include +#endif + +namespace profiler { + +#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION) + // std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag... + // By the way, Windows critical sections are slower than std::atomic_flag on Unix. + class spin_lock { CRITICAL_SECTION m_lock; public: + + void lock() { + EnterCriticalSection(&m_lock); + } + + void unlock() { + LeaveCriticalSection(&m_lock); + } + + spin_lock() { + InitializeCriticalSection(&m_lock); + } + + ~spin_lock() { + DeleteCriticalSection(&m_lock); + } + }; +#else + // std::atomic_flag on Unix works fine and very fast (almost instant!) + class spin_lock { ::std::atomic_flag m_lock; public: + + void lock() { + while (m_lock.test_and_set(::std::memory_order_acquire)); + } + + void unlock() { + m_lock.clear(::std::memory_order_release); + } + + spin_lock() { + m_lock.clear(); + } + }; +#endif + + template + class guard_lock + { + T& m_lock; + bool m_isLocked = false; + + public: + + explicit guard_lock(T& m) : m_lock(m) { + m_lock.lock(); + m_isLocked = true; + } + + ~guard_lock() { + unlock(); + } + + inline void unlock() { + if (m_isLocked) { + m_lock.unlock(); + m_isLocked = false; + } + } + }; + +} // END of namespace profiler. + +#ifdef EASY_USE_CRITICAL_SECTION +# undef EASY_USE_CRITICAL_SECTION +#endif + +#endif // EASY_PROFILER_SPIN_LOCK_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/stack_buffer.h b/3rdparty/easyprofiler/easy_profiler_core/stack_buffer.h new file mode 100644 index 0000000..a59ad06 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/stack_buffer.h @@ -0,0 +1,140 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_STACK_BUFFER_H +#define EASY_PROFILER_STACK_BUFFER_H + +#include "nonscoped_block.h" +#include +#include +#include + +#ifdef max +#undef max +#endif + +template +inline void destroy_elem(T*) +{ + +} + +inline void destroy_elem(NonscopedBlock* _elem) +{ + _elem->destroy(); +} + +template +class StackBuffer +{ + struct chunk { int8_t data[sizeof(T)]; }; + + std::list m_overflow; ///< List of additional stack elements if current capacity of buffer is not enough + T* m_buffer; ///< Contiguous buffer used for stack + uint32_t m_size; ///< Current size of stack + uint32_t m_capacity; ///< Current capacity of m_buffer + uint32_t m_maxcapacity; ///< Maximum used capacity including m_buffer and m_overflow + +public: + + StackBuffer() = delete; + StackBuffer(const StackBuffer&) = delete; + StackBuffer(StackBuffer&&) = delete; + + explicit StackBuffer(uint32_t N) + : m_buffer(static_cast(malloc(N * sizeof(T)))) + , m_size(0) + , m_capacity(N) + , m_maxcapacity(N) + { + } + + ~StackBuffer() + { + for (uint32_t i = 0; i < m_size; ++i) + destroy_elem(m_buffer + i); + + free(m_buffer); + + for (auto& elem : m_overflow) + destroy_elem(reinterpret_cast(elem.data + 0)); + } + + template + T& push(TArgs ... _args) + { + if (m_size < m_capacity) + return *(::new (m_buffer + m_size++) T(_args...)); + + m_overflow.emplace_back(); + const uint32_t cap = m_capacity + static_cast(m_overflow.size()); + if (m_maxcapacity < cap) + m_maxcapacity = cap; + + return *(::new (m_overflow.back().data + 0) T(_args...)); + } + + void pop() + { + if (m_overflow.empty()) + { + // m_size should not be equal to 0 here because ProfileManager behavior does not allow such situation + destroy_elem(m_buffer + --m_size); + + if (m_size == 0 && m_maxcapacity > m_capacity) + { + // When stack gone empty we can resize buffer to use enough space in the future + free(m_buffer); + m_maxcapacity = m_capacity = std::max(m_maxcapacity, m_capacity << 1); + m_buffer = static_cast(malloc(m_capacity * sizeof(T))); + } + + return; + } + + destroy_elem(reinterpret_cast(m_overflow.back().data + 0)); + m_overflow.pop_back(); + } + +}; // END of class StackBuffer. + +#endif // EASY_PROFILER_STACK_BUFFER_H diff --git a/3rdparty/easyprofiler/easy_profiler_core/thread_storage.cpp b/3rdparty/easyprofiler/easy_profiler_core/thread_storage.cpp new file mode 100644 index 0000000..beb6839 --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/thread_storage.cpp @@ -0,0 +1,157 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#include "thread_storage.h" +#include "current_thread.h" +#include "current_time.h" + +ThreadStorage::ThreadStorage() + : nonscopedBlocks(16) + , frameStartTime(0) + , id(getCurrentThreadId()) + , stackSize(0) + , allowChildren(true) + , named(false) + , guarded(false) + , frameOpened(false) + , halt(false) +{ + expired = ATOMIC_VAR_INIT(0); + profiledFrameOpened = ATOMIC_VAR_INIT(false); +} + +void ThreadStorage::storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin) +{ + const uint16_t serializedDataSize = static_cast(sizeof(profiler::ArbitraryValue) + _size); + void* data = blocks.closedList.allocate(serializedDataSize); + + ::new (data) profiler::ArbitraryValue(_timestamp, _vin.m_id, _id, static_cast(_size), _type, _isArray); + + char* cdata = reinterpret_cast(data); + memcpy(cdata + sizeof(profiler::ArbitraryValue), _data, _size); + + blocks.usedMemorySize += serializedDataSize; +} + +void ThreadStorage::storeBlock(const profiler::Block& block) +{ +#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 + EASY_LOCAL_STATIC_PTR(const BaseBlockDescriptor*, desc, \ + MANAGER.addBlockDescriptor(EASY_OPTION_STORAGE_EXPAND_BLOCKS_ON ? profiler::ON : profiler::OFF, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage", \ + __FILE__, __LINE__, profiler::BlockType::Block, EASY_COLOR_INTERNAL_EVENT)); + + EASY_THREAD_LOCAL static profiler::timestamp_t beginTime = 0ULL; + EASY_THREAD_LOCAL static profiler::timestamp_t endTime = 0ULL; +#endif + + uint16_t nameLength = static_cast(strlen(block.name())); + uint16_t serializedDataSize = static_cast(sizeof(profiler::BaseBlockData) + nameLength + 1); + +#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 + const bool expanded = (desc->m_status & profiler::ON) && blocks.closedList.need_expand(serializedDataSize); + if (expanded) beginTime = getCurrentTime(); +#endif + + void* data = blocks.closedList.allocate(serializedDataSize); + +#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 + if (expanded) endTime = getCurrentTime(); +#endif + + ::new (data) profiler::SerializedBlock(block, nameLength); + blocks.usedMemorySize += serializedDataSize; + +#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 + if (expanded) + { + profiler::Block b(beginTime, desc->id(), ""); + b.finish(endTime); + + serializedDataSize = static_cast(sizeof(profiler::BaseBlockData) + 1); + data = blocks.closedList.allocate(serializedDataSize); + ::new (data) profiler::SerializedBlock(b, 0); + blocks.usedMemorySize += serializedDataSize; + } +#endif +} + +void ThreadStorage::storeCSwitch(const CSwitchBlock& block) +{ + uint16_t nameLength = static_cast(strlen(block.name())); + uint16_t serializedDataSize = static_cast(sizeof(profiler::CSwitchEvent) + nameLength + 1); + void* data = sync.closedList.allocate(serializedDataSize); + ::new (data) profiler::SerializedCSwitch(block, nameLength); + sync.usedMemorySize += serializedDataSize; +} + +void ThreadStorage::clearClosed() +{ + blocks.clearClosed(); + sync.clearClosed(); +} + +void ThreadStorage::popSilent() +{ + if (!blocks.openedList.empty()) + { + profiler::Block& top = blocks.openedList.back(); + top.m_end = top.m_begin; + if (!top.m_isScoped) + nonscopedBlocks.pop(); + blocks.openedList.pop_back(); + } +} + +void ThreadStorage::beginFrame() +{ + if (!frameOpened) + { + frameStartTime = getCurrentTime(); + frameOpened = true; + } +} + +profiler::timestamp_t ThreadStorage::endFrame() +{ + frameOpened = false; + return getCurrentTime() - frameStartTime; +} diff --git a/3rdparty/easyprofiler/easy_profiler_core/thread_storage.h b/3rdparty/easyprofiler/easy_profiler_core/thread_storage.h new file mode 100644 index 0000000..bdfc08f --- /dev/null +++ b/3rdparty/easyprofiler/easy_profiler_core/thread_storage.h @@ -0,0 +1,134 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + 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. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_THREAD_STORAGE_H +#define EASY_PROFILER_THREAD_STORAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include "stack_buffer.h" +#include "chunk_allocator.h" + +////////////////////////////////////////////////////////////////////////// + +template +struct BlocksList +{ + BlocksList() = default; + + std::vector openedList; + chunk_allocator closedList; + uint64_t usedMemorySize = 0; + + void clearClosed() { + //closedList.clear(); + usedMemorySize = 0; + } + +private: + + BlocksList(const BlocksList&) = delete; + BlocksList(BlocksList&&) = delete; + +}; // END of struct BlocksList. + +////////////////////////////////////////////////////////////////////////// + +class CSwitchBlock : public profiler::CSwitchEvent +{ + const char* m_name; + +public: + + CSwitchBlock(profiler::timestamp_t _begin_time, profiler::thread_id_t _tid, const char* _runtimeName) EASY_NOEXCEPT; + inline const char* name() const EASY_NOEXCEPT { return m_name; } +}; + +////////////////////////////////////////////////////////////////////////// + +const uint16_t SIZEOF_BLOCK = sizeof(profiler::BaseBlockData) + 1 + sizeof(uint16_t); // SerializedBlock stores BaseBlockData + at least 1 character for name ('\0') + 2 bytes for size of serialized data +const uint16_t SIZEOF_CSWITCH = sizeof(profiler::CSwitchEvent) + 1 + sizeof(uint16_t); // SerializedCSwitch also stores additional 4 bytes to be able to save 64-bit thread_id + +struct ThreadStorage EASY_FINAL +{ + StackBuffer nonscopedBlocks; + BlocksList, SIZEOF_BLOCK * (uint16_t)128U> blocks; + BlocksList sync; + + std::string name; ///< Thread name + profiler::timestamp_t frameStartTime; ///< Current frame start time. Used to calculate FPS. + const profiler::thread_id_t id; ///< Thread ID + std::atomic expired; ///< Is thread expired + std::atomic_bool profiledFrameOpened; ///< Is new profiled frame opened (this is true when profiling is enabled and there is an opened frame) \sa frameOpened + int32_t stackSize; ///< Current thread stack depth. Used when switching profiler state to begin collecting blocks only when new frame would be opened. + bool allowChildren; ///< False if one of previously opened blocks has OFF_RECURSIVE or ON_WITHOUT_CHILDREN status + bool named; ///< True if thread name was set + bool guarded; ///< True if thread has been registered using ThreadGuard + bool frameOpened; ///< Is new frame opened (this does not depend on profiling status) \sa profiledFrameOpened + bool halt; ///< This is set to true when new frame started while dumping blocks. Used to restrict collecting blocks during dumping process. + + void storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin); + void storeBlock(const profiler::Block& _block); + void storeCSwitch(const CSwitchBlock& _block); + void clearClosed(); + void popSilent(); + + void beginFrame(); + profiler::timestamp_t endFrame(); + + ThreadStorage(); + +private: + + ThreadStorage(const ThreadStorage&) = delete; + ThreadStorage(ThreadStorage&&) = delete; + +}; // END of struct ThreadStorage. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_THREAD_STORAGE_H diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/CMakeDirectoryInformation.cmake b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..9ecfcf5 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/alex/Work/C++Projects/easyprofiler") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/alex/Work/C++Projects/easyprofiler") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/DependInfo.cmake b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/DependInfo.cmake new file mode 100644 index 0000000..6d9b6e3 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/DependInfo.cmake @@ -0,0 +1,59 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/qrc_resources.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_graphics_view.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_tree_widget.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/common_functions.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/descriptors_tree_widget.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_chronometer_item.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_item.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_qtimer.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals_qobjects.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/main.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/main_window.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/profiler_gui_automoc.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_item.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_loader.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o" + "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp" "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BUILD_WITH_EASY_PROFILER=1" + "EASY_DEFAULT_PORT=28077" + "EASY_PROFILER_VERSION_MAJOR=1" + "EASY_PROFILER_VERSION_MINOR=3" + "EASY_PROFILER_VERSION_PATCH=0" + "QT_CORE_LIB" + "QT_GUI_LIB" + "QT_NO_DEBUG" + "QT_WIDGETS_LIB" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "profiler_gui" + "/home/alex/Work/Qt/5.8/gcc_64/include" + "/home/alex/Work/Qt/5.8/gcc_64/include/QtWidgets" + "/home/alex/Work/Qt/5.8/gcc_64/include/QtGui" + "/home/alex/Work/Qt/5.8/gcc_64/include/QtCore" + "/home/alex/Work/Qt/5.8/gcc_64/./mkspecs/linux-g++" + "easy_profiler_core/include" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake" + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/build.make b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/build.make new file mode 100644 index 0000000..8554a13 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/build.make @@ -0,0 +1,603 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/alex/Work/C++Projects/easyprofiler + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/alex/Work/C++Projects/easyprofiler + +# Include any dependencies generated for this target. +include profiler_gui/CMakeFiles/profiler_gui.dir/depend.make + +# Include the progress variables for this target. +include profiler_gui/CMakeFiles/profiler_gui.dir/progress.make + +# Include the compile flags for this target's objects. +include profiler_gui/CMakeFiles/profiler_gui.dir/flags.make + +profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o: profiler_gui/main.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/main.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/main.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/main.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/main.cpp > CMakeFiles/profiler_gui.dir/main.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/main.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/main.cpp -o CMakeFiles/profiler_gui.dir/main.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o: profiler_gui/arbitrary_value_inspector.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp > CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp -o CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o: profiler_gui/blocks_graphics_view.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_graphics_view.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_graphics_view.cpp > CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_graphics_view.cpp -o CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o: profiler_gui/blocks_tree_widget.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_tree_widget.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_tree_widget.cpp > CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_tree_widget.cpp -o CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o: profiler_gui/common_functions.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_5) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/common_functions.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/common_functions.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/common_functions.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/common_functions.cpp > CMakeFiles/profiler_gui.dir/common_functions.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/common_functions.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/common_functions.cpp -o CMakeFiles/profiler_gui.dir/common_functions.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o: profiler_gui/descriptors_tree_widget.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_6) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/descriptors_tree_widget.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/descriptors_tree_widget.cpp > CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/descriptors_tree_widget.cpp -o CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o: profiler_gui/easy_chronometer_item.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_7) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_chronometer_item.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_chronometer_item.cpp > CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_chronometer_item.cpp -o CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o: profiler_gui/easy_frame_rate_viewer.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_8) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp > CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp -o CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o: profiler_gui/easy_graphics_item.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_9) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_item.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_item.cpp > CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_item.cpp -o CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o: profiler_gui/easy_graphics_scrollbar.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_10) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp > CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp -o CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o: profiler_gui/easy_qtimer.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_11) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_qtimer.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_qtimer.cpp > CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_qtimer.cpp -o CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o: profiler_gui/globals.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_12) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/globals.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/globals.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals.cpp > CMakeFiles/profiler_gui.dir/globals.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/globals.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals.cpp -o CMakeFiles/profiler_gui.dir/globals.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o: profiler_gui/globals_qobjects.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_13) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals_qobjects.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals_qobjects.cpp > CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals_qobjects.cpp -o CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o: profiler_gui/main_window.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_14) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/main_window.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/main_window.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/main_window.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/main_window.cpp > CMakeFiles/profiler_gui.dir/main_window.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/main_window.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/main_window.cpp -o CMakeFiles/profiler_gui.dir/main_window.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o: profiler_gui/tree_widget_item.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_15) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_item.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_item.cpp > CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_item.cpp -o CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o: profiler_gui/tree_widget_loader.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_16) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_loader.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_loader.cpp > CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_loader.cpp -o CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o: profiler_gui/treeview_first_column_delegate.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_17) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp > CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp -o CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o: profiler_gui/profiler_gui_automoc.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_18) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/profiler_gui_automoc.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/profiler_gui_automoc.cpp > CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/profiler_gui_automoc.cpp -o CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o + + +profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/flags.make +profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o: profiler_gui/CMakeFiles/profiler_gui.dir/qrc_resources.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_19) "Building CXX object profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/qrc_resources.cpp + +profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/qrc_resources.cpp > CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.i + +profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/qrc_resources.cpp -o CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.s + +profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.requires: + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.requires + +profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.provides: profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.requires + $(MAKE) -f profiler_gui/CMakeFiles/profiler_gui.dir/build.make profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.provides.build +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.provides + +profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.provides.build: profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o + + +# Object files for target profiler_gui +profiler_gui_OBJECTS = \ +"CMakeFiles/profiler_gui.dir/main.cpp.o" \ +"CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o" \ +"CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o" \ +"CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o" \ +"CMakeFiles/profiler_gui.dir/common_functions.cpp.o" \ +"CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o" \ +"CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o" \ +"CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o" \ +"CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o" \ +"CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o" \ +"CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o" \ +"CMakeFiles/profiler_gui.dir/globals.cpp.o" \ +"CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o" \ +"CMakeFiles/profiler_gui.dir/main_window.cpp.o" \ +"CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o" \ +"CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o" \ +"CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o" \ +"CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o" \ +"CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o" + +# External object files for target profiler_gui +profiler_gui_EXTERNAL_OBJECTS = + +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/build.make +bin/profiler_gui: /home/alex/Work/Qt/5.8/gcc_64/lib/libQt5Widgets.so.5.8.0 +bin/profiler_gui: bin/libeasy_profiler.so +bin/profiler_gui: /home/alex/Work/Qt/5.8/gcc_64/lib/libQt5Gui.so.5.8.0 +bin/profiler_gui: /home/alex/Work/Qt/5.8/gcc_64/lib/libQt5Core.so.5.8.0 +bin/profiler_gui: profiler_gui/CMakeFiles/profiler_gui.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_20) "Linking CXX executable ../bin/profiler_gui" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/profiler_gui.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +profiler_gui/CMakeFiles/profiler_gui.dir/build: bin/profiler_gui + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/build + +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/main.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/common_functions.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/globals.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/main_window.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o.requires +profiler_gui/CMakeFiles/profiler_gui.dir/requires: profiler_gui/CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o.requires + +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/requires + +profiler_gui/CMakeFiles/profiler_gui.dir/clean: + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && $(CMAKE_COMMAND) -P CMakeFiles/profiler_gui.dir/cmake_clean.cmake +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/clean + +profiler_gui/CMakeFiles/profiler_gui.dir/depend: + cd /home/alex/Work/C++Projects/easyprofiler && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/profiler_gui /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/profiler_gui /home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : profiler_gui/CMakeFiles/profiler_gui.dir/depend + diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/cmake_clean.cmake b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/cmake_clean.cmake new file mode 100644 index 0000000..bdaff9d --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/cmake_clean.cmake @@ -0,0 +1,30 @@ +file(REMOVE_RECURSE + "profiler_gui_automoc.cpp" + "CMakeFiles/profiler_gui.dir/qrc_resources.cpp" + "CMakeFiles/profiler_gui.dir/main.cpp.o" + "CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o" + "CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o" + "CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o" + "CMakeFiles/profiler_gui.dir/common_functions.cpp.o" + "CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o" + "CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o" + "CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o" + "CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o" + "CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o" + "CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o" + "CMakeFiles/profiler_gui.dir/globals.cpp.o" + "CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o" + "CMakeFiles/profiler_gui.dir/main_window.cpp.o" + "CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o" + "CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o" + "CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o" + "CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o" + "CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o" + "../bin/profiler_gui.pdb" + "../bin/profiler_gui" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/profiler_gui.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/depend.make b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/depend.make new file mode 100644 index 0000000..cc4c540 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/depend.make @@ -0,0 +1,2 @@ +# Empty dependencies file for profiler_gui. +# This may be replaced when dependencies are built. diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/flags.make b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/flags.make new file mode 100644 index 0000000..f9e76bf --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -O3 -DNDEBUG -fPIC -DEASY_CHRONO_STEADY_CLOCK=0 -DEASY_CHRONO_HIGHRES_CLOCK=0 -DEASY_OPTION_START_LISTEN_ON_STARTUP=0 -DEASY_OPTION_MEASURE_STORAGE_EXPAND=0 -DEASY_OPTION_STORAGE_EXPAND_BLOCKS_ON=0 -DEASY_OPTION_IMPLICIT_THREAD_REGISTRATION=1 -DEASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS=0 -DEASY_OPTION_LOG_ENABLED=0 -DEASY_OPTION_PRETTY_PRINT_FUNCTIONS=0 -DEASY_OPTION_BUILTIN_COLORS=1 -std=gnu++11 -std=gnu++11 + +CXX_DEFINES = -DBUILD_WITH_EASY_PROFILER=1 -DEASY_DEFAULT_PORT=28077 -DEASY_PROFILER_VERSION_MAJOR=1 -DEASY_PROFILER_VERSION_MINOR=3 -DEASY_PROFILER_VERSION_PATCH=0 -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NO_DEBUG -DQT_WIDGETS_LIB + +CXX_INCLUDES = -I/home/alex/Work/C++Projects/easyprofiler/profiler_gui -isystem /home/alex/Work/Qt/5.8/gcc_64/include -isystem /home/alex/Work/Qt/5.8/gcc_64/include/QtWidgets -isystem /home/alex/Work/Qt/5.8/gcc_64/include/QtGui -isystem /home/alex/Work/Qt/5.8/gcc_64/include/QtCore -isystem /home/alex/Work/Qt/5.8/gcc_64/./mkspecs/linux-g++ -I/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include + diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/link.txt b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/link.txt new file mode 100644 index 0000000..cd1eaaf --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/link.txt @@ -0,0 +1 @@ +/usr/bin/c++ -O3 -DNDEBUG CMakeFiles/profiler_gui.dir/main.cpp.o CMakeFiles/profiler_gui.dir/arbitrary_value_inspector.cpp.o CMakeFiles/profiler_gui.dir/blocks_graphics_view.cpp.o CMakeFiles/profiler_gui.dir/blocks_tree_widget.cpp.o CMakeFiles/profiler_gui.dir/common_functions.cpp.o CMakeFiles/profiler_gui.dir/descriptors_tree_widget.cpp.o CMakeFiles/profiler_gui.dir/easy_chronometer_item.cpp.o CMakeFiles/profiler_gui.dir/easy_frame_rate_viewer.cpp.o CMakeFiles/profiler_gui.dir/easy_graphics_item.cpp.o CMakeFiles/profiler_gui.dir/easy_graphics_scrollbar.cpp.o CMakeFiles/profiler_gui.dir/easy_qtimer.cpp.o CMakeFiles/profiler_gui.dir/globals.cpp.o CMakeFiles/profiler_gui.dir/globals_qobjects.cpp.o CMakeFiles/profiler_gui.dir/main_window.cpp.o CMakeFiles/profiler_gui.dir/tree_widget_item.cpp.o CMakeFiles/profiler_gui.dir/tree_widget_loader.cpp.o CMakeFiles/profiler_gui.dir/treeview_first_column_delegate.cpp.o CMakeFiles/profiler_gui.dir/profiler_gui_automoc.cpp.o CMakeFiles/profiler_gui.dir/CMakeFiles/profiler_gui.dir/qrc_resources.cpp.o -o ../bin/profiler_gui -rdynamic /home/alex/Work/Qt/5.8/gcc_64/lib/libQt5Widgets.so.5.8.0 ../bin/libeasy_profiler.so /home/alex/Work/Qt/5.8/gcc_64/lib/libQt5Gui.so.5.8.0 /home/alex/Work/Qt/5.8/gcc_64/lib/libQt5Core.so.5.8.0 -lpthread -Wl,-rpath,/home/alex/Work/Qt/5.8/gcc_64/lib:/home/alex/Work/C++Projects/easyprofiler/bin: diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/progress.make b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/progress.make new file mode 100644 index 0000000..c417330 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui.dir/progress.make @@ -0,0 +1,21 @@ +CMAKE_PROGRESS_1 = 9 +CMAKE_PROGRESS_2 = 10 +CMAKE_PROGRESS_3 = 11 +CMAKE_PROGRESS_4 = 12 +CMAKE_PROGRESS_5 = 13 +CMAKE_PROGRESS_6 = 14 +CMAKE_PROGRESS_7 = 15 +CMAKE_PROGRESS_8 = 16 +CMAKE_PROGRESS_9 = 17 +CMAKE_PROGRESS_10 = 18 +CMAKE_PROGRESS_11 = 19 +CMAKE_PROGRESS_12 = 20 +CMAKE_PROGRESS_13 = 21 +CMAKE_PROGRESS_14 = 22 +CMAKE_PROGRESS_15 = 23 +CMAKE_PROGRESS_16 = 24 +CMAKE_PROGRESS_17 = 25 +CMAKE_PROGRESS_18 = 26 +CMAKE_PROGRESS_19 = 27 +CMAKE_PROGRESS_20 = 28 + diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/AutogenInfo.cmake b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/AutogenInfo.cmake new file mode 100644 index 0000000..3f8c232 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/AutogenInfo.cmake @@ -0,0 +1,29 @@ +set(AM_SOURCES "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/main.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_graphics_view.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_tree_widget.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/common_functions.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/descriptors_tree_widget.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_chronometer_item.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_item.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_qtimer.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals_qobjects.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/main_window.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_item.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_loader.cpp;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp" ) +set(AM_RCC_SOURCES "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/resources.qrc" ) +set(AM_RCC_INPUTS "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/logo.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/arrow-up-hover.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/radio-indicator.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/settings.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/reload.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/open-folder2.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/arrow-up-disabled.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/statistics.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/lan_on.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/wifi.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/check-disabled.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/close-white.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/maximize-white-pressed.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/save.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/play.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/close-white-hover.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/collapse.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/reload-folder2.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/list.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/search-prev.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/off.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/arrow-down-disabled.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/radio-indicator-disabled.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/search-next.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/arrow-up.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/maximize-white.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/check.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/lan.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/maximize-white-hover.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/lan.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/minimize-white.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/stop.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/minimize-white-pressed.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/delete.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/arrow-down.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/wifi_on.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/lan_on.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/expand.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/close-white-pressed.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/arrow-down-hover.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/minimize-white-hover.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/images/default/statistics2.svg@list_sep@/home/alex/Work/C++Projects/easyprofiler/profiler_gui/themes/default.css") +set(AM_SKIP_MOC "" ) +set(AM_SKIP_UIC ) +set(AM_HEADERS "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/arbitrary_value_inspector.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_graphics_view.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/blocks_tree_widget.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/common_functions.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/common_types.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/descriptors_tree_widget.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_chronometer_item.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_frame_rate_viewer.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_item.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_graphics_scrollbar.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/easy_qtimer.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/globals.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/main_window.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_item.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/tree_widget_loader.h;/home/alex/Work/C++Projects/easyprofiler/profiler_gui/treeview_first_column_delegate.h" ) +set(AM_MOC_COMPILE_DEFINITIONS "BUILD_WITH_EASY_PROFILER=1;EASY_DEFAULT_PORT=28077;EASY_PROFILER_VERSION_MAJOR=1;EASY_PROFILER_VERSION_MINOR=3;EASY_PROFILER_VERSION_PATCH=0;QT_CORE_LIB;QT_GUI_LIB;QT_NO_DEBUG;QT_WIDGETS_LIB") +set(AM_MOC_INCLUDES "/home/alex/Work/C++Projects/easyprofiler/profiler_gui;/home/alex/Work/Qt/5.8/gcc_64/include;/home/alex/Work/Qt/5.8/gcc_64/include/QtWidgets;/home/alex/Work/Qt/5.8/gcc_64/include/QtGui;/home/alex/Work/Qt/5.8/gcc_64/include/QtCore;/home/alex/Work/Qt/5.8/gcc_64/./mkspecs/linux-g++;/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include;/usr/include") +set(AM_MOC_OPTIONS "") +set(AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE "") +set(AM_CMAKE_BINARY_DIR "/home/alex/Work/C++Projects/easyprofiler/") +set(AM_CMAKE_SOURCE_DIR "/home/alex/Work/C++Projects/easyprofiler/") +set(AM_QT_MOC_EXECUTABLE "/home/alex/Work/Qt/5.8/gcc_64/bin/moc") +set(AM_QT_UIC_EXECUTABLE "") +set(AM_QT_RCC_EXECUTABLE "/home/alex/Work/Qt/5.8/gcc_64/bin/rcc") +if(DEFINED ENV{DEB_BUILD_MULTIARCH} AND DEFINED ENV{DEB_HOST_MULTIARCH} AND "/home/alex/Work/Qt/5.8/gcc_64/bin/moc" MATCHES "/usr/lib/$ENV{DEB_HOST_MULTIARCH}/qt5/bin/moc") + set(AM_QT_MOC_EXECUTABLE "/usr/lib/$ENV{DEB_BUILD_MULTIARCH}/qt5/bin/moc") +endif() +set(AM_CMAKE_CURRENT_SOURCE_DIR "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/") +set(AM_CMAKE_CURRENT_BINARY_DIR "/home/alex/Work/C++Projects/easyprofiler/profiler_gui/") +set(AM_QT_VERSION_MAJOR "5") +set(AM_TARGET_NAME "profiler_gui_automoc") +set(AM_ORIGIN_TARGET_NAME "profiler_gui") +set(AM_RELAXED_MODE "FALSE") +set(AM_UIC_TARGET_OPTIONS ) +set(AM_UIC_OPTIONS_FILES ) +set(AM_UIC_OPTIONS_OPTIONS ) +set(AM_RCC_OPTIONS_FILES "") +set(AM_RCC_OPTIONS_OPTIONS "") diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/DependInfo.cmake b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/DependInfo.cmake new file mode 100644 index 0000000..19fab21 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/DependInfo.cmake @@ -0,0 +1,11 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + ) +# The set of files for implicit dependencies of each language: + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/build.make b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/build.make new file mode 100644 index 0000000..8b445e6 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/build.make @@ -0,0 +1,77 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/alex/Work/C++Projects/easyprofiler + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/alex/Work/C++Projects/easyprofiler + +# Utility rule file for profiler_gui_automoc. + +# Include the progress variables for this target. +include profiler_gui/CMakeFiles/profiler_gui_automoc.dir/progress.make + +profiler_gui/CMakeFiles/profiler_gui_automoc: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --blue --bold --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Automatic moc and rcc for target profiler_gui" + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && /usr/bin/cmake -E cmake_autogen /home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/ Release + +profiler_gui_automoc: profiler_gui/CMakeFiles/profiler_gui_automoc +profiler_gui_automoc: profiler_gui/CMakeFiles/profiler_gui_automoc.dir/build.make + +.PHONY : profiler_gui_automoc + +# Rule to build all files generated by this target. +profiler_gui/CMakeFiles/profiler_gui_automoc.dir/build: profiler_gui_automoc + +.PHONY : profiler_gui/CMakeFiles/profiler_gui_automoc.dir/build + +profiler_gui/CMakeFiles/profiler_gui_automoc.dir/clean: + cd /home/alex/Work/C++Projects/easyprofiler/profiler_gui && $(CMAKE_COMMAND) -P CMakeFiles/profiler_gui_automoc.dir/cmake_clean.cmake +.PHONY : profiler_gui/CMakeFiles/profiler_gui_automoc.dir/clean + +profiler_gui/CMakeFiles/profiler_gui_automoc.dir/depend: + cd /home/alex/Work/C++Projects/easyprofiler && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/profiler_gui /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/profiler_gui /home/alex/Work/C++Projects/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : profiler_gui/CMakeFiles/profiler_gui_automoc.dir/depend + diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/cmake_clean.cmake b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/cmake_clean.cmake new file mode 100644 index 0000000..186b8e5 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/cmake_clean.cmake @@ -0,0 +1,10 @@ +file(REMOVE_RECURSE + "profiler_gui_automoc.cpp" + "CMakeFiles/profiler_gui.dir/qrc_resources.cpp" + "CMakeFiles/profiler_gui_automoc" +) + +# Per-language clean rules from dependency scanning. +foreach(lang ) + include(CMakeFiles/profiler_gui_automoc.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/progress.make b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/progress.make new file mode 100644 index 0000000..c7c4328 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/profiler_gui_automoc.dir/progress.make @@ -0,0 +1,2 @@ +CMAKE_PROGRESS_1 = 29 + diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeFiles/progress.marks b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/progress.marks new file mode 100644 index 0000000..f04c001 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeFiles/progress.marks @@ -0,0 +1 @@ +29 diff --git a/3rdparty/easyprofiler/profiler_gui/CMakeLists.txt b/3rdparty/easyprofiler/profiler_gui/CMakeLists.txt new file mode 100644 index 0000000..e78c964 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/CMakeLists.txt @@ -0,0 +1,70 @@ +#set(CMAKE_PREFIX_PATH f:/qt/5.5/5.6/msvc2013_64/lib/cmake) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(Qt5Widgets) + +if (Qt5Widgets_FOUND) + if (NOT("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") AND WIN32) + set(APPLICATION_PLATFORM WIN32) + endif () + add_executable(profiler_gui ${APPLICATION_PLATFORM} + main.cpp + arbitrary_value_inspector.h + arbitrary_value_inspector.cpp + blocks_graphics_view.h + blocks_graphics_view.cpp + blocks_tree_widget.h + blocks_tree_widget.cpp + common_functions.h + common_functions.cpp + common_types.h + descriptors_tree_widget.h + descriptors_tree_widget.cpp + easy_chronometer_item.h + easy_chronometer_item.cpp + easy_frame_rate_viewer.h + easy_frame_rate_viewer.cpp + easy_graphics_item.h + easy_graphics_item.cpp + easy_graphics_scrollbar.h + easy_graphics_scrollbar.cpp + easy_qtimer.h + easy_qtimer.cpp + globals.h + globals.cpp + globals_qobjects.cpp + main_window.h + main_window.cpp + tree_widget_item.h + tree_widget_item.cpp + tree_widget_loader.h + tree_widget_loader.cpp + treeview_first_column_delegate.h + treeview_first_column_delegate.cpp + resources.qrc + resources.rc + ) + target_link_libraries(profiler_gui Qt5::Widgets easy_profiler) + if (WIN32) + target_compile_definitions(profiler_gui PRIVATE -D_WIN32_WINNT=0x0600) + endif () + if (MINGW) + target_compile_definitions(profiler_gui PRIVATE -DSTRSAFE_NO_DEPRECATE) + endif () + + install( + TARGETS + profiler_gui + RUNTIME + DESTINATION + bin + ) + + set_property(TARGET profiler_gui PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) +else () + message(STATUS "INFO\n\n\tQt5 not found! Generating EasyProfiler projects without GUI.\n") +endif () + diff --git a/3rdparty/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp b/3rdparty/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp new file mode 100644 index 0000000..2655827 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/arbitrary_value_inspector.cpp @@ -0,0 +1,1060 @@ +/************************************************************************ +* file name : arbitrary_value_inspector.cpp +* ----------------- : +* creation time : 2017/11/30 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of . +* ----------------- : +* change log : * 2017/11/30 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "arbitrary_value_inspector.h" +#include "treeview_first_column_delegate.h" +#include "globals.h" + +////////////////////////////////////////////////////////////////////////// + +void getChartPoints(const ArbitraryValuesCollection& _collection, Points& _points, qreal& _minValue, qreal& _maxValue) +{ + _minValue = 1e300; + _maxValue = -1e300; + + const auto size = _collection.size(); + _points.clear(); + _points.reserve(size); + + if (size == 0) + return; + + const auto& valuesByThread = _collection.valuesMap(); + + if (valuesByThread.size() == 1) + { + const auto& values = valuesByThread.begin()->second; + for (auto value : values) + { + const qreal x = sceneX(value->begin()); + const qreal y = profiler_gui::value2real(*value); + _points.emplace_back(x, y); + + if (y > _maxValue) + _maxValue = y; + if (y < _minValue) + _minValue = y; + } + } + else + { + std::list threadIds; + for (const auto& it : valuesByThread) + threadIds.push_back(it.first); + + size_t i = 0; + while (!threadIds.empty()) + { + for (auto it = threadIds.begin(); it != threadIds.end();) + { + const auto& values = valuesByThread.at(*it); + if (i >= values.size()) + { + it = threadIds.erase(it); + continue; + } + + const qreal x = sceneX(values[i]->begin()); + const qreal y = profiler_gui::value2real(*values[i]); + _points.emplace_back(x, y); + + if (y > _maxValue) + _maxValue = y; + if (y < _minValue) + _minValue = y; + + ++it; + } + } + + std::sort(_points.begin(), _points.end(), [](const QPointF& lhs, const QPointF& rhs) -> bool { + return lhs.x() < rhs.x(); + }); + } +} + +////////////////////////////////////////////////////////////////////////// + +ArbitraryValuesCollection::ArbitraryValuesCollection() + : m_beginTime(0) + , m_minValue(1e300) + , m_maxValue(-1e300) + , m_jobType(0) +{ + m_status = ATOMIC_VAR_INIT(Idle); + m_bInterrupt = ATOMIC_VAR_INIT(false); +} + +ArbitraryValuesCollection::~ArbitraryValuesCollection() +{ + interrupt(); +} + +const ArbitraryValuesMap& ArbitraryValuesCollection::valuesMap() const +{ + return m_values; +} + +const Points& ArbitraryValuesCollection::points() const +{ + return m_points; +} + +ArbitraryValuesCollection::JobStatus ArbitraryValuesCollection::status() const +{ + return static_cast(m_status.load(std::memory_order_acquire)); +} + +size_t ArbitraryValuesCollection::size() const +{ + size_t totalSize = 0; + for (const auto& it : m_values) + totalSize += it.second.size(); + return totalSize; +} + +qreal ArbitraryValuesCollection::minValue() const +{ + return m_minValue; +} + +qreal ArbitraryValuesCollection::maxValue() const +{ + return m_maxValue; +} + +void ArbitraryValuesCollection::collectValues(profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName) +{ + interrupt(); + + setStatus(InProgress); + m_points.clear(); + m_jobType = ValuesJob; + + if (_valueId == 0) + m_collectorThread = std::thread(&This::collectByName, this, _threadId, _valueName); + else + m_collectorThread = std::thread(&This::collectById, this, _threadId, _valueId); +} + +void ArbitraryValuesCollection::collectValues(profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::timestamp_t _beginTime) +{ + interrupt(); + + setStatus(InProgress); + m_points.clear(); + m_beginTime = _beginTime; + m_minValue = 1e300; + m_maxValue = -1e300; + m_jobType = ValuesJob | PointsJob; + + if (_valueId == 0) + m_collectorThread = std::thread(&This::collectByName, this, _threadId, _valueName); + else + m_collectorThread = std::thread(&This::collectById, this, _threadId, _valueId); +} + +bool ArbitraryValuesCollection::calculatePoints(profiler::timestamp_t _beginTime) +{ + if (status() != Ready || m_values.empty()) + return false; + + if (m_collectorThread.joinable()) + m_collectorThread.join(); + + setStatus(InProgress); + m_points.clear(); + m_beginTime = _beginTime; + m_minValue = 1e300; + m_maxValue = -1e300; + m_jobType = PointsJob; + + m_collectorThread = std::thread([this] + { + getChartPoints(*this, m_points, m_minValue, m_maxValue); + setStatus(Ready); + }); + + return true; +} + +void ArbitraryValuesCollection::interrupt() +{ + if (!m_collectorThread.joinable()) + return; + + m_bInterrupt.store(true, std::memory_order_release); + m_collectorThread.join(); + m_bInterrupt.store(false, std::memory_order_release); + + setStatus(Idle); + m_jobType = None; + m_values.clear(); +} + +void ArbitraryValuesCollection::setStatus(JobStatus _status) +{ + m_status.store(static_cast(_status), std::memory_order_release); +} + +void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId) +{ + if (_threadId == 0) + { + const auto threadsCount = EASY_GLOBALS.profiler_blocks.size(); + const bool calculatePointsInner = (m_jobType & PointsJob) != 0 && threadsCount == 1; + + for (const auto& it : EASY_GLOBALS.profiler_blocks) + { + if (!collectByIdForThread(it.second, _valueId, calculatePointsInner)) + return; + } + + if ((m_jobType & PointsJob) != 0 && threadsCount > 1) + getChartPoints(*this, m_points, m_minValue, m_maxValue); + } + else + { + const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId); + if (t != EASY_GLOBALS.profiler_blocks.end() && !collectByIdForThread(t->second, _valueId, (m_jobType & PointsJob) != 0)) + return; + } + + setStatus(Ready); +} + +bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot, + profiler::vin_t _valueId, bool _calculatePoints) +{ + auto& valuesList = m_values[_threadRoot.thread_id]; + + for (auto i : _threadRoot.events) + { + if (m_bInterrupt.load(std::memory_order_acquire)) + return false; + + const auto& block = easyBlock(i).tree; + const auto& desc = easyDescriptor(block.node->id()); + if (desc.type() != profiler::BlockType::Value) + continue; + + const auto value = block.value; + if (value->value_id() != _valueId) + continue; + + valuesList.push_back(value); + + if (_calculatePoints) + { + const auto p = point(*block.value); + + if (p.y() > m_maxValue) + m_maxValue = p.y(); + if (p.y() < m_minValue) + m_minValue = p.y(); + + m_points.push_back(p); + } + } + + return true; +} + +void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, const std::string _valueName) +{ + if (_threadId == 0) + { + const auto threadsCount = EASY_GLOBALS.profiler_blocks.size(); + const bool calculatePointsInner = (m_jobType & PointsJob) != 0 && threadsCount == 1; + + for (const auto& it : EASY_GLOBALS.profiler_blocks) + { + if (!collectByNameForThread(it.second, _valueName, calculatePointsInner)) + return; + } + + if ((m_jobType & PointsJob) != 0 && threadsCount > 1) + getChartPoints(*this, m_points, m_minValue, m_maxValue); + } + else + { + const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId); + if (t != EASY_GLOBALS.profiler_blocks.end() && !collectByNameForThread(t->second, _valueName, (m_jobType & PointsJob) != 0)) + return; + } + + setStatus(Ready); +} + +bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot, + const std::string& _valueName, bool _calculatePoints) +{ + auto& valuesList = m_values[_threadRoot.thread_id]; + + for (auto i : _threadRoot.events) + { + if (m_bInterrupt.load(std::memory_order_acquire)) + return false; + + const auto& block = easyBlock(i).tree; + const auto& desc = easyDescriptor(block.node->id()); + if (desc.type() != profiler::BlockType::Value || _valueName != desc.name()) + continue; + + valuesList.push_back(block.value); + + if (_calculatePoints) + { + const auto p = point(*block.value); + + if (p.y() > m_maxValue) + m_maxValue = p.y(); + if (p.y() < m_minValue) + m_minValue = p.y(); + + m_points.push_back(p); + } + } + + return true; +} + +QPointF ArbitraryValuesCollection::point(const profiler::ArbitraryValue& _value) const +{ + const qreal x = PROF_MICROSECONDS(qreal(_value.begin() - m_beginTime)); + const qreal y = profiler_gui::value2real(_value); + return {x, y}; +} + +////////////////////////////////////////////////////////////////////////// + +EasyArbitraryValuesChartItem::EasyArbitraryValuesChartItem() + : Parent(nullptr) +{ +} + +EasyArbitraryValuesChartItem::~EasyArbitraryValuesChartItem() +{ + +} + +void EasyArbitraryValuesChartItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + if (m_collections.empty()) + return; + + const auto& chart = *reinterpret_cast(scene()->parent()); + const auto scale = chart.xscale(); + + qreal minValue = 1e300, maxValue = -1e300; + for (const auto& c : m_collections) + { + const auto& collection = *c.ptr; + if (minValue > collection.minValue()) + minValue = collection.minValue(); + if (maxValue < collection.maxValue()) + maxValue = collection.maxValue(); + } + + const qreal height = std::max(maxValue - minValue, 1.); + + auto r = scene()->sceneRect(); + + + _painter->save(); + + for (const auto& c : m_collections) + { + const auto& points = c.ptr->points(); + if (points.empty()) + continue; + + if (c.selected) + { + auto pen = _painter->pen(); + pen.setColor(QColor::fromRgba(c.color)); + pen.setWidth(3); + _painter->setPen(pen); + } + else + { + _painter->setPen(QColor::fromRgba(c.color)); + } + + if (points.size() == 1) + _painter->drawPoint(points.front()); + else + { + auto gety = [&r, &minValue, &maxValue, &height] (qreal y) + { + y = maxValue - y; + y /= height; + y *= r.height() - 10; + y += r.top() + 5; + return y; + }; + + if (c.chartType == ChartType::Points) + { + for (const auto& p : points) + _painter->drawPoint(QPointF {p.x() * scale, gety(p.y())}); + } + else + { + QPointF p1 = points.front(); + for (int i = 1; i < points.size(); ++i) + { + const auto& p2 = points[i]; + _painter->drawLine(QPointF {p1.x() * scale, gety(p1.y())}, QPointF {p2.x() * scale, gety(p2.y())}); + p1 = p2; + } + } + //_painter->drawPolyline(points.data(), static_cast(points.size())); + } + } + + _painter->restore(); +} + +QRectF EasyArbitraryValuesChartItem::boundingRect() const +{ + return m_boundingRect; +} + +void EasyArbitraryValuesChartItem::setBoundingRect(const QRectF& _rect) +{ + m_boundingRect = _rect; +} + +void EasyArbitraryValuesChartItem::setBoundingRect(qreal _left, qreal _top, qreal _width, qreal _height) +{ + m_boundingRect.setRect(_left, _top, _width, _height); +} + +void EasyArbitraryValuesChartItem::update(Collections _collections) +{ + m_collections = std::move(_collections); +} + +void EasyArbitraryValuesChartItem::update(const ArbitraryValuesCollection* _selected) +{ + for (auto& collection : m_collections) + collection.selected = collection.ptr == _selected; +} + +////////////////////////////////////////////////////////////////////////// + +EasyGraphicsChart::EasyGraphicsChart(QWidget* _parent) + : Parent(_parent) + , m_chartItem(new EasyArbitraryValuesChartItem()) + , m_left(0) + , m_right(100) + , m_offset(0) + , m_xscale(1) + , m_visibleRegionWidth(100) + , m_bBindMode(false) +{ + setCacheMode(QGraphicsView::CacheNone); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + //setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setOptimizationFlag(QGraphicsView::DontSavePainterState, true); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setContentsMargins(0, 0, 0, 0); + + setScene(new QGraphicsScene(this)); + scene()->setSceneRect(0, -250, 500, 500); + scene()->addItem(m_chartItem); + + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::sceneSizeChanged, + this, &This::onSceneSizeChanged, Qt::QueuedConnection); + + onSceneSizeChanged(); +} + +EasyGraphicsChart::~EasyGraphicsChart() +{ + +} + +void EasyGraphicsChart::onSceneSizeChanged() +{ + setRange(EASY_GLOBALS.scene_left, EASY_GLOBALS.scene_right); +} + +void EasyGraphicsChart::resizeEvent(QResizeEvent* _event) +{ + auto size = _event->size(); + onWindowSizeChanged(size.width(), size.height()); + scene()->update(); +} + +void EasyGraphicsChart::clear() +{ + m_chartItem->update(Collections {}); +} + +bool EasyGraphicsChart::bindMode() const +{ + return m_bBindMode; +} + +qreal EasyGraphicsChart::xscale() const +{ + return m_xscale; +} + +qreal EasyGraphicsChart::left() const +{ + return m_left; +} + +qreal EasyGraphicsChart::right() const +{ + return m_right; +} + +qreal EasyGraphicsChart::range() const +{ + return m_right - m_left; +} + +qreal EasyGraphicsChart::offset() const +{ + return m_bBindMode ? m_offset : 0; +} + +qreal EasyGraphicsChart::region() const +{ + return m_bBindMode ? m_visibleRegionWidth : range(); +} + +void EasyGraphicsChart::setOffset(qreal _offset) +{ + m_offset = std::min(std::max(m_left, m_offset), m_right - m_visibleRegionWidth); +} + +void EasyGraphicsChart::setRange(qreal _left, qreal _right) +{ + const auto oldRange = range(); + const auto oldOffsetPart = oldRange < 1e-3 ? 0.0 : m_offset / oldRange; + + m_left = _left; + m_right = _right; + + if (m_left > m_right) + std::swap(m_left, m_right); + + const auto sceneRange = range(); + //scene()->setSceneRect(m_left, -(height() >> 1), sceneRange, height()); + //m_chartItem->setBoundingRect(scene()->sceneRect()); + + m_offset = m_left + oldOffsetPart * sceneRange; + m_visibleRegionWidth = std::min(m_visibleRegionWidth, sceneRange); + + //const auto oldXScale = m_xscale; + m_xscale = sceneRange < 1e-3 ? 1.0 : width() / sceneRange; + //scale(m_xscale / oldXScale, 1); + + scene()->update(); +} + +void EasyGraphicsChart::setRegion(qreal _visibleRegionWidth) +{ + m_visibleRegionWidth = std::min(_visibleRegionWidth, range()); + setOffset(m_offset); +} + +void EasyGraphicsChart::onWindowSizeChanged(qreal _width, qreal _height) +{ + //const auto oldXScale = m_xscale; + const auto sceneRange = range(); + + m_xscale = sceneRange < 1e-3 ? 1.0 : _width / sceneRange; + + scene()->setSceneRect(0, -_height * 0.5, _width, _height); + //scene()->setSceneRect(m_left, -_height * 0.5, sceneRange, _height); + m_chartItem->setBoundingRect(scene()->sceneRect()); + //scale(m_xscale / oldXScale, 1); +} + +void EasyGraphicsChart::update(Collections _collections) +{ + m_chartItem->update(std::move(_collections)); + scene()->update(); +} + +void EasyGraphicsChart::update(const ArbitraryValuesCollection* _selected) +{ + m_chartItem->update(_selected); + scene()->update(); +} + +////////////////////////////////////////////////////////////////////////// + +enum class ArbitraryColumns : uint8_t +{ + Name = 0, + Type, + Value, + Vin, + + Count +}; + +EASY_CONSTEXPR auto CheckColumn = int_cast(ArbitraryColumns::Name); +EASY_CONSTEXPR auto StdItemType = QTreeWidgetItem::UserType; +EASY_CONSTEXPR auto ValueItemType = QTreeWidgetItem::UserType + 1; + +struct UsedValueTypes { + EasyArbitraryTreeWidgetItem* items[int_cast(profiler::DataType::TypesCount)]; + UsedValueTypes(int = 0) { memset(items, 0, sizeof(items)); } +}; + +////////////////////////////////////////////////////////////////////////// + +EasyArbitraryTreeWidgetItem::EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin) + : Parent(_parent, ValueItemType) + , m_vin(_vin) + , m_color(_color) + , m_widthHint(0) +{ + setFlags(flags() | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable); + setCheckState(CheckColumn, Qt::Unchecked); +} + +EasyArbitraryTreeWidgetItem::~EasyArbitraryTreeWidgetItem() +{ + +} + +QVariant EasyArbitraryTreeWidgetItem::data(int _column, int _role) const +{ + if (_column == CheckColumn && _role == Qt::SizeHintRole) + return QSize(m_widthHint, 26); + return Parent::data(_column, _role); +} + +void EasyArbitraryTreeWidgetItem::setWidthHint(int _width) +{ + m_widthHint = _width; +} + +const ArbitraryValuesCollection* EasyArbitraryTreeWidgetItem::collection() const +{ + return m_collection.get(); +} + +void EasyArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId) +{ + if (!m_collection) + m_collection = CollectionPtr(new ArbitraryValuesCollection); + else + m_collection->interrupt(); + + m_collection->collectValues(_threadId, m_vin, text(int_cast(ArbitraryColumns::Name)).toStdString().c_str(), EASY_GLOBALS.begin_time); +} + +void EasyArbitraryTreeWidgetItem::interrupt() +{ + if (!m_collection) + return; + + m_collection->interrupt(); + m_collection.release(); +} + +profiler::color_t EasyArbitraryTreeWidgetItem::color() const +{ + return m_color; +} + +////////////////////////////////////////////////////////////////////////// + +EasyArbitraryValuesWidget::EasyArbitraryValuesWidget(QWidget* _parent) + : Parent(_parent) + , m_treeWidget(new QTreeWidget(this)) + , m_chart(new EasyGraphicsChart(this)) +{ + auto layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_treeWidget); + layout->addWidget(m_chart); + layout->setStretch(0, 1); + layout->setStretch(1, 1); + + m_treeWidget->setAutoFillBackground(false); + m_treeWidget->setAlternatingRowColors(true); + m_treeWidget->setItemsExpandable(true); + m_treeWidget->setAnimated(true); + //m_treeWidget->setSortingEnabled(false); + m_treeWidget->setColumnCount(int_cast(ArbitraryColumns::Count)); + m_treeWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + m_treeWidget->setItemDelegateForColumn(0, new EasyTreeViewFirstColumnItemDelegate(this)); + + auto headerItem = new QTreeWidgetItem(); + headerItem->setText(int_cast(ArbitraryColumns::Type), "Type"); + headerItem->setText(int_cast(ArbitraryColumns::Name), "Name"); + headerItem->setText(int_cast(ArbitraryColumns::Value), "Value"); + headerItem->setText(int_cast(ArbitraryColumns::Vin), "ID"); + m_treeWidget->setHeaderItem(headerItem); + +// auto mainLayout = new QVBoxLayout(this); +// mainLayout->setContentsMargins(1, 1, 1, 1); +// mainLayout->addWidget(m_treeWidget); + + connect(&m_timer, &QTimer::timeout, this, &This::rebuild); + connect(&m_collectionsTimer, &QTimer::timeout, this, &This::onCollectionsTimeout); + + connect(m_treeWidget, &QTreeWidget::itemDoubleClicked, this, &This::onItemDoubleClicked); + connect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + connect(m_treeWidget, &QTreeWidget::currentItemChanged, this, &This::onCurrentItemChanged); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChanged); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChanged); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockIdChanged, this, &This::onSelectedBlockIdChanged); +} + +EasyArbitraryValuesWidget::~EasyArbitraryValuesWidget() +{ + +} + +void EasyArbitraryValuesWidget::clear() +{ + if (m_collectionsTimer.isActive()) + m_collectionsTimer.stop(); + if (m_timer.isActive()) + m_timer.stop(); + m_checkedItems.clear(); + m_treeWidget->clear(); +} + +void EasyArbitraryValuesWidget::onSelectedThreadChanged(::profiler::thread_id_t) +{ + if (!m_timer.isActive()) + m_timer.start(100); +} + +void EasyArbitraryValuesWidget::onSelectedBlockChanged(uint32_t) +{ + if (!m_timer.isActive()) + m_timer.start(100); +} + +void EasyArbitraryValuesWidget::onSelectedBlockIdChanged(::profiler::block_id_t) +{ + if (!m_timer.isActive()) + m_timer.start(100); +} + +void EasyArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int) +{ + if (_item == nullptr || _item->type() != ValueItemType) + return; + + _item->setCheckState(CheckColumn, _item->checkState(CheckColumn) == Qt::Checked ? Qt::Unchecked : Qt::Checked); +} + +void EasyArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column) +{ + if (_item == nullptr || _item->type() != ValueItemType || _column != CheckColumn) + return; + + auto item = static_cast(_item); + + if (item->checkState(CheckColumn) == Qt::Checked) + { + m_checkedItems.push_back(item); + item->collectValues(EASY_GLOBALS.selected_thread); + if (!m_collectionsTimer.isActive()) + m_collectionsTimer.start(100); + } + else + { + m_checkedItems.removeOne(item); + item->interrupt(); + onCollectionsTimeout(); + } +} + +void EasyArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current, QTreeWidgetItem*) +{ + if (_current == nullptr || _current->type() != ValueItemType) + { + m_chart->update(nullptr); + return; + } + + auto item = static_cast(_current); + m_chart->update(item->collection()); +} + +void EasyArbitraryValuesWidget::rebuild() +{ + clear(); + + buildTree(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block, EASY_GLOBALS.selected_block_id); + + m_treeWidget->expandAll(); + for (int i = 0, columns = m_treeWidget->columnCount(); i < columns; ++i) + m_treeWidget->resizeColumnToContents(i); +} + +void EasyArbitraryValuesWidget::onCollectionsTimeout() +{ + if (m_checkedItems.isEmpty()) + { + if (m_collectionsTimer.isActive()) + m_collectionsTimer.stop(); + m_chart->update(Collections {}); + return; + } + + Collections collections; + collections.reserve(m_checkedItems.size()); + for (auto item : m_checkedItems) + { + if (item->collection()->status() != ArbitraryValuesCollection::InProgress) + { + collections.push_back(EasyCollectionPaintData {item->collection(), item->color(), + ChartType::Line, item == m_treeWidget->currentItem()}); + } + } + + if (collections.size() == m_checkedItems.size()) + { + if (m_collectionsTimer.isActive()) + m_collectionsTimer.stop(); + m_chart->update(std::move(collections)); + } +} + +void EasyArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) +{ + m_treeWidget->clear(); + m_treeWidget->setColumnHidden(int_cast(ArbitraryColumns::Value), profiler_gui::is_max(_blockIndex)); + + if (_threadId != 0) + { + auto it = EASY_GLOBALS.profiler_blocks.find(_threadId); + if (it != EASY_GLOBALS.profiler_blocks.end()) + { + auto threadItem = buildTreeForThread(it->second, _blockIndex, _blockId); + m_treeWidget->addTopLevelItem(threadItem); + } + } + else + { + for (const auto& it : EASY_GLOBALS.profiler_blocks) + { + auto threadItem = buildTreeForThread(it.second, _blockIndex, _blockId); + m_treeWidget->addTopLevelItem(threadItem); + } + } +} + +QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) +{ + auto fm = m_treeWidget->fontMetrics(); + + auto rootItem = new QTreeWidgetItem(StdItemType); + rootItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Thread")); + rootItem->setText(int_cast(ArbitraryColumns::Name), + profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, _threadRoot, EASY_GLOBALS.hex_thread_id)); + + const bool hasConcreteBlock = !profiler_gui::is_max(_blockIndex); + if (hasConcreteBlock) + { + const auto& block = easyBlocksTree(_blockIndex); + const auto& desc = easyDescriptor(block.node->id()); + if (desc.type() == profiler::BlockType::Value) + { + auto valueItem = new EasyArbitraryTreeWidgetItem(rootItem, desc.color(), block.value->value_id()); + valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*block.value)); + valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name()); + valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(block.value->value_id(), 0, 16)); + valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*block.value)); + + const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); + valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32); + + return rootItem; + } + + _blockId = block.node->id(); + } + + const bool noId = profiler_gui::is_max(_blockId); + QTreeWidgetItem* blockItem = nullptr; + if (!noId) + { + blockItem = new QTreeWidgetItem(rootItem, StdItemType); + blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block")); + if (hasConcreteBlock) + blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(_blockIndex)); + else + blockItem->setText(int_cast(ArbitraryColumns::Name), easyDescriptor(_blockId).name()); + } + + std::unordered_map > blocks; + std::unordered_map > vins; + std::unordered_map names; + + std::vector stack; + for (auto childIndex : _threadRoot.children) + { + stack.push_back(childIndex); + while (!stack.empty()) + { + const auto i = stack.back(); + stack.pop_back(); + + const auto& block = easyBlocksTree(i); + if (noId || block.node->id() == _blockId || easyDescriptor(block.node->id()).id() == _blockId) + { + for (auto c : block.children) + { + if (noId) + stack.push_back(c); + + const auto& child = easyBlocksTree(c); + const auto& desc = easyDescriptor(child.node->id()); + if (desc.type() != profiler::BlockType::Value) + continue; + + if (blockItem == nullptr) + { + const auto id = block.node->id(); + auto it = blocks.find(id); + if (it != blocks.end()) + { + blockItem = it->second; + } + else + { + blockItem = new QTreeWidgetItem(rootItem, StdItemType); + blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block")); + blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(block)); + blocks.emplace(id, blockItem); + } + } + + const auto typeIndex = int_cast(child.value->type()); + auto vin = child.value->value_id(); + + EasyArbitraryTreeWidgetItem** usedItems = nullptr; + EasyArbitraryTreeWidgetItem* valueItem = nullptr; + if (vin == 0) + { + auto result = names.emplace(desc.name(), 0); + usedItems = result.first->second.items; + if (!result.second && (valueItem = *(usedItems + typeIndex))) + { + if (i == _blockIndex) + valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*child.value)); + continue; // already in set + } + } + else + { + auto result = vins.emplace(vin, 0); + usedItems = result.first->second.items; + if (!result.second && (valueItem = *(usedItems + typeIndex))) + { + if (i == _blockIndex) + valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*child.value)); + continue; // already in set + } + } + + valueItem = new EasyArbitraryTreeWidgetItem(blockItem, desc.color(), vin); + valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*child.value)); + valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name()); + valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(vin, 0, 16)); + + if (i == _blockIndex) + valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*child.value)); + + const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); + valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32); + + *(usedItems + typeIndex) = valueItem; + } + + if (noId) + blockItem = nullptr; + } + else + { + for (auto c : block.children) + stack.push_back(c); + } + } + } + + return rootItem; +} + diff --git a/3rdparty/easyprofiler/profiler_gui/arbitrary_value_inspector.h b/3rdparty/easyprofiler/profiler_gui/arbitrary_value_inspector.h new file mode 100644 index 0000000..01779d4 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/arbitrary_value_inspector.h @@ -0,0 +1,299 @@ +/************************************************************************ +* file name : arbitrary_value_inspector.h +* ----------------- : +* creation time : 2017/11/30 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of . +* ----------------- : +* change log : * 2017/11/30 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER_GUI_ARBITRARY_VALUE_INSPECTOR_H +#define EASY_PROFILER_GUI_ARBITRARY_VALUE_INSPECTOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// + +using Points = std::vector; +using ArbitraryValues = std::vector; +using ArbitraryValuesMap = std::unordered_map >; + +class ArbitraryValuesCollection EASY_FINAL +{ +public: + + enum JobStatus : uint8_t { Idle = 0, Ready, InProgress }; + enum JobType : uint8_t { None = 0, ValuesJob = 1 << 0, PointsJob = 1 << 1 }; + +private: + + using This = ArbitraryValuesCollection; + + ArbitraryValuesMap m_values; + Points m_points; + std::thread m_collectorThread; + profiler::timestamp_t m_beginTime; + qreal m_minValue; + qreal m_maxValue; + std::atomic m_status; + std::atomic_bool m_bInterrupt; + uint8_t m_jobType; + +public: + + explicit ArbitraryValuesCollection(); + ~ArbitraryValuesCollection(); + + const ArbitraryValuesMap& valuesMap() const; + const Points& points() const; + JobStatus status() const; + size_t size() const; + + qreal minValue() const; + qreal maxValue() const; + + void collectValues(profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName); + void collectValues(profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::timestamp_t _beginTime); + bool calculatePoints(profiler::timestamp_t _beginTime); + void interrupt(); + +private: + + void setStatus(JobStatus _status); + void collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId); + void collectByName(profiler::thread_id_t _threadId, const std::string _valueName); + bool collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::vin_t _valueId, bool _calculatePoints); + bool collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot, const std::string& _valueName, bool _calculatePoints); + + QPointF point(const profiler::ArbitraryValue& _value) const; + +}; // end of class ArbitraryValuesCollection. + +enum class ChartType : uint8_t +{ + Line = 0, + Points +}; + +struct EasyCollectionPaintData EASY_FINAL +{ + const ArbitraryValuesCollection* ptr; + QRgb color; + ChartType chartType; + bool selected; +}; + +using Collections = std::vector; + +////////////////////////////////////////////////////////////////////////// + +class EasyArbitraryValuesChartItem : public QGraphicsItem +{ + using Parent = QGraphicsItem; + using This = EasyArbitraryValuesChartItem; + + Collections m_collections; + QRectF m_boundingRect; + +public: + + explicit EasyArbitraryValuesChartItem(); + ~EasyArbitraryValuesChartItem() override; + + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + + QRectF boundingRect() const override; + void setBoundingRect(const QRectF& _rect); + void setBoundingRect(qreal _left, qreal _top, qreal _width, qreal _height); + + void update(Collections _collections); + void update(const ArbitraryValuesCollection* _selected); + +}; // end of class EasyArbitraryValuesChartItem. + +class EasyGraphicsChart : public QGraphicsView +{ + Q_OBJECT + +private: + + using Parent = QGraphicsView; + using This = EasyGraphicsChart; + + EasyArbitraryValuesChartItem* m_chartItem; + qreal m_left; + qreal m_right; + qreal m_offset; + qreal m_xscale; + qreal m_visibleRegionWidth; + bool m_bBindMode; + +public: + + explicit EasyGraphicsChart(QWidget* _parent = nullptr); + ~EasyGraphicsChart() override; + + void resizeEvent(QResizeEvent* _event) override; + + void clear(); + + bool bindMode() const; + qreal xscale() const; + + qreal left() const; + qreal right() const; + qreal range() const; + qreal offset() const; + qreal region() const; + + void setOffset(qreal _offset); + void setRange(qreal _left, qreal _right); + void setRegion(qreal _visibleRegionWidth); + + void update(Collections _collections); + void update(const ArbitraryValuesCollection* _selected); + +private slots: + + void onSceneSizeChanged(); + void onWindowSizeChanged(qreal _width, qreal _height); + +}; // end of class EasyGraphicsChart. + +////////////////////////////////////////////////////////////////////////// + +class EasyArbitraryTreeWidgetItem : public QTreeWidgetItem +{ + using Parent = QTreeWidgetItem; + using This = EasyArbitraryTreeWidgetItem; + using CollectionPtr = std::unique_ptr; + + CollectionPtr m_collection; + profiler::vin_t m_vin; + profiler::color_t m_color; + int m_widthHint; + +public: + + explicit EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin = 0); + ~EasyArbitraryTreeWidgetItem() override; + + QVariant data(int _column, int _role) const override; + + void setWidthHint(int _width); + + const ArbitraryValuesCollection* collection() const; + void collectValues(profiler::thread_id_t _threadId); + void interrupt(); + + profiler::color_t color() const; + +}; // end of class EasyArbitraryTreeWidgetItem. + +////////////////////////////////////////////////////////////////////////// + +class EasyArbitraryValuesWidget : public QWidget +{ + Q_OBJECT + + using Parent = QWidget; + using This = EasyArbitraryValuesWidget; + + QTimer m_timer; + QTimer m_collectionsTimer; + QList m_checkedItems; + QTreeWidget* m_treeWidget; + EasyGraphicsChart* m_chart; + +public: + + explicit EasyArbitraryValuesWidget(QWidget* _parent = nullptr); + ~EasyArbitraryValuesWidget() override; + + void clear(); + +public slots: + + void rebuild(); + +private slots: + + void onSelectedThreadChanged(profiler::thread_id_t _id); + void onSelectedBlockChanged(uint32_t _block_index); + void onSelectedBlockIdChanged(profiler::block_id_t _id); + void onItemDoubleClicked(QTreeWidgetItem* _item, int _column); + void onItemChanged(QTreeWidgetItem* _item, int _column); + void onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); + void onCollectionsTimeout(); + +private: + + void buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId); + QTreeWidgetItem* buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId); + +}; // end of class EasyArbitraryValuesWidget. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_GUI_ARBITRARY_VALUE_INSPECTOR_H diff --git a/3rdparty/easyprofiler/profiler_gui/blocks_graphics_view.cpp b/3rdparty/easyprofiler/profiler_gui/blocks_graphics_view.cpp new file mode 100644 index 0000000..1639bac --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/blocks_graphics_view.cpp @@ -0,0 +1,2509 @@ +/************************************************************************ +* file name : blocks_graphics_view.cpp +* ----------------- : +* creation time : 2016/06/26 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of GraphicsScene and GraphicsView and +* : it's auxiliary classes for displyaing easy_profiler blocks tree. +* ----------------- : +* change log : * 2016/06/26 Victor Zarubkin: Moved sources from graphics_view.h +* : and renamed classes from My* to Prof*. +* : +* : * 2016/06/27 Victor Zarubkin: Added text shifting relatively to it's parent item. +* : Disabled border lines painting because of vertical lines painting bug. +* : Changed height of blocks. Variable thread-block height. +* : +* : * 2016/06/29 Victor Zarubkin: Highly optimized painting performance and memory consumption. +* : +* : * 2016/06/30 Victor Zarubkin: Replaced doubles with floats (in ProfBlockItem) for less memory consumption. +* : +* : * 2016/09/15 Victor Zarubkin: Moved sources of EasyGraphicsItem and EasyChronometerItem to separate files. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blocks_graphics_view.h" +#include "easy_graphics_item.h" +#include "easy_chronometer_item.h" +#include "easy_graphics_scrollbar.h" +#include "globals.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +const qreal MIN_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 70); // Up to 1000 sec scale +const qreal MAX_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT, 45); // ~23000 --- Up to 10 ns scale +const qreal BASE_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 25); // ~0.003 + +EASY_CONSTEXPR uint16_t TIMELINE_ROW_SIZE = 24; + +EASY_CONSTEXPR QRgb BACKGROUND_1 = 0xffe4e4ec; +EASY_CONSTEXPR QRgb BACKGROUND_2 = ::profiler::colors::White; +EASY_CONSTEXPR QRgb TIMELINE_BACKGROUND = 0x20000000 | (::profiler::colors::Grey800 & 0x00ffffff);// 0x20303030; +EASY_CONSTEXPR QRgb TIMELINE_BORDER = 0xffa8a0a0; + +EASY_CONSTEXPR int IDLE_TIMER_INTERVAL = 200; // 5Hz +EASY_CONSTEXPR uint64_t IDLE_TIME = 400; + +EASY_CONSTEXPR int FLICKER_INTERVAL = 10; // 100Hz +EASY_CONSTEXPR qreal FLICKER_FACTOR = 16.0 / FLICKER_INTERVAL; + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +using estd::clamp; + +////////////////////////////////////////////////////////////////////////// + +EasyBoldLabel::EasyBoldLabel(const QString& _text, QWidget* _parent) : QLabel(_text, _parent) +{ + auto f = font(); + f.setBold(true); + setFont(f); +} + +EasyBoldLabel::~EasyBoldLabel() +{ + +} + +////////////////////////////////////////////////////////////////////////// + +void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + auto const sceneView = static_cast(scene()->parent()); + const auto visibleSceneRect = sceneView->visibleSceneRect(); + const auto currentScale = sceneView->scale(); + const auto offset = sceneView->offset(); + const auto left = offset * currentScale; + const auto h = visibleSceneRect.height(); + const auto visibleBottom = h - 1; + const auto borderColor = QColor::fromRgb(TIMELINE_BORDER); + const auto textShiftY = h + TIMELINE_ROW_SIZE - 5; + + QRectF rect; + + _painter->save(); + _painter->setTransform(QTransform::fromTranslate(-x(), -y())); + + const auto& items = sceneView->getItems(); + if (!items.empty()) + { + static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1; + static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)}; + int i = -1; + + // Draw background + _painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); + for (auto item : items) + { + ++i; + + auto br = item->boundingRect(); + auto top = item->y() + br.top() - visibleSceneRect.top(); + auto bottom = top + br.height(); + + if (top > h || bottom < 0) + continue; + + if (item->threadId() == EASY_GLOBALS.selected_thread) + _painter->setBrush(QBrush(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_BACKGROUND))); + else + _painter->setBrush(brushes[i & 1]); + + rect.setRect(0, top - OVERLAP, visibleSceneRect.width(), br.height() + ::profiler_gui::THREADS_ROW_SPACING); + const auto dh = rect.bottom() - visibleBottom; + if (dh > 0) + rect.setHeight(rect.height() - dh); + + if (rect.top() < 0) + rect.setTop(0); + + _painter->drawRect(rect); + } + } + + // Draw timeline scale marks ---------------- + _painter->setBrush(QColor::fromRgba(TIMELINE_BACKGROUND)); + + const auto sceneStep = sceneView->timelineStep(); + const auto factor = ::profiler_gui::timeFactor(sceneStep); + const auto step = sceneStep * currentScale; + auto first = static_cast(offset / sceneStep); + const int odd = first & 1; + const auto nsteps = (1 + odd) * 2 + static_cast(visibleSceneRect.width() / step); + first -= odd; + + QPen pen(borderColor); + pen.setWidth(2); + _painter->setPen(pen); + _painter->drawLine(QPointF(0, h), QPointF(visibleSceneRect.width(), h)); + _painter->setPen(borderColor); + + QLineF marks[20]; + qreal first_x = first * sceneStep; + const auto textWidth = QFontMetricsF(_painter->font(), sceneView).width(QString::number(static_cast(0.5 + first_x * factor))) * ::profiler_gui::FONT_METRICS_FACTOR + 10; + const int n = 1 + static_cast(textWidth / step); + int next = first % n; + if (next) + next = n - next; + + first_x *= currentScale; + for (int i = 0; i < nsteps; ++i, --next) + { + auto current = first_x - left + step * i; + + if ((i & 1) == 0) + { + rect.setRect(current, 0, step, h); + _painter->drawRect(rect); + + for (int j = 0; j < 20; ++j) + { + auto xmark = current + j * step * 0.1; + marks[j].setLine(xmark, h, xmark, h + ((j % 5) ? 4 : 8)); + } + + _painter->drawLines(marks, 20); + } + + if (next <= 0) + { + next = n; + _painter->setPen(::profiler_gui::TEXT_COLOR); + _painter->drawText(QPointF(current + 1, textShiftY), + QString::number(static_cast(0.5 + (current + left) * factor / currentScale))); + _painter->setPen(borderColor); + } + + // TEST + // this is for testing (order of lines will be painted): + //_painter->setPen(Qt::black); + //_painter->drawText(QPointF(current + step * 0.4, h - 20), QString::number(i)); + //_painter->setPen(Qt::gray); + // TEST + } + // END Draw timeline scale marks ~~~~~~~~~~~~ + + _painter->restore(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTimelineIndicatorItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + const auto sceneView = static_cast(scene()->parent()); + const auto visibleSceneRect = sceneView->visibleSceneRect(); + const auto step = sceneView->timelineStep() * sceneView->scale(); + const QString text = ::profiler_gui::autoTimeStringInt(units2microseconds(sceneView->timelineStep())); // Displayed text + + // Draw scale indicator + _painter->save(); + _painter->setTransform(QTransform::fromTranslate(-x(), -y())); + //_painter->setCompositionMode(QPainter::CompositionMode_Difference); + _painter->setBrush(Qt::NoBrush); + + QPen pen(Qt::black); + pen.setWidth(3); + _painter->setPen(pen); + + _painter->drawLine(QLineF(visibleSceneRect.width() - 9 - step, visibleSceneRect.height() - 10, visibleSceneRect.width() - 11, visibleSceneRect.height() - 10)); + + _painter->setPen(Qt::black); + _painter->drawLine(QLineF(visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 6, visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 14)); + _painter->drawLine(QLineF(visibleSceneRect.width() - 10, visibleSceneRect.height() - 6, visibleSceneRect.width() - 10, visibleSceneRect.height() - 14)); + + _painter->setPen(Qt::black); + _painter->setFont(EASY_GLOBALS.bg_font); + _painter->drawText(QRectF(visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 63, step, 50), Qt::AlignRight | Qt::AlignBottom | Qt::TextDontClip, text); + + _painter->restore(); +} + +////////////////////////////////////////////////////////////////////////// + +EasyGraphicsView::EasyGraphicsView(QWidget* _parent) + : Parent(_parent) + , m_beginTime(::std::numeric_limits::max()) + , m_sceneWidth(0) + , m_scale(1) + , m_offset(0) + , m_timelineStep(0) + , m_idleTime(0) + , m_mouseButtons(Qt::NoButton) + , m_pScrollbar(nullptr) + , m_chronometerItem(nullptr) + , m_chronometerItemAux(nullptr) + , m_popupWidget(nullptr) + , m_flickerSpeedX(0) + , m_flickerSpeedY(0) + , m_flickerCounterX(0) + , m_flickerCounterY(0) + , m_bDoubleClick(false) + , m_bUpdatingRect(false) + , m_bEmpty(true) +{ + initMode(); + setScene(new QGraphicsScene(this)); + updateVisibleSceneRect(); +} + +EasyGraphicsView::~EasyGraphicsView() +{ +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::removePopup(bool _removeFromScene) +{ + if (m_popupWidget != nullptr) + { + auto widget = m_popupWidget->widget(); + widget->setParent(nullptr); + m_popupWidget->setWidget(nullptr); + delete widget; + + if (_removeFromScene) + scene()->removeItem(m_popupWidget); + + m_popupWidget = nullptr; + } +} + +////////////////////////////////////////////////////////////////////////// + +qreal EasyGraphicsView::sceneWidth() const +{ + return m_sceneWidth; +} + +qreal EasyGraphicsView::chronoTime() const +{ + return m_chronometerItem->width(); +} + +qreal EasyGraphicsView::chronoTimeAux() const +{ + return m_chronometerItemAux->width(); +} + +////////////////////////////////////////////////////////////////////////// + +EasyChronometerItem* EasyGraphicsView::createChronometer(bool _main) +{ + auto chronoItem = new EasyChronometerItem(_main); + chronoItem->setColor(_main ? ::profiler_gui::CHRONOMETER_COLOR : ::profiler_gui::CHRONOMETER_COLOR2); + chronoItem->setBoundingRect(sceneRect()); + chronoItem->hide(); + scene()->addItem(chronoItem); + + return chronoItem; +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::clear() +{ + const QSignalBlocker blocker(this), sceneBlocker(scene()); // block all scene signals (otherwise clear() would be extremely slow!) + + // Stop flicking + m_flickerTimer.stop(); + m_flickerSpeedX = 0; + m_flickerSpeedY = 0; + m_flickerCounterX = 0; + m_flickerCounterY = 0; + + // Clear all items + removePopup(); + scene()->clear(); + m_items.clear(); + m_selectedBlocks.clear(); + + m_beginTime = ::std::numeric_limits::max(); // reset begin time + m_scale = 1; // scale back to initial 100% scale + m_timelineStep = 1; + m_offset = 0; // scroll back to the beginning of the scene + + m_idleTimer.stop(); + m_idleTime = 0; + + // Reset necessary flags + m_bEmpty = true; + + m_sceneWidth = 10; + setSceneRect(0, 0, 10, 10); + + // notify ProfTreeWidget that selection was reset + emit intervalChanged(m_selectedBlocks, m_beginTime, 0, 0, false); +} + +void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTree) +{ + // clear scene + clear(); + + if (_blocksTree.empty()) + { + return; + } + + auto bgItem = new EasyBackgroundItem(); + scene()->addItem(bgItem); + + // set new blocks tree + // calculate scene size and fill it with items + + // Calculating start and end time + ::profiler::timestamp_t finish = 0, busyTime = 0; + ::profiler::thread_id_t longestTree = 0, mainTree = 0; + for (const auto& threadTree : _blocksTree) + { + const auto& t = threadTree.second; + + auto timestart = m_beginTime; + auto timefinish = finish; + + if (!t.children.empty()) + timestart = easyBlocksTree(t.children.front()).node->begin(); + if (!t.sync.empty()) + timestart = ::std::min(timestart, easyBlocksTree(t.sync.front()).node->begin()); + + if (!t.children.empty()) + timefinish = easyBlocksTree(t.children.back()).node->end(); + if (!t.sync.empty()) + timefinish = ::std::max(timefinish, easyBlocksTree(t.sync.back()).node->end()); + + if (m_beginTime > timestart) + m_beginTime = timestart; + + if (finish < timefinish) + finish = timefinish; + + if (t.profiled_time > busyTime) { + busyTime = t.profiled_time; + longestTree = threadTree.first; + } + + if (mainTree == 0 && strcmp(t.name(), "Main") == 0) + mainTree = threadTree.first; + } + + const decltype(m_beginTime) additional_offset = (finish - m_beginTime) / 20; // Additional 5% before first block and after last block + finish += additional_offset; + m_beginTime -= ::std::min(m_beginTime, additional_offset); + EASY_GLOBALS.begin_time = m_beginTime; + + // Sort threads by name + ::std::vector<::std::reference_wrapper > sorted_roots; + sorted_roots.reserve(_blocksTree.size()); + for (const auto& threadTree : _blocksTree) + sorted_roots.push_back(threadTree.second); + ::std::sort(sorted_roots.begin(), sorted_roots.end(), [](const ::profiler::BlocksTreeRoot& _a, const ::profiler::BlocksTreeRoot& _b) { + return _a.thread_name < _b.thread_name; + }); + + // Filling scene with items + m_items.reserve(_blocksTree.size()); + qreal y = TIMELINE_ROW_SIZE; + const EasyGraphicsItem *longestItem = nullptr, *mainThreadItem = nullptr; + for (const ::profiler::BlocksTreeRoot& t : sorted_roots) + { + if (m_items.size() == 0xff) + { + qWarning() << "Warning: Maximum threads number (255 threads) exceeded! See EasyGraphicsView::setTree() : " << __LINE__ << " in file " << __FILE__; + break; + } + + // fill scene with new items + qreal h = 0, x = 0; + + if (!t.children.empty()) + x = time2position(easyBlocksTree(t.children.front()).node->begin()); + else if (!t.sync.empty()) + x = time2position(easyBlocksTree(t.sync.front()).node->begin()); + + auto item = new EasyGraphicsItem(static_cast(m_items.size()), t); + if (t.depth) + item->setLevels(t.depth); + item->setPos(0, y); + + qreal children_duration = 0; + + if (!t.children.empty()) + { + uint32_t dummy = 0; + children_duration = setTree(item, t.children, h, dummy, y, 0); + } + else + { + if (!t.sync.empty()) + children_duration = time2position(easyBlocksTree(t.sync.back()).node->end()) - x; + h = ::profiler_gui::GRAPHICS_ROW_SIZE; + } + + item->setBoundingRect(0, 0, children_duration + x, h); + m_items.push_back(item); + scene()->addItem(item); + + y += h + ::profiler_gui::THREADS_ROW_SPACING; + + if (longestTree == t.thread_id) + longestItem = item; + + if (mainTree == t.thread_id) + mainThreadItem = item; + } + + // Calculating scene rect + m_sceneWidth = time2position(finish); + setSceneRect(0, 0, m_sceneWidth, y + TIMELINE_ROW_SIZE); + + EASY_GLOBALS.scene_left = 0; + EASY_GLOBALS.scene_right = m_sceneWidth; + emit EASY_GLOBALS.events.sceneSizeChanged(); + + // Center view on the beginning of the scene + updateVisibleSceneRect(); + setScrollbar(m_pScrollbar); + + // Create new chronometer item (previous item was destroyed by scene on scene()->clear()). + // It will be shown on mouse right button click. + m_chronometerItemAux = createChronometer(false); + m_chronometerItem = createChronometer(true); + + bgItem->setBoundingRect(0, 0, m_sceneWidth, y); + auto indicator = new EasyTimelineIndicatorItem(); + indicator->setBoundingRect(0, 0, m_sceneWidth, y); + scene()->addItem(indicator); + + // Setting flags + m_bEmpty = false; + + scaleTo(BASE_SCALE); + + + emit treeChanged(); + + if (mainThreadItem != nullptr) + { + longestItem = mainThreadItem; + } + + if (longestItem != nullptr) + { + EASY_GLOBALS.selected_thread = longestItem->threadId(); + emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId()); + + scrollTo(longestItem); + m_pScrollbar->setHistogramSource(longestItem->threadId(), longestItem->items(0)); + if (!longestItem->items(0).empty()) + m_pScrollbar->setValue(longestItem->items(0).front().left() - m_pScrollbar->sliderWidth() * 0.25); + } + + m_idleTimer.start(IDLE_TIMER_INTERVAL); +} + +const EasyGraphicsView::Items &EasyGraphicsView::getItems() const +{ + return m_items; +} + +qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::BlocksTree::children_t& _children, qreal& _height, uint32_t& _maxDepthChild, qreal _y, short _level) +{ + if (_children.empty()) + { + return 0; + } + + const auto level = static_cast(_level); + const auto n = static_cast(_children.size()); + _item->reserve(level, n); + + _maxDepthChild = 0; + uint8_t maxDepth = 0; + const short next_level = _level + 1; + bool warned = false; + qreal total_duration = 0, prev_end = 0, maxh = 0; + qreal start_time = -1; + uint32_t j = 0; + for (auto child_index : _children) + { + auto& gui_block = easyBlock(child_index); + const auto& child = gui_block.tree; + if (child.depth > maxDepth) + { + maxDepth = child.depth; + _maxDepthChild = j; + } + + auto xbegin = time2position(child.node->begin()); + if (start_time < 0) + { + start_time = xbegin; + } + + auto duration = time2position(child.node->end()) - xbegin; + + //const auto dt = xbegin - prev_end; + //if (dt < 0) + //{ + // duration += dt; + // xbegin -= dt; + //} + + //static const qreal MIN_DURATION = 0.25; + //if (duration < MIN_DURATION) + // duration = MIN_DURATION; + + const auto i = _item->addItem(level); + auto& b = _item->getItem(level, i); + + gui_block.graphics_item = _item->index(); + gui_block.graphics_item_level = level; + gui_block.graphics_item_index = i; + + if (next_level < 256 && next_level < _item->levels() && !child.children.empty()) + { + b.children_begin = static_cast(_item->items(static_cast(next_level)).size()); + } + else + { + ::profiler_gui::set_max(b.children_begin); + } + + qreal h = 0; + qreal children_duration = 0; + uint32_t maxDepthChild = 0; + + if (next_level < 256) + { + children_duration = setTree(_item, child.children, h, maxDepthChild, _y + ::profiler_gui::GRAPHICS_ROW_SIZE_FULL, next_level); + } + else if (!child.children.empty() && !warned) + { + warned = true; + qWarning() << "Warning: Maximum blocks depth (255) exceeded! See EasyGraphicsView::setTree() : " << __LINE__ << " in file " << __FILE__; + } + + if (duration < children_duration) + { + duration = children_duration; + } + + if (h > maxh) + { + maxh = h; + } + + b.block = child_index;// &child; + +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + b.neighbours = n; + b.state = j > 0 || level == 0 ? 0 : -1; +#else + b.max_depth_child = maxDepthChild; +#endif + + b.setPos(xbegin, duration); + //b.totalHeight = ::profiler_gui::GRAPHICS_ROW_SIZE + h; + + prev_end = xbegin + duration; + total_duration = prev_end - start_time; + + ++j; + } + + _height += ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + maxh; + + return total_duration; +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::setScrollbar(EasyGraphicsScrollbar* _scrollbar) +{ + auto const prevScrollbar = m_pScrollbar; + const bool makeConnect = prevScrollbar == nullptr || prevScrollbar != _scrollbar; + + if (prevScrollbar != nullptr && prevScrollbar != _scrollbar) + { + disconnect(prevScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange); + disconnect(prevScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel); + } + + m_pScrollbar = _scrollbar; + m_pScrollbar->clear(); + m_pScrollbar->setRange(0, m_sceneWidth); + + auto vbar = verticalScrollBar(); + const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0); + m_pScrollbar->setSliderWidth(m_visibleSceneRect.width() + vbar_width); + + if (makeConnect) + { + connect(m_pScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange); + connect(m_pScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel); + } + + EASY_GLOBALS.selected_thread = 0; + emit EASY_GLOBALS.events.selectedThreadChanged(0); +} + +////////////////////////////////////////////////////////////////////////// + +int EasyGraphicsView::updateVisibleSceneRect() +{ + m_visibleSceneRect = mapToScene(rect()).boundingRect(); + + auto vbar = verticalScrollBar(); + int vbar_width = 0; + if (vbar != nullptr && vbar->isVisible()) + vbar_width = vbar->width() + 2; + + m_visibleSceneRect.setWidth(m_visibleSceneRect.width() - vbar_width); + m_visibleSceneRect.setHeight(m_visibleSceneRect.height() - TIMELINE_ROW_SIZE); + + return vbar_width; +} + +void EasyGraphicsView::updateTimelineStep(qreal _windowWidth) +{ + const auto time = units2microseconds(_windowWidth); + if (time < 100) + m_timelineStep = 1e-2; + else if (time < 10e3) + m_timelineStep = 1; + else if (time < 10e6) + m_timelineStep = 1e3; + else + m_timelineStep = 1e6; + + const auto optimal_steps = static_cast(40 * m_visibleSceneRect.width() / 1500); + auto steps = time / m_timelineStep; + while (steps > optimal_steps) { + m_timelineStep *= 10; + steps *= 0.1; + } + + m_timelineStep = microseconds2units(m_timelineStep); +} + +void EasyGraphicsView::repaintScene() +{ + scene()->update(m_visibleSceneRect); + emit sceneUpdated(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::scaleTo(qreal _scale) +{ + if (m_bEmpty) + { + return; + } + + // have to limit scale because of Qt's QPainter feature: it doesn't draw text + // with very big coordinates (but it draw rectangles with the same coordinates good). + m_scale = clamp(MIN_SCALE, _scale, MAX_SCALE); + const int vbar_width = updateVisibleSceneRect(); + + // Update slider width for scrollbar + const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale; + m_pScrollbar->setSliderWidth(windowWidth); + + updateTimelineStep(windowWidth); + repaintScene(); +} + +void EasyGraphicsView::wheelEvent(QWheelEvent* _event) +{ + m_idleTime = 0; + + if (!m_bEmpty) + onWheel(mapToScene(_event->pos()).x(), _event->delta()); + _event->accept(); +} + +void EasyGraphicsView::onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta) +{ + m_idleTime = 0; + + for (auto item : m_items) + { + if (item->threadId() == EASY_GLOBALS.selected_thread) + { + scrollTo(item); + break; + } + } + + onWheel(_mouseX, _wheelDelta); +} + +void EasyGraphicsView::scrollTo(const EasyGraphicsItem* _item) +{ + m_bUpdatingRect = true; + auto vbar = verticalScrollBar(); + vbar->setValue(_item->y() + (_item->boundingRect().height() - vbar->pageStep()) * 0.5); + m_bUpdatingRect = false; +} + +void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta) +{ + const decltype(m_scale) scaleCoeff = _wheelDelta > 0 ? ::profiler_gui::SCALING_COEFFICIENT : ::profiler_gui::SCALING_COEFFICIENT_INV; + + // Remember current mouse position + _mouseX = clamp(0., _mouseX, m_sceneWidth); + const auto mousePosition = m_offset + _mouseX / m_scale; + + // have to limit scale because of Qt's QPainter feature: it doesn't draw text + // with very big coordinates (but it draw rectangles with the same coordinates good). + m_scale = clamp(MIN_SCALE, m_scale * scaleCoeff, MAX_SCALE); + + //updateVisibleSceneRect(); // Update scene rect + + // Update slider width for scrollbar + auto vbar = verticalScrollBar(); + const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0); + const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale; + m_pScrollbar->setSliderWidth(windowWidth); + + // Calculate new offset to simulate QGraphicsView::AnchorUnderMouse scaling behavior + m_offset = clamp(0., mousePosition - _mouseX / m_scale, m_sceneWidth - windowWidth); + + // Update slider position + m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change + m_pScrollbar->setValue(m_offset); + m_bUpdatingRect = false; + + updateVisibleSceneRect(); // Update scene rect + updateTimelineStep(windowWidth); + repaintScene(); // repaint scene +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::mousePressEvent(QMouseEvent* _event) +{ + m_idleTime = 0; + + if (m_bEmpty) + { + _event->accept(); + return; + } + + m_mouseButtons = _event->buttons(); + m_mousePressPos = _event->pos(); + + if (m_mouseButtons & Qt::LeftButton) + { + if (m_chronometerItemAux->isVisible() && (m_chronometerItemAux->hoverLeft() || m_chronometerItemAux->hoverRight())) + { + m_chronometerItemAux->setReverse(m_chronometerItemAux->hoverLeft()); + m_bDoubleClick = true; + } + else if (m_chronometerItem->isVisible() && (m_chronometerItem->hoverLeft() || m_chronometerItem->hoverRight())) + { + m_chronometerItem->setReverse(m_chronometerItem->hoverLeft()); + m_mouseButtons = Qt::RightButton; + return; + } + } + + if (m_mouseButtons & Qt::RightButton) + { + if (m_chronometerItem->isVisible() && (m_chronometerItem->hoverLeft() || m_chronometerItem->hoverRight())) + { + m_chronometerItem->setReverse(m_chronometerItem->hoverLeft()); + } + else + { + const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale; + m_chronometerItem->setLeftRight(mouseX, mouseX); + m_chronometerItem->hide(); + m_pScrollbar->hideChrono(); + } + } + + _event->accept(); +} + +void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event) +{ + m_idleTime = 0; + + if (m_bEmpty) + { + _event->accept(); + return; + } + + m_mouseButtons = _event->buttons(); + m_mousePressPos = _event->pos(); + m_bDoubleClick = true; + + if (m_mouseButtons & Qt::LeftButton) + { + const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale; + m_chronometerItemAux->setLeftRight(mouseX, mouseX); + m_chronometerItemAux->hide(); + emit sceneUpdated(); + } + + _event->accept(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) +{ + m_idleTime = 0; + + if (m_bEmpty) + { + _event->accept(); + return; + } + + bool chronoHidden = false; + bool changedSelection = false, changedSelectedItem = false; + if (m_mouseButtons & Qt::RightButton) + { + if (m_chronometerItem->isVisible() && m_chronometerItem->width() < 1e-6) + { + m_chronometerItem->hide(); + m_pScrollbar->hideChrono(); + } + + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + m_selectedBlocks.clear(); + } + + if (m_chronometerItem->isVisible()) + { + //printf("INTERVAL: {%lf, %lf} ms\n", m_chronometerItem->left(), m_chronometerItem->right()); + + for (auto item : m_items) + { + if (!EASY_GLOBALS.only_current_thread_hierarchy || item->threadId() == EASY_GLOBALS.selected_thread) + item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks); + } + + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + } + } + } + + const ::profiler_gui::EasyBlock* selectedBlock = nullptr; + ::profiler::thread_id_t selectedBlockThread = 0; + const auto previouslySelectedBlock = EASY_GLOBALS.selected_block; + if (m_mouseButtons & Qt::LeftButton) + { + bool clicked = false; + + if (m_chronometerItemAux->isVisible() && m_chronometerItemAux->width() < 1e-6) + { + chronoHidden = true; + m_chronometerItemAux->hide(); + } + else if (m_chronometerItem->isVisible() && m_chronometerItem->hoverIndicator()) + { + // Jump to selected zone + clicked = true; + m_flickerSpeedX = m_flickerSpeedY = 0; + m_pScrollbar->setValue(m_chronometerItem->left() + m_chronometerItem->width() * 0.5 - m_pScrollbar->sliderHalfWidth()); + } + + if (!clicked && m_mouseMovePath.manhattanLength() < 5) + { + // Handle Click + + //clicked = true; + auto mouseClickPos = mapToScene(m_mousePressPos); + if (mouseClickPos.x() >= 0) + { + mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale); + + // Try to select one of item blocks + for (auto item : m_items) + { + ::profiler::block_index_t i = ~0U; + auto block = item->intersect(mouseClickPos, i); + if (block) + { + changedSelectedItem = true; + selectedBlock = block; + selectedBlockThread = item->threadId(); + EASY_GLOBALS.selected_block = i; + EASY_GLOBALS.selected_block_id = easyBlock(i).tree.node->id(); + break; + } + } + + if (!changedSelectedItem && !::profiler_gui::is_max(EASY_GLOBALS.selected_block)) + { + changedSelectedItem = true; + ::profiler_gui::set_max(EASY_GLOBALS.selected_block); + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + } + } + } + } + + m_bDoubleClick = false; + m_mouseButtons = _event->buttons(); + m_mouseMovePath = QPoint(); + _event->accept(); + + if (changedSelection) + { + emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse()); + } + + if (changedSelectedItem) + { + m_bUpdatingRect = true; + if (selectedBlock != nullptr && previouslySelectedBlock == EASY_GLOBALS.selected_block && !selectedBlock->tree.children.empty()) + { + EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded = !EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded; + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + } + emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block); + + if (EASY_GLOBALS.selecting_block_changes_thread && selectedBlock != nullptr && EASY_GLOBALS.selected_thread != selectedBlockThread) + { + EASY_GLOBALS.selected_thread = selectedBlockThread; + + m_pScrollbar->lock(); + emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread); + m_pScrollbar->unlock(); + } + m_bUpdatingRect = false; + + if (selectedBlock != nullptr && selectedBlockThread == EASY_GLOBALS.selected_thread) + m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id); + else + { + for (auto item : m_items) + { + if (item->threadId() == EASY_GLOBALS.selected_thread) + { + m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0)); + break; + } + } + } + + repaintScene(); + } + else if (chronoHidden) + { + emit sceneUpdated(); + } +} + +////////////////////////////////////////////////////////////////////////// + +bool EasyGraphicsView::moveChrono(EasyChronometerItem* _chronometerItem, qreal _mouseX) +{ + if (_chronometerItem->reverse()) + { + if (_mouseX > _chronometerItem->right()) + { + _chronometerItem->setReverse(false); + _chronometerItem->setLeftRight(_chronometerItem->right(), _mouseX); + + if (_chronometerItem->hoverLeft()) + { + _chronometerItem->setHoverLeft(false); + _chronometerItem->setHoverRight(true); + } + } + else + { + _chronometerItem->setLeftRight(_mouseX, _chronometerItem->right()); + } + } + else + { + if (_mouseX < _chronometerItem->left()) + { + _chronometerItem->setReverse(true); + _chronometerItem->setLeftRight(_mouseX, _chronometerItem->left()); + + if (_chronometerItem->hoverRight()) + { + _chronometerItem->setHoverLeft(true); + _chronometerItem->setHoverRight(false); + } + } + else + { + _chronometerItem->setLeftRight(_chronometerItem->left(), _mouseX); + } + } + + if (!_chronometerItem->isVisible() && _chronometerItem->width() > 1e-6) + { + _chronometerItem->show(); + return true; + } + + return false; +} + +void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) +{ + m_idleTime = 0; + + if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible() && !m_chronometerItemAux->isVisible())) + { + _event->accept(); + return; + } + + bool needUpdate = false; + const auto pos = _event->pos(); + const auto delta = pos - m_mousePressPos; + m_mousePressPos = pos; + + if (m_mouseButtons != 0) + { + m_mouseMovePath.setX(m_mouseMovePath.x() + qAbs(delta.x())); + m_mouseMovePath.setY(m_mouseMovePath.y() + qAbs(delta.y())); + } + + auto mouseScenePos = mapToScene(m_mousePressPos); + mouseScenePos.setX(m_offset + mouseScenePos.x() / m_scale); + const auto x = clamp(0., mouseScenePos.x(), m_sceneWidth); + + if (m_mouseButtons & Qt::RightButton) + { + bool showItem = moveChrono(m_chronometerItem, x); + m_pScrollbar->setChronoPos(m_chronometerItem->left(), m_chronometerItem->right()); + + if (showItem) + { + m_pScrollbar->showChrono(); + } + + needUpdate = true; + } + + if (m_mouseButtons & Qt::LeftButton) + { + if (m_bDoubleClick) + { + moveChrono(m_chronometerItemAux, x); + } + else + { + auto vbar = verticalScrollBar(); + + m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once + vbar->setValue(vbar->value() - delta.y()); + m_pScrollbar->setValue(m_pScrollbar->value() - delta.x() / m_scale); + m_bUpdatingRect = false; + // Seems like an ugly stub, but QSignalBlocker is also a bad decision + // because if scrollbar does not emit valueChanged signal then viewport does not move + + updateVisibleSceneRect(); // Update scene visible rect only once + + // Update flicker speed + m_flickerSpeedX += delta.x() >> 1; + m_flickerSpeedY += delta.y(); + if (!m_flickerTimer.isActive()) + { + // If flicker timer is not started, then start it + m_flickerTimer.start(FLICKER_INTERVAL); + } + } + + needUpdate = true; + } + + if (m_mouseButtons == 0) + { + if (m_chronometerItem->isVisible()) + { + auto prevValue = m_chronometerItem->hoverIndicator(); + m_chronometerItem->setHoverIndicator(m_chronometerItem->indicatorContains(mouseScenePos)); + needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverIndicator()); + + prevValue = m_chronometerItem->hoverLeft(); + m_chronometerItem->setHoverLeft(m_chronometerItem->hoverLeft(mouseScenePos.x())); + needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverLeft()); + + if (!m_chronometerItem->hoverLeft()) + { + prevValue = m_chronometerItem->hoverRight(); + m_chronometerItem->setHoverRight(m_chronometerItem->hoverRight(mouseScenePos.x())); + needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverRight()); + } + } + + if (m_chronometerItemAux->isVisible()) + { + auto prevValue = m_chronometerItemAux->hoverLeft(); + m_chronometerItemAux->setHoverLeft(m_chronometerItemAux->hoverLeft(mouseScenePos.x())); + needUpdate = needUpdate || (prevValue != m_chronometerItemAux->hoverLeft()); + + if (!m_chronometerItemAux->hoverLeft()) + { + prevValue = m_chronometerItemAux->hoverRight(); + m_chronometerItemAux->setHoverRight(m_chronometerItemAux->hoverRight(mouseScenePos.x())); + needUpdate = needUpdate || (prevValue != m_chronometerItemAux->hoverRight()); + } + } + } + + if (needUpdate) + { + repaintScene(); // repaint scene + } + + _event->accept(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::keyPressEvent(QKeyEvent* _event) +{ + static const int KeyStep = 100; + + const int key = _event->key(); + m_idleTime = 0; + + switch (key) + { + case Qt::Key_Right: + case Qt::Key_6: + { + m_pScrollbar->setValue(m_pScrollbar->value() + KeyStep / m_scale); + break; + } + + case Qt::Key_Left: + case Qt::Key_4: + { + m_pScrollbar->setValue(m_pScrollbar->value() - KeyStep / m_scale); + break; + } + + case Qt::Key_Up: + case Qt::Key_8: + { + auto vbar = verticalScrollBar(); + vbar->setValue(vbar->value() - KeyStep); + break; + } + + case Qt::Key_Down: + case Qt::Key_2: + { + auto vbar = verticalScrollBar(); + vbar->setValue(vbar->value() + KeyStep); + break; + } + + case Qt::Key_Plus: + case Qt::Key_Equal: + { + onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), KeyStep); + break; + } + + case Qt::Key_Minus: + { + onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), -KeyStep); + break; + } + } + + //m_keys.insert(key); + _event->accept(); +} + +void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event) +{ + //const int key = _event->key(); + m_idleTime = 0; + + //m_keys.erase(key); + _event->accept(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::resizeEvent(QResizeEvent* _event) +{ + Parent::resizeEvent(_event); + + const QRectF previousRect = m_visibleSceneRect; + const int vbar_width = updateVisibleSceneRect(); // Update scene visible rect only once + + // Update slider width for scrollbar + const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale; + m_pScrollbar->setSliderWidth(windowWidth); + + // Calculate new offset to save old screen center + const auto deltaWidth = m_visibleSceneRect.width() - previousRect.width(); + m_offset = clamp(0., m_offset - deltaWidth * 0.5 / m_scale, m_sceneWidth - windowWidth); + + // Update slider position + m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change + m_pScrollbar->setValue(m_offset); + m_bUpdatingRect = false; + + repaintScene(); // repaint scene +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::initMode() +{ + // TODO: find mode with least number of bugs :) + // There are always some display bugs... + + setCacheMode(QGraphicsView::CacheNone); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setOptimizationFlag(QGraphicsView::DontSavePainterState, true); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange); + connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout); + connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout); + + auto globalSignals = &EASY_GLOBALS.events; + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::hierarchyFlagChanged, this, &This::onHierarchyFlagChange); + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::itemsExpandStateChanged, this, &This::onRefreshRequired); + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::refreshRequired, this, &This::onRefreshRequired); + + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedBlockIdChanged, [this](::profiler::block_id_t) + { + if (::profiler_gui::is_max(EASY_GLOBALS.selected_block_id)) + { + if (EASY_GLOBALS.selected_thread != 0) + { + for (auto item : m_items) + { + if (item->threadId() == EASY_GLOBALS.selected_thread) + { + m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0)); + break; + } + } + } + else + { + m_pScrollbar->setHistogramSource(0, nullptr); + } + } + else + m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id); + onRefreshRequired(); + }); + + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged); + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged); + + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::blocksTreeModeChanged, [this]() + { + if (!m_selectedBlocks.empty()) + emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse()); + }); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onThreadViewChanged() +{ + if (m_bEmpty) + return; + + for (auto item : m_items) + item->validateName(); + + emit treeChanged(); + + updateVisibleSceneRect(); + onHierarchyFlagChange(EASY_GLOBALS.only_current_thread_hierarchy); + + repaintScene(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onScrollbarValueChange(int) +{ + if (!m_bUpdatingRect && !m_bEmpty) + updateVisibleSceneRect(); +} + +void EasyGraphicsView::onGraphicsScrollbarValueChange(qreal _value) +{ + if (!m_bEmpty) + { + m_offset = _value; + if (!m_bUpdatingRect) + { + updateVisibleSceneRect(); + repaintScene(); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onFlickerTimeout() +{ + ++m_flickerCounterX; + ++m_flickerCounterY; + + if (m_mouseButtons & Qt::LeftButton) + { + // Fast slow-down and stop if mouse button is pressed, no flicking. + m_flickerSpeedX >>= 1; + m_flickerSpeedY >>= 1; + if (m_flickerSpeedX == -1) m_flickerSpeedX = 0; + if (m_flickerSpeedY == -1) m_flickerSpeedY = 0; + } + else + { + // Flick when mouse button is not pressed + + using estd::sign; + using estd::absmin; + + auto vbar = verticalScrollBar(); + + m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once + m_pScrollbar->setValue(m_pScrollbar->value() - m_flickerSpeedX / m_scale); + vbar->setValue(vbar->value() - m_flickerSpeedY); + m_bUpdatingRect = false; + // Seems like an ugly stub, but QSignalBlocker is also a bad decision + // because if scrollbar does not emit valueChanged signal then viewport does not move + + updateVisibleSceneRect(); // Update scene visible rect only once + repaintScene(); // repaint scene + + const int dx = static_cast(sign(m_flickerSpeedX) * m_flickerCounterX / FLICKER_FACTOR); + const int dy = static_cast(sign(m_flickerSpeedY) * m_flickerCounterY / FLICKER_FACTOR); + + if (abs(dx) > 0) + { + m_flickerSpeedX -= absmin(dx, m_flickerSpeedX); + m_flickerCounterX = 0; + } + + if (abs(dy) > 0) + { + m_flickerSpeedY -= absmin(dy, m_flickerSpeedY); + m_flickerCounterY = 0; + } + } + + if (m_flickerSpeedX == 0 && m_flickerSpeedY == 0) + { + // Flicker stopped, no timer needed. + m_flickerTimer.stop(); + m_flickerSpeedX = 0; + m_flickerSpeedY = 0; + m_flickerCounterX = 0; + m_flickerCounterY = 0; + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onIdleTimeout() +{ + m_idleTime += IDLE_TIMER_INTERVAL; + + if (m_idleTime < IDLE_TIME) + { + removePopup(true); + return; + } + + if (m_popupWidget != nullptr) + return; + + auto scenePos = mapToScene(mapFromGlobal(QCursor::pos())); + + if (scenePos.x() < m_visibleSceneRect.left() || scenePos.x() > m_visibleSceneRect.right()) + return; + + if (scenePos.y() < m_visibleSceneRect.top() || scenePos.y() > m_visibleSceneRect.bottom()) + return; + + decltype(scenePos) pos(m_offset + scenePos.x() / m_scale, scenePos.y()); + + // Try to select one of context switches or items + for (auto item : m_items) + { + auto cse = item->intersectEvent(pos); + if (cse != nullptr) + { + const auto& itemBlock = cse->tree; + + auto widget = new QWidget(nullptr, Qt::FramelessWindowHint); + if (widget == nullptr) + return; + + widget->setAttribute(Qt::WA_ShowWithoutActivating, true); + widget->setFocusPolicy(Qt::NoFocus); + + auto lay = new QGridLayout(widget); + if (lay == nullptr) + return; + + int row = 0; + lay->addWidget(new EasyBoldLabel("Context switch event", widget), row, 0, 1, 3, Qt::AlignHCenter); + ++row; + + lay->addWidget(new QLabel("Thread:", widget), row, 0, Qt::AlignRight); + + const char* process_name = ""; + ::profiler::thread_id_t tid = 0; + if (EASY_GLOBALS.version < ::profiler_gui::V130) + { + tid = cse->tree.node->id(); + process_name = cse->tree.node->name(); + } + else + { + tid = cse->tree.cs->tid(); + process_name = cse->tree.cs->name(); + } + + auto it = EASY_GLOBALS.profiler_blocks.find(tid); + + if (it != EASY_GLOBALS.profiler_blocks.end()) + { + if (EASY_GLOBALS.hex_thread_id) + lay->addWidget(new QLabel(QString("0x%1 %2").arg(tid, 0, 16).arg(it->second.name()), widget), row, 1, 1, 2, Qt::AlignLeft); + else + lay->addWidget(new QLabel(QString("%1 %2").arg(tid).arg(it->second.name()), widget), row, 1, 1, 2, Qt::AlignLeft); + } + else if (EASY_GLOBALS.hex_thread_id) + lay->addWidget(new QLabel(QString("0x%1").arg(tid, 0, 16), widget), row, 1, 1, 2, Qt::AlignLeft); + else + lay->addWidget(new QLabel(QString::number(tid), widget), row, 1, 1, 2, Qt::AlignLeft); + ++row; + + lay->addWidget(new QLabel("Process:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(process_name, widget), row, 1, 1, 2, Qt::AlignLeft); + ++row; + + const auto duration = itemBlock.node->duration(); + lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, 1, 2, Qt::AlignLeft); + ++row; + + if (itemBlock.per_thread_stats) + { + lay->addWidget(new QLabel("Sum:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, itemBlock.per_thread_stats->total_duration, 3), widget), row, 1, 1, 2, Qt::AlignLeft); + ++row; + + lay->addWidget(new EasyBoldLabel("-------- Statistics --------", widget), row, 0, 1, 3, Qt::AlignHCenter); + lay->addWidget(new QLabel("per ", widget), row + 1, 0, Qt::AlignRight); + lay->addWidget(new QLabel("This %:", widget), row + 2, 0, Qt::AlignRight); + lay->addWidget(new QLabel("Sum %:", widget), row + 3, 0, Qt::AlignRight); + lay->addWidget(new QLabel("N Calls:", widget), row + 4, 0, Qt::AlignRight); + + lay->addWidget(new QLabel("Thread", widget), row + 1, 1, Qt::AlignHCenter); + + auto percent = ::profiler_gui::percentReal(duration, item->root()->profiled_time); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 2, 1, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->profiled_time)), widget), row + 3, 1, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row + 4, 1, Qt::AlignHCenter); + + if (itemBlock.per_frame_stats && !::profiler_gui::is_max(itemBlock.per_frame_stats->parent_block)) + { + int col = 2; + auto frame_duration = easyBlocksTree(itemBlock.per_frame_stats->parent_block).node->duration(); + + lay->addWidget(new QLabel("Frame", widget), row + 1, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(duration, frame_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 2, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration, frame_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 3, col, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 4, col, Qt::AlignHCenter); + } + } + + m_popupWidget = new QGraphicsProxyWidget(); + m_popupWidget->setWidget(widget); + + break; + } + + ::profiler::block_index_t i = ~0U; + auto block = item->intersect(pos, i); + if (block != nullptr) + { + const auto& itemBlock = block->tree; + const auto& itemDesc = easyDescriptor(itemBlock.node->id()); + + auto widget = new QWidget(nullptr, Qt::FramelessWindowHint); + if (widget == nullptr) + return; + + widget->setObjectName(QStringLiteral("DiagramPopup")); + widget->setAttribute(Qt::WA_ShowWithoutActivating, true); + widget->setFocusPolicy(Qt::NoFocus); + + auto lay = new QGridLayout(widget); + if (lay == nullptr) + return; + + lay->setSpacing(2); + + int row = 0; + switch (itemDesc.type()) + { + case ::profiler::BlockType::Block: + { + const auto name = *itemBlock.node->name() != 0 ? itemBlock.node->name() : itemDesc.name(); + + //lay->addWidget(new QLabel("Name:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new EasyBoldLabel(::profiler_gui::toUnicode(name), widget), row, 0, 1, 5, + Qt::AlignHCenter); + ++row; + + const auto duration = itemBlock.node->duration(); + lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), + widget), row, 1, 1, 3, Qt::AlignLeft); + ++row; + + ::profiler::timestamp_t children_duration = 0; + for (auto child : itemBlock.children) + children_duration += easyBlock(child).tree.node->duration(); + + const auto self_duration = duration - children_duration; + const auto self_percent = + duration == 0 ? 100. : ::profiler_gui::percentReal(self_duration, duration); + lay->addWidget(new QLabel("Self:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(QString("%1 (%2%)") + .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, + self_duration, 3)) + .arg(QString::number(self_percent, 'g', 3)), widget), + row, 1, 1, 3, Qt::AlignLeft); + ++row; + + break; + } + + case ::profiler::BlockType::Event: + { + const auto name = *itemBlock.node->name() != 0 ? itemBlock.node->name() : itemDesc.name(); + + lay->addWidget(new EasyBoldLabel("User defined event", widget), row, 0, 1, 2, Qt::AlignHCenter); + ++row; + + lay->addWidget(new QLabel("Name:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::toUnicode(name), widget), row, 1, Qt::AlignLeft); + ++row; + + break; + } + + case ::profiler::BlockType::Value: + { + lay->addWidget(new EasyBoldLabel("Arbitrary Value", widget), row, 0, 1, 2, Qt::AlignHCenter); + ++row; + + lay->addWidget(new QLabel("Name:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::toUnicode(itemDesc.name()), widget), row, 1, Qt::AlignLeft); + ++row; + + lay->addWidget(new QLabel("Value:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::valueString(*itemBlock.value), widget), row, 1, Qt::AlignLeft); + ++row; + + lay->addWidget(new QLabel("VIN:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(QString("0x%1").arg(itemBlock.value->value_id(), 0, 16), widget), row, 1, Qt::AlignLeft); + ++row; + + break; + } + + default: + { + delete widget; + return; + } + } + + if (itemBlock.per_thread_stats != nullptr) + { + if (itemDesc.type() == ::profiler::BlockType::Block) + { + const auto duration = itemBlock.node->duration(); + + lay->addWidget(new QLabel("Average:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, itemBlock.per_thread_stats->average_duration(), 3), widget), row, 1, 1, 3, Qt::AlignLeft); + ++row; + + // Calculate idle/active time + { + auto threadRoot = item->root(); + + ::profiler::block_index_t ind = 0; + auto it = ::std::lower_bound(threadRoot->sync.begin(), threadRoot->sync.end(), itemBlock.node->begin(), [](::profiler::block_index_t _cs_index, ::profiler::timestamp_t _val) + { + return EASY_GLOBALS.gui_blocks[_cs_index].tree.node->begin() < _val; + }); + + if (it != threadRoot->sync.end()) + { + ind = static_cast<::profiler::block_index_t>(it - threadRoot->sync.begin()); + if (ind > 0) + --ind; + } + else + { + ind = static_cast<::profiler::block_index_t>(threadRoot->sync.size()); + } + + ::profiler::timestamp_t idleTime = 0; + for (auto ncs = static_cast<::profiler::block_index_t>(threadRoot->sync.size()); ind < ncs; ++ind) + { + auto cs_index = threadRoot->sync[ind]; + const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + + if (cs->begin() > itemBlock.node->end()) + break; + + if (itemBlock.node->begin() <= cs->begin() && cs->end() <= itemBlock.node->end()) + idleTime += cs->duration(); + } + + const auto active_time = duration - idleTime; + const auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration); + lay->addWidget(new QLabel("Active time:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, active_time, 3)).arg(QString::number(active_percent, 'g', 3)), widget), row, 1, 1, 3, Qt::AlignLeft); + ++row; + } + + lay->addWidget(new EasyBoldLabel("-------- Statistics --------", widget), row, 0, 1, 5, Qt::AlignHCenter); + lay->addWidget(new QLabel("per ", widget), row + 1, 0, Qt::AlignRight); + lay->addWidget(new QLabel("This %:", widget), row + 2, 0, Qt::AlignRight); + lay->addWidget(new QLabel("Sum %:", widget), row + 3, 0, Qt::AlignRight); + lay->addWidget(new QLabel("Sum self %:", widget), row + 4, 0, Qt::AlignRight); + lay->addWidget(new QLabel("N Calls:", widget), row + 5, 0, Qt::AlignRight); + + lay->addWidget(new QLabel("Thread", widget), row + 1, 1, Qt::AlignHCenter); + + auto percent = ::profiler_gui::percentReal(duration, item->root()->profiled_time); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 2, 1, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->profiled_time)), widget), row + 3, 1, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration - itemBlock.per_thread_stats->total_children_duration, item->root()->profiled_time)), widget), row + 4, 1, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row + 5, 1, Qt::AlignHCenter); + + int col = 1; + + if (itemBlock.per_frame_stats->parent_block != i && !::profiler_gui::is_max(itemBlock.per_frame_stats->parent_block)) + { + ++col; + auto frame_duration = easyBlocksTree(itemBlock.per_frame_stats->parent_block).node->duration(); + + lay->addWidget(new QLabel("Frame", widget), row + 1, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(duration, frame_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 2, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration, frame_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 3, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration - itemBlock.per_frame_stats->total_children_duration, frame_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 4, col, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 5, col, Qt::AlignHCenter); + } + + if (!::profiler_gui::is_max(itemBlock.per_parent_stats->parent_block))// != item->threadId()) + { + ++col; + auto parent_duration = easyBlocksTree(itemBlock.per_parent_stats->parent_block).node->duration(); + + lay->addWidget(new QLabel("Parent", widget), row + 1, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(duration, parent_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 2, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(itemBlock.per_parent_stats->total_duration, parent_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 3, col, Qt::AlignHCenter); + + percent = ::profiler_gui::percentReal(itemBlock.per_parent_stats->total_duration - itemBlock.per_parent_stats->total_children_duration, parent_duration); + lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)), widget), row + 4, col, Qt::AlignHCenter); + + lay->addWidget(new QLabel(QString::number(itemBlock.per_parent_stats->calls_number), widget), row + 5, col, Qt::AlignHCenter); + + ++col; + } + } + else + { + lay->addWidget(new QLabel("N calls/Thread:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row, 1, Qt::AlignLeft); + } + } + + m_popupWidget = new QGraphicsProxyWidget(); + m_popupWidget->setWidget(widget); + + break; + } + } + + if (m_popupWidget != nullptr) + { + auto effect = new QGraphicsDropShadowEffect(); + effect->setBlurRadius(5); + effect->setOffset(3, 3); + m_popupWidget->setGraphicsEffect(effect); + + scene()->addItem(m_popupWidget); + + auto br = m_popupWidget->boundingRect(); + if (scenePos.y() + br.height() > m_visibleSceneRect.bottom()) + scenePos.setY(::std::max(scenePos.y() - br.height(), m_visibleSceneRect.top())); + + if (scenePos.x() + br.width() > m_visibleSceneRect.right()) + scenePos.setX(::std::max(scenePos.x() - br.width(), m_visibleSceneRect.left())); + + m_popupWidget->setPos(scenePos); + m_popupWidget->setOpacity(0.95); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onHierarchyFlagChange(bool) +{ + bool changedSelection = false; + + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + m_selectedBlocks.clear(); + } + + if (m_chronometerItem->isVisible()) + { + for (auto item : m_items) + { + if (!EASY_GLOBALS.only_current_thread_hierarchy || item->threadId() == EASY_GLOBALS.selected_thread) + item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks); + } + + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + } + } + + if (changedSelection) + { + emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse()); + } +} + +void EasyGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id) +{ + if (m_pScrollbar == nullptr || m_pScrollbar->hystThread() == _id) + { + return; + } + + if (_id == 0) + { + m_pScrollbar->setHistogramSource(0, nullptr); + return; + } + + for (auto item : m_items) + { + if (item->threadId() == _id) + { + m_pScrollbar->setHistogramSource(_id, item->items(0)); + + bool changedSelection = false; + if (EASY_GLOBALS.only_current_thread_hierarchy) + { + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + m_selectedBlocks.clear(); + } + + if (m_chronometerItem->isVisible()) + { + item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks); + if (!m_selectedBlocks.empty()) + changedSelection = true; + } + } + + if (changedSelection) + { + emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse()); + } + + repaintScene(); + return; + } + } + + m_pScrollbar->setHistogramSource(0, nullptr); + repaintScene(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onSelectedBlockChange(unsigned int _block_index) +{ + if (!m_bUpdatingRect) + { + if (_block_index < EASY_GLOBALS.gui_blocks.size()) + { + // Scroll to item + + const auto& guiblock = EASY_GLOBALS.gui_blocks[_block_index]; + const auto thread_item = m_items[guiblock.graphics_item]; + const auto& item = thread_item->items(guiblock.graphics_item_level)[guiblock.graphics_item_index]; + + m_flickerSpeedX = m_flickerSpeedY = 0; + + m_bUpdatingRect = true; + verticalScrollBar()->setValue(static_cast(thread_item->levelY(guiblock.graphics_item_level) - m_visibleSceneRect.height() * 0.5)); + m_pScrollbar->setValue(item.left() + item.width() * 0.5 - m_pScrollbar->sliderHalfWidth()); + + if (EASY_GLOBALS.selecting_block_changes_thread && EASY_GLOBALS.selected_thread != thread_item->threadId()) + { + EASY_GLOBALS.selected_thread = thread_item->threadId(); + + m_pScrollbar->lock(); + emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread); + m_pScrollbar->unlock(); + } + + m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, guiblock.tree.node->id()); + + m_bUpdatingRect = false; + } + else if (EASY_GLOBALS.selected_thread != 0) + { + for (auto item : m_items) + { + if (item->threadId() == EASY_GLOBALS.selected_thread) + { + m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0)); + break; + } + } + } + else + { + m_pScrollbar->setHistogramSource(0, nullptr); + } + + updateVisibleSceneRect(); + repaintScene(); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onRefreshRequired() +{ + if (!m_bUpdatingRect) + { + repaintScene(); + } +} + +////////////////////////////////////////////////////////////////////////// + +EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent) + : QWidget(_parent) + , m_scrollbar(new EasyGraphicsScrollbar(this)) + , m_view(new EasyGraphicsView(this)) + , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, m_scrollbar->height(), this)) +{ + initWidget(); +} + +void EasyGraphicsViewWidget::initWidget() +{ + auto lay = new QGridLayout(this); + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(1); + lay->addWidget(m_threadNamesWidget, 0, 0, 2, 1); + lay->addWidget(m_view, 0, 1); + lay->addWidget(m_scrollbar, 1, 1); + setLayout(lay); + + m_view->setScrollbar(m_scrollbar); +} + +EasyGraphicsViewWidget::~EasyGraphicsViewWidget() +{ + +} + +EasyGraphicsView* EasyGraphicsViewWidget::view() +{ + return m_view; +} + +void EasyGraphicsViewWidget::clear() +{ + m_scrollbar->clear(); + m_threadNamesWidget->clear(); + m_view->clear(); +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + auto const parentView = static_cast(scene()->parent()); + const auto view = parentView->view(); + const auto& items = view->getItems(); + if (items.empty()) + return; + + const auto visibleSceneRect = view->visibleSceneRect(); + const auto h = visibleSceneRect.height() + TIMELINE_ROW_SIZE - 2; + const auto w = parentView->width();//parentView->sceneRect().width(); + + EASY_STATIC_CONSTEXPR uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1; + static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)}; + int i = -1; + + QRectF rect; + + _painter->resetTransform(); + + // Draw thread names + auto default_font = _painter->font(); + _painter->setFont(EASY_GLOBALS.bg_font); + for (auto item : items) + { + ++i; + + auto br = item->boundingRect(); + auto top = item->y() + br.top() - visibleSceneRect.top() - OVERLAP; + auto hgt = br.height() + ::profiler_gui::THREADS_ROW_SPACING; + auto bottom = top + hgt; + + if (top > h || bottom < 0) + continue; + + if (item->threadId() == EASY_GLOBALS.selected_thread) + _painter->setBrush(QBrush(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_BACKGROUND))); + else + _painter->setBrush(brushes[i & 1]); + + if (top < 0) + { + hgt += top; + top = 0; + } + + const auto dh = top + hgt - h; + if (dh > 0) + hgt -= dh; + + rect.setRect(0, top, w, hgt); + + _painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); + _painter->drawRect(rect); + + rect.translate(-5, 0); + _painter->setPen(::profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignRight | Qt::AlignVCenter, item->threadName()); + } + + const auto rect_bottom = rect.bottom(); + if (rect_bottom < h) + { + ++i; + rect.translate(5, rect.height()); + rect.setHeight(h - rect_bottom); + _painter->setBrush(brushes[i & 1]); + _painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); + _painter->drawRect(rect); + } + + // Draw separator between thread names area and information area + _painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); + _painter->drawLine(QLineF(0, h, w, h)); + _painter->drawLine(QLineF(0, h + 2, w, h + 2)); + + // Draw information + _painter->setFont(EASY_GLOBALS.chronometer_font); + QFontMetricsF fm(EASY_GLOBALS.chronometer_font, parentView); + const qreal th = fm.height(); // Calculate displayed text height + const qreal time1 = view->chronoTime(); + const qreal time2 = view->chronoTimeAux(); + + auto y = h + 2; + + auto drawTimeText = [&rect, &w, &y, &fm, &_painter](qreal time, qreal th, QRgb color) + { + if (time > 0) + { + const QString text = ::profiler_gui::autoTimeStringReal(time); // Displayed text + rect.setRect(0, y, w, th); + + _painter->setPen(color); + _painter->drawText(rect, Qt::AlignCenter, text); + + y += th; + } + }; + + drawTimeText(time1, th, ::profiler_gui::CHRONOMETER_COLOR.rgb() & 0x00ffffff); + drawTimeText(time2, th, ::profiler_gui::CHRONOMETER_COLOR2.rgb() & 0x00ffffff); +} + +////////////////////////////////////////////////////////////////////////// + +EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent) + : Parent(_parent) + , m_idleTime(0) + , m_view(_view) + , m_popupWidget(nullptr) + , m_maxLength(100) + , m_additionalHeight(_additionalHeight + 1) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + + setScene(new QGraphicsScene(this)); + + setCacheMode(QGraphicsView::CacheNone); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setFixedWidth(m_maxLength); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, [this](::profiler::thread_id_t){ repaintScene(); }); + connect(m_view, &EasyGraphicsView::treeChanged, this, &This::onTreeChange); + connect(m_view, &EasyGraphicsView::sceneUpdated, this, &This::repaintScene); + connect(m_view->verticalScrollBar(), &QScrollBar::valueChanged, verticalScrollBar(), &QScrollBar::setValue); + connect(m_view->verticalScrollBar(), &QScrollBar::rangeChanged, this, &This::setVerticalScrollbarRange); + connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout); +} + +EasyThreadNamesWidget::~EasyThreadNamesWidget() +{ + +} + +void EasyThreadNamesWidget::removePopup(bool _removeFromScene) +{ + if (m_popupWidget != nullptr) + { + auto widget = m_popupWidget->widget(); + widget->setParent(nullptr); + m_popupWidget->setWidget(nullptr); + delete widget; + + if (_removeFromScene) + scene()->removeItem(m_popupWidget); + + m_popupWidget = nullptr; + } +} + +void EasyThreadNamesWidget::clear() +{ + const QSignalBlocker b(this); + removePopup(); + scene()->clear(); + + m_maxLength = 100; + setFixedWidth(m_maxLength); + + m_idleTimer.stop(); + m_idleTime = 0; +} + +void EasyThreadNamesWidget::setVerticalScrollbarRange(int _minValue, int _maxValue) +{ + verticalScrollBar()->setRange(_minValue, _maxValue + m_additionalHeight); +} + +void EasyThreadNamesWidget::onTreeChange() +{ + const QSignalBlocker b(this); + removePopup(); + scene()->clear(); + + m_idleTimer.stop(); + m_idleTime = 0; + + QFontMetricsF fm(EASY_GLOBALS.bg_font, this); + qreal maxLength = 100; + const auto& graphicsItems = m_view->getItems(); + for (auto graphicsItem : graphicsItems) + maxLength = ::std::max(maxLength, (10 + fm.width(graphicsItem->threadName())) * ::profiler_gui::FONT_METRICS_FACTOR); + + auto vbar = verticalScrollBar(); + auto viewBar = m_view->verticalScrollBar(); + + setVerticalScrollbarRange(viewBar->minimum(), viewBar->maximum()); + vbar->setSingleStep(viewBar->singleStep()); + vbar->setPageStep(viewBar->pageStep()); + + auto r = m_view->sceneRect(); + setSceneRect(0, r.top(), maxLength, r.height() + m_additionalHeight); + + auto item = new EasyThreadNameItem(); + item->setPos(0, 0); + item->setBoundingRect(sceneRect()); + scene()->addItem(item); + + m_maxLength = static_cast(maxLength); + setFixedWidth(m_maxLength); + + m_idleTimer.start(IDLE_TIMER_INTERVAL); +} + +void EasyThreadNamesWidget::onIdleTimeout() +{ + static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1; + + m_idleTime += IDLE_TIMER_INTERVAL; + + if (m_idleTime < IDLE_TIME) + { + removePopup(true); + return; + } + + if (m_popupWidget != nullptr) + return; + + auto visibleSceneRect = mapToScene(rect()).boundingRect(); + auto scenePos = mapToScene(mapFromGlobal(QCursor::pos())); + + if (scenePos.x() < visibleSceneRect.left() || scenePos.x() > visibleSceneRect.right()) + { + if (m_idleTime > 3000) + setFixedWidth(m_maxLength); + return; + } + + if (scenePos.y() < visibleSceneRect.top() || scenePos.y() > visibleSceneRect.bottom()) + { + if (m_idleTime > 3000) + setFixedWidth(m_maxLength); + return; + } + + auto const parentView = static_cast(scene()->parent()); + const auto view = parentView->view(); + + if (scenePos.y() > view->visibleSceneRect().bottom()) + { + if (m_idleTime > 3000) + setFixedWidth(m_maxLength); + return; + } + + const qreal y = scenePos.y() - visibleSceneRect.top(); + + const auto& items = view->getItems(); + if (items.empty()) + { + if (m_idleTime > 3000) + setFixedWidth(m_maxLength); + return; + } + + EasyGraphicsItem* intersectingItem = nullptr; + for (auto item : items) + { + auto br = item->boundingRect(); + auto top = item->y() + br.top() - visibleSceneRect.top() - OVERLAP; + auto hgt = br.height() + ::profiler_gui::THREADS_ROW_SPACING; + auto bottom = top + hgt; + + if (bottom < y || y < top) + continue; + + intersectingItem = item; + + break; + } + + if (intersectingItem != nullptr) + { + auto widget = new QWidget(nullptr, Qt::FramelessWindowHint); + if (widget == nullptr) + return; + + widget->setObjectName(QStringLiteral("ThreadsPopup")); + widget->setAttribute(Qt::WA_ShowWithoutActivating, true); + widget->setFocusPolicy(Qt::NoFocus); + + auto lay = new QGridLayout(widget); + if (lay == nullptr) + return; + + int row = 0; + + lay->setSpacing(2); + lay->addWidget(new EasyBoldLabel(intersectingItem->threadName(), widget), row, 0, 1, 2, Qt::AlignHCenter); + ++row; + + ::profiler::timestamp_t duration = 0; + const auto& root = *intersectingItem->root(); + if (!root.children.empty()) + duration = easyBlock(root.children.back()).tree.node->end() - easyBlock(root.children.front()).tree.node->begin(); + + lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, Qt::AlignLeft); + ++row; + + lay->addWidget(new QLabel("Profiled:", widget), row, 0, Qt::AlignRight); + if (duration) + { + lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.profiled_time, 3)) + .arg(QString::number(100. * (double)root.profiled_time / (double)duration, 'f', 2)), widget), row, 1, Qt::AlignLeft); + } + else + { + lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.profiled_time, 3), widget), row, 1, Qt::AlignLeft); + } + ++row; + + lay->addWidget(new QLabel("Wait:", widget), row, 0, Qt::AlignRight); + if (duration) + { + lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.wait_time, 3)) + .arg(QString::number(100. * (double)root.wait_time / (double)duration, 'f', 2)), widget), row, 1, Qt::AlignLeft); + } + else + { + lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.wait_time, 3), widget), row, 1, Qt::AlignLeft); + } + ++row; + + const auto eventsSize = root.events.size(); + + lay->addWidget(new QLabel("Frames:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(QString::number(root.frames_number), widget), row, 1, Qt::AlignLeft); + ++row; + + lay->addWidget(new QLabel("Blocks:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(QString::number(root.blocks_number - eventsSize), widget), row, 1, Qt::AlignLeft); + ++row; + + lay->addWidget(new QLabel("Markers:", widget), row, 0, Qt::AlignRight); + lay->addWidget(new QLabel(QString::number(eventsSize), widget), row, 1, Qt::AlignLeft); + ++row; + + m_popupWidget = new QGraphicsProxyWidget(); + if (m_popupWidget != nullptr) + { + auto effect = new QGraphicsDropShadowEffect(); + effect->setBlurRadius(5); + effect->setOffset(3, 3); + m_popupWidget->setGraphicsEffect(effect); + + m_popupWidget->setWidget(widget); + scene()->addItem(m_popupWidget); + + auto br = m_popupWidget->boundingRect(); + + if (maximumWidth() < br.width()) + { + setFixedWidth(static_cast(br.width())); + visibleSceneRect.setWidth(br.width()); + } + + if (scenePos.y() + br.height() > visibleSceneRect.bottom()) + scenePos.setY(::std::max(scenePos.y() - br.height(), visibleSceneRect.top())); + + if (scenePos.x() + br.width() > visibleSceneRect.right()) + scenePos.setX(::std::max(scenePos.x() - br.width(), visibleSceneRect.left())); + + m_popupWidget->setPos(scenePos); + m_popupWidget->setOpacity(0.95); + } + } +} + +void EasyThreadNamesWidget::repaintScene() +{ + scene()->update(); +} + +void EasyThreadNamesWidget::mousePressEvent(QMouseEvent* _event) +{ + m_idleTime = 0; + + QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers()); + m_view->mousePressEvent(&e); + _event->accept(); +} + +void EasyThreadNamesWidget::mouseDoubleClickEvent(QMouseEvent* _event) +{ + static const auto OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1; + + m_idleTime = 0; + + auto y = mapToScene(_event->pos()).y(); + const auto& items = m_view->getItems(); + for (auto item : items) + { + auto br = item->boundingRect(); + auto top = item->y() + br.top() - OVERLAP; + auto bottom = top + br.height() + OVERLAP; + + if (y < top || y > bottom) + continue; + + const auto thread_id = item->threadId(); + if (thread_id != EASY_GLOBALS.selected_thread) + { + EASY_GLOBALS.selected_thread = thread_id; + emit EASY_GLOBALS.events.selectedThreadChanged(thread_id); + } + + break; + } + + _event->accept(); +} + +void EasyThreadNamesWidget::mouseReleaseEvent(QMouseEvent* _event) +{ + m_idleTime = 0; + + QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers()); + m_view->mouseReleaseEvent(&e); + _event->accept(); +} + +void EasyThreadNamesWidget::mouseMoveEvent(QMouseEvent* _event) +{ + m_idleTime = 0; + + QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers()); + m_view->mouseMoveEvent(&e); + _event->accept(); +} + +void EasyThreadNamesWidget::keyPressEvent(QKeyEvent* _event) +{ + m_idleTime = 0; + m_view->keyPressEvent(_event); +} + +void EasyThreadNamesWidget::keyReleaseEvent(QKeyEvent* _event) +{ + m_idleTime = 0; + m_view->keyReleaseEvent(_event); +} + +void EasyThreadNamesWidget::wheelEvent(QWheelEvent* _event) +{ + m_idleTime = 0; + + auto vbar = m_view->verticalScrollBar(); + if (vbar != nullptr) + { + _event->accept(); + vbar->setValue(vbar->value() - _event->delta()); + } +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/profiler_gui/blocks_graphics_view.h b/3rdparty/easyprofiler/profiler_gui/blocks_graphics_view.h new file mode 100644 index 0000000..255862b --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/blocks_graphics_view.h @@ -0,0 +1,349 @@ +/************************************************************************ +* file name : blocks_graphics_view.h +* ----------------- : +* creation time : 2016/06/26 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of GraphicsScene and GraphicsView and +* : it's auxiliary classes for displyaing easy_profiler blocks tree. +* ----------------- : +* change log : * 2016/06/26 Victor Zarubkin: moved sources from graphics_view.h +* : and renamed classes from My* to Prof*. +* : +* : * 2016/06/29 Victor Zarubkin: Highly optimized painting performance and memory consumption. +* : +* : * 2016/06/30 Victor Zarubkin: Replaced doubles with floats (in ProfBlockItem) for less memory consumption. +* : +* : * 2016/09/15 Victor Zarubkin: Moved sources of EasyGraphicsItem and EasyChronometerItem to separate files. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_GRAPHICS_VIEW_H +#define EASY_GRAPHICS_VIEW_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "common_functions.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +class QGraphicsProxyWidget; +class EasyGraphicsView; +class EasyGraphicsItem; +class EasyGraphicsScrollbar; +class EasyChronometerItem; + +////////////////////////////////////////////////////////////////////////// + +#define EASY_QGRAPHICSITEM(ClassName) \ +class ClassName : public QGraphicsItem { \ + QRectF m_boundingRect; \ +public: \ + ClassName() : QGraphicsItem() {} \ + virtual ~ClassName() {} \ + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; \ + QRectF boundingRect() const override { return m_boundingRect; } \ + void setBoundingRect(qreal x, qreal y, qreal w, qreal h) { m_boundingRect.setRect(x, y, w, h); } \ + void setBoundingRect(const QRectF& _rect) { m_boundingRect = _rect; } \ +} + +EASY_QGRAPHICSITEM(EasyBackgroundItem); +EASY_QGRAPHICSITEM(EasyTimelineIndicatorItem); +EASY_QGRAPHICSITEM(EasyThreadNameItem); + +#undef EASY_QGRAPHICSITEM + +////////////////////////////////////////////////////////////////////////// + +struct EasyBoldLabel : public QLabel { + EasyBoldLabel(const QString& _text, QWidget* _parent = nullptr); + virtual ~EasyBoldLabel(); +}; + +////////////////////////////////////////////////////////////////////////// + +class EasyGraphicsView : public QGraphicsView +{ + Q_OBJECT + +private: + + using Parent = QGraphicsView; + using This = EasyGraphicsView; + using Items = ::std::vector; + //using Keys = ::std::unordered_set >; + + Items m_items; ///< Array of all EasyGraphicsItem items + //Keys m_keys; ///< Pressed keyboard keys + ::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem) + QTimer m_flickerTimer; ///< Timer for flicking behavior + QTimer m_idleTimer; ///< + QRectF m_visibleSceneRect; ///< Visible scene rectangle + ::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks. + qreal m_sceneWidth; ///< + qreal m_scale; ///< Current scale + qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :( + qreal m_timelineStep; ///< + uint64_t m_idleTime; ///< + QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent) + QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button + Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons + EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget + EasyChronometerItem* m_chronometerItem; ///< Pointer to the EasyChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget. + EasyChronometerItem* m_chronometerItemAux; ///< Pointer to the EasyChronometerItem which is displayed when you double click left mouse button and move mouse left or right. This item is used only to measure time. + QGraphicsProxyWidget* m_popupWidget; ///< + int m_flickerSpeedX; ///< Current flicking speed x + int m_flickerSpeedY; ///< Current flicking speed y + int m_flickerCounterX; + int m_flickerCounterY; + bool m_bDoubleClick; ///< Is mouse buttons double clicked + bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on) + bool m_bEmpty; ///< Indicates whether scene is empty and has no items + +public: + + explicit EasyGraphicsView(QWidget* _parent = nullptr); + virtual ~EasyGraphicsView(); + + // Public virtual methods + + void wheelEvent(QWheelEvent* _event) override; + void mousePressEvent(QMouseEvent* _event) override; + void mouseDoubleClickEvent(QMouseEvent* _event) override; + void mouseReleaseEvent(QMouseEvent* _event) override; + void mouseMoveEvent(QMouseEvent* _event) override; + void keyPressEvent(QKeyEvent* _event) override; + void keyReleaseEvent(QKeyEvent* _event) override; + void resizeEvent(QResizeEvent* _event) override; + + void dragEnterEvent(QDragEnterEvent*) override {} + +public: + + // Public non-virtual methods + + qreal sceneWidth() const; + qreal chronoTime() const; + qreal chronoTimeAux() const; + + void setScrollbar(EasyGraphicsScrollbar* _scrollbar); + void clear(); + + void setTree(const ::profiler::thread_blocks_tree_t& _blocksTree); + + const Items& getItems() const; + +signals: + + // Signals + + void sceneUpdated(); + void treeChanged(); + void intervalChanged(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict); + +private: + + // Private non-virtual methods + + void removePopup(bool _removeFromScene = false); + + EasyChronometerItem* createChronometer(bool _main = true); + bool moveChrono(EasyChronometerItem* _chronometerItem, qreal _mouseX); + void initMode(); + int updateVisibleSceneRect(); + void updateTimelineStep(qreal _windowWidth); + void scaleTo(qreal _scale); + void scrollTo(const EasyGraphicsItem* _item); + void onWheel(qreal _mouseX, int _wheelDelta); + qreal setTree(EasyGraphicsItem* _item, const ::profiler::BlocksTree::children_t& _children, qreal& _height, uint32_t& _maxDepthChild, qreal _y, short _level); + +private slots: + + // Private Slots + + void repaintScene(); + void onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta); + void onScrollbarValueChange(int); + void onGraphicsScrollbarValueChange(qreal); + void onFlickerTimeout(); + void onIdleTimeout(); + void onHierarchyFlagChange(bool _value); + void onSelectedThreadChange(::profiler::thread_id_t _id); + void onSelectedBlockChange(unsigned int _block_index); + void onRefreshRequired(); + void onThreadViewChanged(); + +public: + + // Public inline methods + + inline qreal scale() const + { + return m_scale; + } + + inline qreal offset() const + { + return m_offset; + } + + inline const QRectF& visibleSceneRect() const + { + return m_visibleSceneRect; + } + + inline qreal timelineStep() const + { + return m_timelineStep; + } + + inline qreal time2position(const profiler::timestamp_t& _time) const + { + return PROF_MICROSECONDS(qreal(_time - m_beginTime)); + //return PROF_MILLISECONDS(qreal(_time - m_beginTime)); + } + + inline ::profiler::timestamp_t position2time(qreal _pos) const + { + return PROF_FROM_MICROSECONDS(_pos); + //return PROF_FROM_MILLISECONDS(_pos); + } + +}; // END of class EasyGraphicsView. + +////////////////////////////////////////////////////////////////////////// + +class EasyThreadNamesWidget : public QGraphicsView +{ + Q_OBJECT + +private: + + typedef QGraphicsView Parent; + typedef EasyThreadNamesWidget This; + + QTimer m_idleTimer; ///< + uint64_t m_idleTime; ///< + EasyGraphicsView* m_view; ///< + QGraphicsProxyWidget* m_popupWidget; ///< + int m_maxLength; ///< + const int m_additionalHeight; ///< + +public: + + explicit EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent = nullptr); + virtual ~EasyThreadNamesWidget(); + + void mousePressEvent(QMouseEvent* _event) override; + void mouseDoubleClickEvent(QMouseEvent* _event) override; + void mouseReleaseEvent(QMouseEvent* _event) override; + void mouseMoveEvent(QMouseEvent* _event) override; + void keyPressEvent(QKeyEvent* _event) override; + void keyReleaseEvent(QKeyEvent* _event) override; + void wheelEvent(QWheelEvent* _event) override; + + void dragEnterEvent(QDragEnterEvent*) override {} + + void clear(); + + const EasyGraphicsView* view() const + { + return m_view; + } + +private: + + void removePopup(bool _removeFromScene = false); + +private slots: + + void setVerticalScrollbarRange(int _minValue, int _maxValue); + void onTreeChange(); + void onIdleTimeout(); + void repaintScene(); + +}; // END of class EasyThreadNamesWidget. + +////////////////////////////////////////////////////////////////////////// + +class EasyGraphicsViewWidget : public QWidget +{ + Q_OBJECT + +private: + + EasyGraphicsScrollbar* m_scrollbar; + EasyGraphicsView* m_view; + EasyThreadNamesWidget* m_threadNamesWidget; + +public: + + explicit EasyGraphicsViewWidget(QWidget* _parent = nullptr); + virtual ~EasyGraphicsViewWidget(); + + EasyGraphicsView* view(); + void clear(); + +private: + + void initWidget(); + +}; // END of class EasyGraphicsViewWidget. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_GRAPHICS_VIEW_H diff --git a/3rdparty/easyprofiler/profiler_gui/blocks_tree_widget.cpp b/3rdparty/easyprofiler/profiler_gui/blocks_tree_widget.cpp new file mode 100644 index 0000000..45e7858 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/blocks_tree_widget.cpp @@ -0,0 +1,1280 @@ +/************************************************************************ +* file name : blocks_tree_widget.cpp +* ----------------- : +* creation time : 2016/06/26 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of EasyTreeWidget and it's auxiliary classes +* : for displyaing easy_profiler blocks tree. +* ----------------- : +* change log : * 2016/06/26 Victor Zarubkin: Moved sources from tree_view.h +* : and renamed classes from My* to Prof*. +* : +* : * 2016/06/27 Victor Zarubkin: Added possibility to colorize rows +* : with profiler blocks' colors. +* : Also added displaying frame statistics for blocks. +* : Disabled sorting by name to save order of threads displayed on graphics view. +* : +* : * 2016/06/29 Victor Zarubkin: Added clearSilent() method. +* : +* : * 2016/08/18 Victor Zarubkin: Moved sources of TreeWidgetItem into tree_widget_item.h/.cpp +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blocks_tree_widget.h" +#include "globals.h" + +#ifdef _WIN32 +#include + +#ifdef __MINGW32__ +#include +#endif + +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +////////////////////////////////////////////////////////////////////////// + +const int HIERARCHY_BUILDER_TIMER_INTERVAL = 40; + +const bool SIMPLIFIED_REGIME_COLUMNS[COL_COLUMNS_NUMBER] = { + true, //COL_NAME, + true, //COL_BEGIN, + true, //COL_DURATION, + true, //COL_SELF_DURATION, + false, //COL_DURATION_SUM_PER_PARENT, + false, //COL_DURATION_SUM_PER_FRAME, + true, //COL_DURATION_SUM_PER_THREAD, + true, //COL_SELF_DURATION_PERCENT, + false, //COL_PERCENT_PER_PARENT, + true, //COL_PERCENT_PER_FRAME, + false, //COL_PERCENT_SUM_PER_PARENT, + false, //COL_PERCENT_SUM_PER_FRAME, + true, //COL_PERCENT_SUM_PER_THREAD, + true, //COL_END, + true, //COL_MIN_PER_FRAME, + true, //COL_MAX_PER_FRAME, + true, //COL_AVERAGE_PER_FRAME, + true, //COL_NCALLS_PER_FRAME, + true, //COL_MIN_PER_THREAD, + true, //COL_MAX_PER_THREAD, + true, //COL_AVERAGE_PER_THREAD, + true, //COL_NCALLS_PER_THREAD, + false, //COL_MIN_PER_PARENT, + false, //COL_MAX_PER_PARENT, + false, //COL_AVERAGE_PER_PARENT, + false, //COL_NCALLS_PER_PARENT, + true, //COL_ACTIVE_TIME, + true //COL_ACTIVE_PERCENT, +}; + +////////////////////////////////////////////////////////////////////////// + +EasyTreeWidget::EasyTreeWidget(QWidget* _parent) + : Parent(_parent) + , m_beginTime(::std::numeric_limits::max()) + , m_lastFound(nullptr) + , m_progress(nullptr) + , m_hintLabel(nullptr) + , m_mode(EasyTreeMode_Plain) + , m_bLocked(false) + , m_bSilentExpandCollapse(false) +{ + memset(m_columnsHiddenStatus, 0, sizeof(m_columnsHiddenStatus)); + + setAutoFillBackground(false); + setAlternatingRowColors(true); + setItemsExpandable(true); + setAnimated(true); + setSortingEnabled(false); + setColumnCount(COL_COLUMNS_NUMBER); + setSelectionBehavior(QAbstractItemView::SelectRows); + + auto header_item = new QTreeWidgetItem(); + auto f = header()->font(); + f.setBold(true); + header()->setFont(f);// ::profiler_gui::EFont("Helvetica", 9, QFont::Bold)); + + header_item->setText(COL_NAME, "Name"); + + header_item->setText(COL_BEGIN, "Begin, ms"); + + header_item->setText(COL_DURATION, "Duration"); + header_item->setText(COL_SELF_DURATION, "Self dur."); + //header_item->setToolTip(COL_SELF_DURATION, ""); + header_item->setText(COL_DURATION_SUM_PER_PARENT, "Total / Parent"); + header_item->setText(COL_DURATION_SUM_PER_FRAME, "Total / Frame"); + header_item->setText(COL_DURATION_SUM_PER_THREAD, "Total / Thread"); + + header_item->setText(COL_SELF_DURATION_PERCENT, "Self %"); + header_item->setText(COL_PERCENT_PER_PARENT, "% / Parent"); + header_item->setText(COL_PERCENT_PER_FRAME, "% / Frame"); + header_item->setText(COL_PERCENT_SUM_PER_FRAME, "Sum % / Frame"); + header_item->setText(COL_PERCENT_SUM_PER_PARENT, "Sum % / Parent"); + header_item->setText(COL_PERCENT_SUM_PER_THREAD, "Sum % / Thread"); + + header_item->setText(COL_END, "End, ms"); + + header_item->setText(COL_MIN_PER_FRAME, "Min / Frame"); + header_item->setText(COL_MAX_PER_FRAME, "Max / Frame"); + header_item->setText(COL_AVERAGE_PER_FRAME, "Avg / Frame"); + header_item->setText(COL_NCALLS_PER_FRAME, "N Calls / Frame"); + + header_item->setText(COL_MIN_PER_PARENT, "Min / Parent"); + header_item->setText(COL_MAX_PER_PARENT, "Max / Parent"); + header_item->setText(COL_AVERAGE_PER_PARENT, "Avg / Parent"); + header_item->setText(COL_NCALLS_PER_PARENT, "N Calls / Parent"); + + header_item->setText(COL_MIN_PER_THREAD, "Min / Thread"); + header_item->setText(COL_MAX_PER_THREAD, "Max / Thread"); + header_item->setText(COL_AVERAGE_PER_THREAD, "Avg / Thread"); + header_item->setText(COL_NCALLS_PER_THREAD, "N Calls / Thread"); + + header_item->setText(COL_ACTIVE_TIME, "Active time"); + header_item->setText(COL_ACTIVE_PERCENT, "Active %"); + + auto color = QColor::fromRgb(::profiler::colors::DeepOrange900); + header_item->setForeground(COL_MIN_PER_THREAD, color); + header_item->setForeground(COL_MAX_PER_THREAD, color); + header_item->setForeground(COL_AVERAGE_PER_THREAD, color); + header_item->setForeground(COL_NCALLS_PER_THREAD, color); + header_item->setForeground(COL_PERCENT_SUM_PER_THREAD, color); + header_item->setForeground(COL_DURATION_SUM_PER_THREAD, color); + + color = QColor::fromRgb(::profiler::colors::Blue900); + header_item->setForeground(COL_MIN_PER_FRAME, color); + header_item->setForeground(COL_MAX_PER_FRAME, color); + header_item->setForeground(COL_AVERAGE_PER_FRAME, color); + header_item->setForeground(COL_NCALLS_PER_FRAME, color); + header_item->setForeground(COL_PERCENT_SUM_PER_FRAME, color); + header_item->setForeground(COL_DURATION_SUM_PER_FRAME, color); + header_item->setForeground(COL_PERCENT_PER_FRAME, color); + + color = QColor::fromRgb(::profiler::colors::Teal900); + header_item->setForeground(COL_MIN_PER_PARENT, color); + header_item->setForeground(COL_MAX_PER_PARENT, color); + header_item->setForeground(COL_AVERAGE_PER_PARENT, color); + header_item->setForeground(COL_NCALLS_PER_PARENT, color); + header_item->setForeground(COL_PERCENT_SUM_PER_PARENT, color); + header_item->setForeground(COL_DURATION_SUM_PER_PARENT, color); + header_item->setForeground(COL_PERCENT_PER_PARENT, color); + + setHeaderItem(header_item); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange, Qt::QueuedConnection); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange, Qt::QueuedConnection); + connect(&m_fillTimer, &QTimer::timeout, this, &This::onFillTimerTimeout); + + loadSettings(); + + m_columnsHiddenStatus[0] = 0; + setColumnHidden(0, false); + + if (m_mode == EasyTreeMode_Full) + { + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + m_columnsHiddenStatus[i] = isColumnHidden(i) ? 1 : 0; + } + else + { + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + { + if (SIMPLIFIED_REGIME_COLUMNS[i]) + { + if (isColumnHidden(i)) + m_columnsHiddenStatus[i] = 1; + } + else if (!isColumnHidden(i)) + { + setColumnHidden(i, true); + } + } + } + + m_hintLabel = new QLabel("Use Right Mouse Button on the Diagram to build a hierarchy...\nPress and hold, move, release", this); + m_hintLabel->setAlignment(Qt::AlignCenter); + m_hintLabel->setStyleSheet("QLabel { color: gray; font: 12pt; }"); + + QTimer::singleShot(1500, this, &This::alignProgressBar); + + setItemDelegateForColumn(0, new EasyItemDelegate(this)); +} + +EasyTreeWidget::~EasyTreeWidget() +{ + saveSettings(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::onFillTimerTimeout() +{ + if (m_hierarchyBuilder.done()) + { + m_fillTimer.stop(); + + ThreadedItems toplevelitems; + m_hierarchyBuilder.takeItems(m_items); + m_hierarchyBuilder.takeTopLevelItems(toplevelitems); + m_hierarchyBuilder.interrupt(); + { + const QSignalBlocker b(this); + for (auto& item : toplevelitems) + { + addTopLevelItem(item.second); + m_roots[item.first] = item.second; + } + } + + destroyProgressDialog(); + + m_bLocked = false; + m_inputBlocks.clear(); + + setSortingEnabled(true); + + sortByColumn(COL_BEGIN, Qt::AscendingOrder); // sort by begin time + if (m_mode == EasyTreeMode_Plain) // and after that, sort by frame % + sortByColumn(COL_PERCENT_PER_FRAME, Qt::DescendingOrder); + + //resizeColumnToContents(COL_NAME); + resizeColumnsToContents(); + + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); + connect(this, &Parent::itemCollapsed, this, &This::onItemCollapse); + connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); + onSelectedThreadChange(EASY_GLOBALS.selected_thread); + onSelectedBlockChange(EASY_GLOBALS.selected_block); + } + else if (m_progress != nullptr) + { + m_progress->setValue(m_hierarchyBuilder.progress()); + } +} + +void EasyTreeWidget::setTree(const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree) +{ + clearSilent(); + + if (!_blocksTree.empty()) + { + m_bLocked = true; + m_hintLabel->hide(); + createProgressDialog(); + m_hierarchyBuilder.fillTree(m_beginTime, _blocksNumber, _blocksTree, m_mode); + m_fillTimer.start(HIERARCHY_BUILDER_TIMER_INTERVAL); + } + + //StubLocker l; + //ThreadedItems toplevelitems; + //FillTreeClass::setTreeInternal1(l, m_items, toplevelitems, m_beginTime, _blocksNumber, _blocksTree, m_bColorRows); + //{ + // const QSignalBlocker b(this); + // for (auto& item : toplevelitems) + // { + // addTopLevelItem(item.second); + // m_roots[item.first] = item.second; + // if (item.first == EASY_GLOBALS.selected_thread) + // item.second->setMain(true); + // } + //} +} + +void EasyTreeWidget::setTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict) +{ + clearSilent(); + + m_beginTime = _session_begin_time; + _left += m_beginTime;// - ::std::min(m_beginTime, 1000ULL); + _right += m_beginTime;// + 1000; + + m_inputBlocks = _blocks; + if (!m_inputBlocks.empty()) + { + m_bLocked = true; + m_hintLabel->hide(); + createProgressDialog(); + m_hierarchyBuilder.fillTreeBlocks(m_inputBlocks, _session_begin_time, _left, _right, _strict, m_mode); + m_fillTimer.start(HIERARCHY_BUILDER_TIMER_INTERVAL); + } + + //StubLocker l; + //ThreadedItems toplevelitems; + //FillTreeClass::setTreeInternal2(l, m_items, toplevelitems, m_beginTime, _blocks, _left, _right, _strict, m_bColorRows); + //{ + // const QSignalBlocker b(this); + // for (auto& item : toplevelitems) + // { + // addTopLevelItem(item.second); + // m_roots[item.first] = item.second; + // if (item.first == EASY_GLOBALS.selected_thread) + // item.second->setMain(true); + // } + //} + + //setSortingEnabled(true); + //sortByColumn(COL_BEGIN, Qt::AscendingOrder); + //resizeColumnToContents(COL_NAME); + + //connect(this, &Parent::itemExpanded, this, &This::onItemExpand); + //connect(this, &Parent::itemCollapsed, this, &This::onItemCollapse); + //onSelectedBlockChange(EASY_GLOBALS.selected_block); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::clearSilent(bool _global) +{ + const QSignalBlocker b(this); + + m_hierarchyBuilder.interrupt(); + destroyProgressDialog(); + m_hintLabel->show(); + + m_bLocked = false; + m_beginTime = ::std::numeric_limits::max(); + + setSortingEnabled(false); + disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand); + disconnect(this, &Parent::itemCollapsed, this, &This::onItemCollapse); + disconnect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); + m_lastFound = nullptr; + m_lastSearch.clear(); + + if (!_global) + { + if (EASY_GLOBALS.collapse_items_on_tree_close) +#ifdef EASY_TREE_WIDGET__USE_VECTOR + for (auto item : m_items) +#else + for (auto& item : m_items) +#endif + { +#ifdef EASY_TREE_WIDGET__USE_VECTOR + auto& gui_block = item->guiBlock(); + gui_block.expanded = false; + ::profiler_gui::set_max(gui_block.tree_item); +#else + item.second->guiBlock().expanded = false; +#endif + } +#ifdef EASY_TREE_WIDGET__USE_VECTOR + else for (auto item : m_items) + { + ::profiler_gui::set_max(item->guiBlock().tree_item); + } +#endif + } + + m_items.clear(); + m_roots.clear(); + + ::std::vector topLevelItems; + topLevelItems.reserve(static_cast(topLevelItemCount())); + for (int i = topLevelItemCount() - 1; i >= 0; --i) + topLevelItems.push_back(takeTopLevelItem(i)); + + auto deleter_thread = ::std::thread([](decltype(topLevelItems) _items) + { +#ifdef _WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); +#endif + + for (auto item : _items) + delete item; + + }, ::std::move(topLevelItems)); + + deleter_thread.detach(); + + //clear(); + + if (!_global) + emit EASY_GLOBALS.events.itemsExpandStateChanged(); +} + +////////////////////////////////////////////////////////////////////////// + +int EasyTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) +{ + if (m_bLocked || _str.isEmpty()) + return 0; + + const bool isNewSearch = (m_lastSearch != _str); + auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, COL_NAME); + + if (!isNewSearch) + { + if (!itemsList.empty()) + { + bool stop = false; + decltype(m_lastFound) next = nullptr; + for (auto item : itemsList) + { + if (item->parent() == nullptr) + continue; + + if (stop) + { + next = item; + break; + } + + stop = item == m_lastFound; + } + + m_lastFound = next == nullptr ? itemsList.front() : next; + } + else + { + m_lastFound = nullptr; + } + } + else + { + m_lastSearch = _str; + m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; + } + + if (m_lastFound != nullptr) + { + scrollToItem(m_lastFound, QAbstractItemView::PositionAtCenter); + setCurrentItem(m_lastFound); + } + + return itemsList.size(); +} + +int EasyTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags) +{ + if (m_bLocked || _str.isEmpty()) + return 0; + + const bool isNewSearch = (m_lastSearch != _str); + auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, COL_NAME); + + if (!isNewSearch) + { + if (!itemsList.empty()) + { + decltype(m_lastFound) prev = nullptr; + for (auto item : itemsList) + { + if (item->parent() == nullptr) + continue; + + if (item == m_lastFound) + break; + + prev = item; + } + + m_lastFound = prev == nullptr ? itemsList.back() : prev; + } + else + { + m_lastFound = nullptr; + } + } + else + { + m_lastSearch = _str; + m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; + } + + if (m_lastFound != nullptr) + { + scrollToItem(m_lastFound, QAbstractItemView::PositionAtCenter); + setCurrentItem(m_lastFound); + } + + return itemsList.size(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event) +{ + if (m_bLocked) + { + _event->accept(); + return; + } + + const auto col = currentColumn(); + auto item = static_cast(currentItem()); + QMenu menu; + menu.setToolTipsVisible(true); + QAction* action = nullptr; + + if (!m_items.empty()) + { + action = menu.addAction("Expand all"); + connect(action, &QAction::triggered, this, &This::onExpandAllClicked); + action->setIcon(QIcon(imagePath("expand"))); + + action = menu.addAction("Collapse all"); + connect(action, &QAction::triggered, this, &This::onCollapseAllClicked); + action->setIcon(QIcon(imagePath("collapse"))); + + if (item != nullptr && col >= 0) + { + menu.addSeparator(); + + action = menu.addAction("Expand all children"); + connect(action, &QAction::triggered, this, &This::onExpandAllChildrenClicked); + action->setIcon(QIcon(imagePath("expand"))); + + action = menu.addAction("Collapse all children"); + connect(action, &QAction::triggered, this, &This::onCollapseAllChildrenClicked); + action->setIcon(QIcon(imagePath("collapse"))); + } + + menu.addSeparator(); + } + + auto actionGroup = new QActionGroup(&menu); + actionGroup->setExclusive(true); + + auto actionHierarchy = new QAction("Hierarchy mode", actionGroup); + actionHierarchy->setCheckable(true); + actionHierarchy->setChecked(m_mode == EasyTreeMode_Full); + actionHierarchy->setToolTip("Display full blocks hierarchy"); + actionHierarchy->setData((quint32)EasyTreeMode_Full); + menu.addAction(actionHierarchy); + + auto actionPlain = new QAction("Plain mode", actionGroup); + actionPlain->setCheckable(true); + actionPlain->setChecked(m_mode == EasyTreeMode_Plain); + actionPlain->setToolTip("Display plain list of blocks per frame.\nSome columns are disabled with this mode."); + actionPlain->setData((quint32)EasyTreeMode_Plain); + menu.addAction(actionPlain); + + connect(actionHierarchy, &QAction::triggered, this, &This::onModeChange); + connect(actionPlain, &QAction::triggered, this, &This::onModeChange); + + menu.addSeparator(); + + if (item != nullptr && item->parent() != nullptr) + { + if (col >= 0) + { + switch (col) + { + case COL_MIN_PER_THREAD: + case COL_MIN_PER_PARENT: + case COL_MIN_PER_FRAME: + case COL_MAX_PER_THREAD: + case COL_MAX_PER_PARENT: + case COL_MAX_PER_FRAME: + { + auto& block = item->block(); + auto i = ::profiler_gui::numeric_max(); + switch (col) + { + case COL_MIN_PER_THREAD: i = block.per_thread_stats->min_duration_block; break; + case COL_MIN_PER_PARENT: i = block.per_parent_stats->min_duration_block; break; + case COL_MIN_PER_FRAME: i = block.per_frame_stats->min_duration_block; break; + case COL_MAX_PER_THREAD: i = block.per_thread_stats->max_duration_block; break; + case COL_MAX_PER_PARENT: i = block.per_parent_stats->max_duration_block; break; + case COL_MAX_PER_FRAME: i = block.per_frame_stats->max_duration_block; break; + } + + if (i != ::profiler_gui::numeric_max(i)) + { + menu.addSeparator(); + auto itemAction = new QAction("Jump to such item", nullptr); + itemAction->setData(i); + itemAction->setToolTip("Jump to item with min/max duration (depending on clicked column)"); + connect(itemAction, &QAction::triggered, this, &This::onJumpToItemClicked); + menu.addAction(itemAction); + } + + break; + } + + default: + break; + } + } + + const auto& desc = easyDescriptor(item->block().node->id()); + auto submenu = menu.addMenu("Block status"); + submenu->setToolTipsVisible(true); + +#define ADD_STATUS_ACTION(NameValue, StatusValue, ToolTipValue)\ + action = submenu->addAction(NameValue);\ + action->setCheckable(true);\ + action->setChecked(desc.status() == StatusValue);\ + action->setData(static_cast(StatusValue));\ + action->setToolTip(ToolTipValue);\ + connect(action, &QAction::triggered, this, &This::onBlockStatusChangeClicked) + + ADD_STATUS_ACTION("Off", ::profiler::OFF, "Do not profile this block."); + ADD_STATUS_ACTION("On", ::profiler::ON, "Profile this block\nif parent enabled children."); + ADD_STATUS_ACTION("Force-On", ::profiler::FORCE_ON, "Always profile this block even\nif it's parent disabled children."); + ADD_STATUS_ACTION("Off-recursive", ::profiler::OFF_RECURSIVE, "Do not profile neither this block\nnor it's children."); + ADD_STATUS_ACTION("On-without-children", ::profiler::ON_WITHOUT_CHILDREN, "Profile this block, but\ndo not profile it's children."); + ADD_STATUS_ACTION("Force-On-without-children", ::profiler::FORCE_ON_WITHOUT_CHILDREN, "Always profile this block, but\ndo not profile it's children."); +#undef ADD_STATUS_ACTION + + submenu->setEnabled(EASY_GLOBALS.connected); + if (!EASY_GLOBALS.connected) + submenu->setTitle(QString("%1 (connection needed)").arg(submenu->title())); + } + + auto hidemenu = menu.addMenu("Select columns"); + auto hdr = headerItem(); + + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + { + auto columnAction = new QAction(hdr->text(i), nullptr); + columnAction->setData(i); + columnAction->setCheckable(true); + columnAction->setChecked(m_columnsHiddenStatus[i] == 0); + if ((m_mode == EasyTreeMode_Full || SIMPLIFIED_REGIME_COLUMNS[i])) + connect(columnAction, &QAction::triggered, this, &This::onHideShowColumn); + else + columnAction->setEnabled(false); + hidemenu->addAction(columnAction); + } + + menu.exec(QCursor::pos()); + + _event->accept(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::resizeEvent(QResizeEvent* _event) +{ + Parent::resizeEvent(_event); + alignProgressBar(); +} + +void EasyTreeWidget::moveEvent(QMoveEvent* _event) +{ + Parent::moveEvent(_event); + alignProgressBar(); +} + +void EasyTreeWidget::alignProgressBar() +{ + auto center = rect().center(); + auto pos = mapToGlobal(center); + + if (m_progress != nullptr) + m_progress->move(pos.x() - (m_progress->width() >> 1), pos.y() - (m_progress->height() >> 1)); + + m_hintLabel->move(center.x() - (m_hintLabel->width() >> 1), std::max(center.y() - (m_hintLabel->height() >> 1), header()->height())); +} + +void EasyTreeWidget::destroyProgressDialog() +{ + if (m_progress != nullptr) + { + m_progress->setValue(100); + m_progress->deleteLater(); + m_progress = nullptr; + } +} + +void EasyTreeWidget::createProgressDialog() +{ + destroyProgressDialog(); + + m_progress = new QProgressDialog("Building blocks hierarchy...", "", 0, 100, this, Qt::FramelessWindowHint); + m_progress->setAttribute(Qt::WA_TranslucentBackground); + m_progress->setCancelButton(nullptr); + m_progress->setValue(0); + m_progress->show(); + + alignProgressBar(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::onJumpToItemClicked(bool) +{ + auto action = qobject_cast(sender()); + if (action == nullptr) + return; + + auto block_index = action->data().toUInt(); + EASY_GLOBALS.selected_block = block_index; + if (block_index < EASY_GLOBALS.gui_blocks.size()) + EASY_GLOBALS.selected_block_id = easyBlock(block_index).tree.node->id(); + else + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + emit EASY_GLOBALS.events.selectedBlockChanged(block_index); +} + +void EasyTreeWidget::onCollapseAllClicked(bool) +{ + const QSignalBlocker b(this); + + m_bSilentExpandCollapse = true; + collapseAll(); + m_bSilentExpandCollapse = false; + + if (EASY_GLOBALS.bind_scene_and_tree_expand_status) + { +#ifdef EASY_TREE_WIDGET__USE_VECTOR + for (auto item : m_items) + item->guiBlock().expanded = false; +#else + for (auto& item : m_items) + item.second->guiBlock().expanded = false; +#endif + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + } +} + +void EasyTreeWidget::onExpandAllClicked(bool) +{ + const QSignalBlocker b(this); + + m_bSilentExpandCollapse = true; + expandAll(); + resizeColumnsToContents(); + m_bSilentExpandCollapse = false; + + if (EASY_GLOBALS.bind_scene_and_tree_expand_status) + { +#ifdef EASY_TREE_WIDGET__USE_VECTOR + for (auto item : m_items){ + auto& b = item->guiBlock(); +#else + for (auto& item : m_items){ + auto& b = item.second->guiBlock(); +#endif + b.expanded = !b.tree.children.empty(); + } + + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + } +} + +void EasyTreeWidget::onCollapseAllChildrenClicked(bool) +{ + auto current = static_cast(currentItem()); + if (current != nullptr) + { + const QSignalBlocker b(this); + + m_bSilentExpandCollapse = true; + current->collapseAll(); + m_bSilentExpandCollapse = false; + + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + } +} + +void EasyTreeWidget::onExpandAllChildrenClicked(bool) +{ + auto current = static_cast(currentItem()); + if (current != nullptr) + { + const QSignalBlocker b(this); + + m_bSilentExpandCollapse = true; + current->expandAll(); + resizeColumnsToContents(); + m_bSilentExpandCollapse = false; + + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::onBlockStatusChangeClicked(bool _checked) +{ + if (!_checked) + return; + + auto item = static_cast(currentItem()); + if (item == nullptr) + return; + + auto action = qobject_cast(sender()); + if (action != nullptr) + { + auto& desc = easyDescriptor(item->block().node->id()); + desc.setStatus(static_cast<::profiler::EasyBlockStatus>(action->data().toUInt())); + emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status()); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::onItemExpand(QTreeWidgetItem* _item) +{ + if (!EASY_GLOBALS.bind_scene_and_tree_expand_status || _item->parent() == nullptr) + { + resizeColumnsToContents(); + return; + } + + static_cast(_item)->guiBlock().expanded = true; + + if (!m_bSilentExpandCollapse) + { + resizeColumnsToContents(); + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + } +} + +void EasyTreeWidget::onItemCollapse(QTreeWidgetItem* _item) +{ + if (!EASY_GLOBALS.bind_scene_and_tree_expand_status || _item->parent() == nullptr) + return; + + static_cast(_item)->guiBlock().expanded = false; + + if (!m_bSilentExpandCollapse) + emit EASY_GLOBALS.events.itemsExpandStateChanged(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _previous) +{ + if (_previous != nullptr) + static_cast(_previous)->setBold(false); + + if (_item == nullptr) + { + ::profiler_gui::set_max(EASY_GLOBALS.selected_block); + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + } + else + { + auto item = static_cast(_item); + item->setBold(true); + + EASY_GLOBALS.selected_block = item->block_index(); + if (EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size()) + EASY_GLOBALS.selected_block_id = easyBlock(EASY_GLOBALS.selected_block).tree.node->id(); + else + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + } + + disconnect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); + emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::onSelectedThreadChange(::profiler::thread_id_t _id) +{ + for (auto& it : m_roots) + { + auto item = it.second; + item->setMain(it.first == _id); + } + + // Calling update() or repaint() (or both!) does not work even if setUpdatesEnabled(true) have been set in constructor. + // Have to set focus to this widget to force update/repaint. :( + // TODO: Find valid solution instead of this workaround. + auto f = qApp->focusWidget(); + setFocus(); + if (f != nullptr) + f->setFocus(); +} + +void EasyTreeWidget::onSelectedBlockChange(uint32_t _block_index) +{ + disconnect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); + + EasyTreeWidgetItem* item = nullptr; + + if (_block_index < EASY_GLOBALS.gui_blocks.size()) + { +#ifdef EASY_TREE_WIDGET__USE_VECTOR + const auto i = easyBlock(_block_index).tree_item; + if (i < m_items.size()) + item = m_items[i]; +#else + auto it = m_items.find(_block_index); + if (it != m_items.end()) + item = it->second; +#endif + } + + auto previous = static_cast(currentItem()); + if (previous != nullptr) + previous->setBold(false); + + if (item != nullptr) + { + //const QSignalBlocker b(this); + + if (EASY_GLOBALS.bind_scene_and_tree_expand_status) + { + m_bSilentExpandCollapse = true; + setCurrentItem(item); + scrollToItem(item, QAbstractItemView::PositionAtCenter); + if (item->guiBlock().expanded) + expandItem(item); + else + collapseItem(item); + resizeColumnsToContents(); + m_bSilentExpandCollapse = false; + + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + } + else + { + disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand); + setCurrentItem(item); + scrollToItem(item, QAbstractItemView::PositionAtCenter); + resizeColumnsToContents(); + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); + } + + item->setBold(true); + } + else + { + setCurrentItem(item); + } + + connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::resizeColumnsToContents() +{ + for (int i = 0; i < COL_COLUMNS_NUMBER; ++i) + { + resizeColumnToContents(i); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::onHideShowColumn(bool) +{ + auto action = qobject_cast(sender()); + if (action == nullptr) + return; + + const auto col = action->data().toInt(); + const bool hideCol = m_columnsHiddenStatus[col] == 0; + setColumnHidden(col, hideCol); + m_columnsHiddenStatus[col] = hideCol ? 1 : 0; +} + +void EasyTreeWidget::onModeChange(bool) +{ + auto action = qobject_cast(sender()); + if (action == nullptr) + return; + + const auto prev = m_mode; + m_mode = static_cast(action->data().toUInt()); + + if (m_mode == prev) + return; + + if (m_mode == EasyTreeMode_Full) + { + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + setColumnHidden(i, m_columnsHiddenStatus[i] != 0); + } + else + { + for (int i = 1; i < COL_COLUMNS_NUMBER; ++i) + setColumnHidden(i, m_columnsHiddenStatus[i] != 0 || !SIMPLIFIED_REGIME_COLUMNS[i]); + } + + emit EASY_GLOBALS.events.blocksTreeModeChanged(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidget::loadSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("tree_widget"); + + auto val = settings.value("regime"); + if (!val.isNull()) + m_mode = static_cast(val.toUInt()); + + val = settings.value("columns"); + if (!val.isNull()) + { + auto byteArray = val.toByteArray(); + memcpy(m_columnsHiddenStatus, byteArray.constData(), ::std::min(sizeof(m_columnsHiddenStatus), (size_t)byteArray.size())); + } + + auto state = settings.value("headerState").toByteArray(); + if (!state.isEmpty()) + header()->restoreState(state); + + settings.endGroup(); +} + +void EasyTreeWidget::saveSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("tree_widget"); + settings.setValue("regime", static_cast(m_mode)); + settings.setValue("columns", QByteArray(m_columnsHiddenStatus, COL_COLUMNS_NUMBER)); + settings.setValue("headerState", header()->saveState()); + settings.endGroup(); +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +EasyHierarchyWidget::EasyHierarchyWidget(QWidget* _parent) : Parent(_parent) + , m_tree(new EasyTreeWidget(this)) + , m_searchBox(new QLineEdit(this)) + , m_foundNumber(new QLabel("Found 0 matches", this)) + , m_searchButton(nullptr) + , m_bCaseSensitiveSearch(false) +{ + loadSettings(); + + m_searchBox->setFixedWidth(300); + m_searchBox->setContentsMargins(5, 0, 0, 0); + + auto menu = new QMenu(this); + m_searchButton = menu->menuAction(); + m_searchButton->setText("Find next"); + m_searchButton->setIcon(QIcon(imagePath("find-next"))); + m_searchButton->setData(true); + connect(m_searchButton, &QAction::triggered, this, &This::findNext); + + auto actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + auto a = new QAction(tr("Find next"), actionGroup); + a->setCheckable(true); + a->setChecked(true); + connect(a, &QAction::triggered, this, &This::findNextFromMenu); + menu->addAction(a); + + a = new QAction(tr("Find previous"), actionGroup); + a->setCheckable(true); + connect(a, &QAction::triggered, this, &This::findPrevFromMenu); + menu->addAction(a); + + a = menu->addAction("Case sensitive"); + a->setCheckable(true); + a->setChecked(m_bCaseSensitiveSearch); + connect(a, &QAction::triggered, [this](bool _checked){ m_bCaseSensitiveSearch = _checked; }); + menu->addAction(a); + + auto tb = new QToolBar(this); + tb->setIconSize(::profiler_gui::ICONS_SIZE); + tb->setContentsMargins(0, 0, 0, 0); + tb->addAction(m_searchButton); + tb->addWidget(m_searchBox); + + auto searchbox = new QHBoxLayout(); + searchbox->setContentsMargins(0, 0, 5, 0); + searchbox->addWidget(tb); + searchbox->addStretch(100); + searchbox->addWidget(m_foundNumber, Qt::AlignRight); + + auto lay = new QVBoxLayout(this); + lay->setContentsMargins(1, 1, 1, 1); + lay->addLayout(searchbox); + lay->addWidget(m_tree); + + connect(m_searchBox, &QLineEdit::returnPressed, this, &This::onSeachBoxReturnPressed); +} + +EasyHierarchyWidget::~EasyHierarchyWidget() +{ + saveSettings(); +} + +void EasyHierarchyWidget::loadSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("EasyHierarchyWidget"); + + auto val = settings.value("case_sensitive"); + if (!val.isNull()) + m_bCaseSensitiveSearch = val.toBool(); + + settings.endGroup(); +} + +void EasyHierarchyWidget::saveSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("EasyHierarchyWidget"); + settings.setValue("case_sensitive", m_bCaseSensitiveSearch); + settings.endGroup(); +} + +void EasyHierarchyWidget::keyPressEvent(QKeyEvent* _event) +{ + if (_event->key() == Qt::Key_F3) + { + if (_event->modifiers() & Qt::ShiftModifier) + findPrev(true); + else + findNext(true); + } + + _event->accept(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void EasyHierarchyWidget::contextMenuEvent(QContextMenuEvent* _event) +{ + m_tree->contextMenuEvent(_event); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +EasyTreeWidget* EasyHierarchyWidget::tree() +{ + return m_tree; +} + +void EasyHierarchyWidget::clear(bool _global) +{ + m_tree->clearSilent(_global); + m_foundNumber->setText(QString("Found 0 matches")); +} + +void EasyHierarchyWidget::onSeachBoxReturnPressed() +{ + if (m_searchButton->data().toBool() == true) + findNext(true); + else + findPrev(true); +} + +void EasyHierarchyWidget::findNext(bool) +{ + auto matches = m_tree->findNext(m_searchBox->text(), m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); + + if (matches == 1) + m_foundNumber->setText(QString("Found 1 match")); + else + m_foundNumber->setText(QString("Found %1 matches").arg(matches)); +} + +void EasyHierarchyWidget::findPrev(bool) +{ + auto matches = m_tree->findPrev(m_searchBox->text(), m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); + + if (matches == 1) + m_foundNumber->setText(QString("Found 1 match")); + else + m_foundNumber->setText(QString("Found %1 matches").arg(matches)); +} + +void EasyHierarchyWidget::findNextFromMenu(bool _checked) +{ + if (!_checked) + return; + + if (m_searchButton->data().toBool() == false) + { + m_searchButton->setData(true); + m_searchButton->setText(tr("Find next")); + m_searchButton->setIcon(QIcon(imagePath("find-next"))); + disconnect(m_searchButton, &QAction::triggered, this, &This::findPrev); + connect(m_searchButton, &QAction::triggered, this, &This::findNext); + } + + findNext(true); +} + +void EasyHierarchyWidget::findPrevFromMenu(bool _checked) +{ + if (!_checked) + return; + + if (m_searchButton->data().toBool() == true) + { + m_searchButton->setData(false); + m_searchButton->setText(tr("Find prev")); + m_searchButton->setIcon(QIcon(imagePath("find-prev"))); + disconnect(m_searchButton, &QAction::triggered, this, &This::findNext); + connect(m_searchButton, &QAction::triggered, this, &This::findPrev); + } + + findPrev(true); +} + +////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/easyprofiler/profiler_gui/blocks_tree_widget.h b/3rdparty/easyprofiler/profiler_gui/blocks_tree_widget.h new file mode 100644 index 0000000..f5d484e --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/blocks_tree_widget.h @@ -0,0 +1,219 @@ +/************************************************************************ +* file name : blocks_tree_widget.h +* ----------------- : +* creation time : 2016/06/26 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of EasyTreeWidget and it's auxiliary classes +* : for displyaing EasyProfiler blocks tree. +* ----------------- : +* change log : * 2016/06/26 Victor Zarubkin: moved sources from tree_view.h +* : and renamed classes from My* to Prof*. +* : +* : * 2016/06/27 Victor Zarubkin: Added possibility to colorize rows +* : with profiler blocks' colors. +* : +* : * 2016/06/29 Victor Zarubkin: Added clearSilent() method. +* : +* : * 2016/08/18 Victor Zarubkin: Added loading blocks hierarchy in separate thread; +* : Moved sources of TreeWidgetItem into tree_widget_item.h/.cpp +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_TREE_WIDGET_H +#define EASY_TREE_WIDGET_H + +#include +#include + +#include "tree_widget_loader.h" +#include "tree_widget_item.h" + +#include + +////////////////////////////////////////////////////////////////////////// + +class EasyTreeWidget : public QTreeWidget +{ + Q_OBJECT + + typedef QTreeWidget Parent; + typedef EasyTreeWidget This; + +protected: + + EasyTreeWidgetLoader m_hierarchyBuilder; + Items m_items; + RootsMap m_roots; + ::profiler_gui::TreeBlocks m_inputBlocks; + QTimer m_fillTimer; + QString m_lastSearch; + QTreeWidgetItem* m_lastFound; + ::profiler::timestamp_t m_beginTime; + class QProgressDialog* m_progress; + class QLabel* m_hintLabel; + EasyTreeMode m_mode; + bool m_bLocked; + bool m_bSilentExpandCollapse; + char m_columnsHiddenStatus[COL_COLUMNS_NUMBER]; + +public: + + explicit EasyTreeWidget(QWidget* _parent = nullptr); + virtual ~EasyTreeWidget(); + + void contextMenuEvent(QContextMenuEvent* _event) override; + + void clearSilent(bool _global = false); + int findNext(const QString& _str, Qt::MatchFlags _flags); + int findPrev(const QString& _str, Qt::MatchFlags _flags); + +public slots: + + void setTree(const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree); + + void setTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict); + +protected: + + void resizeEvent(QResizeEvent* _event) override; + void moveEvent(QMoveEvent* _event) override; + +private slots: + + void onJumpToItemClicked(bool); + + void onCollapseAllClicked(bool); + + void onExpandAllClicked(bool); + + void onCollapseAllChildrenClicked(bool); + + void onExpandAllChildrenClicked(bool); + + void onItemExpand(QTreeWidgetItem* _item); + void onItemCollapse(QTreeWidgetItem* _item); + void onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem*); + + void onSelectedThreadChange(::profiler::thread_id_t _id); + + void onSelectedBlockChange(uint32_t _block_index); + + void onBlockStatusChangeClicked(bool); + + void resizeColumnsToContents(); + + void onHideShowColumn(bool); + void onModeChange(bool); + + void onFillTimerTimeout(); + +protected: + + void loadSettings(); + void saveSettings(); + void alignProgressBar(); + +private: + + void destroyProgressDialog(); + void createProgressDialog(); + +}; // END of class EasyTreeWidget. + +////////////////////////////////////////////////////////////////////////// + +class EasyHierarchyWidget : public QWidget +{ + Q_OBJECT + + typedef QWidget Parent; + typedef EasyHierarchyWidget This; + +private: + + EasyTreeWidget* m_tree; + class QLineEdit* m_searchBox; + class QLabel* m_foundNumber; + class QAction* m_searchButton; + bool m_bCaseSensitiveSearch; + +public: + + // Public virtual methods + + explicit EasyHierarchyWidget(QWidget* _parent = nullptr); + virtual ~EasyHierarchyWidget(); + void keyPressEvent(QKeyEvent* _event) override; + void contextMenuEvent(QContextMenuEvent* _event) override; + +public: + + // Public non-virtual methods + + EasyTreeWidget* tree(); + void clear(bool _global = false); + +private slots: + + // Private slots + + void onSeachBoxReturnPressed(); + void findNext(bool); + void findPrev(bool); + void findNextFromMenu(bool); + void findPrevFromMenu(bool); + +private: + + // Private non-virtual methods + + void loadSettings(); + void saveSettings(); + +}; // END of class EasyHierarchyWidget. + + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_TREE_WIDGET_H diff --git a/3rdparty/easyprofiler/profiler_gui/cmake_install.cmake b/3rdparty/easyprofiler/profiler_gui/cmake_install.cmake new file mode 100644 index 0000000..b537650 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/cmake_install.cmake @@ -0,0 +1,54 @@ +# Install script for directory: /home/alex/Work/C++Projects/easyprofiler/profiler_gui + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Release") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +if(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified") + if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/profiler_gui" AND + NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/profiler_gui") + file(RPATH_CHECK + FILE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/profiler_gui" + RPATH "$ORIGIN:/home/alex/Work/Qt/5.8/gcc_64/lib") + endif() + file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/home/alex/Work/C++Projects/easyprofiler/bin/profiler_gui") + if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/profiler_gui" AND + NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/profiler_gui") + file(RPATH_CHANGE + FILE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/profiler_gui" + OLD_RPATH "/home/alex/Work/Qt/5.8/gcc_64/lib:/home/alex/Work/C++Projects/easyprofiler/bin:" + NEW_RPATH "$ORIGIN:/home/alex/Work/Qt/5.8/gcc_64/lib") + if(CMAKE_INSTALL_DO_STRIP) + execute_process(COMMAND "/usr/bin/strip" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/profiler_gui") + endif() + endif() +endif() + diff --git a/3rdparty/easyprofiler/profiler_gui/common_functions.cpp b/3rdparty/easyprofiler/profiler_gui/common_functions.cpp new file mode 100644 index 0000000..9e337d9 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/common_functions.cpp @@ -0,0 +1,345 @@ +/************************************************************************ +* file name : common_functions.cpp +* ----------------- : +* creation time : 2017/12/06 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementaion of common functions used by different UI widgets. +* ----------------- : +* change log : * 2017/12/06 Victor Zarubkin: Initial commit. Moved sources from common_types.h +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include "common_functions.h" + +namespace profiler_gui { + + ////////////////////////////////////////////////////////////////////////// + + qreal timeFactor(qreal _interval) + { + if (_interval < 1) // interval in nanoseconds + return 1e3; + + if (_interval < 1e3) // interval in microseconds + return 1; + + if (_interval < 1e6) // interval in milliseconds + return 1e-3; + + // interval in seconds + return 1e-6; + } + + ////////////////////////////////////////////////////////////////////////// + + QString autoTimeStringReal(qreal _interval, int _precision) + { + if (_interval < 1) // interval in nanoseconds + return QString("%1 ns").arg(static_cast(_interval * 1e3)); + + if (_interval < 1e3) // interval in microseconds + return QString("%1 us").arg(_interval, 0, 'f', _precision); + + if (_interval < 1e6) // interval in milliseconds + return QString("%1 ms").arg(_interval * 1e-3, 0, 'f', _precision); + + // interval in seconds + return QString("%1 s").arg(_interval * 1e-6, 0, 'f', _precision); + } + + QString autoTimeStringInt(qreal _interval) + { + if (_interval < 1) // interval in nanoseconds + return QString("%1 ns").arg(static_cast(_interval * 1e3 + 0.5)); + + if (_interval < 1e3) // interval in microseconds + return QString("%1 us").arg(static_cast(_interval + 0.5)); + + if (_interval < 1e6) // interval in milliseconds + return QString("%1 ms").arg(static_cast(_interval * 1e-3 + 0.5)); + + // interval in seconds + return QString("%1 s").arg(static_cast(_interval * 1e-6 + 0.5)); + } + + QString autoTimeStringRealNs(::profiler::timestamp_t _interval, int _precision) + { + if (_interval < 1000) // interval in nanoseconds + return QString("%1 ns").arg(_interval); + + if (_interval < 1000000) // interval in microseconds + return QString("%1 us").arg(_interval * 1e-3, 0, 'f', _precision); + + if (_interval < 1000000000U) // interval in milliseconds + return QString("%1 ms").arg(_interval * 1e-6, 0, 'f', _precision); + + // interval in seconds + return QString("%1 s").arg(_interval * 1e-9, 0, 'f', _precision); + } + + QString autoTimeStringIntNs(::profiler::timestamp_t _interval) + { + if (_interval < 1000) // interval in nanoseconds + return QString("%1 ns").arg(_interval); + + if (_interval < 1000000) // interval in microseconds + return QString("%1 us").arg(static_cast(_interval * 1e-3 + 0.5)); + + if (_interval < 1000000000U) // interval in milliseconds + return QString("%1 ms").arg(static_cast(_interval * 1e-6 + 0.5)); + + // interval in seconds + return QString("%1 s").arg(static_cast(_interval * 1e-9 + 0.5)); + } + + ////////////////////////////////////////////////////////////////////////// + + QString timeStringReal(TimeUnits _units, qreal _interval, int _precision) + { + switch (_units) + { + case TimeUnits_ms:{ + const char fmt = _interval <= 1 ? 'g' : 'f'; + return QString("%1 ms").arg(_interval * 1e-3, 0, fmt, _precision); + } + + case TimeUnits_us: + return QString("%1 us").arg(_interval, 0, 'f', _precision); + + case TimeUnits_ns: + return QString("%1 ns").arg(static_cast(_interval * 1e3 + 0.5)); + + case TimeUnits_auto: + default: + return autoTimeStringReal(_interval, _precision); + } + } + + QString timeStringRealNs(TimeUnits _units, ::profiler::timestamp_t _interval, int _precision) + { + switch (_units) + { + case TimeUnits_ms:{ + const char fmt = _interval <= 1000 ? 'g' : 'f'; + return QString("%1 ms").arg(_interval * 1e-6, 0, fmt, _precision); + } + + case TimeUnits_us: + return QString("%1 us").arg(_interval * 1e-3, 0, 'f', _precision); + + case TimeUnits_ns: + return QString("%1 ns").arg(_interval); + + case TimeUnits_auto: + default: + return autoTimeStringRealNs(_interval, _precision); + } + } + + QString timeStringInt(TimeUnits _units, qreal _interval) + { + switch (_units) + { + case TimeUnits_ms: + return QString("%1 ms").arg(static_cast(_interval * 1e-3 + 0.5)); + + case TimeUnits_us: + return QString("%1 us").arg(static_cast(_interval + 0.5)); + + case TimeUnits_ns: + return QString("%1 ns").arg(static_cast(_interval * 1e3 + 0.5)); + + case TimeUnits_auto: + default: + return autoTimeStringInt(_interval); + } + } + + QString timeStringIntNs(TimeUnits _units, ::profiler::timestamp_t _interval) + { + switch (_units) + { + case TimeUnits_ms: + return QString("%1 ms").arg(static_cast(_interval * 1e-6 + 0.5)); + + case TimeUnits_us: + return QString("%1 us").arg(static_cast(_interval * 1e-3 + 0.5)); + + case TimeUnits_ns: + return QString("%1 ns").arg(_interval); + + case TimeUnits_auto: + default: + return autoTimeStringIntNs(_interval); + } + } + + ////////////////////////////////////////////////////////////////////////// + + QFont EFont(QFont::StyleHint _hint, const char* _family, int _size, int _weight) + { + QFont f; + f.setStyleHint(_hint, QFont::PreferMatch); + f.setFamily(_family); + f.setPointSize(_size); + f.setWeight(_weight); + return f; + } + + ////////////////////////////////////////////////////////////////////////// + + QString valueTypeString(::profiler::DataType _dataType) + { + switch (_dataType) + { + case ::profiler::DataType::Bool: return QStringLiteral("bool"); + case ::profiler::DataType::Char: return QStringLiteral("char"); + case ::profiler::DataType::Int8: return QStringLiteral("int8"); + case ::profiler::DataType::Uint8: return QStringLiteral("unsigned int8"); + case ::profiler::DataType::Int16: return QStringLiteral("int16"); + case ::profiler::DataType::Uint16: return QStringLiteral("unsigned int16"); + case ::profiler::DataType::Int32: return QStringLiteral("int32"); + case ::profiler::DataType::Uint32: return QStringLiteral("unsigned int32"); + case ::profiler::DataType::Int64: return QStringLiteral("int64"); + case ::profiler::DataType::Uint64: return QStringLiteral("unsigned int64"); + case ::profiler::DataType::Float: return QStringLiteral("float"); + case ::profiler::DataType::Double: return QStringLiteral("double"); + case ::profiler::DataType::String: return QStringLiteral("string"); + default: return QStringLiteral("unknown"); + } + } + + QString valueTypeString(const ::profiler::ArbitraryValue& _serializedValue) + { + const auto type = _serializedValue.type(); + if (_serializedValue.isArray() && type != ::profiler::DataType::String) + return valueTypeString(type) + QStringLiteral("[]"); + return valueTypeString(type); + } + + QString valueString(const ::profiler::ArbitraryValue& _serializedValue) + { + if (_serializedValue.isArray()) + { + if (_serializedValue.type() == ::profiler::DataType::String) + return _serializedValue.data(); + return QStringLiteral("[...] array"); + } + + switch (_serializedValue.type()) + { + case ::profiler::DataType::Bool: return _serializedValue.toValue()->value() ? QStringLiteral("true") : QStringLiteral("false"); + case ::profiler::DataType::Char: return QChar(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int8: return QChar(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint8: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int16: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint16: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int32: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint32: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int64: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint64: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Float: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Double: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::String: return _serializedValue.data(); + default: return QStringLiteral("Unknown"); + } + } + + double value2real(const ::profiler::ArbitraryValue& _serializedValue, int _index) + { + if (_serializedValue.isArray()) + { + switch (_serializedValue.type()) + { + case ::profiler::DataType::Bool: + { + const auto value = _serializedValue.toArray()->at(_index); + return value ? 1 : 0; + } + + case ::profiler::DataType::Char: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Int8: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Uint8: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Int16: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Uint16: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Int32: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Uint32: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Int64: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Uint64: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Float: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Double: return _serializedValue.toArray()->at(_index); + case ::profiler::DataType::String: return static_cast(_serializedValue.data()[_index]); + default: return 0; + } + } + + switch (_serializedValue.type()) + { + case ::profiler::DataType::Bool: + { + const auto value = _serializedValue.toValue()->value(); + return value ? 1 : 0; + } + + case ::profiler::DataType::Char: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int8: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint8: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int16: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint16: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int32: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint32: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Int64: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint64: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Float: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Double: return _serializedValue.toValue()->value(); + case ::profiler::DataType::String: return static_cast(_serializedValue.data()[_index]); + default: return 0; + } + } + + ////////////////////////////////////////////////////////////////////////// + +} // end of namespace profiler_gui. diff --git a/3rdparty/easyprofiler/profiler_gui/common_functions.h b/3rdparty/easyprofiler/profiler_gui/common_functions.h new file mode 100644 index 0000000..7528962 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/common_functions.h @@ -0,0 +1,205 @@ +/************************************************************************ +* file name : common_functions.h +* ----------------- : +* creation time : 2017/12/06 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains common functions used by different UI widgets. +* ----------------- : +* change log : * 2017/12/06 Victor Zarubkin: Initial commit. Moved sources from common_types.h +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER_GUI_COMMON_FUNCTIONS_H +#define EASY_PROFILER_GUI_COMMON_FUNCTIONS_H + +#include +#include +#include +#include +#include "common_types.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#define PROF_MICROSECONDS(timestamp) ((timestamp) * 1e-3) +//#define PROF_MICROSECONDS(timestamp) (timestamp) + +#define PROF_FROM_MICROSECONDS(to_timestamp) ((to_timestamp) * 1e3) +//#define PROF_FROM_MICROSECONDS(to_timestamp) (to_timestamp) + +#define PROF_MILLISECONDS(timestamp) ((timestamp) * 1e-6) +//#define PROF_MILLISECONDS(timestamp) ((timestamp) * 1e-3) + +#define PROF_FROM_MILLISECONDS(to_timestamp) ((to_timestamp) * 1e6) +//#define PROF_FROM_MILLISECONDS(to_timestamp) ((to_timestamp) * 1e3) + +#define PROF_NANOSECONDS(timestamp) (timestamp) +//#define PROF_NANOSECONDS(timestamp) ((timestamp) * 1000) + +////////////////////////////////////////////////////////////////////////// + +EASY_FORCE_INLINE qreal units2microseconds(qreal _value) { + return _value; + //return _value * 1e3; +} + +EASY_FORCE_INLINE qreal microseconds2units(qreal _value) { + return _value; + //return _value * 1e-3; +} + +#ifdef EASY_CONSTEXPR_AVAILABLE +template +EASY_FORCE_INLINE EASY_CONSTEXPR_FCN typename ::std::underlying_type::type int_cast(TEnum _enumValue) { + return static_cast::type>(_enumValue); +} +#else +# define int_cast(_enumValue) static_cast<::std::underlying_type::type>(_enumValue) +#endif + +////////////////////////////////////////////////////////////////////////// + +namespace profiler_gui { + +////////////////////////////////////////////////////////////////////////// + +template inline +EASY_CONSTEXPR_FCN T numeric_max() { + return ::std::numeric_limits::max(); +} + +template inline +EASY_CONSTEXPR_FCN T numeric_max(T) { + return ::std::numeric_limits::max(); +} + +template inline +EASY_CONSTEXPR_FCN bool is_max(const T& _value) { + return _value == ::std::numeric_limits::max(); +} + +template inline +void set_max(T& _value) { + _value = ::std::numeric_limits::max(); +} + +////////////////////////////////////////////////////////////////////////// + +inline EASY_CONSTEXPR_FCN QRgb toRgb(uint32_t _red, uint32_t _green, uint32_t _blue) { + return (_red << 16) + (_green << 8) + _blue; +} + +inline EASY_CONSTEXPR_FCN QRgb fromProfilerRgb(uint32_t _red, uint32_t _green, uint32_t _blue) { + return _red == 0 && _green == 0 && _blue == 0 ? ::profiler::colors::Default : toRgb(_red, _green, _blue) | 0x00141414; +} + +EASY_FORCE_INLINE EASY_CONSTEXPR_FCN qreal colorSum(::profiler::color_t _color) { + return 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114); +} + +inline EASY_CONSTEXPR_FCN bool isLightColor(::profiler::color_t _color) { + return colorSum(_color) < 76.5 || ((_color & 0xff000000) >> 24) < 0x80; +} + +inline EASY_CONSTEXPR_FCN bool isLightColor(::profiler::color_t _color, qreal _maxSum) { + return colorSum(_color) < _maxSum || ((_color & 0xff000000) >> 24) < 0x80; +} + +inline EASY_CONSTEXPR_FCN ::profiler::color_t textColorForFlag(bool _is_light) { + return _is_light ? ::profiler::colors::Dark : ::profiler::colors::CreamWhite; +} + +inline EASY_CONSTEXPR_FCN ::profiler::color_t textColorForRgb(::profiler::color_t _color) { + return isLightColor(_color) ? ::profiler::colors::Dark : ::profiler::colors::CreamWhite; +} + +////////////////////////////////////////////////////////////////////////// + +qreal timeFactor(qreal _interval); + +QString autoTimeStringReal(qreal _interval, int _precision = 1); +QString autoTimeStringInt(qreal _interval); +QString autoTimeStringRealNs(::profiler::timestamp_t _interval, int _precision = 1); +QString autoTimeStringIntNs(::profiler::timestamp_t _interval); + +QString timeStringReal(TimeUnits _units, qreal _interval, int _precision = 1); +QString timeStringRealNs(TimeUnits _units, ::profiler::timestamp_t _interval, int _precision = 1); +QString timeStringInt(TimeUnits _units, qreal _interval); +QString timeStringIntNs(TimeUnits _units, ::profiler::timestamp_t _interval); + +////////////////////////////////////////////////////////////////////////// + +inline double percentReal(::profiler::timestamp_t _partial, ::profiler::timestamp_t _total) { + return _total != 0 ? 100. * static_cast(_partial) / static_cast(_total) : 0.; +} + +inline int percent(::profiler::timestamp_t _partial, ::profiler::timestamp_t _total) { + return static_cast(0.5 + percentReal(_partial, _total)); +} + +////////////////////////////////////////////////////////////////////////// + +QFont EFont(QFont::StyleHint _hint, const char* _family, int _size, int _weight = -1); + +inline QFont EFont(const char* _family, int _size, int _weight = -1) { + return EFont(QFont::Helvetica, _family, _size, _weight); +} + +////////////////////////////////////////////////////////////////////////// + +QString valueTypeString(::profiler::DataType _dataType); +QString valueTypeString(const ::profiler::ArbitraryValue& _serializedValue); +QString valueString(const ::profiler::ArbitraryValue& _serializedValue); +double value2real(const ::profiler::ArbitraryValue& _serializedValue, int _index = 0); + +////////////////////////////////////////////////////////////////////////// + +} // END of namespace profiler_gui. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_GUI_COMMON_FUNCTIONS_H diff --git a/3rdparty/easyprofiler/profiler_gui/common_types.h b/3rdparty/easyprofiler/profiler_gui/common_types.h new file mode 100644 index 0000000..c272703 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/common_types.h @@ -0,0 +1,205 @@ +/************************************************************************ +* file name : common_types.h +* ----------------- : +* creation time : 2016/07/31 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of common types for both GraphicsView +* : and TreeWidget. +* ----------------- : +* change log : * 2016/07/31 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER__GUI_COMMON_TYPES_H +#define EASY_PROFILER__GUI_COMMON_TYPES_H + +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler_gui { + +#define EASY_GRAPHICS_ITEM_RECURSIVE_PAINT +//#undef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + +#pragma pack(push, 1) +struct EasyBlockItem Q_DECL_FINAL +{ + qreal x; ///< x coordinate of the item (this is made qreal=double to avoid mistakes on very wide scene) + float w; ///< Width of the item + ::profiler::block_index_t block; ///< Index of profiler block + +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + ::profiler::block_index_t neighbours; ///< Number of neighbours (parent.children.size()) + uint32_t children_begin; ///< Index of first child item on the next sublevel + int8_t state; ///< 0 = no change, 1 = paint, -1 = do not paint +#else + ::profiler::block_index_t max_depth_child; ///< Index of child with maximum tree depth + uint32_t children_begin; ///< Index of first child item on the next sublevel +#endif + + // Possible optimizations: + // 1) We can save 1 more byte per block if we will use char instead of short + real time calculations for "totalHeight" var; + // 2) We can save 12 bytes per block if "x" and "w" vars will be removed (all this information exist inside BlocksTree), + // but this requires runtime x-coodinate calculation because BlocksTree has x value in nanoseconds. + + inline void setPos(qreal _x, float _w) { x = _x; w = _w; } + inline qreal left() const { return x; } + inline qreal right() const { return x + w; } + inline float width() const { return w; } + +}; // END of struct EasyBlockItem. + +//#define EASY_TREE_WIDGET__USE_VECTOR +struct EasyBlock Q_DECL_FINAL +{ + ::profiler::BlocksTree tree; +#ifdef EASY_TREE_WIDGET__USE_VECTOR + uint32_t tree_item; +#endif + uint32_t graphics_item_index; + uint8_t graphics_item_level; + uint8_t graphics_item; + bool expanded; + + EasyBlock() = default; + + EasyBlock(EasyBlock&& that) + : tree(::std::move(that.tree)) +#ifdef EASY_TREE_WIDGET__USE_VECTOR + , tree_item(that.tree_item) +#endif + , graphics_item_index(that.graphics_item_index) + , graphics_item_level(that.graphics_item_level) + , graphics_item(that.graphics_item) + , expanded(that.expanded) + { + } + +private: + + EasyBlock(const EasyBlock&) = delete; +}; +#pragma pack(pop) + +typedef ::std::vector EasyItems; +typedef ::std::vector EasyBlocks; + +////////////////////////////////////////////////////////////////////////// + +struct EasySelectedBlock Q_DECL_FINAL +{ + const ::profiler::BlocksTreeRoot* root; + ::profiler::block_index_t tree; + + EasySelectedBlock() : root(nullptr), tree(0xffffffff) + { + } + + EasySelectedBlock(const ::profiler::BlocksTreeRoot* _root, const ::profiler::block_index_t _tree) + : root(_root) + , tree(_tree) + { + } + +}; // END of struct EasySelectedBlock. + +typedef ::std::vector TreeBlocks; + +////////////////////////////////////////////////////////////////////////// + +enum TimeUnits : int8_t +{ + TimeUnits_ms = 0, + TimeUnits_us, + TimeUnits_ns, + TimeUnits_auto + +}; // END of enum TimeUnits. + +////////////////////////////////////////////////////////////////////////// + +} // END of namespace profiler_gui. + +template +struct Overload +{ + template + static EASY_CONSTEXPR_FCN auto of(TReturn (TClass::*method)(Args...)) -> decltype(method) + { + return method; + } + + template + static EASY_CONSTEXPR_FCN auto of(TReturn (*func)(Args...)) -> decltype(func) + { + return func; + } +}; + +template <> +struct Overload +{ + template + static EASY_CONSTEXPR_FCN auto of(TReturn (TClass::*method)()) -> decltype(method) + { + return method; + } + + template + static EASY_CONSTEXPR_FCN auto of(TReturn (*func)()) -> decltype(func) + { + return func; + } +}; + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__GUI_COMMON_TYPES_H diff --git a/3rdparty/easyprofiler/profiler_gui/descriptors_tree_widget.cpp b/3rdparty/easyprofiler/profiler_gui/descriptors_tree_widget.cpp new file mode 100644 index 0000000..4d4c813 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/descriptors_tree_widget.cpp @@ -0,0 +1,982 @@ +/************************************************************************ +* file name : descriptors_tree_widget.cpp +* ----------------- : +* creation time : 2016/09/17 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of EasyDescTreeWidget and it's auxiliary classes +* : for displyaing EasyProfiler blocks descriptors tree. +* ----------------- : +* change log : * 2016/09/17 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "descriptors_tree_widget.h" +#include "arbitrary_value_inspector.h" +#include "treeview_first_column_delegate.h" +#include "globals.h" + +#ifdef _WIN32 +#include + +#ifdef __MINGW32__ +#include +#endif + +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +////////////////////////////////////////////////////////////////////////// + +enum DescColumns +{ + DESC_COL_FILE_LINE = 0, + DESC_COL_TYPE, + DESC_COL_NAME, + DESC_COL_STATUS, + + DESC_COL_COLUMNS_NUMBER +}; + +////////////////////////////////////////////////////////////////////////// + +::profiler::EasyBlockStatus nextStatus(::profiler::EasyBlockStatus _status) +{ + switch (_status) + { + case ::profiler::OFF: + return ::profiler::ON; + + case ::profiler::ON: + return ::profiler::FORCE_ON; + + case ::profiler::FORCE_ON: + return ::profiler::OFF_RECURSIVE; + + case ::profiler::OFF_RECURSIVE: + return ::profiler::ON_WITHOUT_CHILDREN; + + case ::profiler::ON_WITHOUT_CHILDREN: + return ::profiler::FORCE_ON_WITHOUT_CHILDREN; + + case ::profiler::FORCE_ON_WITHOUT_CHILDREN: + return ::profiler::OFF; + } + + return ::profiler::OFF; +} + +const char* statusText(::profiler::EasyBlockStatus _status) +{ + switch (_status) + { + case ::profiler::OFF: + return "OFF"; + + case ::profiler::ON: + return "ON"; + + case ::profiler::FORCE_ON: + return "FORCE_ON"; + + case ::profiler::OFF_RECURSIVE: + return "OFF_RECURSIVE"; + + case ::profiler::ON_WITHOUT_CHILDREN: + return "ON_WITHOUT_CHILDREN"; + + case ::profiler::FORCE_ON_WITHOUT_CHILDREN: + return "FORCE_ON_WITHOUT_CHILDREN"; + } + + return ""; +} + +::profiler::color_t statusColor(::profiler::EasyBlockStatus _status) +{ + switch (_status) + { + case ::profiler::OFF: + return ::profiler::colors::Red900; + + case ::profiler::ON: + return ::profiler::colors::LightGreen900; + + case ::profiler::FORCE_ON: + return ::profiler::colors::LightGreen900; + + case ::profiler::OFF_RECURSIVE: + return ::profiler::colors::Red900; + + case ::profiler::ON_WITHOUT_CHILDREN: + return ::profiler::colors::Lime900; + + case ::profiler::FORCE_ON_WITHOUT_CHILDREN: + return ::profiler::colors::Lime900; + } + + return ::profiler::colors::Black; +} + +////////////////////////////////////////////////////////////////////////// + +EasyDescWidgetItem::EasyDescWidgetItem(::profiler::block_id_t _desc, Parent* _parent) + : Parent(_parent, QTreeWidgetItem::UserType) + , m_desc(_desc) + , m_type(EasyDescWidgetItem::Type::File) +{ + +} + +EasyDescWidgetItem::~EasyDescWidgetItem() +{ + +} + +bool EasyDescWidgetItem::operator < (const Parent& _other) const +{ + const auto col = treeWidget()->sortColumn(); + + switch (col) + { + case DESC_COL_FILE_LINE: + { + if (parent() != nullptr) + return data(col, Qt::UserRole).toInt() < _other.data(col, Qt::UserRole).toInt(); + } + } + + return Parent::operator < (_other); +} + +QVariant EasyDescWidgetItem::data(int _column, int _role) const +{ + if (_column == DESC_COL_TYPE) + { + if (_role == Qt::ToolTipRole) + { + switch (m_type) + { + case Type::File: return QStringLiteral("File"); + case Type::Event: return QStringLiteral("Event"); + case Type::Block: return QStringLiteral("Block"); + case Type::Value: return QStringLiteral("Arbitrary Value"); + } + } + else if (_role == Qt::DisplayRole) + { + switch (m_type) + { + case Type::File: return QStringLiteral("F"); + case Type::Event: return QStringLiteral("E"); + case Type::Block: return QStringLiteral("B"); + case Type::Value: return QStringLiteral("V"); + } + } + } + + return QTreeWidgetItem::data(_column, _role); +} + +////////////////////////////////////////////////////////////////////////// + +EasyDescTreeWidget::EasyDescTreeWidget(QWidget* _parent) + : Parent(_parent) + , m_lastFound(nullptr) + , m_lastSearchColumn(-1) + , m_searchColumn(DESC_COL_NAME) + , m_bLocked(false) +{ + setAutoFillBackground(false); + setAlternatingRowColors(true); + setItemsExpandable(true); + setAnimated(true); + setSortingEnabled(false); + setColumnCount(DESC_COL_COLUMNS_NUMBER); + setSelectionBehavior(QAbstractItemView::SelectRows); + + auto header_item = new QTreeWidgetItem(); + header_item->setText(DESC_COL_FILE_LINE, "File/Line"); + header_item->setText(DESC_COL_TYPE, "Type"); + header_item->setText(DESC_COL_NAME, "Name"); + header_item->setText(DESC_COL_STATUS, "Status"); + setHeaderItem(header_item); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blockStatusChanged, this, &This::onBlockStatusChange); + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); + connect(this, &Parent::itemDoubleClicked, this, &This::onDoubleClick); + connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); + + loadSettings(); + + setItemDelegateForColumn(0, new EasyTreeViewFirstColumnItemDelegate(this)); +} + +EasyDescTreeWidget::~EasyDescTreeWidget() +{ + if (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && !::profiler_gui::is_max(EASY_GLOBALS.selected_block_id)) + { + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + emit EASY_GLOBALS.events.refreshRequired(); + } + + saveSettings(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::setSearchColumn(int column) +{ + m_searchColumn = column; +} + +int EasyDescTreeWidget::searchColumn() const +{ + return m_searchColumn; +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::contextMenuEvent(QContextMenuEvent* _event) +{ + _event->accept(); + + QMenu menu; + menu.setToolTipsVisible(true); + auto action = menu.addAction("Expand all"); + action->setIcon(QIcon(imagePath("expand"))); + connect(action, &QAction::triggered, this, &This::expandAll); + + action = menu.addAction("Collapse all"); + action->setIcon(QIcon(imagePath("collapse"))); + connect(action, &QAction::triggered, this, &This::collapseAll); + + auto item = currentItem(); + if (item != nullptr && item->parent() != nullptr && currentColumn() >= DESC_COL_TYPE) + { + const auto& desc = easyDescriptor(static_cast(item)->desc()); + + menu.addSeparator(); + auto submenu = menu.addMenu("Change status"); + submenu->setToolTipsVisible(true); + +#define ADD_STATUS_ACTION(NameValue, StatusValue, ToolTipValue)\ + action = submenu->addAction(NameValue);\ + action->setCheckable(true);\ + action->setChecked(desc.status() == StatusValue);\ + action->setData(static_cast(StatusValue));\ + action->setToolTip(ToolTipValue);\ + connect(action, &QAction::triggered, this, &This::onBlockStatusChangeClicked) + + ADD_STATUS_ACTION("Off", ::profiler::OFF, "Do not profile this block."); + ADD_STATUS_ACTION("On", ::profiler::ON, "Profile this block\nif parent enabled children."); + ADD_STATUS_ACTION("Force-On", ::profiler::FORCE_ON, "Always profile this block even\nif it's parent disabled children."); + ADD_STATUS_ACTION("Off-recursive", ::profiler::OFF_RECURSIVE, "Do not profile neither this block\nnor it's children."); + ADD_STATUS_ACTION("On-without-children", ::profiler::ON_WITHOUT_CHILDREN, "Profile this block, but\ndo not profile it's children."); + ADD_STATUS_ACTION("Force-On-without-children", ::profiler::FORCE_ON_WITHOUT_CHILDREN, "Always profile this block, but\ndo not profile it's children."); +#undef ADD_STATUS_ACTION + + submenu->setEnabled(EASY_GLOBALS.connected); + if (!EASY_GLOBALS.connected) + submenu->setTitle(QString("%1 (connection needed)").arg(submenu->title())); + } + + menu.exec(QCursor::pos()); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::clearSilent(bool _global) +{ + const QSignalBlocker b(this); + + setSortingEnabled(false); + m_lastFound = nullptr; + m_lastSearch.clear(); + + m_highlightItems.clear(); + m_items.clear(); + + ::std::vector topLevelItems; + topLevelItems.reserve(topLevelItemCount()); + for (int i = topLevelItemCount() - 1; i >= 0; --i) + { + const bool expanded = !_global && topLevelItem(i)->isExpanded(); + auto item = takeTopLevelItem(i); + if (expanded) + m_expandedFilesTemp.insert(item->text(DESC_COL_FILE_LINE).toStdString()); + topLevelItems.push_back(item); + } + + auto deleter_thread = ::std::thread([](decltype(topLevelItems) _items) + { +#ifdef _WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); +#endif + + for (auto item : _items) + delete item; + + }, ::std::move(topLevelItems)); + + deleter_thread.detach(); + + //clear(); +} + +////////////////////////////////////////////////////////////////////////// + +struct FileItems +{ + using Items = ::std::unordered_map >; + Items children; + QTreeWidgetItem* item = nullptr; +}; + +void EasyDescTreeWidget::build() +{ + auto f = font(); + f.setBold(true); + + typedef ::std::unordered_map<::std::string, FileItems> Files; + Files fileItems; + + m_items.resize(EASY_GLOBALS.descriptors.size()); + memset(m_items.data(), 0, sizeof(void*) * m_items.size()); + + const QSignalBlocker b(this); + ::profiler::block_id_t id = 0; + for (auto desc : EASY_GLOBALS.descriptors) + { + if (desc != nullptr) + { + auto& p = fileItems[desc->file()]; + if (p.item == nullptr) + { + auto item = new EasyDescWidgetItem(0); + item->setText(DESC_COL_FILE_LINE, QString(desc->file()).remove(QRegExp("^(\\.{2}\\\\+|\\/+)+"))); + item->setType(EasyDescWidgetItem::Type::File); + p.item = item; + } + + auto it = p.children.find(desc->line()); + if (it == p.children.end()) + { + auto item = new EasyDescWidgetItem(desc->id(), p.item); + item->setText(DESC_COL_FILE_LINE, QString::number(desc->line())); + item->setData(DESC_COL_FILE_LINE, Qt::UserRole, desc->line()); + item->setText(DESC_COL_NAME, desc->name()); + + switch (desc->type()) + { + case ::profiler::BlockType::Block: + item->setType(EasyDescWidgetItem::Type::Block); + break; + + case ::profiler::BlockType::Event: + item->setType(EasyDescWidgetItem::Type::Event); + break; + + case ::profiler::BlockType::Value: + item->setType(EasyDescWidgetItem::Type::Value); + break; + } + + item->setFont(DESC_COL_STATUS, f); + item->setText(DESC_COL_STATUS, statusText(desc->status())); + item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc->status()))); + + m_items[id] = item; + p.children.insert(::std::make_pair(desc->line(), item)); + } + else + { + m_items[id] = it->second; + } + } + + ++id; + } + + for (auto& p : fileItems) + { + addTopLevelItem(p.second.item); + if (m_expandedFilesTemp.find(p.first) != m_expandedFilesTemp.end()) + p.second.item->setExpanded(true); + } + + m_expandedFilesTemp.clear(); + setSortingEnabled(true); + sortByColumn(DESC_COL_FILE_LINE, Qt::AscendingOrder); + resizeColumnsToContents(); + QTimer::singleShot(100, [this](){ onSelectedBlockChange(EASY_GLOBALS.selected_block); }); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::onItemExpand(QTreeWidgetItem*) +{ + resizeColumnsToContents(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::onDoubleClick(QTreeWidgetItem* _item, int _column) +{ + if (!EASY_GLOBALS.connected) + return; + + if (_column >= DESC_COL_TYPE && _item->parent() != nullptr) + { + auto item = static_cast(_item); + auto& desc = easyDescriptor(item->desc()); + desc.setStatus(nextStatus(desc.status())); + + item->setText(DESC_COL_STATUS, statusText(desc.status())); + item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc.status()))); + + m_bLocked = true; + emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status()); + m_bLocked = false; + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _prev) +{ + if (_prev != nullptr) + { + auto f = font(); + for (int i = 0; i < DESC_COL_STATUS; ++i) + _prev->setFont(i, f); + } + + if (_item != nullptr) + { + auto f = font(); + f.setBold(true); + for (int i = 0; i < DESC_COL_STATUS; ++i) + _item->setFont(i, f); + + if (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && _item->parent() != nullptr) + { + const auto id = static_cast(_item)->desc(); + if (EASY_GLOBALS.selected_block_id != id) + { + EASY_GLOBALS.selected_block_id = id; + emit EASY_GLOBALS.events.selectedBlockIdChanged(id); + } + } + } + else if (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && !::profiler_gui::is_max(EASY_GLOBALS.selected_block_id)) + { + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + emit EASY_GLOBALS.events.selectedBlockIdChanged(EASY_GLOBALS.selected_block_id); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::onBlockStatusChangeClicked(bool _checked) +{ + if (!_checked || !EASY_GLOBALS.connected) + return; + + auto item = currentItem(); + if (item == nullptr || item->parent() == nullptr) + return; + + auto action = qobject_cast(sender()); + if (action != nullptr) + { + auto& desc = easyDescriptor(static_cast(item)->desc()); + desc.setStatus(static_cast<::profiler::EasyBlockStatus>(action->data().toUInt())); + item->setText(DESC_COL_STATUS, statusText(desc.status())); + item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc.status()))); + + m_bLocked = true; + emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status()); + m_bLocked = false; + } +} + +void EasyDescTreeWidget::onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status) +{ + if (m_bLocked) + return; + + auto item = m_items[_id]; + if (item == nullptr) + return; + + auto& desc = easyDescriptor(item->desc()); + item->setText(DESC_COL_STATUS, statusText(desc.status())); + item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc.status()))); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::resizeColumnsToContents() +{ + for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) + resizeColumnToContents(i); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::onSelectedBlockChange(uint32_t _block_index) +{ + if (::profiler_gui::is_max(_block_index)) + { + setCurrentItem(nullptr); + return; + } + + auto item = m_items[easyBlocksTree(_block_index).node->id()]; + if (item == nullptr) + return; + + scrollToItem(item, QAbstractItemView::PositionAtCenter); + setCurrentItem(item); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyDescTreeWidget::resetHighlight() +{ + for (auto item : m_highlightItems) { + for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) + item->setBackground(i, Qt::NoBrush); + } + m_highlightItems.clear(); +} + +void EasyDescTreeWidget::loadSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("desc_tree_widget"); + + auto val = settings.value("searchColumn"); + if (!val.isNull()) + m_searchColumn = val.toInt(); + + settings.endGroup(); +} + +void EasyDescTreeWidget::saveSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("desc_tree_widget"); + + settings.setValue("searchColumn", m_searchColumn); + + settings.endGroup(); +} + +////////////////////////////////////////////////////////////////////////// + +int EasyDescTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags) +{ + if (_str.isEmpty()) + { + resetHighlight(); + m_lastSearchColumn = m_searchColumn; + return 0; + } + + const bool isNewSearch = (m_lastSearchColumn != m_searchColumn || m_lastSearch != _str); + auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, m_searchColumn); + + if (!isNewSearch) + { + if (!itemsList.empty()) + { + bool stop = false; + decltype(m_lastFound) next = nullptr; + for (auto item : itemsList) + { + if (stop) + { + next = item; + break; + } + + stop = item == m_lastFound; + } + + m_lastFound = next == nullptr ? itemsList.front() : next; + } + else + { + m_lastFound = nullptr; + } + } + else + { + resetHighlight(); + + m_lastSearchColumn = m_searchColumn; + m_lastSearch = _str; + m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; + + for (auto item : itemsList) + { + m_highlightItems.push_back(item); + for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) + item->setBackgroundColor(i, QColor::fromRgba(0x80000000 | (0x00ffffff & ::profiler::colors::Yellow))); + } + } + + if (m_lastFound != nullptr) + { + scrollToItem(m_lastFound, QAbstractItemView::PositionAtCenter); + setCurrentItem(m_lastFound); + } + + return itemsList.size(); +} + +int EasyDescTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags) +{ + if (_str.isEmpty()) + { + resetHighlight(); + m_lastSearchColumn = m_searchColumn; + return 0; + } + + const bool isNewSearch = (m_lastSearchColumn != m_searchColumn || m_lastSearch != _str); + auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, m_searchColumn); + + if (!isNewSearch) + { + if (!itemsList.empty()) + { + decltype(m_lastFound) prev = nullptr; + for (auto item : itemsList) + { + if (item == m_lastFound) + break; + + prev = item; + } + + m_lastFound = prev == nullptr ? itemsList.back() : prev; + } + else + { + m_lastFound = nullptr; + } + } + else + { + resetHighlight(); + + m_lastSearchColumn = m_searchColumn; + m_lastSearch = _str; + m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr; + + m_highlightItems.reserve(itemsList.size()); + for (auto item : itemsList) + { + m_highlightItems.push_back(item); + for (int i = 0; i < DESC_COL_COLUMNS_NUMBER; ++i) + item->setBackgroundColor(i, QColor::fromRgba(0x80000000 | (0x00ffffff & ::profiler::colors::Yellow))); + } + } + + if (m_lastFound != nullptr) + { + scrollToItem(m_lastFound, QAbstractItemView::PositionAtCenter); + setCurrentItem(m_lastFound); + } + + return itemsList.size(); +} + +////////////////////////////////////////////////////////////////////////// + +EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) + , m_tree(new EasyDescTreeWidget(this)) + , m_values(new EasyArbitraryValuesWidget(this)) + , m_searchBox(new QLineEdit(this)) + , m_foundNumber(new QLabel("Found 0 matches", this)) + , m_searchButton(nullptr) + , m_bCaseSensitiveSearch(false) +{ + loadSettings(); + + m_searchBox->setFixedWidth(300); + m_searchBox->setContentsMargins(5, 0, 0, 0); + + auto tb = new QToolBar(this); + tb->setIconSize(::profiler_gui::ICONS_SIZE); + auto refreshButton = tb->addAction(QIcon(imagePath("reload")), tr("Refresh blocks list")); + refreshButton->setEnabled(EASY_GLOBALS.connected); + refreshButton->setToolTip(tr("Refresh blocks list.\nConnection needed.")); + connect(refreshButton, &QAction::triggered, &EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blocksRefreshRequired); + + + + auto menu = new QMenu(this); + m_searchButton = menu->menuAction(); + m_searchButton->setText("Find next"); + m_searchButton->setIcon(QIcon(imagePath("find-next"))); + m_searchButton->setData(true); + connect(m_searchButton, &QAction::triggered, this, &This::findNext); + + auto actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + auto a = new QAction(tr("Find next"), actionGroup); + a->setCheckable(true); + a->setChecked(true); + connect(a, &QAction::triggered, this, &This::findNextFromMenu); + menu->addAction(a); + + a = new QAction(tr("Find previous"), actionGroup); + a->setCheckable(true); + connect(a, &QAction::triggered, this, &This::findPrevFromMenu); + menu->addAction(a); + + a = menu->addAction("Case sensitive"); + a->setCheckable(true); + a->setChecked(m_bCaseSensitiveSearch); + connect(a, &QAction::triggered, [this](bool _checked){ m_bCaseSensitiveSearch = _checked; }); + menu->addAction(a); + + menu->addSeparator(); + auto headerItem = m_tree->headerItem(); + actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + for (int i = 0; i < DESC_COL_STATUS; ++i) + { + if (i == DESC_COL_TYPE) + continue; + + a = new QAction(QStringLiteral("Search by ") + headerItem->text(i), actionGroup); + a->setData(i); + a->setCheckable(true); + if (i == m_tree->searchColumn()) + a->setChecked(true); + connect(a, &QAction::triggered, this, &This::onSearchColumnChange); + + menu->addAction(a); + } + + tb->addSeparator(); + tb->addAction(m_searchButton); + tb->addWidget(m_searchBox); + + auto searchbox = new QHBoxLayout(); + searchbox->setContentsMargins(0, 0, 5, 0); + searchbox->addWidget(tb); + searchbox->addStretch(100); + searchbox->addWidget(m_foundNumber, Qt::AlignRight); + + auto lay = new QVBoxLayout(this); + lay->setContentsMargins(1, 1, 1, 1); + lay->addLayout(searchbox); + lay->addWidget(m_tree); + lay->addWidget(m_values); + + connect(m_searchBox, &QLineEdit::returnPressed, this, &This::onSeachBoxReturnPressed); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::connectionChanged, refreshButton, &QAction::setEnabled); +} + +EasyDescWidget::~EasyDescWidget() +{ + saveSettings(); +} + +void EasyDescWidget::loadSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("EasyDescWidget"); + + auto val = settings.value("case_sensitive"); + if (!val.isNull()) + m_bCaseSensitiveSearch = val.toBool(); + + settings.endGroup(); +} + +void EasyDescWidget::saveSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("EasyDescWidget"); + settings.setValue("case_sensitive", m_bCaseSensitiveSearch); + settings.endGroup(); +} + +void EasyDescWidget::keyPressEvent(QKeyEvent* _event) +{ + if (_event->key() == Qt::Key_F3) + { + if (_event->modifiers() & Qt::ShiftModifier) + findPrev(true); + else + findNext(true); + } + + _event->accept(); +} + +void EasyDescWidget::contextMenuEvent(QContextMenuEvent* _event) +{ + m_tree->contextMenuEvent(_event); +} + +void EasyDescWidget::build() +{ + m_tree->clearSilent(false); + m_foundNumber->setText(QString("Found 0 matches")); + m_tree->build(); + m_values->rebuild(); +} + +void EasyDescWidget::clear() +{ + m_tree->clearSilent(true); + m_foundNumber->setText(QString("Found 0 matches")); + m_values->clear(); +} + +void EasyDescWidget::onSeachBoxReturnPressed() +{ + if (m_searchButton->data().toBool() == true) + findNext(true); + else + findPrev(true); +} + +void EasyDescWidget::onSearchColumnChange(bool) +{ + auto action = qobject_cast(sender()); + if (action != nullptr) + m_tree->setSearchColumn(action->data().toInt()); +} + +void EasyDescWidget::findNext(bool) +{ + auto matches = m_tree->findNext(m_searchBox->text(), m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); + + if (matches == 1) + m_foundNumber->setText(QString("Found 1 match")); + else + m_foundNumber->setText(QString("Found %1 matches").arg(matches)); +} + +void EasyDescWidget::findPrev(bool) +{ + auto matches = m_tree->findPrev(m_searchBox->text(), m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags()); + + if (matches == 1) + m_foundNumber->setText(QString("Found 1 match")); + else + m_foundNumber->setText(QString("Found %1 matches").arg(matches)); +} + +void EasyDescWidget::findNextFromMenu(bool _checked) +{ + if (!_checked) + return; + + if (m_searchButton->data().toBool() == false) + { + m_searchButton->setData(true); + m_searchButton->setText(tr("Find next")); + m_searchButton->setIcon(QIcon(imagePath("find-next"))); + disconnect(m_searchButton, &QAction::triggered, this, &This::findPrev); + connect(m_searchButton, &QAction::triggered, this, &This::findNext); + } + + findNext(true); +} + +void EasyDescWidget::findPrevFromMenu(bool _checked) +{ + if (!_checked) + return; + + if (m_searchButton->data().toBool() == true) + { + m_searchButton->setData(false); + m_searchButton->setText(tr("Find prev")); + m_searchButton->setIcon(QIcon(imagePath("find-prev"))); + disconnect(m_searchButton, &QAction::triggered, this, &This::findNext); + connect(m_searchButton, &QAction::triggered, this, &This::findPrev); + } + + findPrev(true); +} + +////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/easyprofiler/profiler_gui/descriptors_tree_widget.h b/3rdparty/easyprofiler/profiler_gui/descriptors_tree_widget.h new file mode 100644 index 0000000..3a00d67 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/descriptors_tree_widget.h @@ -0,0 +1,234 @@ +/************************************************************************ +* file name : descriptors_tree_widget.h +* ----------------- : +* creation time : 2016/09/17 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of EasyDescTreeWidget and it's auxiliary classes +* : for displyaing EasyProfiler blocks descriptors tree. +* ----------------- : +* change log : * 2016/09/17 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_DESCRIPTORS_WIDGET_H +#define EASY_DESCRIPTORS_WIDGET_H + +#include +#include +#include + +#include +#include + +#include + +////////////////////////////////////////////////////////////////////////// + +class EasyDescWidgetItem : public QTreeWidgetItem +{ + using Parent = QTreeWidgetItem; + using This = EasyDescWidgetItem; + +public: + + enum class Type : uint8_t + { + File, + Event, + Block, + Value + }; + +private: + + ::profiler::block_id_t m_desc; + Type m_type; + +public: + + explicit EasyDescWidgetItem(::profiler::block_id_t _desc, Parent* _parent = nullptr); + virtual ~EasyDescWidgetItem(); + + bool operator < (const Parent& _other) const override; + QVariant data(int _column, int _role) const override; + +public: + + // Public inline methods + + inline ::profiler::block_id_t desc() const + { + return m_desc; + } + + inline void setType(Type _type) + { + m_type = _type; + } + +}; // END of class EasyDescWidgetItem. + +////////////////////////////////////////////////////////////////////////// + +class EasyDescTreeWidget : public QTreeWidget +{ + Q_OBJECT + + typedef QTreeWidget Parent; + typedef EasyDescTreeWidget This; + + typedef ::std::vector Items; + typedef ::std::vector TreeItems; + typedef ::std::unordered_set<::std::string> ExpandedFiles; + +protected: + + ExpandedFiles m_expandedFilesTemp; + Items m_items; + TreeItems m_highlightItems; + QString m_lastSearch; + QTreeWidgetItem* m_lastFound; + int m_lastSearchColumn; + int m_searchColumn; + bool m_bLocked; + +public: + + // Public virtual methods + + explicit EasyDescTreeWidget(QWidget* _parent = nullptr); + virtual ~EasyDescTreeWidget(); + void contextMenuEvent(QContextMenuEvent* _event) override; + +public: + + // Public non-virtual methods + + int findNext(const QString& _str, Qt::MatchFlags _flags); + int findPrev(const QString& _str, Qt::MatchFlags _flags); + void setSearchColumn(int column); + int searchColumn() const; + +public slots: + + void clearSilent(bool _global = false); + void build(); + +private slots: + + void onBlockStatusChangeClicked(bool); + void onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _prev); + void onItemExpand(QTreeWidgetItem* _item); + void onDoubleClick(QTreeWidgetItem* _item, int _column); + void onSelectedBlockChange(uint32_t _block_index); + void onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status); + void resizeColumnsToContents(); + +private: + + // Private methods + + void resetHighlight(); + void loadSettings(); + void saveSettings(); + +}; // END of class EasyDescTreeWidget. + +////////////////////////////////////////////////////////////////////////// + +class EasyDescWidget : public QWidget +{ + Q_OBJECT + + typedef QWidget Parent; + typedef EasyDescWidget This; + +private: + + EasyDescTreeWidget* m_tree; + class EasyArbitraryValuesWidget* m_values; + class QLineEdit* m_searchBox; + class QLabel* m_foundNumber; + class QAction* m_searchButton; + bool m_bCaseSensitiveSearch; + +public: + + // Public virtual methods + + explicit EasyDescWidget(QWidget* _parent = nullptr); + virtual ~EasyDescWidget(); + void keyPressEvent(QKeyEvent* _event) override; + void contextMenuEvent(QContextMenuEvent* _event) override; + +public: + + // Public non-virtual methods + + void build(); + void clear(); + +private slots: + + void onSeachBoxReturnPressed(); + void findNext(bool); + void findPrev(bool); + void findNextFromMenu(bool); + void findPrevFromMenu(bool); + void onSearchColumnChange(bool); + +private: + + // Private non-virtual slots + + void loadSettings(); + void saveSettings(); + +}; // END of class EasyDescWidget. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_DESCRIPTORS_WIDGET_H diff --git a/3rdparty/easyprofiler/profiler_gui/easy_chronometer_item.cpp b/3rdparty/easyprofiler/profiler_gui/easy_chronometer_item.cpp new file mode 100644 index 0000000..357833e --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_chronometer_item.cpp @@ -0,0 +1,396 @@ +/************************************************************************ +* file name : easy_chronometer_item.cpp +* ----------------- : +* creation time : 2016/09/15 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of EasyChronometerItem. +* ----------------- : +* change log : * 2016/09/15 Victor Zarubkin: moved sources from blocks_graphics_view.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include "blocks_graphics_view.h" +#include "easy_chronometer_item.h" +#include "globals.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +////////////////////////////////////////////////////////////////////////// + +EasyChronometerItem::EasyChronometerItem(bool _main) + : Parent() + , m_color(::profiler_gui::CHRONOMETER_COLOR) + , m_left(0) + , m_right(0) + , m_bMain(_main) + , m_bReverse(false) + , m_bHoverIndicator(false) + , m_bHoverLeftBorder(false) + , m_bHoverRightBorder(false) +{ + m_indicator.reserve(3); +} + +EasyChronometerItem::~EasyChronometerItem() +{ +} + +QRectF EasyChronometerItem::boundingRect() const +{ + return m_boundingRect; +} + +void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + auto const sceneView = view(); + const auto currentScale = sceneView->scale(); + const auto offset = sceneView->offset(); + const auto visibleSceneRect = sceneView->visibleSceneRect(); + auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale; + + if (m_bMain) + m_indicator.clear(); + + if (m_left > sceneRight || m_right < sceneLeft) + { + // This item is out of screen + + if (m_bMain) + { + const int size = m_bHoverIndicator ? 12 : 10; + auto vcenter = visibleSceneRect.top() + visibleSceneRect.height() * 0.5; + auto color = QColor::fromRgb(m_color.rgb()); + auto pen = _painter->pen(); + pen.setColor(color); + + m_indicator.clear(); + if (m_left > sceneRight) + { + sceneRight = (sceneRight - offset) * currentScale; + m_indicator.push_back(QPointF(sceneRight - size, vcenter - size)); + m_indicator.push_back(QPointF(sceneRight, vcenter)); + m_indicator.push_back(QPointF(sceneRight - size, vcenter + size)); + } + else + { + sceneLeft = (sceneLeft - offset) * currentScale; + m_indicator.push_back(QPointF(sceneLeft + size, vcenter - size)); + m_indicator.push_back(QPointF(sceneLeft, vcenter)); + m_indicator.push_back(QPointF(sceneLeft + size, vcenter + size)); + } + + _painter->save(); + _painter->setTransform(QTransform::fromTranslate(-x(), -y()), true); + _painter->setBrush(m_bHoverIndicator ? QColor::fromRgb(0xffff0000) : color); + _painter->setPen(pen); + _painter->drawPolygon(m_indicator); + _painter->restore(); + } + + return; + } + + auto selectedInterval = width(); + QRectF rect((m_left - offset) * currentScale, visibleSceneRect.top(), ::std::max(selectedInterval * currentScale, 1.0), visibleSceneRect.height()); + selectedInterval = units2microseconds(selectedInterval); + + const QString text = ::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, selectedInterval); // Displayed text + const auto textRect = QFontMetricsF(EASY_GLOBALS.chronometer_font, sceneView).boundingRect(text); // Calculate displayed text boundingRect + const auto rgb = m_color.rgb() & 0x00ffffff; + + + + // Paint!-------------------------- + _painter->save(); + + // instead of scrollbar we're using manual offset + _painter->setTransform(QTransform::fromTranslate(-x(), -y()), true); + + if (m_left < sceneLeft) + rect.setLeft(0); + + if (m_right > sceneRight) + rect.setWidth((sceneRight - offset) * currentScale - rect.left()); + + // draw transparent rectangle + auto vcenter = rect.top() + rect.height() * 0.5; + QLinearGradient g(rect.left(), vcenter, rect.right(), vcenter); + g.setColorAt(0, m_color); + g.setColorAt(0.2, QColor::fromRgba(0x14000000 | rgb)); + g.setColorAt(0.8, QColor::fromRgba(0x14000000 | rgb)); + g.setColorAt(1, m_color); + _painter->setBrush(g); + _painter->setPen(Qt::NoPen); + _painter->drawRect(rect); + + // draw left and right borders + _painter->setBrush(Qt::NoBrush); + if (m_bMain && !m_bReverse) + { + QPen p(QColor::fromRgba(0xd0000000 | rgb)); + p.setStyle(Qt::DotLine); + _painter->setPen(p); + } + else + { + _painter->setPen(QColor::fromRgba(0xd0000000 | rgb)); + } + + if (m_left > sceneLeft) + { + if (m_bHoverLeftBorder) + { + // Set bold if border is hovered + QPen p = _painter->pen(); + p.setWidth(3); + _painter->setPen(p); + } + + _painter->drawLine(QPointF(rect.left(), rect.top()), QPointF(rect.left(), rect.bottom())); + } + + if (m_right < sceneRight) + { + if (m_bHoverLeftBorder) + { + // Restore width + QPen p = _painter->pen(); + p.setWidth(1); + _painter->setPen(p); + } + else if (m_bHoverRightBorder) + { + // Set bold if border is hovered + QPen p = _painter->pen(); + p.setWidth(3); + _painter->setPen(p); + } + + _painter->drawLine(QPointF(rect.right(), rect.top()), QPointF(rect.right(), rect.bottom())); + + // This is not necessary because another setPen() invoked for draw text + //if (m_bHoverRightBorder) + //{ + // // Restore width + // QPen p = _painter->pen(); + // p.setWidth(1); + // _painter->setPen(p); + //} + } + + // draw text + _painter->setCompositionMode(QPainter::CompositionMode_Difference); // This lets the text to be visible on every background + _painter->setRenderHint(QPainter::TextAntialiasing); + _painter->setPen(0x00ffffff - rgb); + _painter->setFont(EASY_GLOBALS.chronometer_font); + + int textFlags = 0; + switch (EASY_GLOBALS.chrono_text_position) + { + case ::profiler_gui::ChronoTextPosition_Top: + textFlags = Qt::AlignTop | Qt::AlignHCenter; + if (!m_bMain) rect.setTop(rect.top() + textRect.height() * 0.75); + break; + + case ::profiler_gui::ChronoTextPosition_Center: + textFlags = Qt::AlignCenter; + if (!m_bMain) rect.setTop(rect.top() + textRect.height() * 1.5); + break; + + case ::profiler_gui::ChronoTextPosition_Bottom: + textFlags = Qt::AlignBottom | Qt::AlignHCenter; + if (!m_bMain) rect.setHeight(rect.height() - textRect.height() * 0.75); + break; + } + + const auto textRect_width = textRect.width() * ::profiler_gui::FONT_METRICS_FACTOR; + if (textRect_width < rect.width()) + { + // Text will be drawed inside rectangle + _painter->drawText(rect, textFlags, text); + _painter->restore(); + return; + } + + const auto w = textRect_width / currentScale; + if (m_right + w < sceneRight) + { + // Text will be drawed to the right of rectangle + rect.translate(rect.width(), 0); + textFlags &= ~Qt::AlignHCenter; + textFlags |= Qt::AlignLeft; + } + else if (m_left - w > sceneLeft) + { + // Text will be drawed to the left of rectangle + rect.translate(-rect.width(), 0); + textFlags &= ~Qt::AlignHCenter; + textFlags |= Qt::AlignRight; + } + //else // Text will be drawed inside rectangle + + _painter->drawText(rect, textFlags | Qt::TextDontClip, text); + + _painter->restore(); + // END Paint!~~~~~~~~~~~~~~~~~~~~~~ +} + +void EasyChronometerItem::hide() +{ + m_bHoverIndicator = false; + m_bHoverLeftBorder = false; + m_bHoverRightBorder = false; + m_bReverse = false; + Parent::hide(); +} + +bool EasyChronometerItem::indicatorContains(const QPointF& _pos) const +{ + if (m_indicator.empty()) + return false; + + const auto itemX = toItem(_pos.x()); + return m_indicator.containsPoint(QPointF(itemX, _pos.y()), Qt::OddEvenFill); +} + +void EasyChronometerItem::setHoverLeft(bool _hover) +{ + m_bHoverLeftBorder = _hover; +} + +void EasyChronometerItem::setHoverRight(bool _hover) +{ + m_bHoverRightBorder = _hover; +} + +bool EasyChronometerItem::hoverLeft(qreal _x) const +{ + const auto dx = fabs(_x - m_left) * view()->scale(); + return dx < 4; +} + +bool EasyChronometerItem::hoverRight(qreal _x) const +{ + const auto dx = fabs(_x - m_right) * view()->scale(); + return dx < 4; +} + +QPointF EasyChronometerItem::toItem(const QPointF& _pos) const +{ + const auto sceneView = view(); + return QPointF((_pos.x() - sceneView->offset()) * sceneView->scale() - x(), _pos.y()); +} + +qreal EasyChronometerItem::toItem(qreal _x) const +{ + const auto sceneView = view(); + return (_x - sceneView->offset()) * sceneView->scale() - x(); +} + +void EasyChronometerItem::setColor(const QColor& _color) +{ + m_color = _color; +} + +void EasyChronometerItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) +{ + m_boundingRect.setRect(x, y, w, h); +} + +void EasyChronometerItem::setBoundingRect(const QRectF& _rect) +{ + m_boundingRect = _rect; +} + +void EasyChronometerItem::setLeftRight(qreal _left, qreal _right) +{ + if (_left < _right) + { + m_left = _left; + m_right = _right; + } + else + { + m_left = _right; + m_right = _left; + } +} + +void EasyChronometerItem::setReverse(bool _reverse) +{ + m_bReverse = _reverse; +} + +void EasyChronometerItem::setHoverIndicator(bool _hover) +{ + m_bHoverIndicator = _hover; +} + +const EasyGraphicsView* EasyChronometerItem::view() const +{ + return static_cast(scene()->parent()); +} + +EasyGraphicsView* EasyChronometerItem::view() +{ + return static_cast(scene()->parent()); +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/profiler_gui/easy_chronometer_item.h b/3rdparty/easyprofiler/profiler_gui/easy_chronometer_item.h new file mode 100644 index 0000000..1c20865 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_chronometer_item.h @@ -0,0 +1,171 @@ +/************************************************************************ +* file name : easy_chronometer_item.h +* ----------------- : +* creation time : 2016/09/15 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of EasyChronometerItem - an item +* : used to display selected interval on graphics scene. +* ----------------- : +* change log : * 2016/09/15 Victor Zarubkin: moved sources from blocks_graphics_view.h +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_CHRONOMETER_ITEM_H +#define EASY_CHRONOMETER_ITEM_H + +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +class QWidget; +class QPainter; +class QStyleOptionGraphicsItem; +class EasyGraphicsView; + +class EasyChronometerItem : public QGraphicsItem +{ + typedef QGraphicsItem Parent; + typedef EasyChronometerItem This; + + QPolygonF m_indicator; ///< Indicator displayed when this chrono item is out of screen (displaying only for main item) + QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem) + QColor m_color; ///< Color of the item + qreal m_left, m_right; ///< Left and right bounds of the selection zone + bool m_bMain; ///< Is this chronometer main (true, by default) + bool m_bReverse; ///< + bool m_bHoverIndicator; ///< Mouse hover above indicator + bool m_bHoverLeftBorder; + bool m_bHoverRightBorder; + +public: + + explicit EasyChronometerItem(bool _main = true); + virtual ~EasyChronometerItem(); + + // Public virtual methods + + QRectF boundingRect() const override; + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + +public: + + // Public non-virtual methods + + void hide(); + + void setColor(const QColor& _color); + + void setBoundingRect(qreal x, qreal y, qreal w, qreal h); + void setBoundingRect(const QRectF& _rect); + + void setLeftRight(qreal _left, qreal _right); + + void setReverse(bool _reverse); + + void setHoverIndicator(bool _hover); + + bool indicatorContains(const QPointF& _pos) const; + + void setHoverLeft(bool _hover); + void setHoverRight(bool _hover); + + bool hoverLeft(qreal _x) const; + bool hoverRight(qreal _x) const; + + QPointF toItem(const QPointF& _pos) const; + qreal toItem(qreal _x) const; + + inline bool hoverIndicator() const + { + return m_bHoverIndicator; + } + + inline bool hoverLeft() const + { + return m_bHoverLeftBorder; + } + + inline bool hoverRight() const + { + return m_bHoverRightBorder; + } + + inline bool reverse() const + { + return m_bReverse; + } + + inline qreal left() const + { + return m_left; + } + + inline qreal right() const + { + return m_right; + } + + inline qreal width() const + { + return m_right - m_left; + } + +private: + + ///< Returns pointer to the EasyGraphicsView widget. + const EasyGraphicsView* view() const; + EasyGraphicsView* view(); + +}; // END of class EasyChronometerItem. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_CHRONOMETER_ITEM_H diff --git a/3rdparty/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp b/3rdparty/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp new file mode 100644 index 0000000..9cdde8b --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_frame_rate_viewer.cpp @@ -0,0 +1,339 @@ +/************************************************************************ +* file name : easy_frame_rate_viewer.cpp +* ----------------- : +* creation time : 2017/04/02 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains implementation of EasyFrameRateViewer widget. +* ----------------- : +* change log : * 2017/04/02 Victor Zarubkin: Initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include +#include +#include "easy_frame_rate_viewer.h" +#include "globals.h" + +const int INTERVAL_WIDTH = 20; + +////////////////////////////////////////////////////////////////////////// + +EasyFPSGraphicsItem::EasyFPSGraphicsItem() : Parent(nullptr) +{ + +} + +EasyFPSGraphicsItem::~EasyFPSGraphicsItem() +{ + +} + +////////////////////////////////////////////////////////////////////////// + +QRectF EasyFPSGraphicsItem::boundingRect() const +{ + return m_boundingRect; +} + +void EasyFPSGraphicsItem::setBoundingRect(const QRectF& _boundingRect) +{ + m_boundingRect = _boundingRect; +} + +void EasyFPSGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) +{ + m_boundingRect.setRect(x, y, w, h); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyFPSGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + if (m_frames.empty()) + return; + + const auto fontMetrics = QFontMetrics(scene()->font()); + const int fontHeight = fontMetrics.height() + 2; + const qreal h = m_boundingRect.height() - (fontHeight << 1) - 4; + if (h < 0) + return; + + const qreal halfWidth = m_boundingRect.width() * 0.5 - INTERVAL_WIDTH; + const int halfMax = static_cast(0.5 + halfWidth / INTERVAL_WIDTH); + const int half = static_cast(m_frames.size() / 2); + const qreal top = fontHeight, bottom = h + 4 + fontHeight; + qreal y; + + _painter->save(); + + _painter->drawLine(QPointF(0, top), QPointF(m_boundingRect.width(), top)); + _painter->drawLine(QPointF(0, bottom), QPointF(m_boundingRect.width(), bottom)); + + _painter->setPen(Qt::lightGray); + y = m_boundingRect.height() * 0.5; + _painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y)); + y -= h * 0.25; + _painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y)); + y += h * 0.5; + _painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y)); + + m_points1.reserve(m_frames.size()); + m_points2.reserve(m_frames.size()); + int n = 0; + qreal x = m_boundingRect.width() * 0.5 + std::min(halfMax, half) * INTERVAL_WIDTH, localMax = 0, localMin = 1e30; + const qreal xCurrent = x; + for (int i = static_cast(m_frames.size()) - 1; i > -1 && x >= 0; --i, x -= INTERVAL_WIDTH, ++n) + { + const auto& val = m_frames[i]; + + if (val.first > localMax) + localMax = val.first; + if (val.first < localMin) + localMin = val.first; + m_points1.emplace_back(x, static_cast(val.first) + 1e-3); + + if (val.second > localMax) + localMax = val.second; + if (val.second < localMin) + localMin = val.second; + m_points2.emplace_back(x, static_cast(val.second) + 1e-3); + + _painter->drawLine(QPointF(x, top + 1), QPointF(x, bottom - 1)); + } + + const auto delta = std::max(localMax - localMin, 1e-3); + _painter->setPen(Qt::black); + + qreal frameTime = std::max(localMax, 1.); + _painter->drawText(5, 0, m_boundingRect.width() - 10, fontHeight, Qt::AlignVCenter | Qt::AlignLeft, QString("Slowest %1 FPS (%2)") + .arg(static_cast(1e6 / frameTime)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, localMax, 1))); + + frameTime = std::max(m_frames.back().first, 1U); + _painter->drawText(5, 0, xCurrent - 5, fontHeight, Qt::AlignVCenter | Qt::AlignRight, QString("Max current %1 FPS (%2)") + .arg(static_cast(1e6 / frameTime)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, m_frames.back().first, 1))); + + frameTime = std::max(m_frames.back().second, 1U); + _painter->drawText(5, bottom, xCurrent - 5, fontHeight, Qt::AlignVCenter | Qt::AlignRight, QString("Avg current %1 FPS (%2)") + .arg(static_cast(1e6 / frameTime)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, m_frames.back().second, 1))); + + frameTime = std::max(localMin, 1.); + _painter->drawText(5, bottom, m_boundingRect.width() - 10, fontHeight, Qt::AlignVCenter | Qt::AlignLeft, QString("Fastest %1 FPS (%2)") + .arg(static_cast(1e6 / frameTime)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, localMin, 1))); + + if (localMin < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < localMax) + { + y = fontHeight + 2 + h * (1. - (EASY_GLOBALS.frame_time - localMin) / delta); + _painter->setPen(Qt::DashLine); + _painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y)); + } + + for (int i = 0; i < n; ++i) + { + auto& point1 = m_points1[i]; + point1.setY(fontHeight + 2 + h * (1. - (point1.y() - localMin) / delta)); + + auto& point2 = m_points2[i]; + point2.setY(fontHeight + 2 + h * (1. - (point2.y() - localMin) / delta)); + } + + _painter->setRenderHint(QPainter::Antialiasing, true); + + QPen pen(QColor::fromRgba(0x80ff0000)); + pen.setWidth(EASY_GLOBALS.fps_widget_line_width); + _painter->setPen(pen); + if (n > 1) + { + _painter->drawPolyline(m_points1.data(), n); + + pen.setColor(QColor::fromRgba(0x800000ff)); + _painter->setPen(pen); + _painter->drawPolyline(m_points2.data(), n); + } + else + { + _painter->drawPoint(m_points1.back()); + + pen.setColor(QColor::fromRgba(0x800000ff)); + _painter->setPen(pen); + _painter->drawPoint(m_points2.back()); + } + + const auto txtTop = ::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, localMin + delta * 0.75, 1); + const auto txtMid = ::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, localMin + delta * 0.5, 1); + const auto txtBtm = ::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, localMin + delta * 0.25, 1); + + _painter->setPen(Qt::NoPen); + _painter->setBrush(Qt::white); + _painter->drawRect(0, top + 1, std::max({fontMetrics.width(txtTop), fontMetrics.width(txtMid), fontMetrics.width(txtBtm)}) + 8, bottom - top - 1); + + _painter->setPen(Qt::black); + _painter->setBrush(Qt::NoBrush); + + y = m_boundingRect.height() * 0.5; + _painter->drawText(5, y - (fontHeight >> 1), m_boundingRect.width(), fontHeight, Qt::AlignVCenter | Qt::AlignLeft, txtMid); + + y -= h * 0.25; + _painter->drawText(5, y - (fontHeight >> 1), m_boundingRect.width(), fontHeight, Qt::AlignVCenter | Qt::AlignLeft, txtTop); + + y += h * 0.5; + _painter->drawText(5, y - (fontHeight >> 1), m_boundingRect.width(), fontHeight, Qt::AlignVCenter | Qt::AlignLeft, txtBtm); + + _painter->restore(); + + m_points1.clear(); + m_points2.clear(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyFPSGraphicsItem::clear() +{ + m_frames.clear(); +} + +void EasyFPSGraphicsItem::addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime) +{ + m_frames.emplace_back(_maxFrameTime, _avgFrameTime); + if (static_cast(m_frames.size()) > EASY_GLOBALS.max_fps_history) + m_frames.pop_front(); +} + +////////////////////////////////////////////////////////////////////////// + +EasyFrameRateViewer::EasyFrameRateViewer(QWidget* _parent) : Parent(_parent), m_fpsItem(nullptr) +{ + setCacheMode(QGraphicsView::CacheNone); + //setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setOptimizationFlag(QGraphicsView::DontSavePainterState, true); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setContentsMargins(0, 0, 0, 0); + setScene(new QGraphicsScene(this)); + scene()->setSceneRect(0, 0, 50, 50); + + m_fpsItem = new EasyFPSGraphicsItem(); + m_fpsItem->setPos(0, 0); + m_fpsItem->setBoundingRect(0, 0, 50, 50); + scene()->addItem(m_fpsItem); + + centerOn(0, 0); + + // Dirty hack for QDockWidget stupid initial size policy :( + setFixedHeight(10); // Set very small height to enable appropriate minimum height on the application startup + QTimer::singleShot(100, [this] + { + // Now set appropriate minimum height + setMinimumHeight((QFontMetrics(scene()->font()).height() + 3) * 6); + setMaximumHeight(minimumHeight() * 20); + }); +} + +EasyFrameRateViewer::~EasyFrameRateViewer() +{ + +} + +void EasyFrameRateViewer::clear() +{ + m_fpsItem->clear(); + scene()->update(); +} + +void EasyFrameRateViewer::addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime) +{ + m_fpsItem->addPoint(_maxFrameTime, _avgFrameTime); + scene()->update(); +} + +void EasyFrameRateViewer::resizeEvent(QResizeEvent* _event) +{ + Parent::resizeEvent(_event); + + auto size = _event->size(); + m_fpsItem->setBoundingRect(0, 0, size.width(), size.height()); + + scene()->setSceneRect(m_fpsItem->boundingRect()); + scene()->update(); +} + +void EasyFrameRateViewer::hideEvent(QHideEvent* _event) +{ + Parent::hideEvent(_event); + EASY_GLOBALS.fps_enabled = isVisible(); + clear(); +} + +void EasyFrameRateViewer::showEvent(QShowEvent* _event) +{ + Parent::showEvent(_event); + EASY_GLOBALS.fps_enabled = isVisible(); + clear(); +} + +void EasyFrameRateViewer::contextMenuEvent(QContextMenuEvent* _event) +{ + QMenu menu; + QAction* action = nullptr; + + action = menu.addAction(QIcon(imagePath("delete")), "Clear"); + connect(action, &QAction::triggered, [this](bool){ clear(); }); + + action = menu.addAction("Close"); + connect(action, &QAction::triggered, [this](bool){ parentWidget()->hide(); }); + + menu.exec(QCursor::pos()); + + _event->accept(); +} + +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/profiler_gui/easy_frame_rate_viewer.h b/3rdparty/easyprofiler/profiler_gui/easy_frame_rate_viewer.h new file mode 100644 index 0000000..ca07303 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_frame_rate_viewer.h @@ -0,0 +1,126 @@ +/************************************************************************ +* file name : easy_frame_rate_viewer.cpp +* ----------------- : +* creation time : 2017/04/02 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains declaration of EasyFrameRateViewer widget. +* ----------------- : +* change log : * 2017/04/02 Victor Zarubkin: Initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY__FRAME_RATE_VIEWER__H +#define EASY__FRAME_RATE_VIEWER__H + +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// + +class EasyFPSGraphicsItem : public QGraphicsItem +{ + typedef QGraphicsItem Parent; + typedef EasyFPSGraphicsItem This; + typedef std::deque > FrameTimes; + + std::vector m_points1, m_points2; + FrameTimes m_frames; + QRectF m_boundingRect; + +public: + + explicit EasyFPSGraphicsItem(); + virtual ~EasyFPSGraphicsItem(); + + QRectF boundingRect() const override; + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + + void setBoundingRect(const QRectF& _boundingRect); + void setBoundingRect(qreal x, qreal y, qreal w, qreal h); + + void clear(); + void addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime); + +}; // END of class EasyFPSGraphicsItem. + +////////////////////////////////////////////////////////////////////////// + +class EasyFrameRateViewer : public QGraphicsView +{ + Q_OBJECT + +private: + + typedef QGraphicsView Parent; + typedef EasyFrameRateViewer This; + + EasyFPSGraphicsItem* m_fpsItem; + +public: + + explicit EasyFrameRateViewer(QWidget* _parent = nullptr); + virtual ~EasyFrameRateViewer(); + + void resizeEvent(QResizeEvent* _event) override; + void hideEvent(QHideEvent* _event) override; + void showEvent(QShowEvent* _event) override; + void contextMenuEvent(QContextMenuEvent* _event) override; + +public slots: + + void clear(); + void addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime); + +}; // END of class EasyFrameRateViewer. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY__FRAME_RATE_VIEWER__H diff --git a/3rdparty/easyprofiler/profiler_gui/easy_graphics_item.cpp b/3rdparty/easyprofiler/profiler_gui/easy_graphics_item.cpp new file mode 100644 index 0000000..1c51fff --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_graphics_item.cpp @@ -0,0 +1,1460 @@ +/************************************************************************ +* file name : easy_graphics_item.cpp +* ----------------- : +* creation time : 2016/09/15 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of EasyGraphicsItem. +* ----------------- : +* change log : * 2016/09/15 Victor Zarubkin: Moved sources from blocks_graphics_view.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include "easy_graphics_item.h" +#include "blocks_graphics_view.h" +#include "globals.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +enum BlockItemState : int8_t +{ + BLOCK_ITEM_DO_PAINT_FIRST = -2, + BLOCK_ITEM_DO_NOT_PAINT = -1, + BLOCK_ITEM_UNCHANGED, + BLOCK_ITEM_DO_PAINT +}; + +////////////////////////////////////////////////////////////////////////// + +EASY_CONSTEXPR int MIN_SYNC_SPACING = 1; +EASY_CONSTEXPR int MIN_SYNC_SIZE = 3; +EASY_CONSTEXPR int EVENT_HEIGHT = 4; +EASY_CONSTEXPR QRgb BORDERS_COLOR = ::profiler::colors::Grey600 & 0x00ffffff;// 0x00686868; + +inline QRgb selectedItemBorderColor(::profiler::color_t _color) { + return ::profiler_gui::isLightColor(_color, 192) ? ::profiler::colors::Black : ::profiler::colors::RichRed; +} + +const QPen HIGHLIGHTER_PEN = ([]() -> QPen { QPen p(::profiler::colors::Black); p.setStyle(Qt::DotLine); p.setWidth(2); return p; })(); + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +////////////////////////////////////////////////////////////////////////// + +EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root) + : QGraphicsItem(nullptr) + , m_threadName(::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, _root, EASY_GLOBALS.hex_thread_id)) + , m_pRoot(&_root) + , m_index(_index) +{ +} + +EasyGraphicsItem::~EasyGraphicsItem() +{ +} + +void EasyGraphicsItem::validateName() +{ + m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, *m_pRoot, EASY_GLOBALS.hex_thread_id); +} + +const EasyGraphicsView* EasyGraphicsItem::view() const +{ + return static_cast(scene()->parent()); +} + +////////////////////////////////////////////////////////////////////////// + +QRectF EasyGraphicsItem::boundingRect() const +{ + return m_boundingRect; +} + +////////////////////////////////////////////////////////////////////////// + +struct EasyPainterInformation EASY_FINAL +{ + const QRectF visibleSceneRect; + QRectF rect; + QBrush brush; + const qreal visibleBottom; + const qreal currentScale; + const qreal offset; + const qreal sceneLeft; + const qreal sceneRight; + const qreal dx; + QRgb previousColor; + QRgb textColor; + Qt::PenStyle previousPenStyle; + bool is_light; + bool selectedItemsWasPainted; + + explicit EasyPainterInformation(const EasyGraphicsView* sceneView) + : visibleSceneRect(sceneView->visibleSceneRect()) + , visibleBottom(visibleSceneRect.bottom() - 1) + , currentScale(sceneView->scale()) + , offset(sceneView->offset()) + , sceneLeft(offset) + , sceneRight(offset + visibleSceneRect.width() / currentScale) + , dx(offset * currentScale) + , previousColor(0) + , textColor(0) + , previousPenStyle(Qt::NoPen) + , is_light(false) + , selectedItemsWasPainted(false) + { + brush.setStyle(Qt::SolidPattern); + } + + EasyPainterInformation() = delete; +}; + +#ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT +void EasyGraphicsItem::paintChildren(const float _minWidth, const int _narrowSizeHalf, const uint8_t _levelsNumber, QPainter* _painter, struct EasyPainterInformation& p, ::profiler_gui::EasyBlockItem& _item, const ::profiler_gui::EasyBlock& _itemBlock, RightBounds& _rightBounds, uint8_t _level, int8_t _mode) +{ + if (_level >= _levelsNumber || _itemBlock.tree.children.empty()) + return; + + const auto top = levelY(_level); + if (top > p.visibleBottom) + return; + + qreal& prevRight = _rightBounds[_level]; + auto& level = m_levels[_level]; + const short next_level = _level + 1; + + uint32_t neighbours = (uint32_t)_itemBlock.tree.children.size(); + uint32_t last = neighbours - 1; + uint32_t neighbour = 0; + + if (_mode == BLOCK_ITEM_DO_PAINT_FIRST) + { + neighbour = last = _item.max_depth_child; + neighbours = neighbour + 1; + } + + for (uint32_t i = _item.children_begin + neighbour; neighbour < neighbours; ++i, ++neighbour) + { + auto& item = level[i]; + + if (item.left() > p.sceneRight) + break; // This is first totally invisible item. No need to check other items. + + if (item.right() < p.sceneLeft) + continue; // This item is not visible + + const auto& itemBlock = easyBlock(item.block); + const uint16_t totalHeight = itemBlock.tree.depth * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + ::profiler_gui::GRAPHICS_ROW_SIZE; + if ((top + totalHeight) < p.visibleSceneRect.top()) + continue; // This item is not visible + + const auto item_width = ::std::max(item.width(), _minWidth); + auto x = item.left() * p.currentScale - p.dx; + auto w = item_width * p.currentScale; + //const auto right = x + w; + if ((x + w) <= prevRight) + { + // This item is not visible + if (!(EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size)) + paintChildren(_minWidth, _narrowSizeHalf, _levelsNumber, _painter, p, item, itemBlock, _rightBounds, next_level, BLOCK_ITEM_DO_PAINT_FIRST); + continue; + } + + if (x < prevRight) + { + w -= prevRight - x; + x = prevRight; + } + + if (EASY_GLOBALS.hide_minsize_blocks && w < EASY_GLOBALS.blocks_size_min) + continue; // Hide blocks (except top-level blocks) which width is less than 1 pixel + + const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id()); + + int h = 0; + bool do_paint_children = false; + if ((EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size) || !itemBlock.expanded) + { + // Items which width is less than 20 will be painted as big rectangles which are hiding it's children + + //x = item.left() * p.currentScale - p.dx; + h = totalHeight; + const auto dh = top + h - p.visibleBottom; + if (dh > 0) + h -= dh; + + if (item.block == EASY_GLOBALS.selected_block) + p.selectedItemsWasPainted = true; + + const bool colorChange = (p.previousColor != itemDesc.color()); + if (colorChange) + { + // Set background color brush for rectangle + p.previousColor = itemDesc.color(); + //p.inverseColor = 0xffffffff - p.previousColor; + p.is_light = ::profiler_gui::isLightColor(p.previousColor); + p.textColor = ::profiler_gui::textColorForFlag(p.is_light); + p.brush.setColor(QColor::fromRgba(p.previousColor)); + _painter->setBrush(p.brush); + } + + if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id() + || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id()))) + { + if (p.previousPenStyle != Qt::DotLine) + { + p.previousPenStyle = Qt::DotLine; + _painter->setPen(HIGHLIGHTER_PEN); + } + } + else if (EASY_GLOBALS.draw_graphics_items_borders) + { + if (p.previousPenStyle != Qt::SolidLine)// || colorChange) + { + // Restore pen for item which is wide enough to paint borders + p.previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR);//BORDERS_COLOR & inverseColor); + } + } + else if (p.previousPenStyle != Qt::NoPen) + { + p.previousPenStyle = Qt::NoPen; + _painter->setPen(Qt::NoPen); + } + + const auto wprev = w; + decltype(w) dw = 0; + if (item.left() < p.sceneLeft) + { + // if item left border is out of screen then attach text to the left border of the screen + // to ensure text is always visible for items presenting on the screen. + w += (item.left() - p.sceneLeft) * p.currentScale; + x = p.sceneLeft * p.currentScale - p.dx - 2; + w += 2; + dw = 2; + } + + if (item.right() > p.sceneRight) + { + w -= (item.right() - p.sceneRight) * p.currentScale; + w += 2; + dw += 2; + } + + if (w < EASY_GLOBALS.blocks_size_min) + w = EASY_GLOBALS.blocks_size_min; + + // Draw rectangle + p.rect.setRect(x, top, w, h); + _painter->drawRect(p.rect); + + prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing; + //skip_children(next_level, item.children_begin); + if (wprev < EASY_GLOBALS.blocks_narrow_size) + continue; + + if (dw > 1) + { + w -= dw; + x += 2; + } + } + else + { + if (item.block == EASY_GLOBALS.selected_block) + p.selectedItemsWasPainted = true; + + const bool colorChange = (p.previousColor != itemDesc.color()); + if (colorChange) + { + // Set background color brush for rectangle + p.previousColor = itemDesc.color(); + //p.inverseColor = 0xffffffff - p.previousColor; + p.is_light = ::profiler_gui::isLightColor(p.previousColor); + p.textColor = ::profiler_gui::textColorForFlag(p.is_light); + p.brush.setColor(QColor::fromRgba(p.previousColor)); + _painter->setBrush(p.brush); + } + + if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id() + || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id()))) + { + if (p.previousPenStyle != Qt::DotLine) + { + p.previousPenStyle = Qt::DotLine; + _painter->setPen(HIGHLIGHTER_PEN); + } + } + else if (EASY_GLOBALS.draw_graphics_items_borders) + { + if (p.previousPenStyle != Qt::SolidLine)// || colorChange) + { + // Restore pen for item which is wide enough to paint borders + p.previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor); + } + } + else if (p.previousPenStyle != Qt::NoPen) + { + p.previousPenStyle = Qt::NoPen; + _painter->setPen(Qt::NoPen); + } + + // Draw rectangle + //x = item.left() * currentScale - p.dx; + h = ::profiler_gui::GRAPHICS_ROW_SIZE; + const auto dh = top + h - p.visibleBottom; + if (dh > 0) + h -= dh; + + const auto wprev = w; + decltype(w) dw = 0; + if (item.left() < p.sceneLeft) + { + // if item left border is out of screen then attach text to the left border of the screen + // to ensure text is always visible for items presenting on the screen. + w += (item.left() - p.sceneLeft) * p.currentScale; + x = p.sceneLeft * p.currentScale - p.dx - 2; + w += 2; + dw = 2; + } + + if (item.right() > p.sceneRight) + { + w -= (item.right() - p.sceneRight) * p.currentScale; + w += 2; + dw += 2; + } + + if (w < EASY_GLOBALS.blocks_size_min) + w = EASY_GLOBALS.blocks_size_min; + + p.rect.setRect(x, top, w, h); + _painter->drawRect(p.rect); + + prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing; + if (wprev < EASY_GLOBALS.blocks_narrow_size) + { + paintChildren(_minWidth, _narrowSizeHalf, _levelsNumber, _painter, p, item, itemBlock, _rightBounds, next_level, wprev < _narrowSizeHalf ? BLOCK_ITEM_DO_PAINT_FIRST : BLOCK_ITEM_DO_PAINT); + continue; + } + + if (dw > 1) + { + w -= dw; + x += 2; + } + + do_paint_children = true; + } + + // Draw text----------------------------------- + p.rect.setRect(x + 1, top, w - 1, h); + + // text will be painted with inverse color + //auto textColor = inverseColor < 0x00808080 ? profiler::colors::Black : profiler::colors::White; + //if (textColor == previousColor) textColor = 0; + _painter->setPen(p.textColor); + + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(EASY_GLOBALS.selected_item_font); + + // drawing text + auto name = easyBlockName(itemBlock.tree, itemDesc); + _painter->drawText(p.rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name)); + + // restore previous pen color + if (p.previousPenStyle == Qt::NoPen) + _painter->setPen(Qt::NoPen); + else if (p.previousPenStyle == Qt::DotLine) + { + _painter->setPen(HIGHLIGHTER_PEN); + } + else + _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor); // restore pen for rectangle painting + + // restore font + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(EASY_GLOBALS.items_font); + // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + if (do_paint_children) + paintChildren(_minWidth, _narrowSizeHalf, _levelsNumber, _painter, p, item, itemBlock, _rightBounds, next_level, _mode); + } +} +#endif + +void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + const bool gotItems = !m_levels.empty() && !m_levels.front().empty(); + const bool gotSync = !m_pRoot->sync.empty(); + + if (!gotItems && !gotSync) + { + return; + } + + EasyPainterInformation p(view()); + + _painter->save(); + _painter->setFont(EASY_GLOBALS.items_font); + + // Reset indices of first visible item for each layer + const auto levelsNumber = levels(); + m_rightBounds[0] = -1e100; + for (uint8_t i = 1; i < levelsNumber; ++i) { + ::profiler_gui::set_max(m_levelsIndexes[i]); + m_rightBounds[i] = -1e100; + } + + + // Search for first visible top-level item + if (gotItems) + { + auto& level0 = m_levels.front(); + auto first = ::std::lower_bound(level0.begin(), level0.end(), p.sceneLeft, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); + + if (first != level0.end()) + { + m_levelsIndexes[0] = first - level0.begin(); + if (m_levelsIndexes[0] > 0) + m_levelsIndexes[0] -= 1; + } + else + { + m_levelsIndexes[0] = static_cast(level0.size() - 1); + } + } + + + + // This is to make _painter->drawText() work properly + // (it seems there is a bug in Qt5.6 when drawText called for big coordinates, + // drawRect at the same time called for actually same coordinates + // works fine without using this additional shifting) + //const auto dx = p.offset * p.currentScale; + + // Shifting coordinates to current screen offset + _painter->setTransform(QTransform::fromTranslate(0, -y()), true); + + + + if (EASY_GLOBALS.draw_graphics_items_borders) + { + p.previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR); + } + else + { + _painter->setPen(Qt::NoPen); + } + + + const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f; + + + // Iterate through layers and draw visible items + if (gotItems) + { + const int narrow_size_half = EASY_GLOBALS.blocks_narrow_size >> 1; + +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max(); + auto const dont_skip_children = [this, &levelsNumber](short next_level, decltype(::profiler_gui::EasyBlockItem::children_begin) children_begin, int8_t _state) + { + if (next_level < levelsNumber && children_begin != MAX_CHILD_INDEX) + { + if (m_levelsIndexes[next_level] == MAX_CHILD_INDEX) + { + // Mark first potentially visible child item on next sublevel + m_levelsIndexes[next_level] = children_begin; + } + + // Mark children items that we want to draw them + m_levels[next_level][children_begin].state = _state; + } + }; +#endif + + //size_t iterations = 0; +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + for (uint8_t l = 0; l < levelsNumber; ++l) +#else + for (uint8_t l = 0; l < 1; ++l) +#endif + { + auto& level = m_levels[l]; + const short next_level = l + 1; + + const auto top = levelY(l); + if (top > p.visibleBottom) + break; + + //qreal& prevRight = m_rightBounds[l]; + qreal prevRight = -1e100; + uint32_t neighbour = 0; + for (uint32_t i = m_levelsIndexes[l], end = static_cast(level.size()); i < end; ++i, ++neighbour) + { + //++iterations; + + auto& item = level[i]; + + if (item.left() > p.sceneRight) + break; // This is first totally invisible item. No need to check other items. + +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + char state = BLOCK_ITEM_DO_PAINT; + if (item.state != BLOCK_ITEM_UNCHANGED) + { + neighbour = 0; // first block in parent's children list + state = item.state; + item.state = BLOCK_ITEM_DO_NOT_PAINT; + } +#endif + + if (item.right() < p.sceneLeft) + continue; // This item is not visible + +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + if (state == BLOCK_ITEM_DO_NOT_PAINT) + { + // This item is not visible + if (neighbour < item.neighbours) + i += item.neighbours - neighbour - 1; // Skip all neighbours + continue; + } + + if (state == BLOCK_ITEM_DO_PAINT_FIRST && item.children_begin == MAX_CHILD_INDEX && next_level < levelsNumber && neighbour < (item.neighbours-1)) + // Paint only first child which has own children + continue; // This item has no children and would not be painted +#endif + + const auto& itemBlock = easyBlock(item.block); + const uint16_t totalHeight = itemBlock.tree.depth * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + ::profiler_gui::GRAPHICS_ROW_SIZE; + if ((top + totalHeight) < p.visibleSceneRect.top()) + continue; // This item is not visible + + const auto item_width = ::std::max(item.width(), MIN_WIDTH); + auto x = item.left() * p.currentScale - p.dx; + auto w = item_width * p.currentScale; + if ((x + w) <= prevRight) + { + // This item is not visible +#ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + if (!EASY_GLOBALS.hide_narrow_children || w >= EASY_GLOBALS.blocks_narrow_size) + paintChildren(MIN_WIDTH, narrow_size_half, levelsNumber, _painter, p, item, itemBlock, m_rightBounds, next_level, BLOCK_ITEM_DO_PAINT_FIRST); +#else + if (!(EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size) && l > 0) + dont_skip_children(next_level, item.children_begin, BLOCK_ITEM_DO_PAINT_FIRST); +#endif + continue; + } + + if (x < prevRight) + { + w -= prevRight - x; + x = prevRight; + } + +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + if (EASY_GLOBALS.hide_minsize_blocks && w < EASY_GLOBALS.blocks_size_min && l > 0) + continue; // Hide blocks (except top-level blocks) which width is less than 1 pixel + + if (state == BLOCK_ITEM_DO_PAINT_FIRST && neighbour < item.neighbours) + { + // Paint only first child which has own children + i += item.neighbours - neighbour - 1; // Skip all neighbours + } +#endif + + const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id()); + int h = 0; + +#ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + bool do_paint_children = false; +#endif + + if ((EASY_GLOBALS.hide_narrow_children && w < EASY_GLOBALS.blocks_narrow_size) || !itemBlock.expanded) + { + // Items which width is less than 20 will be painted as big rectangles which are hiding it's children + + //x = item.left() * p.currentScale - p.dx; + h = totalHeight; + const auto dh = top + h - p.visibleBottom; + if (dh > 0) + h -= dh; + + if (item.block == EASY_GLOBALS.selected_block) + p.selectedItemsWasPainted = true; + + const bool colorChange = (p.previousColor != itemDesc.color()); + if (colorChange) + { + // Set background color brush for rectangle + p.previousColor = itemDesc.color(); + //p.inverseColor = 0xffffffff - p.previousColor; + p.is_light = ::profiler_gui::isLightColor(p.previousColor); + p.textColor = ::profiler_gui::textColorForFlag(p.is_light); + p.brush.setColor(QColor::fromRgba(p.previousColor)); + _painter->setBrush(p.brush); + } + + if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id() + || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id()))) + { + if (p.previousPenStyle != Qt::DotLine) + { + p.previousPenStyle = Qt::DotLine; + _painter->setPen(HIGHLIGHTER_PEN); + } + } + else if (EASY_GLOBALS.draw_graphics_items_borders) + { + if (p.previousPenStyle != Qt::SolidLine)// || colorChange) + { + // Restore pen for item which is wide enough to paint borders + p.previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR);//BORDERS_COLOR & inverseColor); + } + } + else if (p.previousPenStyle != Qt::NoPen) + { + p.previousPenStyle = Qt::NoPen; + _painter->setPen(Qt::NoPen); + } + + const auto wprev = w; + decltype(w) dw = 0; + if (item.left() < p.sceneLeft) + { + // if item left border is out of screen then attach text to the left border of the screen + // to ensure text is always visible for items presenting on the screen. + w += (item.left() - p.sceneLeft) * p.currentScale; + x = p.sceneLeft * p.currentScale - p.dx - 2; + w += 2; + dw = 2; + } + + if (item.right() > p.sceneRight) + { + w -= (item.right() - p.sceneRight) * p.currentScale; + w += 2; + dw += 2; + } + + if (w < EASY_GLOBALS.blocks_size_min) + w = EASY_GLOBALS.blocks_size_min; + + // Draw rectangle + p.rect.setRect(x, top, w, h); + _painter->drawRect(p.rect); + + prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing; + //skip_children(next_level, item.children_begin); + if (wprev < EASY_GLOBALS.blocks_narrow_size) + continue; + + if (dw > 1) { + w -= dw; + x += 2; + } + } + else + { + if (item.block == EASY_GLOBALS.selected_block) + p.selectedItemsWasPainted = true; + + const bool colorChange = (p.previousColor != itemDesc.color()); + if (colorChange) + { + // Set background color brush for rectangle + p.previousColor = itemDesc.color(); + //p.inverseColor = 0xffffffff - p.previousColor; + p.is_light = ::profiler_gui::isLightColor(p.previousColor); + p.textColor = ::profiler_gui::textColorForFlag(p.is_light); + p.brush.setColor(QColor::fromRgba(p.previousColor)); + _painter->setBrush(p.brush); + } + + if (EASY_GLOBALS.highlight_blocks_with_same_id && (EASY_GLOBALS.selected_block_id == itemBlock.tree.node->id() + || (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && EASY_GLOBALS.selected_block_id == itemDesc.id()))) + { + if (p.previousPenStyle != Qt::DotLine) + { + p.previousPenStyle = Qt::DotLine; + _painter->setPen(HIGHLIGHTER_PEN); + } + } + else if (EASY_GLOBALS.draw_graphics_items_borders) + { + if (p.previousPenStyle != Qt::SolidLine)// || colorChange) + { + // Restore pen for item which is wide enough to paint borders + p.previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor); + } + } + else if (p.previousPenStyle != Qt::NoPen) + { + p.previousPenStyle = Qt::NoPen; + _painter->setPen(Qt::NoPen); + } + + // Draw rectangle + //x = item.left() * currentScale - p.dx; + h = ::profiler_gui::GRAPHICS_ROW_SIZE; + const auto dh = top + h - p.visibleBottom; + if (dh > 0) + h -= dh; + + const auto wprev = w; + decltype(w) dw = 0; + if (item.left() < p.sceneLeft) + { + // if item left border is out of screen then attach text to the left border of the screen + // to ensure text is always visible for items presenting on the screen. + w += (item.left() - p.sceneLeft) * p.currentScale; + x = p.sceneLeft * p.currentScale - p.dx - 2; + w += 2; + dw = 2; + } + + if (item.right() > p.sceneRight) + { + w -= (item.right() - p.sceneRight) * p.currentScale; + w += 2; + dw += 2; + } + + if (w < EASY_GLOBALS.blocks_size_min) + w = EASY_GLOBALS.blocks_size_min; + + p.rect.setRect(x, top, w, h); + _painter->drawRect(p.rect); + + prevRight = p.rect.right() + EASY_GLOBALS.blocks_spacing; + if (wprev < EASY_GLOBALS.blocks_narrow_size) + { +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + dont_skip_children(next_level, item.children_begin, wprev < narrow_size_half ? BLOCK_ITEM_DO_PAINT_FIRST : BLOCK_ITEM_DO_PAINT); +#else + paintChildren(MIN_WIDTH, narrow_size_half, levelsNumber, _painter, p, item, itemBlock, m_rightBounds, next_level, wprev < narrow_size_half ? BLOCK_ITEM_DO_PAINT_FIRST : BLOCK_ITEM_DO_PAINT); +#endif + continue; + } + +#ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + dont_skip_children(next_level, item.children_begin, BLOCK_ITEM_DO_PAINT); +#endif + + if (dw > 1) { + w -= dw; + x += 2; + } + +#ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + do_paint_children = true; +#endif + } + + // Draw text----------------------------------- + p.rect.setRect(x + 1, top, w - 1, h); + + // text will be painted with inverse color + //auto textColor = inverseColor < 0x00808080 ? profiler::colors::Black : profiler::colors::White; + //if (textColor == previousColor) textColor = 0; + _painter->setPen(p.textColor); + + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(EASY_GLOBALS.selected_item_font); + + // drawing text + auto name = easyBlockName(itemBlock.tree, itemDesc); + _painter->drawText(p.rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name)); + + // restore previous pen color + if (p.previousPenStyle == Qt::NoPen) + _painter->setPen(Qt::NoPen); + else if (p.previousPenStyle == Qt::DotLine) + { + _painter->setPen(HIGHLIGHTER_PEN); + } + else + _painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor); // restore pen for rectangle painting + + // restore font + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(EASY_GLOBALS.items_font); + // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + if (do_paint_children) + paintChildren(MIN_WIDTH, narrow_size_half, levelsNumber, _painter, p, item, itemBlock, m_rightBounds, next_level, BLOCK_ITEM_DO_PAINT); +#endif + } + } + + if (EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size()) + { + const auto& guiblock = EASY_GLOBALS.gui_blocks[EASY_GLOBALS.selected_block]; + if (guiblock.graphics_item == m_index) + { + const auto& item = m_levels[guiblock.graphics_item_level][guiblock.graphics_item_index]; + if (item.left() < p.sceneRight && item.right() > p.sceneLeft) + { + const auto& itemBlock = easyBlock(item.block); + const auto item_width = ::std::max(item.width(), MIN_WIDTH); + auto top = levelY(guiblock.graphics_item_level); + auto w = ::std::max(item_width * p.currentScale, 1.0); + decltype(top) h = (!itemBlock.expanded || + (w < EASY_GLOBALS.blocks_narrow_size && EASY_GLOBALS.hide_narrow_children)) + ? (itemBlock.tree.depth * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + ::profiler_gui::GRAPHICS_ROW_SIZE) + : ::profiler_gui::GRAPHICS_ROW_SIZE; + + auto dh = top + h - p.visibleBottom; + if (dh < h) + { + if (dh > 0) + h -= dh; + + const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id()); + + QPen pen(Qt::SolidLine); + pen.setJoinStyle(Qt::MiterJoin); + pen.setColor(selectedItemBorderColor(itemDesc.color()));//Qt::red); + pen.setWidth(3); + _painter->setPen(pen); + + if (!p.selectedItemsWasPainted) + { + p.brush.setColor(QColor::fromRgba(itemDesc.color()));// SELECTED_ITEM_COLOR); + _painter->setBrush(p.brush); + } + else + { + _painter->setBrush(Qt::NoBrush); + } + + auto x = item.left() * p.currentScale - p.dx; + decltype(w) dw = 0; + if (item.left() < p.sceneLeft) + { + // if item left border is out of screen then attach text to the left border of the screen + // to ensure text is always visible for items presenting on the screen. + w += (item.left() - p.sceneLeft) * p.currentScale; + x = p.sceneLeft * p.currentScale - p.dx - 2; + w += 2; + dw = 2; + } + + if (item.right() > p.sceneRight) + { + w -= (item.right() - p.sceneRight) * p.currentScale; + w += 2; + dw += 2; + } + + p.rect.setRect(x, top, w, h); + _painter->drawRect(p.rect); + + if (!p.selectedItemsWasPainted && w > EASY_GLOBALS.blocks_narrow_size) + { + if (dw > 1) { + w -= dw; + x += 2; + } + + // Draw text----------------------------------- + p.rect.setRect(x + 1, top, w - 1, h); + + // text will be painted with inverse color + //auto textColor = 0x00ffffff - previousColor; + //if (textColor == previousColor) textColor = 0; + p.textColor = ::profiler_gui::textColorForRgb(itemDesc.color());// SELECTED_ITEM_COLOR); + _painter->setPen(p.textColor); + + _painter->setFont(EASY_GLOBALS.selected_item_font); + + // drawing text + auto name = easyBlockName(itemBlock.tree, itemDesc); + _painter->drawText(p.rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name)); + // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } + } + } + } + } + + //printf("%u: %llu\n", m_index, iterations); + } + + + + if (gotSync) + { + const auto sceneView = view(); + auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), p.sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value) + { + return sceneView->time2position(easyBlocksTree(_index).node->begin()) < _value; + }); + + if (firstSync != m_pRoot->sync.end()) + { + if (firstSync != m_pRoot->sync.begin()) + --firstSync; + } + else if (!m_pRoot->sync.empty()) + { + firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1; + } + //firstSync = m_pRoot->sync.begin(); + + p.previousColor = 0; + qreal prevRight = -1e100; + const qreal top = y() + 1 - EVENT_HEIGHT; + if (top + EVENT_HEIGHT < p.visibleBottom) + { + _painter->setPen(BORDERS_COLOR); + + for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it) + { + const auto& item = easyBlocksTree(*it); + auto left = sceneView->time2position(item.node->begin()); + + if (left > p.sceneRight) + break; // This is first totally invisible item. No need to check other items. + + decltype(left) width = sceneView->time2position(item.node->end()) - left; + if (left + width < p.sceneLeft) // This item is not visible + continue; + + left *= p.currentScale; + left -= p.dx; + width *= p.currentScale; + if (left + width <= prevRight) // This item is not visible + continue; + + if (left < prevRight) + { + width -= prevRight - left; + left = prevRight; + } + + if (width < MIN_SYNC_SIZE) + width = MIN_SYNC_SIZE; + + const ::profiler::thread_id_t tid = EASY_GLOBALS.version < ::profiler_gui::V130 ? item.node->id() : item.cs->tid(); + const bool self_thread = tid != 0 && EASY_GLOBALS.profiler_blocks.find(tid) != EASY_GLOBALS.profiler_blocks.end(); + + ::profiler::color_t color = 0; + if (self_thread) + color = ::profiler::colors::Coral; + else if (item.node->id() == 0) + color = ::profiler::colors::Black; + else + color = ::profiler::colors::RedA400; + + if (p.previousColor != color) + { + p.previousColor = color; + _painter->setBrush(QColor::fromRgb(color)); + } + + p.rect.setRect(left, top, width, EVENT_HEIGHT); + _painter->drawRect(p.rect); + prevRight = left + width + MIN_SYNC_SPACING; + } + } + } + + + + if (EASY_GLOBALS.enable_event_markers && !m_pRoot->events.empty()) + { + const auto sceneView = view(); + auto first = ::std::lower_bound(m_pRoot->events.begin(), m_pRoot->events.end(), p.offset, [&sceneView](::profiler::block_index_t _index, qreal _value) + { + return sceneView->time2position(easyBlocksTree(_index).node->begin()) < _value; + }); + + if (first != m_pRoot->events.end()) + { + if (first != m_pRoot->events.begin()) + --first; + } + else if (!m_pRoot->events.empty()) + { + first = m_pRoot->events.begin() + m_pRoot->events.size() - 1; + } + + p.previousColor = 0; + qreal prevRight = -1e100; + const qreal top = y() + boundingRect().height() - 1; + if (top + EVENT_HEIGHT < p.visibleBottom) + { + _painter->setPen(BORDERS_COLOR); + + for (auto it = first, end = m_pRoot->events.end(); it != end; ++it) + { + const auto& item = easyBlocksTree(*it); + auto left = sceneView->time2position(item.node->begin()); + + if (left > p.sceneRight) + break; // This is first totally invisible item. No need to check other items. + + decltype(left) width = MIN_WIDTH; + if (left + width < p.sceneLeft) // This item is not visible + continue; + + left *= p.currentScale; + left -= p.dx; + width *= p.currentScale; + if (width < 2) + width = 2; + + if (left + width <= prevRight) // This item is not visible + continue; + + if (left < prevRight) + { + width -= prevRight - left; + left = prevRight; + } + + if (width < 2) + width = 2; + + ::profiler::color_t color = easyDescriptor(item.node->id()).color(); + if (p.previousColor != color) + { + p.previousColor = color; + _painter->setBrush(QColor::fromRgb(color)); + } + + p.rect.setRect(left, top, width, EVENT_HEIGHT); + _painter->drawRect(p.rect); + prevRight = left + width + 2; + } + } + } + + + + _painter->restore(); +} + +////////////////////////////////////////////////////////////////////////// + +const ::profiler::BlocksTreeRoot* EasyGraphicsItem::root() const +{ + return m_pRoot; +} + +const QString& EasyGraphicsItem::threadName() const +{ + return m_threadName; +} + +////////////////////////////////////////////////////////////////////////// + +QRect EasyGraphicsItem::getRect() const +{ + return view()->mapFromScene(m_boundingRect).boundingRect(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsItem::getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const +{ + // Search for first visible top-level item + auto& level0 = m_levels.front(); + auto first = ::std::lower_bound(level0.begin(), level0.end(), _left, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); + + size_t itemIndex = 0; + if (first != level0.end()) + { + itemIndex = first - level0.begin(); + if (itemIndex > 0) + itemIndex -= 1; + } + else + { + itemIndex = level0.size() - 1; + } + + // Add all visible top-level items into array of visible blocks + for (size_t i = itemIndex, end = level0.size(); i < end; ++i) + { + const auto& item = level0[i]; + + if (item.left() > _right) + { + // First invisible item. No need to check other items. + break; + } + + if (item.right() < _left) + { + // This item is not visible yet + // This is just to be sure + continue; + } + + _blocks.emplace_back(m_pRoot, item.block); + } +} + +////////////////////////////////////////////////////////////////////////// + +const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersect(const QPointF& _pos, ::profiler::block_index_t& _blockIndex) const +{ + if (m_levels.empty() || m_levels.front().empty()) + { + return nullptr; + } + + const auto& level0 = m_levels.front(); + const auto top = y(); + + if (top > _pos.y()) + { + return nullptr; + } + + EASY_STATIC_CONSTEXPR auto OVERLAP = (::profiler_gui::THREADS_ROW_SPACING >> 1) + 2; + const auto bottom = top + m_levels.size() * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + OVERLAP; + if (bottom < _pos.y()) + { + return nullptr; + } + + const unsigned int levelIndex = static_cast(_pos.y() - top) / ::profiler_gui::GRAPHICS_ROW_SIZE_FULL; + if (levelIndex >= m_levels.size()) + { + // The Y position is out of blocks range + + if (EASY_GLOBALS.enable_event_markers && !m_pRoot->events.empty()) + { + // If event indicators are enabled then try to intersect with one of event indicators + + const auto& sceneView = view(); + auto first = ::std::lower_bound(m_pRoot->events.begin(), m_pRoot->events.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value) + { + return sceneView->time2position(easyBlocksTree(_index).node->begin()) < _value; + }); + + if (first != m_pRoot->events.end()) + { + if (first != m_pRoot->events.begin()) + --first; + } + else if (!m_pRoot->events.empty()) + { + first = m_pRoot->events.begin() + m_pRoot->events.size() - 1; + } + + const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f; + const auto currentScale = sceneView->scale(); + const auto dw = 5. / currentScale; + + for (auto it = first, end = m_pRoot->events.end(); it != end; ++it) + { + _blockIndex = *it; + const auto& item = easyBlock(_blockIndex); + auto left = sceneView->time2position(item.tree.node->begin()); + + if (left - dw > _pos.x()) + break; // This is first totally invisible item. No need to check other items. + + decltype(left) width = MIN_WIDTH; + if (left + width + dw < _pos.x()) // This item is not visible + continue; + + return &item; + } + } + + return nullptr; + } + + // The Y position is inside blocks range + + const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f; + + const auto currentScale = view()->scale(); + const auto dw = 5. / currentScale; + unsigned int i = 0; + size_t itemIndex = ::std::numeric_limits::max(); + size_t firstItem = 0, lastItem = static_cast(level0.size()); + while (i <= levelIndex) + { + const auto& level = m_levels[i]; + + // Search for first visible item + auto first = ::std::lower_bound(level.begin() + firstItem, level.begin() + lastItem, _pos.x(), [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); + + if (first != level.end()) + { + itemIndex = first - level.begin(); + if (itemIndex != 0) + --itemIndex; + } + else + { + itemIndex = level.size() - 1; + } + + for (auto size = level.size(); itemIndex < size; ++itemIndex) + { + const auto& item = level[itemIndex]; + static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max(item.children_begin); + + if (item.left() - dw > _pos.x()) + { + return nullptr; + } + + const auto item_width = ::std::max(item.width(), MIN_WIDTH); + if (item.left() + item_width + dw < _pos.x()) + { + continue; + } + + const auto w = item_width * currentScale; + const auto& guiItem = easyBlock(item.block); + if (i == levelIndex || (w < EASY_GLOBALS.blocks_narrow_size && EASY_GLOBALS.hide_narrow_children) || !guiItem.expanded) + { + _blockIndex = item.block; + return &guiItem; + } + + if (item.children_begin == MAX_CHILD_INDEX) + { + if (itemIndex != 0) + { + auto j = itemIndex; + firstItem = 0; + do { + + --j; + const auto& item2 = level[j]; + if (item2.children_begin != MAX_CHILD_INDEX) + { + firstItem = item2.children_begin; + break; + } + + } while (j != 0); + } + else + { + firstItem = 0; + } + } + else + { + firstItem = item.children_begin; + } + + lastItem = m_levels[i + 1].size(); + for (auto j = itemIndex + 1; j < size; ++j) + { + const auto& item2 = level[j]; + if (item2.children_begin != MAX_CHILD_INDEX) + { + lastItem = item2.children_begin; + break; + } + } + + break; + } + + ++i; + } + + return nullptr; +} + +const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersectEvent(const QPointF& _pos) const +{ + if (m_pRoot->sync.empty()) + { + return nullptr; + } + + const auto top = y() - EVENT_HEIGHT; + if (top > _pos.y()) + { + return nullptr; + } + + const auto bottom = top + EVENT_HEIGHT + 2; + if (bottom < _pos.y()) + { + return nullptr; + } + + const auto sceneView = view(); + auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value) + { + return sceneView->time2position(easyBlocksTree(_index).node->begin()) < _value; + }); + + if (firstSync == m_pRoot->sync.end()) + firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1; + else if (firstSync != m_pRoot->sync.begin()) + --firstSync; + + const auto dw = 4. / view()->scale(); + for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it) + { + const auto& item = easyBlock(*it); + + const auto left = sceneView->time2position(item.tree.node->begin()) - dw; + if (left > _pos.x()) + break; + + const auto right = sceneView->time2position(item.tree.node->end()) + dw; + if (right < _pos.x()) + continue; + + return &item; + } + + return nullptr; +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) +{ + m_boundingRect.setRect(x, y, w, h); +} + +void EasyGraphicsItem::setBoundingRect(const QRectF& _rect) +{ + m_boundingRect = _rect; +} + +////////////////////////////////////////////////////////////////////////// + +::profiler::thread_id_t EasyGraphicsItem::threadId() const +{ + return m_pRoot->thread_id; +} + +////////////////////////////////////////////////////////////////////////// + +uint8_t EasyGraphicsItem::levels() const +{ + return static_cast(m_levels.size()); +} + +float EasyGraphicsItem::levelY(uint8_t _level) const +{ + return y() + static_cast(_level) * static_cast(::profiler_gui::GRAPHICS_ROW_SIZE_FULL); +} + +void EasyGraphicsItem::setLevels(uint8_t _levels) +{ + typedef decltype(m_levelsIndexes) IndexesT; + static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max(); + + m_levels.resize(_levels); + m_levelsIndexes.resize(_levels, MAX_CHILD_INDEX); + m_rightBounds.resize(_levels, -1e100); +} + +void EasyGraphicsItem::reserve(uint8_t _level, unsigned int _items) +{ + m_levels[_level].reserve(_items); +} + +////////////////////////////////////////////////////////////////////////// + +const EasyGraphicsItem::Children& EasyGraphicsItem::items(uint8_t _level) const +{ + return m_levels[_level]; +} + +const ::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(uint8_t _level, unsigned int _index) const +{ + return m_levels[_level][_index]; +} + +::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(uint8_t _level, unsigned int _index) +{ + return m_levels[_level][_index]; +} + +unsigned int EasyGraphicsItem::addItem(uint8_t _level) +{ + m_levels[_level].emplace_back(); + return static_cast(m_levels[_level].size() - 1); +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/profiler_gui/easy_graphics_item.h b/3rdparty/easyprofiler/profiler_gui/easy_graphics_item.h new file mode 100644 index 0000000..a980e58 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_graphics_item.h @@ -0,0 +1,195 @@ +/************************************************************************ +* file name : easy_graphics_item.h +* ----------------- : +* creation time : 2016/09/15 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of EasyGraphicsItem - an item +* : used to draw profiler blocks on graphics scene. +* ----------------- : +* change log : * 2016/09/15 Victor Zarubkin: moved sources from blocks_graphics_view.h/.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_GRAPHICS_ITEM_H +#define EASY_GRAPHICS_ITEM_H + +#include + +#include +#include +#include + +#include + +#include "common_types.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +class EasyGraphicsView; + +class EasyGraphicsItem : public QGraphicsItem +{ + typedef ::profiler_gui::EasyItems Children; + typedef ::std::vector DrawIndexes; + typedef ::std::vector RightBounds; + typedef ::std::vector Sublevels; + + DrawIndexes m_levelsIndexes; ///< Indexes of first item on each level from which we must start painting + RightBounds m_rightBounds; ///< + Sublevels m_levels; ///< Arrays of items for each level + + QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem) + QString m_threadName; ///< + const ::profiler::BlocksTreeRoot* m_pRoot; ///< Pointer to the root profiler block (thread block). Used by ProfTreeWidget to restore hierarchy. + uint8_t m_index; ///< This item's index in the list of items of EasyGraphicsView + +public: + + explicit EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root); + virtual ~EasyGraphicsItem(); + + // Public virtual methods + + QRectF boundingRect() const override; + + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + +public: + + // Public non-virtual methods + + void validateName(); + + const ::profiler::BlocksTreeRoot* root() const; + const QString& threadName() const; + + QRect getRect() const; + + void setBoundingRect(qreal x, qreal y, qreal w, qreal h); + void setBoundingRect(const QRectF& _rect); + + ::profiler::thread_id_t threadId() const; + + ///< Returns number of levels + uint8_t levels() const; + + float levelY(uint8_t _level) const; + + /** \brief Sets number of levels. + + \note Must be set before doing anything else. + + \param _levels Desired number of levels */ + void setLevels(uint8_t _levels); + + /** \brief Reserves memory for desired number of items on specified level. + + \param _level Index of the level + \param _items Desired number of items on this level */ + void reserve(uint8_t _level, unsigned int _items); + + /**\brief Returns reference to the array of items of specified level. + + \param _level Index of the level */ + const Children& items(uint8_t _level) const; + + /**\brief Returns reference to the item with required index on specified level. + + \param _level Index of the level + \param _index Index of required item */ + const ::profiler_gui::EasyBlockItem& getItem(uint8_t _level, unsigned int _index) const; + + /**\brief Returns reference to the item with required index on specified level. + + \param _level Index of the level + \param _index Index of required item */ + ::profiler_gui::EasyBlockItem& getItem(uint8_t _level, unsigned int _index); + + /** \brief Adds new item to required level. + + \param _level Index of the level + + \retval Index of the new created item */ + unsigned int addItem(uint8_t _level); + + /** \brief Finds top-level blocks which are intersects with required selection zone. + + \note Found blocks will be added into the array of selected blocks. + + \param _left Left bound of the selection zone + \param _right Right bound of the selection zone + \param _blocks Reference to the array of selected blocks */ + void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const; + + const ::profiler_gui::EasyBlock* intersect(const QPointF& _pos, ::profiler::block_index_t& _blockIndex) const; + const ::profiler_gui::EasyBlock* intersectEvent(const QPointF& _pos) const; + +private: + + ///< Returns pointer to the EasyGraphicsView widget. + const EasyGraphicsView* view() const; + +#ifdef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT + void paintChildren(const float _minWidth, const int _narrowSizeHalf, const uint8_t _levelsNumber, QPainter* _painter, struct EasyPainterInformation& p, ::profiler_gui::EasyBlockItem& _item, const ::profiler_gui::EasyBlock& _itemBlock, RightBounds& _rightBounds, uint8_t _level, int8_t _mode); +#endif + +public: + + // Public inline methods + + ///< Returns this item's index in the list of graphics items of EasyGraphicsView + inline uint8_t index() const + { + return m_index; + } + +}; // END of class EasyGraphicsItem. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_GRAPHICS_ITEM_H diff --git a/3rdparty/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp b/3rdparty/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp new file mode 100644 index 0000000..c7dee10 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_graphics_scrollbar.cpp @@ -0,0 +1,2078 @@ +/************************************************************************ +* file name : easy_graphics_scrollbar.cpp +* ----------------- : +* creation time : 2016/07/04 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : . +* ----------------- : +* change log : * 2016/07/04 Victor Zarubkin: Initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "easy_graphics_scrollbar.h" +#include "globals.h" + + +// TODO: use profiler_core/spin_lock.h + +#if defined(_WIN32) && defined(EASY_GUI_USE_CRITICAL_SECTION) +# include +# ifdef min +# undef min +# endif +# ifdef max +# undef max +# endif + +namespace profiler_gui { + void spin_lock::lock() { + EnterCriticalSection((CRITICAL_SECTION*)m_lock); + } + + void spin_lock::unlock() { + LeaveCriticalSection((CRITICAL_SECTION*)m_lock); + } + + spin_lock::spin_lock() : m_lock(new CRITICAL_SECTION) { + InitializeCriticalSection((CRITICAL_SECTION*)m_lock); + } + + spin_lock::~spin_lock() { + DeleteCriticalSection((CRITICAL_SECTION*)m_lock); + delete ((CRITICAL_SECTION*)m_lock); + } +} +#endif + +////////////////////////////////////////////////////////////////////////// + +EASY_CONSTEXPR int DEFAULT_TOP = -40; +EASY_CONSTEXPR int DEFAULT_HEIGHT = 80; +EASY_CONSTEXPR int INDICATOR_SIZE = 6; +EASY_CONSTEXPR int INDICATOR_SIZE_x2 = INDICATOR_SIZE << 1; +EASY_CONSTEXPR int HIST_COLUMN_MIN_HEIGHT = 2; +EASY_CONSTEXPR int WORKER_THREAD_CHECK_INTERVAL = 40; +EASY_CONSTEXPR int BOUNDARY_TIMER_INTERVAL = 100; + +////////////////////////////////////////////////////////////////////////// + +using estd::sqr; + +inline qreal calculate_color1(qreal h, qreal, qreal k) +{ + return std::min(h * k, 0.9999999); +} + +inline qreal calculate_color2(qreal, qreal duration, qreal k) +{ + return std::min(sqr(sqr(duration)) * k, 0.9999999); +} + +////////////////////////////////////////////////////////////////////////// + +EasyGraphicsSliderItem::EasyGraphicsSliderItem(bool _main) : Parent(), m_halfwidth(0), m_bMain(_main) +{ + m_indicator.reserve(3); + + if (_main) + { + m_indicator.push_back(QPointF(0, DEFAULT_TOP + INDICATOR_SIZE)); + m_indicator.push_back(QPointF(-INDICATOR_SIZE, DEFAULT_TOP)); + m_indicator.push_back(QPointF(INDICATOR_SIZE, DEFAULT_TOP)); + } + else + { + m_indicator.push_back(QPointF(0, DEFAULT_TOP + DEFAULT_HEIGHT - INDICATOR_SIZE)); + m_indicator.push_back(QPointF(-INDICATOR_SIZE, DEFAULT_TOP + DEFAULT_HEIGHT)); + m_indicator.push_back(QPointF(INDICATOR_SIZE, DEFAULT_TOP + DEFAULT_HEIGHT)); + } + + setWidth(1); + setBrush(Qt::SolidPattern); +} + +EasyGraphicsSliderItem::~EasyGraphicsSliderItem() +{ + +} + +void EasyGraphicsSliderItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) +{ + if (static_cast(scene()->parent())->bindMode()) + { + return; + } + + const auto currentScale = static_cast(scene()->parent())->getWindowScale(); + const auto br = rect(); + + qreal w = width() * currentScale; + QRectF r(br.left() * currentScale, br.top() + INDICATOR_SIZE, w, br.height() - INDICATOR_SIZE_x2); + const auto r_right = r.right(); + const auto r_bottom = r.bottom(); + auto b = brush(); + + _painter->save(); + _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true); + _painter->setBrush(b); + + if (w > 1) + { + _painter->setPen(Qt::NoPen); + _painter->drawRect(r); + + // Draw left and right borders + auto cmode = _painter->compositionMode(); + if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion); + _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb())); + _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom)); + _painter->drawLine(QPointF(r_right, r.top()), QPointF(r_right, r_bottom)); + if (!m_bMain) _painter->setCompositionMode(cmode); + } + else + { + _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb())); + _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom)); + if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion); + } + + // Draw triangle indicators for small slider + _painter->setTransform(QTransform::fromTranslate(r.left() + w * 0.5, 0), true); + _painter->setPen(b.color().rgb()); + _painter->drawPolygon(m_indicator); + + _painter->restore(); +} + +qreal EasyGraphicsSliderItem::width() const +{ + return m_halfwidth * 2.0; +} + +qreal EasyGraphicsSliderItem::halfwidth() const +{ + return m_halfwidth; +} + +void EasyGraphicsSliderItem::setWidth(qreal _width) +{ + m_halfwidth = _width * 0.5; + setRect(-m_halfwidth, DEFAULT_TOP, _width, DEFAULT_HEIGHT); +} + +void EasyGraphicsSliderItem::setHalfwidth(qreal _halfwidth) +{ + m_halfwidth = _halfwidth; + setRect(-m_halfwidth, DEFAULT_TOP, m_halfwidth * 2.0, DEFAULT_HEIGHT); +} + +void EasyGraphicsSliderItem::setColor(QRgb _color) +{ + setColor(QColor::fromRgba(_color)); +} + +void EasyGraphicsSliderItem::setColor(const QColor& _color) +{ + auto b = brush(); + b.setColor(_color); + setBrush(b); +} + +////////////////////////////////////////////////////////////////////////// + +EasyHistogramItem::EasyHistogramItem() : Parent(nullptr) + , m_threadDuration(0) + , m_threadProfiledTime(0) + , m_threadWaitTime(0) + , m_pSource(nullptr) + , m_workerImage(nullptr) + , m_topDuration(0) + , m_maxDuration(0) + , m_minDuration(0) + , m_imageOrigin(0) + , m_imageScale(1) + , m_workerImageOrigin(0) + , m_workerImageScale(1) + , m_workerTopDuration(0) + , m_workerBottomDuration(0) + , m_blockTotalDuraion(0) + , m_timer(::std::bind(&This::onTimeout, this)) + , m_boundaryTimer([this](){ updateImage(); }, true) + , m_pProfilerThread(nullptr) + , m_threadId(0) + , m_blockId(::profiler_gui::numeric_max()) + , m_timeouts(0) + , m_timeUnits(::profiler_gui::TimeUnits_auto) + , m_regime(Hist_Pointer) + , m_bPermitImageUpdate(false) +{ + m_bReady = ATOMIC_VAR_INIT(false); +} + +EasyHistogramItem::~EasyHistogramItem() +{ + m_bReady.store(true, ::std::memory_order_release); + if (m_workerThread.joinable()) + m_workerThread.join(); + delete m_workerImage; +} + +QRectF EasyHistogramItem::boundingRect() const +{ + return m_boundingRect; +} + +void EasyHistogramItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) +{ + if (!m_bPermitImageUpdate || (m_regime == Hist_Pointer && m_pSource == nullptr) || (m_regime == Hist_Id && (m_threadId == 0 || ::profiler_gui::is_max(m_blockId)))) + return; + + if (m_regime == Hist_Pointer) + paintByPtr(_painter); + else + paintById(_painter); +} + +void EasyHistogramItem::paintBusyIndicator(QPainter* _painter, qreal _current_scale) +{ + const auto width = m_boundingRect.width() * _current_scale; + const auto h = _painter->fontMetrics().height(); + + _painter->setPen(::profiler_gui::TEXT_COLOR); + _painter->drawText(QRectF(0, m_boundingRect.top(), width, m_boundingRect.height() - h), + Qt::AlignCenter, "Generating image"); + _painter->drawText(QRectF(0, m_boundingRect.top() + h, width, m_boundingRect.height() - h), + Qt::AlignCenter, QString(m_timeouts, QChar('.'))); +} + +void EasyHistogramItem::paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, qreal _top_width, qreal _mouse_y, qreal _delta_time, int _font_h) +{ + if (_font_h != 0 && _top < _mouse_y && _mouse_y < _bottom) + { + const int half_font_h = _font_h >> 1; + + _painter->setPen(Qt::blue); + + const auto mouseStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration + _delta_time * (_bottom - _mouse_y) / _height, 3); + qreal mouseIndicatorRight = _width; + if (_mouse_y < _top + half_font_h) + mouseIndicatorRight = _top_width; + + qreal mouseIndicatorLeft = 0; + const QRectF rect(0, _mouse_y - _font_h, _width, _font_h << 1); + if (_mouse_y > _bottom - half_font_h) + { + _painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, mouseStr); + } + else if (_mouse_y < _top + half_font_h) + { + _painter->drawText(rect, Qt::AlignLeft | Qt::AlignBottom, mouseStr); + } + else + { + _painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, mouseStr); + mouseIndicatorLeft = _painter->fontMetrics().width(mouseStr) + 3; + } + + _painter->drawLine(QLineF(mouseIndicatorLeft, _mouse_y, mouseIndicatorRight, _mouse_y)); + } +} + +void EasyHistogramItem::paintByPtr(QPainter* _painter) +{ + const auto widget = static_cast(scene()->parent()); + const bool bindMode = widget->bindMode(); + const auto currentScale = widget->getWindowScale(); + const auto bottom = m_boundingRect.bottom(); + const auto width = m_boundingRect.width() * currentScale; + const auto dtime = m_topDuration - m_bottomDuration; + const auto maxColumnHeight = m_boundingRect.height(); + const auto coeff = (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + + QRectF rect; + QBrush brush(Qt::SolidPattern); + //QRgb previousColor = 0; + + _painter->save(); + _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true); + + if (!m_pSource->empty()) + { + _painter->setPen(Qt::NoPen); + + if (!bindMode) + _painter->drawImage(0, m_boundingRect.top(), m_mainImage); + else + { + const auto range = widget->sliderWidth(); + const auto minimum = widget->value(); + const auto slider_k = widget->range() / range; + + /*if (false)//slider_k < 8) + { + _painter->setTransform(QTransform::fromScale(slider_k, 1), true); + _painter->drawImage((widget->minimum() - minimum) * currentScale, m_boundingRect.top(), m_mainImage); + _painter->setTransform(QTransform::fromScale(1. / slider_k, 1), true); + } + else*/ + { + const auto deltaScale = slider_k / m_imageScale; + _painter->setTransform(QTransform::fromScale(deltaScale, 1), true); + _painter->drawImage((widget->minimum() + m_imageOrigin - minimum) * currentScale * m_imageScale, m_boundingRect.top(), m_mainImage); + _painter->setTransform(QTransform::fromScale(1. / deltaScale, 1), true); + } + + /*if (false) + { + const bool gotFrame = EASY_GLOBALS.frame_time > 1e-6f; + qreal frameCoeff = 1; + if (gotFrame) + { + if (EASY_GLOBALS.frame_time <= m_bottomDuration) + frameCoeff = m_boundingRect.height(); + else + frameCoeff = 0.9 / EASY_GLOBALS.frame_time; + } + + auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1; + auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / m_boundingRect.height(); + + const auto& items = *m_pSource; + const auto maximum = minimum + range; + const auto realScale = currentScale * slider_k; + const auto offset = minimum * realScale; + + auto first = ::std::lower_bound(items.begin(), items.end(), minimum, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); + + if (first != items.end()) + { + if (first != items.begin()) + --first; + } + else + { + first = items.begin() + items.size() - 1; + } + + qreal previous_x = -1e30, previous_h = -1e30; + for (auto it = first, end = items.end(); it != end; ++it) + { + // Draw rectangle + + if (it->left() > maximum) + break; + + if (it->right() < minimum) + continue; + + const qreal item_x = it->left() * realScale - offset; + const qreal item_w = ::std::max(it->width() * realScale, 1.0); + const qreal item_r = item_x + item_w; + const qreal h = it->width() <= m_bottomDuration ? HIST_COLUMN_MIN_HEIGHT : + (it->width() > m_topDuration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (it->width() - m_bottomDuration) * coeff)); + + if (h < previous_h && item_r < previous_x) + continue; + + const auto col = calculate_color(h, it->width(), k); + const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb(); + + if (previousColor != color) + { + // Set background color brush for rectangle + previousColor = color; + brush.setColor(QColor::fromRgba(0xc0000000 | color)); + _painter->setBrush(brush); + } + + rect.setRect(item_x, bottom - h, item_w, h); + _painter->drawRect(rect); + + previous_x = item_r; + previous_h = h; + } + }*/ + } + } + + //if (!m_bReady.load(::std::memory_order_acquire)) + // paintBusyIndicator(_painter, currentScale); + + qreal top_width = width, bottom_width = width; + int font_h = 0; + if (!m_topDurationStr.isEmpty()) + { + rect.setRect(0, m_boundingRect.top() - INDICATOR_SIZE, width - 3, m_boundingRect.height() + INDICATOR_SIZE_x2); + + if (m_timeUnits != EASY_GLOBALS.time_units) + { + m_timeUnits = EASY_GLOBALS.time_units; + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + } + + auto fm = _painter->fontMetrics(); + font_h = fm.height(); + //bottom_width -= fm.width(m_bottomDurationStr) + 7; + top_width -= fm.width(m_topDurationStr) + 7; + + _painter->setPen(m_topDuration < m_maxDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_topDurationStr); + + rect.setRect(0, bottom, width - 3, font_h); + _painter->setPen(m_bottomDuration > m_minDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_bottomDurationStr); + } + + _painter->setPen(Qt::darkGray); + _painter->drawLine(QLineF(0, bottom, bottom_width, bottom)); + _painter->drawLine(QLineF(0, m_boundingRect.top(), top_width, m_boundingRect.top())); + + paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, maxColumnHeight - HIST_COLUMN_MIN_HEIGHT, top_width, m_mouseY, dtime, font_h); + + if (m_bottomDuration < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topDuration) + { + // Draw marker displaying expected frame_time step + const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomDuration) * coeff; + _painter->setPen(Qt::DashLine); + + auto w = width; + const auto boundary = INDICATOR_SIZE - font_h; + if (h < (m_boundingRect.top() - boundary)) + w = top_width; + else if (h > (bottom + boundary)) + w = bottom_width; + + _painter->drawLine(QLineF(0, h, w, h)); + } + + _painter->setPen(::profiler_gui::TEXT_COLOR); + rect.setRect(0, bottom + 2, width, widget->defaultFontHeight()); + const auto eventsSize = m_pProfilerThread->events.size(); + _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | duration: %2 | profiled: %3 (%4%) | wait: %5 (%6%) | %7 frames | %8 blocks | %9 markers") + .arg(m_threadName) + .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadDuration)) + .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadProfiledTime)) + .arg(m_threadDuration ? QString::number(100. * (double)m_threadProfiledTime / (double)m_threadDuration, 'f', 2) : QString("0")) + .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadWaitTime)) + .arg(m_threadDuration ? QString::number(100. * (double)m_threadWaitTime / (double)m_threadDuration, 'f', 2) : QString("0")) + .arg(m_pProfilerThread->frames_number) + .arg(m_pProfilerThread->blocks_number - eventsSize) + .arg(eventsSize)); + + _painter->drawText(rect, Qt::AlignLeft, bindMode ? " MODE: zoom" : " MODE: overview"); + + _painter->restore(); +} + +void EasyHistogramItem::paintById(QPainter* _painter) +{ + const auto widget = static_cast(scene()->parent()); + const bool bindMode = widget->bindMode(); + const auto currentScale = widget->getWindowScale(); + const auto bottom = m_boundingRect.bottom(); + const auto width = m_boundingRect.width() * currentScale; + const auto dtime = m_topDuration - m_bottomDuration; + const auto maxColumnHeight = m_boundingRect.height(); + const auto coeff = (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + + QRectF rect; + QBrush brush(Qt::SolidPattern); + //QRgb previousColor = 0; + + _painter->save(); + _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true); + + const auto& items = m_selectedBlocks; + if (!items.empty()) + { + _painter->setPen(Qt::NoPen); + + if (!bindMode) + _painter->drawImage(0, m_boundingRect.top(), m_mainImage); + else + { + const auto range = widget->sliderWidth(); + auto minimum = widget->value(); + const auto slider_k = widget->range() / range; + + /*if (false)//slider_k < 8) + { + _painter->setTransform(QTransform::fromScale(slider_k, 1), true); + _painter->drawImage((widget->minimum() - minimum) * currentScale, m_boundingRect.top(), m_mainImage); + _painter->setTransform(QTransform::fromScale(1. / slider_k, 1), true); + } + else*/ + { + const auto deltaScale = slider_k / m_imageScale; + _painter->setTransform(QTransform::fromScale(deltaScale, 1), true); + _painter->drawImage((widget->minimum() + m_imageOrigin - minimum) * currentScale * m_imageScale, m_boundingRect.top(), m_mainImage); + _painter->setTransform(QTransform::fromScale(1. / deltaScale, 1), true); + } + + /*if (false) + { + minimum *= 1e3; + const auto maximum = minimum + range * 1e3; + const auto realScale = currentScale * slider_k; + const auto offset = minimum * realScale; + + auto first = ::std::lower_bound(items.begin(), items.end(), minimum + EASY_GLOBALS.begin_time, [](::profiler::block_index_t _item, qreal _value) + { + return easyBlock(_item).tree.node->begin() < _value; + }); + + if (first != items.end()) + { + if (first != items.begin()) + --first; + } + else + { + first = items.begin() + (items.size() - 1); + } + + auto last = ::std::upper_bound(first, items.end(), maximum + EASY_GLOBALS.begin_time, [](qreal _value, ::profiler::block_index_t _item) + { + return _value < easyBlock(_item).tree.node->begin(); + }); + + const auto n = static_cast(::std::distance(first, last)); + + if (n > 0) + { + const bool gotFrame = EASY_GLOBALS.frame_time > 1e-6f; + qreal frameCoeff = 1; + if (gotFrame) + { + if (EASY_GLOBALS.frame_time <= m_bottomDuration) + frameCoeff = m_boundingRect.height(); + else + frameCoeff = 0.9 / EASY_GLOBALS.frame_time; + } + + auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1; + auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / m_boundingRect.height(); + + const auto draw = [this, &previousColor, &brush, &_painter](qreal x, qreal y, qreal w, qreal h, QRgb color) + { + m_spin.lock(); + + if (previousColor != color) + { + // Set background color brush for rectangle + previousColor = color; + brush.setColor(QColor::fromRgba(0xc0000000 | color)); + _painter->setBrush(brush); + } + + _painter->drawRect(QRectF(x, y, w, h)); + + m_spin.unlock(); + }; + + ::std::vector<::std::thread> threads; + const auto n_threads = ::std::min(n, ::std::thread::hardware_concurrency()); + threads.reserve(n_threads); + const auto n_items = n / n_threads; + for (uint32_t i = 0; i < n_threads; ++i) + { + auto begin = first + i * n_items; + threads.emplace_back([this, &draw, &maximum, &minimum, &realScale, &offset, &coeff, &calculate_color, &k, &bottom, &maxColumnHeight](decltype(begin) it, decltype(begin) end) + { + qreal previous_x = -1e30, previous_h = -1e30; + + //for (auto it = first, end = items.end(); it != end; ++it) + for (; it != end; ++it) + { + // Draw rectangle + const auto item = easyBlock(*it).tree.node; + + const auto beginTime = item->begin() - EASY_GLOBALS.begin_time; + if (beginTime > maximum) + break; + + const auto endTime = item->end() - EASY_GLOBALS.begin_time; + if (endTime < minimum) + continue; + + const qreal duration = item->duration() * 1e-3; + const qreal item_x = (beginTime * realScale - offset) * 1e-3; + const qreal item_w = ::std::max(duration * realScale, 1.0); + const qreal item_r = item_x + item_w; + const qreal h = duration <= m_bottomDuration ? HIST_COLUMN_MIN_HEIGHT : + (duration > m_topDuration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (duration - m_bottomDuration) * coeff)); + + if (h < previous_h && item_r < previous_x) + continue; + + const auto col = calculate_color(h, duration, k); + const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb(); + + draw(item_x, bottom - h, item_w, h, color); + //if (previousColor != color) + //{ + // // Set background color brush for rectangle + // previousColor = color; + // brush.setColor(QColor::fromRgba(0xc0000000 | color)); + // _painter->setBrush(brush); + //} + + //rect.setRect(item_x, bottom - h, item_w, h); + //_painter->drawRect(rect); + + previous_x = item_r; + previous_h = h; + } + }, begin, i == (n_threads - 1) ? items.end() : begin + n_items); + } + + for (auto& t : threads) + t.join(); + } + }*/ + } + } + + //if (!m_bReady.load(::std::memory_order_acquire)) + // paintBusyIndicator(_painter, currentScale); + + qreal top_width = width, bottom_width = width; + int font_h = 0; + if (!m_topDurationStr.isEmpty()) + { + rect.setRect(0, m_boundingRect.top() - INDICATOR_SIZE, width - 3, m_boundingRect.height() + INDICATOR_SIZE_x2); + + if (m_timeUnits != EASY_GLOBALS.time_units) + { + m_timeUnits = EASY_GLOBALS.time_units; + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + } + + auto fm = _painter->fontMetrics(); + font_h = fm.height(); + //bottom_width -= fm.width(m_bottomDurationStr) + 7; + top_width -= fm.width(m_topDurationStr) + 7; + + _painter->setPen(m_topDuration < m_maxDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_topDurationStr); + + rect.setRect(0, bottom, width - 3, font_h); + _painter->setPen(m_bottomDuration > m_minDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_bottomDurationStr); + } + + _painter->setPen(Qt::darkGray); + _painter->drawLine(QLineF(0, bottom, bottom_width, bottom)); + _painter->drawLine(QLineF(0, m_boundingRect.top(), top_width, m_boundingRect.top())); + + paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, maxColumnHeight - HIST_COLUMN_MIN_HEIGHT, top_width, m_mouseY, dtime, font_h); + + if (m_bottomDuration < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topDuration) + { + // Draw marker displaying required frame_time step + const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomDuration) * coeff; + _painter->setPen(Qt::DashLine); + + auto w = width; + const auto boundary = INDICATOR_SIZE - font_h; + if (h < (m_boundingRect.top() - boundary)) + w = top_width; + else if (h >(bottom + boundary)) + w = bottom_width; + + _painter->drawLine(QLineF(0, h, w, h)); + } + + _painter->setPen(::profiler_gui::TEXT_COLOR); + rect.setRect(0, bottom + 2, width, widget->defaultFontHeight()); + + if (!m_selectedBlocks.empty()) + { + _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | %2 | %3 calls | %4% of thread profiled time") + .arg(m_threadName).arg(m_blockName).arg(m_selectedBlocks.size()) + .arg(m_threadProfiledTime ? QString::number(100. * (double)m_blockTotalDuraion / (double)m_threadProfiledTime, 'f', 2) : QString("100"))); + } + else + { + _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | %2 | 0 calls").arg(m_threadName).arg(m_blockName)); + } + + _painter->drawText(rect, Qt::AlignLeft, bindMode ? " MODE: zoom" : " MODE: overview"); + + _painter->restore(); +} + +::profiler::thread_id_t EasyHistogramItem::threadId() const +{ + return m_threadId; +} + +void EasyHistogramItem::setBoundingRect(const QRectF& _rect) +{ + m_boundingRect = _rect; +} + +void EasyHistogramItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) +{ + m_boundingRect.setRect(x, y, w, h); +} + +void EasyHistogramItem::rebuildSource(HistRegime _regime) +{ + if (m_regime == _regime) + rebuildSource(); +} + +void EasyHistogramItem::rebuildSource() +{ + if (m_regime == Hist_Id) + { + m_regime = Hist_Pointer; + setSource(m_threadId, m_blockId); + } + else + { + m_regime = Hist_Id; + setSource(m_threadId, m_pSource); + } +} + +void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items) +{ + if (m_regime == Hist_Pointer && m_threadId == _thread_id && m_pSource == _items) + return; + + m_timer.stop(); + m_boundaryTimer.stop(); + + m_bReady.store(true, ::std::memory_order_release); + if (m_workerThread.joinable()) + m_workerThread.join(); + + m_blockName.clear(); + m_blockTotalDuraion = 0; + + delete m_workerImage; + m_workerImage = nullptr; + m_imageOriginUpdate = m_imageOrigin = 0; + m_imageScaleUpdate = m_imageScale = 1; + + m_selectedBlocks.clear(); + { ::profiler::BlocksTree::children_t().swap(m_selectedBlocks); } + + m_bPermitImageUpdate = false; + m_regime = Hist_Pointer; + m_pSource = _items; + m_threadId = _thread_id; + ::profiler_gui::set_max(m_blockId); + + if (m_pSource != nullptr) + { + if (m_pSource->empty()) + { + m_pSource = nullptr; + } + else + { + const auto& root = EASY_GLOBALS.profiler_blocks[_thread_id]; + m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id); + + if (root.children.empty()) + m_threadDuration = 0; + else + m_threadDuration = easyBlock(root.children.back()).tree.node->end() - easyBlock(root.children.front()).tree.node->begin(); + + m_threadProfiledTime = root.profiled_time; + m_threadWaitTime = root.wait_time; + m_pProfilerThread = &root; + m_timeUnits = EASY_GLOBALS.time_units; + + m_bReady.store(false, ::std::memory_order_release); + m_workerThread = ::std::thread([this](const ::profiler_gui::EasyItems* _source) + { + m_maxDuration = 0; + m_minDuration = 1e30; + + bool empty = true; + for (const auto& item : *_source) + { + if (m_bReady.load(::std::memory_order_acquire)) + return; + + if (easyDescriptor(easyBlock(item.block).tree.node->id()).type() == ::profiler::BlockType::Event) + continue; + + const auto w = item.width(); + + if (w > m_maxDuration) + m_maxDuration = w; + + if (w < m_minDuration) + m_minDuration = w; + + empty = false; + } + + if ((m_maxDuration - m_minDuration) < 1e-3) + { + if (m_minDuration > 0.1) + { + m_minDuration -= 0.1; + } + else + { + m_maxDuration = 0.1; + m_minDuration = 0; + } + } + + m_topDuration = m_maxDuration; + m_bottomDuration = m_minDuration; + + if (!empty) + { + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + } + else + { + m_topDurationStr.clear(); + m_bottomDurationStr.clear(); + } + + m_bReady.store(true, ::std::memory_order_release); + + }, m_pSource); + + m_timeouts = 3; + m_timer.start(WORKER_THREAD_CHECK_INTERVAL); + show(); + } + } + + if (m_pSource == nullptr) + { + m_pProfilerThread = nullptr; + m_topDurationStr.clear(); + m_bottomDurationStr.clear(); + m_threadName.clear(); + hide(); + } +} + +void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id) +{ + if (m_regime == Hist_Id && m_threadId == _thread_id && m_blockId == _block_id) + return; + + m_bPermitImageUpdate = false; // Set to false because m_workerThread have to parse input data first. This will be set to true when m_workerThread finish - see onTimeout() + m_regime = Hist_Id; + + m_timer.stop(); + m_boundaryTimer.stop(); + + m_bReady.store(true, ::std::memory_order_release); + if (m_workerThread.joinable()) + m_workerThread.join(); + + m_pSource = nullptr; + m_topDurationStr.clear(); + m_bottomDurationStr.clear(); + m_blockName.clear(); + m_blockTotalDuraion = 0; + + delete m_workerImage; + m_workerImage = nullptr; + m_imageOriginUpdate = m_imageOrigin = 0; + m_imageScaleUpdate = m_imageScale = 1; + + m_selectedBlocks.clear(); + { ::profiler::BlocksTree::children_t().swap(m_selectedBlocks); } + + m_threadId = _thread_id; + m_blockId = _block_id; + + if (m_threadId != 0 && !::profiler_gui::is_max(m_blockId)) + { + m_blockName = ::profiler_gui::toUnicode(easyDescriptor(m_blockId).name()); + + const auto& root = EASY_GLOBALS.profiler_blocks[_thread_id]; + m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id); + m_pProfilerThread = &root; + m_timeUnits = EASY_GLOBALS.time_units; + + if (root.children.empty()) + { + m_threadDuration = 0; + m_threadProfiledTime = 0; + m_threadWaitTime = 0; + + m_topDuration = m_maxDuration = 0; + m_bottomDuration = m_minDuration = 1e30; + + m_bPermitImageUpdate = true; + + m_bReady.store(true, ::std::memory_order_release); + } + else + { + m_threadDuration = easyBlock(root.children.back()).tree.node->end() - easyBlock(root.children.front()).tree.node->begin(); + m_threadProfiledTime = root.profiled_time; + m_threadWaitTime = root.wait_time; + + m_bReady.store(false, ::std::memory_order_release); + m_workerThread = ::std::thread([this](decltype(root) profiler_thread, ::profiler::block_index_t selected_block, bool _showOnlyTopLevelBlocks) + { + typedef ::std::vector<::std::pair<::profiler::block_index_t, ::profiler::block_index_t> > Stack; + + m_maxDuration = 0; + m_minDuration = 1e30; + //const auto& profiler_thread = EASY_GLOBALS.profiler_blocks[m_threadId]; + Stack stack; + stack.reserve(profiler_thread.depth); + + const bool has_selected_block = !::profiler_gui::is_max(selected_block); + + for (auto frame : profiler_thread.children) + { + const auto& frame_block = easyBlock(frame).tree; + if (frame_block.node->id() == m_blockId || (!has_selected_block && m_blockId == easyDescriptor(frame_block.node->id()).id())) + { + m_selectedBlocks.push_back(frame); + + const auto w = frame_block.node->duration(); + if (w > m_maxDuration) + m_maxDuration = w; + + if (w < m_minDuration) + m_minDuration = w; + + m_blockTotalDuraion += w; + } + + if (_showOnlyTopLevelBlocks) + continue; + + stack.push_back(::std::make_pair(frame, 0U)); + while (!stack.empty()) + { + if (m_bReady.load(::std::memory_order_acquire)) + return; + + auto& top = stack.back(); + const auto& top_children = easyBlock(top.first).tree.children; + const auto stack_size = stack.size(); + for (auto end = top_children.size(); top.second < end; ++top.second) + { + if (m_bReady.load(::std::memory_order_acquire)) + return; + + const auto child_index = top_children[top.second]; + const auto& child = easyBlock(child_index).tree; + if (child.node->id() == m_blockId || (!has_selected_block && m_blockId == easyDescriptor(child.node->id()).id())) + { + m_selectedBlocks.push_back(child_index); + + const auto w = child.node->duration(); + if (w > m_maxDuration) + m_maxDuration = w; + + if (w < m_minDuration) + m_minDuration = w; + + m_blockTotalDuraion += w; + } + + if (!child.children.empty()) + { + ++top.second; + stack.push_back(::std::make_pair(child_index, 0U)); + break; + } + } + + if (stack_size == stack.size()) + { + stack.pop_back(); + } + } + } + + if (m_selectedBlocks.empty()) + { + m_topDurationStr.clear(); + m_bottomDurationStr.clear(); + } + else + { + if (has_selected_block) + { + const auto& item = easyBlock(selected_block).tree; + if (*item.node->name() != 0) + m_blockName = ::profiler_gui::toUnicode(item.node->name()); + } + + m_maxDuration *= 1e-3; + m_minDuration *= 1e-3; + + if ((m_maxDuration - m_minDuration) < 1e-3) + { + if (m_minDuration > 0.1) + { + m_minDuration -= 0.1; + } + else + { + m_maxDuration = 0.1; + m_minDuration = 0; + } + } + + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_maxDuration, 3); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_minDuration, 3); + } + + + + m_topDuration = m_maxDuration; + m_bottomDuration = m_minDuration; + + m_bReady.store(true, ::std::memory_order_release); + + }, std::ref(root), EASY_GLOBALS.selected_block, EASY_GLOBALS.display_only_frames_on_histogram); + + m_timeouts = 3; + m_timer.start(WORKER_THREAD_CHECK_INTERVAL); + } + + show(); + } + else + { + m_pProfilerThread = nullptr; + m_threadName.clear(); + hide(); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::validateName() +{ + if (m_threadName.isEmpty()) + return; + m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.profiler_blocks[m_threadId], EASY_GLOBALS.hex_thread_id); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::onTimeout() +{ + if (!isVisible()) + { + m_timer.stop(); + return; + } + + if (++m_timeouts > 8) + m_timeouts = 3; + + if (m_bReady.load(::std::memory_order_acquire)) + { + m_timer.stop(); + if (!m_bPermitImageUpdate) + { + // Worker thread have finished parsing input data (when setSource(_block_id) was called) + m_bPermitImageUpdate = true; // From now we can update an image + updateImage(); + } + else + { + // Image updated + + if (m_workerThread.joinable()) + m_workerThread.join(); + + m_workerImage->swap(m_mainImage); + delete m_workerImage; + m_workerImage = nullptr; + + m_imageOriginUpdate = m_imageOrigin = m_workerImageOrigin; + m_imageScaleUpdate = m_imageScale = m_workerImageScale; + + if (EASY_GLOBALS.auto_adjust_histogram_height && !m_topDurationStr.isEmpty()) + { + m_topDuration = m_workerTopDuration; + m_bottomDuration = m_workerBottomDuration; + + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + } + } + } + + scene()->update(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::pickTopBoundary(qreal _y) +{ + if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty()) + { + m_topDuration = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT); + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); + m_boundaryTimer.stop(); + updateImage(); + scene()->update(); // to update top-boundary text right now + } +} + +void EasyHistogramItem::increaseTopBoundary() +{ + if (m_bPermitImageUpdate && m_topDuration < m_maxDuration && !m_topDurationStr.isEmpty()) + { + auto step = 0.05 * (m_maxDuration - m_bottomDuration); + if (m_topDuration < (m_bottomDuration + 1.25 * step)) + step = 0.1 * (m_topDuration - m_bottomDuration); + + m_topDuration = std::min(m_maxDuration, m_topDuration + step); + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); + updateImage(); + scene()->update(); // to update top-boundary text right now + + m_boundaryTimer.stop(); + m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); + } +} + +void EasyHistogramItem::decreaseTopBoundary() +{ + if (m_bPermitImageUpdate && m_topDuration > m_bottomDuration && !m_topDurationStr.isEmpty()) + { + auto step = 0.05 * (m_maxDuration - m_bottomDuration); + if (m_topDuration < (m_bottomDuration + 1.25 * step)) + step = std::max(0.1 * (m_topDuration - m_bottomDuration), 0.3); + + if (m_topDuration > (m_bottomDuration + 1.25 * step)) + { + m_topDuration = std::max(m_bottomDuration + step, m_topDuration - step); + m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); + scene()->update(); // to update top-boundary text right now + + m_boundaryTimer.stop(); + m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::pickBottomBoundary(qreal _y) +{ + if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_bottomDurationStr.isEmpty()) + { + m_bottomDuration = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + m_boundaryTimer.stop(); + updateImage(); + scene()->update(); // to update top-boundary text right now + } +} + +void EasyHistogramItem::increaseBottomBoundary() +{ + if (m_bPermitImageUpdate && m_bottomDuration < m_topDuration && !m_bottomDurationStr.isEmpty()) + { + auto step = 0.05 * (m_topDuration - m_minDuration); + if (m_bottomDuration > (m_topDuration - 1.25 * step)) + step = 0.1 * (m_topDuration - m_bottomDuration); + + if (m_bottomDuration < (m_topDuration - 1.25 * step)) + { + m_bottomDuration = std::min(m_topDuration - step, m_bottomDuration + step); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + scene()->update(); // to update bottom-boundary text right now + + m_boundaryTimer.stop(); + m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); + } + } +} + +void EasyHistogramItem::decreaseBottomBoundary() +{ + if (m_bPermitImageUpdate && m_bottomDuration > m_minDuration && !m_bottomDurationStr.isEmpty()) + { + auto step = 0.05 * (m_topDuration - m_minDuration); + if (m_bottomDuration > (m_topDuration - 1.25 * step)) + step = std::max(0.1 * (m_topDuration - m_bottomDuration), 0.3); + + m_bottomDuration = std::max(m_minDuration, m_bottomDuration - step); + m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + scene()->update(); // to update top-boundary text right now + + m_boundaryTimer.stop(); + m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::setMouseY(qreal _mouseY) +{ + m_mouseY = _mouseY; +} + +void EasyHistogramItem::pickFrameTime(qreal _y) const +{ + if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty()) + { + EASY_GLOBALS.frame_time = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT); + emit EASY_GLOBALS.events.expectedFrameTimeChanged(); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::onValueChanged() +{ + const auto widget = static_cast(scene()->parent()); + + if (!widget->bindMode()) + return; + + m_boundaryTimer.stop(); + + const auto sliderWidth_inv = 1.0 / widget->sliderWidth(); + const auto k = widget->range() * sliderWidth_inv; + + const auto deltaScale = m_imageScaleUpdate < k ? (k / m_imageScaleUpdate) : (m_imageScaleUpdate / k); + if (deltaScale > 4) { + updateImage(); + return; + } + + const auto deltaOffset = (widget->value() - m_imageOriginUpdate) * sliderWidth_inv; + if (deltaOffset < 1.5 || deltaOffset > 4.5) { + updateImage(); + return; + } + + m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::onModeChanged() +{ + if (!m_bPermitImageUpdate) + return; + + const auto widget = static_cast(scene()->parent()); + + if (!widget->bindMode() && EASY_GLOBALS.auto_adjust_histogram_height) + { + m_topDuration = m_maxDuration; + m_bottomDuration = m_minDuration; + } + + m_boundaryTimer.stop(); + updateImage(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyHistogramItem::cancelImageUpdate() +{ + if (!m_bPermitImageUpdate) + return; + + m_bReady.store(true, ::std::memory_order_release); + if (m_workerThread.joinable()) + m_workerThread.join(); + m_bReady.store(false, ::std::memory_order_release); + + delete m_workerImage; + m_workerImage = nullptr; + + m_imageOriginUpdate = m_imageOrigin; + m_imageScaleUpdate = m_imageScale; + + m_timer.stop(); +} + +void EasyHistogramItem::updateImage() +{ + if (!m_bPermitImageUpdate) + return; + + const auto widget = static_cast(scene()->parent()); + + m_bReady.store(true, ::std::memory_order_release); + if (m_workerThread.joinable()) + m_workerThread.join(); + m_bReady.store(false, ::std::memory_order_release); + + delete m_workerImage; + m_workerImage = nullptr; + + m_imageScaleUpdate = widget->range() / widget->sliderWidth(); + m_imageOriginUpdate = widget->bindMode() ? (widget->value() - widget->sliderWidth() * 3) : widget->minimum(); + + m_workerThread = ::std::thread([this](QRectF _boundingRect, HistRegime _regime, qreal _current_scale, + qreal _minimum, qreal _maximum, qreal _range, qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, + bool _bindMode, float _frame_time, ::profiler::timestamp_t _begin_time, qreal _origin, bool _autoAdjustHist) + { + updateImage(_boundingRect, _regime, _current_scale, _minimum, _maximum, _range, _value, _width, _top_duration, _bottom_duration, _bindMode, _frame_time, _begin_time, _origin, _autoAdjustHist); + m_bReady.store(true, ::std::memory_order_release); + }, m_boundingRect, m_regime, widget->getWindowScale(), widget->minimum(), widget->maximum(), widget->range(), widget->value(), widget->sliderWidth(), + m_topDuration, m_bottomDuration, widget->bindMode(), EASY_GLOBALS.frame_time, EASY_GLOBALS.begin_time, m_imageOriginUpdate, EASY_GLOBALS.auto_adjust_histogram_height); + + m_timeouts = 3; + m_timer.start(WORKER_THREAD_CHECK_INTERVAL); +} + +void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qreal _current_scale, + qreal _minimum, qreal _maximum, qreal _range, + qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, + bool _bindMode, float _frame_time, ::profiler::timestamp_t _begin_time, + qreal _origin, bool _autoAdjustHist) +{ + const auto bottom = _boundingRect.height();//_boundingRect.bottom(); + const auto screenWidth = _boundingRect.width() * _current_scale; + const auto maxColumnHeight = _boundingRect.height(); + const auto viewScale = _range / _width; + + if (_bindMode) + { + m_workerImageScale = viewScale; + m_workerImageOrigin = _value - _width * 3; + m_workerImage = new QImage(screenWidth * 7 + 0.5, _boundingRect.height(), QImage::Format_ARGB32); + } + else + { + m_workerImageScale = 1; + m_workerImageOrigin = _minimum; + m_workerImage = new QImage(screenWidth + 0.5, _boundingRect.height(), QImage::Format_ARGB32); + } + + m_workerImage->fill(0); + QPainter p(m_workerImage); + p.setPen(Qt::NoPen); + + QRectF rect; + QBrush brush(Qt::SolidPattern); + QRgb previousColor = 0; + + qreal previous_x = -1e30, previous_h = -1e30, offset = 0.; + auto realScale = _current_scale; + + const bool gotFrame = _frame_time > 1e-6f; + qreal frameCoeff = 1; + if (gotFrame) + { + if (_frame_time <= _bottom_duration) + frameCoeff = _boundingRect.height(); + else + frameCoeff = 0.9 / _frame_time; + } + + auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1; + auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / _boundingRect.height(); + + if (_regime == Hist_Pointer) + { + const auto& items = *m_pSource; + if (items.empty()) + return; + + auto first = items.begin(); + + if (_bindMode) + { + _minimum = m_workerImageOrigin; + _maximum = m_workerImageOrigin + _width * 7; + realScale *= viewScale; + offset = _minimum * realScale; + + first = ::std::lower_bound(items.begin(), items.end(), _minimum, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); + + if (first != items.end()) + { + if (first != items.begin()) + --first; + } + else + { + first = items.begin() + items.size() - 1; + } + + if (_autoAdjustHist) + { + const auto maxVal = _value + _width; + decltype(_top_duration) maxDuration = 0; + decltype(_bottom_duration) minDuration = 1e30; + size_t iterations = 0; + for (auto it = first, end = items.end(); it != end; ++it) + { + // Draw rectangle + if (it->left() > maxVal) + break; + + if (it->right() < _value) + continue; + + if (maxDuration < it->width()) + maxDuration = it->width(); + + if (minDuration > it->width()) + minDuration = it->width(); + + ++iterations; + } + + if (iterations) + { + _top_duration = maxDuration; + _bottom_duration = minDuration; + + if ((_top_duration - _bottom_duration) < 1e-3) + { + if (_bottom_duration > 0.1) + { + _bottom_duration -= 0.1; + } + else + { + _top_duration = 0.1; + _bottom_duration = 0; + } + } + } + } + } + + const auto dtime = _top_duration - _bottom_duration; + const auto coeff = (_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + + for (auto it = first, end = items.end(); it != end; ++it) + { + // Draw rectangle + if (it->left() > _maximum) + break; + + if (it->right() < _minimum) + continue; + + const qreal item_x = it->left() * realScale - offset; + const qreal item_w = ::std::max(it->width() * realScale, 1.0); + const qreal item_r = item_x + item_w; + const qreal h = it->width() <= _bottom_duration ? HIST_COLUMN_MIN_HEIGHT : + (it->width() > _top_duration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (it->width() - _bottom_duration) * coeff)); + + if (h < previous_h && item_r < previous_x) + continue; + + const auto col = calculate_color(h, it->width(), k); + const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb(); + + if (previousColor != color) + { + // Set background color brush for rectangle + previousColor = color; + brush.setColor(QColor::fromRgba(0xc0000000 | color)); + p.setBrush(brush); + } + + rect.setRect(item_x, bottom - h, item_w, h); + p.drawRect(rect); + + previous_x = item_r; + previous_h = h; + } + } + else + { + auto first = m_selectedBlocks.begin(); + + if (_bindMode) + { + _minimum = m_workerImageOrigin; + _maximum = m_workerImageOrigin + _width * 7; + realScale *= viewScale; + offset = _minimum * 1e3 * realScale; + + first = ::std::lower_bound(m_selectedBlocks.begin(), m_selectedBlocks.end(), _minimum * 1e3 + _begin_time, [](::profiler::block_index_t _item, qreal _value) + { + return easyBlock(_item).tree.node->begin() < _value; + }); + + if (first != m_selectedBlocks.end()) + { + if (first != m_selectedBlocks.begin()) + --first; + } + else + { + first = m_selectedBlocks.begin() + m_selectedBlocks.size() - 1; + } + + _minimum *= 1e3; + _maximum *= 1e3; + + if (_autoAdjustHist) + { + const auto minVal = _value * 1e3, maxVal = (_value + _width) * 1e3; + decltype(_top_duration) maxDuration = 0; + decltype(_bottom_duration) minDuration = 1e30; + size_t iterations = 0; + for (auto it = first, end = m_selectedBlocks.end(); it != end; ++it) + { + const auto item = easyBlock(*it).tree.node; + + const auto beginTime = item->begin() - _begin_time; + if (beginTime > maxVal) + break; + + const auto endTime = item->end() - _begin_time; + if (endTime < minVal) + continue; + + const qreal duration = item->duration() * 1e-3; + + if (maxDuration < duration) + maxDuration = duration; + + if (minDuration > duration) + minDuration = duration; + + ++iterations; + } + + if (iterations) + { + _top_duration = maxDuration; + _bottom_duration = minDuration; + + if ((_top_duration - _bottom_duration) < 1e-3) + { + if (_bottom_duration > 0.1) + { + _bottom_duration -= 0.1; + } + else + { + _top_duration = 0.1; + _bottom_duration = 0; + } + } + } + } + } + else + { + _minimum *= 1e3; + _maximum *= 1e3; + } + + const auto dtime = _top_duration - _bottom_duration; + const auto coeff = (_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + + for (auto it = first, end = m_selectedBlocks.end(); it != end; ++it) + { + // Draw rectangle + const auto item = easyBlock(*it).tree.node; + + const auto beginTime = item->begin() - _begin_time; + if (beginTime > _maximum) + break; + + const auto endTime = item->end() - _begin_time; + if (endTime < _minimum) + continue; + + const qreal duration = item->duration() * 1e-3; + const qreal item_x = (beginTime * realScale - offset) * 1e-3; + const qreal item_w = ::std::max(duration * realScale, 1.0); + const qreal item_r = item_x + item_w; + const auto h = duration <= _bottom_duration ? HIST_COLUMN_MIN_HEIGHT : + (duration > _top_duration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (duration - _bottom_duration) * coeff)); + + if (h < previous_h && item_r < previous_x) + continue; + + const auto col = calculate_color(h, duration, k); + const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb(); + + if (previousColor != color) + { + // Set background color brush for rectangle + previousColor = color; + brush.setColor(QColor::fromRgba(0xc0000000 | color)); + p.setBrush(brush); + } + + rect.setRect(item_x, bottom - h, item_w, h); + p.drawRect(rect); + + previous_x = item_r; + previous_h = h; + } + } + + m_workerTopDuration = _top_duration; + m_workerBottomDuration = _bottom_duration; +} + +////////////////////////////////////////////////////////////////////////// + +EasyGraphicsScrollbar::EasyGraphicsScrollbar(QWidget* _parent) + : Parent(_parent) + , m_minimumValue(0) + , m_maximumValue(500) + , m_value(10) + , m_windowScale(1) + , m_mouseButtons(Qt::NoButton) + , m_slider(nullptr) + , m_chronometerIndicator(nullptr) + , m_histogramItem(nullptr) + , m_defaultFontHeight(0) + , m_bScrolling(false) + , m_bBindMode(false) + , m_bLocked(false) +{ + setCacheMode(QGraphicsView::CacheNone); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + //setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setOptimizationFlag(QGraphicsView::DontSavePainterState, true); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setContentsMargins(0, 0, 0, 0); + + auto selfScene = new QGraphicsScene(this); + m_defaultFontHeight = QFontMetrics(selfScene->font()).height(); + selfScene->setSceneRect(0, DEFAULT_TOP, 500, DEFAULT_HEIGHT + m_defaultFontHeight + 2); + setFixedHeight(DEFAULT_HEIGHT + m_defaultFontHeight + 2); + + setScene(selfScene); + + m_histogramItem = new EasyHistogramItem(); + m_histogramItem->setPos(0, 0); + m_histogramItem->setBoundingRect(0, DEFAULT_TOP + INDICATOR_SIZE, scene()->width(), DEFAULT_HEIGHT - INDICATOR_SIZE_x2); + selfScene->addItem(m_histogramItem); + m_histogramItem->hide(); + + m_chronometerIndicator = new EasyGraphicsSliderItem(false); + m_chronometerIndicator->setPos(0, 0); + m_chronometerIndicator->setColor(0x40000000 | ::profiler_gui::CHRONOMETER_COLOR.rgba()); + selfScene->addItem(m_chronometerIndicator); + m_chronometerIndicator->hide(); + + m_slider = new EasyGraphicsSliderItem(true); + m_slider->setPos(0, 0); + m_slider->setColor(0x40c0c0c0); + selfScene->addItem(m_slider); + m_slider->hide(); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, [this]() + { + if (m_histogramItem->isVisible()) + { + m_histogramItem->updateImage(); + scene()->update(); + } + }); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::autoAdjustHistogramChanged, [this]() + { + if (m_histogramItem->isVisible()) + m_histogramItem->onModeChanged(); + }); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::displayOnlyFramesOnHistogramChanged, [this]() + { + if (m_histogramItem->isVisible()) + m_histogramItem->rebuildSource(EasyHistogramItem::Hist_Id); + }); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged); + + centerOn(0, 0); +} + +EasyGraphicsScrollbar::~EasyGraphicsScrollbar() +{ + +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsScrollbar::onThreadViewChanged() +{ + if (m_histogramItem->isVisible()) + { + m_histogramItem->validateName(); + scene()->update(); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsScrollbar::clear() +{ + setHistogramSource(0, nullptr); + hideChrono(); + setRange(0, 100); + setSliderWidth(2); + setValue(0); +} + +////////////////////////////////////////////////////////////////////////// + +bool EasyGraphicsScrollbar::bindMode() const +{ + return m_bBindMode; +} + +qreal EasyGraphicsScrollbar::getWindowScale() const +{ + return m_windowScale; +} + +::profiler::thread_id_t EasyGraphicsScrollbar::hystThread() const +{ + return m_histogramItem->threadId(); +} + +qreal EasyGraphicsScrollbar::minimum() const +{ + return m_minimumValue; +} + +qreal EasyGraphicsScrollbar::maximum() const +{ + return m_maximumValue; +} + +qreal EasyGraphicsScrollbar::range() const +{ + return m_maximumValue - m_minimumValue; +} + +qreal EasyGraphicsScrollbar::value() const +{ + return m_value; +} + +qreal EasyGraphicsScrollbar::sliderWidth() const +{ + return m_slider->width(); +} + +qreal EasyGraphicsScrollbar::sliderHalfWidth() const +{ + return m_slider->halfwidth(); +} + +int EasyGraphicsScrollbar::defaultFontHeight() const +{ + return m_defaultFontHeight; +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsScrollbar::setValue(qreal _value) +{ + using estd::clamp; + m_value = clamp(m_minimumValue, _value, ::std::max(m_minimumValue, m_maximumValue - m_slider->width())); + m_slider->setX(m_value + m_slider->halfwidth()); + emit valueChanged(m_value); + + if (m_histogramItem->isVisible()) + m_histogramItem->onValueChanged(); +} + +void EasyGraphicsScrollbar::setRange(qreal _minValue, qreal _maxValue) +{ + const auto oldRange = range(); + const auto oldValue = oldRange < 1e-3 ? 0.0 : m_value / oldRange; + + m_minimumValue = _minValue; + m_maximumValue = _maxValue; + const auto range = this->range(); + scene()->setSceneRect(_minValue, DEFAULT_TOP, range, DEFAULT_HEIGHT + m_defaultFontHeight + 4); + + m_histogramItem->cancelImageUpdate(); + m_histogramItem->setBoundingRect(_minValue, DEFAULT_TOP + INDICATOR_SIZE, range, DEFAULT_HEIGHT - INDICATOR_SIZE_x2); + + emit rangeChanged(); + + setValue(_minValue + oldValue * range); + + onWindowWidthChange(width()); + + if (m_histogramItem->isVisible()) + m_histogramItem->updateImage(); +} + +void EasyGraphicsScrollbar::setSliderWidth(qreal _width) +{ + m_slider->setWidth(_width); + setValue(m_value); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsScrollbar::setChronoPos(qreal _left, qreal _right) +{ + m_chronometerIndicator->setWidth(_right - _left); + m_chronometerIndicator->setX(_left + m_chronometerIndicator->halfwidth()); +} + +void EasyGraphicsScrollbar::showChrono() +{ + m_chronometerIndicator->show(); +} + +void EasyGraphicsScrollbar::hideChrono() +{ + m_chronometerIndicator->hide(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsScrollbar::setHistogramSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items) +{ + if (m_bLocked) + return; + + m_histogramItem->setSource(_thread_id, _items); + m_slider->setVisible(m_histogramItem->isVisible()); + scene()->update(); +} + +void EasyGraphicsScrollbar::setHistogramSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id) +{ + if (m_bLocked) + return; + + m_histogramItem->setSource(_thread_id, _block_id); + m_slider->setVisible(m_histogramItem->isVisible()); + scene()->update(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsScrollbar::mousePressEvent(QMouseEvent* _event) +{ + _event->accept(); + + m_mouseButtons = _event->buttons(); + + if (m_mouseButtons & Qt::LeftButton) + { + if (_event->modifiers() & Qt::ControlModifier) + { + m_histogramItem->pickBottomBoundary(mapToScene(_event->pos()).y()); + } + else if (_event->modifiers() & Qt::ShiftModifier) + { + m_histogramItem->pickTopBoundary(mapToScene(_event->pos()).y()); + } + else + { + m_bScrolling = true; + m_mousePressPos = _event->pos(); + if (!m_bBindMode) + setValue(mapToScene(m_mousePressPos).x() - m_minimumValue - m_slider->halfwidth()); + } + } + + if (m_mouseButtons & Qt::RightButton) + { + if (_event->modifiers()) + { + m_histogramItem->pickFrameTime(mapToScene(_event->pos()).y()); + } + else + { + m_bBindMode = !m_bBindMode; + if (m_histogramItem->isVisible()) + m_histogramItem->onModeChanged(); + } + } + + //QGraphicsView::mousePressEvent(_event); +} + +void EasyGraphicsScrollbar::mouseReleaseEvent(QMouseEvent* _event) +{ + m_mouseButtons = _event->buttons(); + m_bScrolling = false; + _event->accept(); + //QGraphicsView::mouseReleaseEvent(_event); +} + +void EasyGraphicsScrollbar::mouseMoveEvent(QMouseEvent* _event) +{ + const auto pos = _event->pos(); + + if (m_mouseButtons & Qt::LeftButton) + { + const auto delta = pos - m_mousePressPos; + m_mousePressPos = pos; + + if (m_bScrolling) + { + auto realScale = m_windowScale; + if (m_bBindMode) + realScale *= -range() / sliderWidth(); + setValue(m_value + delta.x() / realScale); + } + } + + if (m_histogramItem->isVisible()) + { + m_histogramItem->setMouseY(mapToScene(pos).y()); + scene()->update(); + } +} + +void EasyGraphicsScrollbar::wheelEvent(QWheelEvent* _event) +{ + _event->accept(); + + if (_event->modifiers() & Qt::ShiftModifier) + { + // Shift + mouse wheel will change histogram top boundary + + if (m_histogramItem->isVisible()) + { + if (_event->delta() > 0) + m_histogramItem->increaseTopBoundary(); + else + m_histogramItem->decreaseTopBoundary(); + } + + return; + } + + if (_event->modifiers() & Qt::ControlModifier) + { + // Ctrl + mouse wheel will change histogram bottom boundary + + if (m_histogramItem->isVisible()) + { + if (_event->delta() > 0) + m_histogramItem->increaseBottomBoundary(); + else + m_histogramItem->decreaseBottomBoundary(); + } + + return; + } + + if (!m_bBindMode) + { + const auto w = m_slider->halfwidth() * (_event->delta() < 0 ? ::profiler_gui::SCALING_COEFFICIENT : ::profiler_gui::SCALING_COEFFICIENT_INV); + setValue(mapToScene(_event->pos()).x() - m_minimumValue - w); + emit wheeled(w * m_windowScale, _event->delta()); + } + else + { + const auto x = (mapToScene(_event->pos()).x() - m_minimumValue) * m_windowScale; + emit wheeled(x, _event->delta()); + } +} + +void EasyGraphicsScrollbar::resizeEvent(QResizeEvent* _event) +{ + onWindowWidthChange(_event->size().width()); + if (m_histogramItem->isVisible()) + m_histogramItem->updateImage(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsScrollbar::onWindowWidthChange(qreal _width) +{ + const auto oldScale = m_windowScale; + const auto scrollingRange = range(); + + if (scrollingRange < 1e-3) + { + m_windowScale = 1; + } + else + { + m_windowScale = _width / scrollingRange; + } + + scale(m_windowScale / oldScale, 1); +} + +////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/easyprofiler/profiler_gui/easy_graphics_scrollbar.h b/3rdparty/easyprofiler/profiler_gui/easy_graphics_scrollbar.h new file mode 100644 index 0000000..06a8ec7 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_graphics_scrollbar.h @@ -0,0 +1,342 @@ +/************************************************************************ +* file name : easy_graphics_scrollbar.h +* ----------------- : +* creation time : 2016/07/04 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains declaration of +* ----------------- : +* change log : * 2016/07/04 Victor Zarubkin: Initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY__GRAPHICS_SCROLLBAR__H +#define EASY__GRAPHICS_SCROLLBAR__H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "easy_qtimer.h" +#include "common_types.h" + +////////////////////////////////////////////////////////////////////////// + +// TODO: use profiler_core/spin_lock.h + +#define EASY_GUI_USE_CRITICAL_SECTION // Use CRITICAL_SECTION instead of std::atomic_flag +#if defined(_WIN32) && defined(EASY_GUI_USE_CRITICAL_SECTION) +namespace profiler_gui { + // std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag... + // By the way, Windows critical sections are slower than std::atomic_flag on Unix. + class spin_lock { void* m_lock; public: + void lock(); + void unlock(); + spin_lock(); + ~spin_lock(); + }; +#else +namespace profiler_gui { + // std::atomic_flag on Unix works fine and very fast (almost instant!) + class spin_lock { + ::std::atomic_flag m_lock; public: + + void lock() { + while (m_lock.test_and_set(::std::memory_order_acquire)); + } + + void unlock() { + m_lock.clear(::std::memory_order_release); + } + + spin_lock() { + m_lock.clear(); + } + }; +#endif + +} // END of namespace profiler_gui. + +////////////////////////////////////////////////////////////////////////// + +class EasyGraphicsSliderItem : public QGraphicsRectItem +{ + typedef QGraphicsRectItem Parent; + typedef EasyGraphicsSliderItem This; + +private: + + QPolygonF m_indicator; + qreal m_halfwidth; + bool m_bMain; + +public: + + explicit EasyGraphicsSliderItem(bool _main); + virtual ~EasyGraphicsSliderItem(); + + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + + qreal width() const; + qreal halfwidth() const; + + void setWidth(qreal _width); + void setHalfwidth(qreal _halfwidth); + + void setColor(QRgb _color); + void setColor(const QColor& _color); + +}; // END of class EasyGraphicsSliderItem. + +////////////////////////////////////////////////////////////////////////// + +class EasyHistogramItem : public QGraphicsItem +{ + typedef QGraphicsItem Parent; + typedef EasyHistogramItem This; + +public: + + enum HistRegime : uint8_t { Hist_Pointer, Hist_Id }; + +private: + + QRectF m_boundingRect; + qreal m_topDuration; + qreal m_bottomDuration; + qreal m_maxDuration; + qreal m_minDuration; + qreal m_mouseY; + qreal m_imageOrigin; + qreal m_imageScale; + qreal m_imageOriginUpdate; + qreal m_imageScaleUpdate; + qreal m_workerImageOrigin; + qreal m_workerImageScale; + qreal m_workerTopDuration; + qreal m_workerBottomDuration; + ::profiler::timestamp_t m_blockTotalDuraion; + QString m_topDurationStr; + QString m_bottomDurationStr; + QString m_threadName; + QString m_blockName; + ::profiler::BlocksTree::children_t m_selectedBlocks; + QImage m_mainImage; + EasyQTimer m_timer; + EasyQTimer m_boundaryTimer; + ::std::thread m_workerThread; + ::profiler::timestamp_t m_threadDuration; + ::profiler::timestamp_t m_threadProfiledTime; + ::profiler::timestamp_t m_threadWaitTime; + const ::profiler_gui::EasyItems* m_pSource; + QImage* m_workerImage; + const ::profiler::BlocksTreeRoot* m_pProfilerThread; + ::profiler::thread_id_t m_threadId; + ::profiler::block_index_t m_blockId; + int m_timeouts; + ::profiler_gui::TimeUnits m_timeUnits; + HistRegime m_regime; + bool m_bPermitImageUpdate; ///< Is false when m_workerThread is parsing input dataset (when setSource(_block_id) is called) + ::profiler_gui::spin_lock m_spin; + ::std::atomic_bool m_bReady; + +public: + + explicit EasyHistogramItem(); + virtual ~EasyHistogramItem(); + + // Public virtual methods + + QRectF boundingRect() const override; + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + +public: + + // Public non-virtual methods + + ::profiler::thread_id_t threadId() const; + + void setBoundingRect(const QRectF& _rect); + void setBoundingRect(qreal x, qreal y, qreal w, qreal h); + + void setSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items); + void setSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id); + void rebuildSource(HistRegime _regime); + void rebuildSource(); + void validateName(); + void updateImage(); + void cancelImageUpdate(); + + void pickTopBoundary(qreal _y); + void increaseTopBoundary(); + void decreaseTopBoundary(); + + void pickBottomBoundary(qreal _y); + void increaseBottomBoundary(); + void decreaseBottomBoundary(); + + void setMouseY(qreal _mouseY); + void pickFrameTime(qreal _y) const; + + void onValueChanged(); + void onModeChanged(); + +private: + + void paintBusyIndicator(QPainter* _painter, qreal _current_scale); + void paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, qreal _top_width, qreal _mouse_y, qreal _delta_time, int _font_h); + void paintByPtr(QPainter* _painter); + void paintById(QPainter* _painter); + void onTimeout(); + void updateImage(QRectF _boundingRect, HistRegime _regime, qreal _current_scale, + qreal _minimum, qreal _maximum, qreal _range, + qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, bool _bindMode, + float _frame_time, ::profiler::timestamp_t _begin_time, qreal _origin, bool _autoAdjustHist); + +}; // END of class EasyHistogramItem. + +////////////////////////////////////////////////////////////////////////// + +class EasyGraphicsScrollbar : public QGraphicsView +{ + Q_OBJECT + +private: + + typedef QGraphicsView Parent; + typedef EasyGraphicsScrollbar This; + + qreal m_minimumValue; + qreal m_maximumValue; + qreal m_value; + qreal m_windowScale; + QPoint m_mousePressPos; + Qt::MouseButtons m_mouseButtons; + EasyGraphicsSliderItem* m_slider; + EasyGraphicsSliderItem* m_chronometerIndicator; + EasyHistogramItem* m_histogramItem; + int m_defaultFontHeight; + bool m_bScrolling; + bool m_bBindMode; + bool m_bLocked; + +public: + + explicit EasyGraphicsScrollbar(QWidget* _parent = nullptr); + virtual ~EasyGraphicsScrollbar(); + + // Public virtual methods + + void mousePressEvent(QMouseEvent* _event) override; + void mouseReleaseEvent(QMouseEvent* _event) override; + void mouseMoveEvent(QMouseEvent* _event) override; + void wheelEvent(QWheelEvent* _event) override; + void resizeEvent(QResizeEvent* _event) override; + + void dragEnterEvent(QDragEnterEvent*) override {} + +public: + + // Public non-virtual methods + + void clear(); + + bool bindMode() const; + qreal getWindowScale() const; + ::profiler::thread_id_t hystThread() const; + + qreal minimum() const; + qreal maximum() const; + qreal range() const; + qreal value() const; + qreal sliderWidth() const; + qreal sliderHalfWidth() const; + int defaultFontHeight() const; + + void setValue(qreal _value); + void setRange(qreal _minValue, qreal _maxValue); + void setSliderWidth(qreal _width); + void setChronoPos(qreal _left, qreal _right); + void showChrono(); + void hideChrono(); + + void setHistogramSource(::profiler::thread_id_t _thread_id, const::profiler_gui::EasyItems* _items); + void setHistogramSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id); + + inline void setHistogramSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems& _items) + { + setHistogramSource(_thread_id, &_items); + } + + inline void lock() + { + m_bLocked = true; + } + + inline void unlock() + { + m_bLocked = false; + } + +signals: + + void rangeChanged(); + void valueChanged(qreal _value); + void wheeled(qreal _mouseX, int _wheelDelta); + +private slots: + + void onThreadViewChanged(); + void onWindowWidthChange(qreal _width); + +}; // END of class EasyGraphicsScrollbar. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY__GRAPHICS_SCROLLBAR__H diff --git a/3rdparty/easyprofiler/profiler_gui/easy_qtimer.cpp b/3rdparty/easyprofiler/profiler_gui/easy_qtimer.cpp new file mode 100644 index 0000000..4eb1740 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_qtimer.cpp @@ -0,0 +1,85 @@ +/************************************************************************ +* file name : easy_qtimer.h +* ----------------- : +* creation time : 2016/12/05 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains implementation of EasyQTimer class used to +* : connect QTimer to non-QObject classes. +* ----------------- : +* change log : * 2016/12/05 Victor Zarubkin: Initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include "easy_qtimer.h" + +////////////////////////////////////////////////////////////////////////// + +EasyQTimer::EasyQTimer() + : QObject() +{ + connect(&m_timer, &QTimer::timeout, [this](){ m_handler(); }); +} + +EasyQTimer::EasyQTimer(::std::function&& _handler, bool _isSignleShot) + : QObject() + , m_handler(::std::forward<::std::function&&>(_handler)) +{ + m_timer.setSingleShot(_isSignleShot); + connect(&m_timer, &QTimer::timeout, [this](){ m_handler(); }); +} + +EasyQTimer::~EasyQTimer() +{ + +} + +void EasyQTimer::setHandler(::std::function&& _handler) +{ + m_handler = _handler; +} + +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/profiler_gui/easy_qtimer.h b/3rdparty/easyprofiler/profiler_gui/easy_qtimer.h new file mode 100644 index 0000000..818e628 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/easy_qtimer.h @@ -0,0 +1,89 @@ +/************************************************************************ +* file name : easy_qtimer.h +* ----------------- : +* creation time : 2016/12/05 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains declaration of EasyQTimer class used to +* : connect QTimer to non-QObject classes. +* ----------------- : +* change log : * 2016/12/05 Victor Zarubkin: Initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY__QTIMER__H +#define EASY__QTIMER__H + +#include +#include + +////////////////////////////////////////////////////////////////////////// + +class EasyQTimer : public QObject +{ + Q_OBJECT + +private: + + QTimer m_timer; + ::std::function m_handler; + +public: + + EasyQTimer(); + EasyQTimer(::std::function&& _handler, bool _isSignleShot = false); + virtual ~EasyQTimer(); + + void setHandler(::std::function&& _handler); + + inline void start(int msec) { m_timer.start(msec); } + inline void stop() { if (m_timer.isActive()) m_timer.stop(); } + inline bool isActive() const { return m_timer.isActive(); } + +}; // END of class EasyQTimer. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY__QTIMER__H diff --git a/3rdparty/easyprofiler/profiler_gui/globals.cpp b/3rdparty/easyprofiler/profiler_gui/globals.cpp new file mode 100644 index 0000000..4487849 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/globals.cpp @@ -0,0 +1,122 @@ +/************************************************************************ +* file name : globals.cpp +* ----------------- : +* creation time : 2016/08/03 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of global constants and variables for profiler gui. +* ----------------- : +* change log : * 2016/08/03 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#define IGNORE_GLOBALS_DECLARATION +#include "globals.h" +#undef IGNORE_GLOBALS_DECLARATION + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler_gui { + + EasyGlobals& EasyGlobals::instance() + { + // It's okay even without C++11 "magic statics" feature because first call happens + // on application initialization - there is only one thread and no data races occur. + static EasyGlobals globals; + return globals; + } + + EasyGlobals::EasyGlobals() + : theme("default") + , bg_font(::profiler_gui::EFont("DejaVu Sans", 10, QFont::Bold)) + , chronometer_font(::profiler_gui::EFont("DejaVu Sans", 16, QFont::Bold)) + , items_font(::profiler_gui::EFont("DejaVu Sans", 10, QFont::Medium)) + , selected_item_font(::profiler_gui::EFont("DejaVu Sans", 10, QFont::Medium)) + , scene_left(0) + , scene_right(100) + , begin_time(0) + , selected_thread(0U) + , selected_block(::profiler_gui::numeric_max()) + , selected_block_id(::profiler_gui::numeric_max()) + , version(0) + , frame_time(16700) + , blocks_spacing(0) + , blocks_size_min(2) + , blocks_narrow_size(20) + , max_fps_history(90) + , fps_timer_interval(500) + , fps_widget_line_width(2) + , chrono_text_position(ChronoTextPosition_Top) + , time_units(TimeUnits_ms) + , connected(false) + , fps_enabled(true) + , use_decorated_thread_name(false) + , hex_thread_id(false) + , enable_event_markers(true) + , enable_statistics(true) + , enable_zero_length(true) + , add_zero_blocks_to_hierarchy(false) + , draw_graphics_items_borders(true) + , hide_narrow_children(false) + , hide_minsize_blocks(false) + , display_only_relevant_stats(true) + , collapse_items_on_tree_close(false) + , all_items_expanded_by_default(true) + , only_current_thread_hierarchy(false) + , highlight_blocks_with_same_id(true) + , selecting_block_changes_thread(true) + , auto_adjust_histogram_height(true) + , display_only_frames_on_histogram(false) + , bind_scene_and_tree_expand_status(true) + { + + } + +} // END of namespace profiler_gui. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/profiler_gui/globals.h b/3rdparty/easyprofiler/profiler_gui/globals.h new file mode 100644 index 0000000..6f346be --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/globals.h @@ -0,0 +1,273 @@ +/************************************************************************ +* file name : globals.h +* ----------------- : +* creation time : 2016/08/03 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of global constants and variables for profiler gui. +* ----------------- : +* change log : * 2016/08/03 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER__GUI_GLOBALS_H +#define EASY_PROFILER__GUI_GLOBALS_H + +#include +#include +#include +#include +#include +#include +#include "common_functions.h" +#include "globals_qobjects.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler_gui { + + const QString ORGANAZATION_NAME = "EasyProfiler"; + const QString APPLICATION_NAME = "Easy profiler gui application"; + + const QColor CHRONOMETER_COLOR = QColor::fromRgba(0x40000000 | (::profiler::colors::RichBlue & 0x00ffffff));// 0x402020c0); + const QColor CHRONOMETER_COLOR2 = QColor::fromRgba(0x40000000 | (::profiler::colors::Dark & 0x00ffffff));// 0x40408040); + const QColor TEXT_COLOR = QColor::fromRgb(0xff504040); + const QColor SYSTEM_BORDER_COLOR = QColor::fromRgb(0xffcccccc); + EASY_CONSTEXPR QRgb SELECTED_THREAD_BACKGROUND = 0xffe0e060; + EASY_CONSTEXPR QRgb SELECTED_THREAD_FOREGROUND = 0xffffffff - (SELECTED_THREAD_BACKGROUND & 0x00ffffff); + + EASY_CONSTEXPR qreal SCALING_COEFFICIENT = 1.25; + EASY_CONSTEXPR qreal SCALING_COEFFICIENT_INV = 1.0 / SCALING_COEFFICIENT; + + EASY_CONSTEXPR uint32_t V130 = 0x01030000; + + Q_CONSTEXPR QSize ICONS_SIZE {28, 28}; + EASY_CONSTEXPR uint16_t GRAPHICS_ROW_SIZE = 20; + EASY_CONSTEXPR uint16_t GRAPHICS_ROW_SPACING = 0; + EASY_CONSTEXPR uint16_t GRAPHICS_ROW_SIZE_FULL = GRAPHICS_ROW_SIZE + GRAPHICS_ROW_SPACING; + EASY_CONSTEXPR uint16_t THREADS_ROW_SPACING = 10; + +#ifdef _WIN32 + EASY_CONSTEXPR qreal FONT_METRICS_FACTOR = 1.05; +#else + EASY_CONSTEXPR qreal FONT_METRICS_FACTOR = 1.; +#endif + + ////////////////////////////////////////////////////////////////////////// + + template + inline auto toUnicode(const T& _inputString) -> decltype(QTextCodec::codecForLocale()->toUnicode(_inputString)) + { + return QTextCodec::codecForLocale()->toUnicode(_inputString); + } + + ////////////////////////////////////////////////////////////////////////// + + inline QString decoratedThreadName(bool _use_decorated_thread_name, const::profiler::BlocksTreeRoot& _root, const QString& _unicodeThreadWord, bool _hex = false) + { + if (_root.got_name()) + { + QString rootname(toUnicode(_root.name())); + if (!_use_decorated_thread_name || rootname.contains(_unicodeThreadWord, Qt::CaseInsensitive)) + { + if (_hex) + return QString("%1 0x%2").arg(rootname).arg(_root.thread_id, 0, 16); + return QString("%1 %2").arg(rootname).arg(_root.thread_id); + } + + if (_hex) + return QString("%1 Thread 0x%2").arg(rootname).arg(_root.thread_id, 0, 16); + return QString("%1 Thread %2").arg(rootname).arg(_root.thread_id); + } + + if (_hex) + return QString("Thread 0x%1").arg(_root.thread_id, 0, 16); + return QString("Thread %1").arg(_root.thread_id); + } + + inline QString decoratedThreadName(bool _use_decorated_thread_name, const ::profiler::BlocksTreeRoot& _root, bool _hex = false) + { + if (_root.got_name()) + { + QString rootname(toUnicode(_root.name())); + if (!_use_decorated_thread_name || rootname.contains(toUnicode("thread"), Qt::CaseInsensitive)) + { + if (_hex) + return QString("%1 0x%2").arg(rootname).arg(_root.thread_id, 0, 16); + return QString("%1 %2").arg(rootname).arg(_root.thread_id); + } + + if (_hex) + return QString("%1 Thread 0x%2").arg(rootname).arg(_root.thread_id, 0, 16); + return QString("%1 Thread %2").arg(rootname).arg(_root.thread_id); + } + + if (_hex) + return QString("Thread 0x%1").arg(_root.thread_id, 0, 16); + return QString("Thread %1").arg(_root.thread_id); + } + + ////////////////////////////////////////////////////////////////////////// + + enum ChronometerTextPosition : int8_t + { + ChronoTextPosition_Center = 0, + ChronoTextPosition_Top, + ChronoTextPosition_Bottom, + + }; // END of enum ChronometerTextPosition. + + ////////////////////////////////////////////////////////////////////////// + + struct EasyGlobals Q_DECL_FINAL + { + static EasyGlobals& instance(); + + EasyGlobalSignals events; ///< Global signals + ::profiler::thread_blocks_tree_t profiler_blocks; ///< Profiler blocks tree loaded from file + ::profiler::descriptors_list_t descriptors; ///< Profiler block descriptors list + EasyBlocks gui_blocks; ///< Profiler graphics blocks builded by GUI + + QString theme; ///< Current UI theme name + QFont bg_font; ///< Font for blocks_graphics_view + QFont chronometer_font; ///< Font for easy_chronometer_item + QFont items_font; ///< Font for easy_graphics_item + QFont selected_item_font; ///< Font for easy_graphics_item + + double scene_left; ///< Graphics scene left boundary + double scene_right; ///< Graphics scene right boundary + ::profiler::timestamp_t begin_time; ///< + ::profiler::thread_id_t selected_thread; ///< Current selected thread id + ::profiler::block_index_t selected_block; ///< Current selected profiler block index + ::profiler::block_id_t selected_block_id; ///< Current selected profiler block id + uint32_t version; ///< Opened file version (files may have different format) + float frame_time; ///< Expected frame time value in microseconds to be displayed at minimap on graphics scrollbar + int blocks_spacing; ///< Minimum blocks spacing on diagram + int blocks_size_min; ///< Minimum blocks size on diagram + int blocks_narrow_size; ///< Width indicating narrow blocks + int max_fps_history; ///< Max frames history displayed in FPS Monitor + int fps_timer_interval; ///< Interval in milliseconds for sending network requests to the profiled application (used by FPS Monitor) + int fps_widget_line_width; ///< Line width in pixels of FPS lines for FPS Monitor + ChronometerTextPosition chrono_text_position; ///< Selected interval text position + TimeUnits time_units; ///< Units type for time (milliseconds, microseconds, nanoseconds or auto-definition) + bool connected; ///< Is connected to source (to be able to capture profiling information) + bool fps_enabled; ///< Is FPS Monitor enabled + bool use_decorated_thread_name; ///< Add "Thread" to the name of each thread (if there is no one) + bool hex_thread_id; ///< Use hex view for thread-id instead of decimal + bool enable_event_markers; ///< Enable event indicators painting (These are narrow rectangles at the bottom of each thread) + bool enable_statistics; ///< Enable gathering and using statistics (Disable if you want to consume less memory) + bool enable_zero_length; ///< Enable zero length blocks (if true, then such blocks will have width == 1 pixel on each scale) + bool add_zero_blocks_to_hierarchy; ///< Enable adding zero blocks into hierarchy tree + bool draw_graphics_items_borders; ///< Draw borders for graphics blocks or not + bool hide_narrow_children; ///< Hide children for narrow graphics blocks (See blocks_narrow_size) + bool hide_minsize_blocks; ///< Hide blocks which screen size is less than blocks_size_min + bool display_only_relevant_stats; ///< Display only relevant information in ProfTreeWidget (excludes min, max, average times if there are only 1 calls number) + bool collapse_items_on_tree_close; ///< Collapse all items which were displayed in the hierarchy tree after tree close/reset + bool all_items_expanded_by_default; ///< Expand all items after file is opened + bool only_current_thread_hierarchy; ///< Build hierarchy tree for current thread only + bool highlight_blocks_with_same_id; ///< Highlight all blocks with same id on diagram + bool selecting_block_changes_thread; ///< If true then current selected thread will change every time you select block + bool auto_adjust_histogram_height; ///< Automatically adjust histogram height to the visible region + bool display_only_frames_on_histogram; ///< Display only top-level blocks on histogram when drawing histogram by block id + bool bind_scene_and_tree_expand_status; /** \brief If true then items on graphics scene and in the tree (blocks hierarchy) are binded on each other + so expanding/collapsing items on scene also expands/collapse items in the tree. */ + + private: + + EasyGlobals(); + + }; // END of struct EasyGlobals. + + ////////////////////////////////////////////////////////////////////////// + +} // END of namespace profiler_gui. + +#ifndef IGNORE_GLOBALS_DECLARATION +#define EASY_GLOBALS ::profiler_gui::EasyGlobals::instance() + +inline ::profiler_gui::EasyBlock& easyBlock(::profiler::block_index_t i) { + return EASY_GLOBALS.gui_blocks[i]; +} + +inline ::profiler::SerializedBlockDescriptor& easyDescriptor(::profiler::block_id_t i) { + return *EASY_GLOBALS.descriptors[i]; +} + +EASY_FORCE_INLINE const ::profiler::BlocksTree& easyBlocksTree(::profiler::block_index_t i) { + return easyBlock(i).tree; +} + +EASY_FORCE_INLINE const char* easyBlockName(const ::profiler::BlocksTree& _block) { + const char* name = _block.node->name(); + return *name != 0 ? name : easyDescriptor(_block.node->id()).name(); +} + +EASY_FORCE_INLINE const char* easyBlockName(const ::profiler::BlocksTree& _block, const ::profiler::SerializedBlockDescriptor& _desc) { + const char* name = _block.node->name(); + return *name != 0 ? name : _desc.name(); +} + +EASY_FORCE_INLINE const char* easyBlockName(::profiler::block_index_t i) { + return easyBlockName(easyBlock(i).tree); +} + +inline qreal sceneX(profiler::timestamp_t _time) { + return PROF_MICROSECONDS(qreal(_time - EASY_GLOBALS.begin_time)); +} + +inline QString imagePath(const QString& _resource) { + return QString(":/images/%1/%2").arg(EASY_GLOBALS.theme).arg(_resource); +} + +inline QString imagePath(const char* _resource) { + return QString(":/images/%1/%2").arg(EASY_GLOBALS.theme).arg(_resource); +} +#endif + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__GUI_GLOBALS_H diff --git a/3rdparty/easyprofiler/profiler_gui/globals_qobjects.cpp b/3rdparty/easyprofiler/profiler_gui/globals_qobjects.cpp new file mode 100644 index 0000000..62319b1 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/globals_qobjects.cpp @@ -0,0 +1,72 @@ +/************************************************************************ +* file name : globals_qobjects.cpp +* ----------------- : +* creation time : 2016/08/08 +* authors : Victor Zarubkin, Sergey Yagovtsev +* email : v.s.zarubkin@gmail.com, yse.sey@gmail.com +* ----------------- : +* description : The file contains implementation of EasyGlobalSignals QObject class. +* ----------------- : +* change log : * 2016/08/08 Sergey Yagovtsev: moved sources from globals.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include "globals_qobjects.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler_gui { + + EasyGlobalSignals::EasyGlobalSignals() : QObject() + { + } + + EasyGlobalSignals::~EasyGlobalSignals() + { + } + +} // END of namespace profiler_gui. + +////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/easyprofiler/profiler_gui/globals_qobjects.h b/3rdparty/easyprofiler/profiler_gui/globals_qobjects.h new file mode 100644 index 0000000..348aede --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/globals_qobjects.h @@ -0,0 +1,95 @@ +/************************************************************************ +* file name : globals_qobjects.h +* ----------------- : +* creation time : 2016/08/08 +* authors : Victor Zarubkin, Sergey Yagovtsev +* email : v.s.zarubkin@gmail.com, yse.sey@gmail.com +* ----------------- : +* description : The file contains declaration of EasyGlobalSignals QObject class. +* ----------------- : +* change log : * 2016/08/08 Sergey Yagovtsev: moved sources from globals.h +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_GLOBALS_QOBJECTS_H +#define EASY_GLOBALS_QOBJECTS_H + +#include +#include + +namespace profiler_gui { + + class EasyGlobalSignals Q_DECL_FINAL : public QObject + { + Q_OBJECT + + public: + + EasyGlobalSignals(); + virtual ~EasyGlobalSignals(); + + signals: + + void selectedThreadChanged(::profiler::thread_id_t _id); + void selectedBlockChanged(uint32_t _block_index); + void selectedBlockIdChanged(::profiler::block_id_t _id); + void itemsExpandStateChanged(); + void blockStatusChanged(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status); + void connectionChanged(bool _connected); + void blocksRefreshRequired(bool); + void expectedFrameTimeChanged(); + void autoAdjustHistogramChanged(); + void displayOnlyFramesOnHistogramChanged(); + void hierarchyFlagChanged(bool); + void threadNameDecorationChanged(); + void hexThreadIdChanged(); + void refreshRequired(); + void blocksTreeModeChanged(); + void sceneSizeChanged(); + + }; // END of class EasyGlobalSignals. + +} // END of namespace profiler_gui. + +#endif // EASY_GLOBALS_QOBJECTS_H diff --git a/3rdparty/easyprofiler/profiler_gui/images/attribution.txt b/3rdparty/easyprofiler/profiler_gui/images/attribution.txt new file mode 100644 index 0000000..e346622 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/attribution.txt @@ -0,0 +1,43 @@ +logo.svg - Icon made by Freepik from www.flaticon.com + +default/off.svg - Icon made by Freepik from www.flaticon.com +default/open-folder.svg - Icon made by Freepik from www.flaticon.com +default/open-folder2.svg - Icon made by Freepik from www.flaticon.com +default/reload-folder2.svg - Icon made by Freepik from www.flaticon.com +default/reload.svg - Icon made by Freepik from www.flaticon.com +default/expand.svg - Icon made by Freepik from www.flaticon.com +default/collapse.svg - Icon made by Freepik from www.flaticon.com +default/colors.svg - Icon made by Freepik from www.flaticon.com +default/colors-black.svg - Icon made by Freepik from www.flaticon.com +default/save.svg - Icon made by Freepik from www.flaticon.com +default/statistics.svg - Icon made by Freepik from www.flaticon.com +default/statistics2.svg - Icon made by Freepik from www.flaticon.com +default/lan.svg - Icon made by Freepik from www.flaticon.com +default/lan_on.svg - Icon made by Freepik from www.flaticon.com +default/wifi.svg - Icon made by Freepik from www.flaticon.com +default/wifi_on.svg - Icon made by Freepik from www.flaticon.com +default/play.svg - Icon made by Google from www.flaticon.com +default/stop.svg - Icon made by Google from www.flaticon.com +default/delete.svg - Icon made by Google from www.flaticon.com +default/list.svg - Icon made by Freepik from www.flaticon.com +default/search-prev.svg - Icon made by Freepik from www.flaticon.com +default/search-next.svg - Icon made by Freepik from www.flaticon.com +default/settings.svg - Icon made by Freepik from www.flaticon.com +default/check.svg - Icon made by Kirill Kazachek from www.flaticon.com +default/check-disabled.svg - Icon made by Kirill Kazachek from www.flaticon.com +default/close-white.svg - Icon made by Cole Bemis from www.flaticon.com +default/close-white-hover.svg - Icon made by Cole Bemis from www.flaticon.com +default/close-white-pressed.svg - Icon made by Cole Bemis from www.flaticon.com +default/maximize-white.svg - Icon made by Freepik from www.flaticon.com +default/maximize-white-hover.svg - Icon made by Freepik from www.flaticon.com +default/maximize-white-pressed.svg - Icon made by Freepik from www.flaticon.com +default/minimize-white.svg - Icon made by Freepik from www.flaticon.com +default/minimize-white-pressed.svg - Icon made by Freepik from www.flaticon.com +default/arrow-down.svg - Icon made by Freepik from www.flaticon.com +default/arrow-down-hover.svg - Icon made by Freepik from www.flaticon.com +default/arrow-down-disabled.svg - Icon made by Freepik from www.flaticon.com +default/arrow-up.svg - Icon made by Freepik from www.flaticon.com +default/arrow-up-hover.svg - Icon made by Freepik from www.flaticon.com +default/arrow-up-disabled.svg - Icon made by Freepik from www.flaticon.com +default/arrow-left.svg - Icon made by Freepik from www.flaticon.com +default/arrow-right.svg - Icon made by Freepik from www.flaticon.com diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down-disabled.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down-disabled.svg new file mode 100644 index 0000000..46d7c82 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down-disabled.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down-hover.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down-hover.svg new file mode 100644 index 0000000..e16736a --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down-hover.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down.svg new file mode 100644 index 0000000..13d0004 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-down.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-left.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-left.svg new file mode 100644 index 0000000..7c452e6 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-left.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-right.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-right.svg new file mode 100644 index 0000000..199bfc8 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-right.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up-disabled.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up-disabled.svg new file mode 100644 index 0000000..4581ed1 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up-disabled.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up-hover.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up-hover.svg new file mode 100644 index 0000000..da0da91 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up-hover.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up.svg b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up.svg new file mode 100644 index 0000000..2120ab9 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/arrow-up.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/check-disabled.svg b/3rdparty/easyprofiler/profiler_gui/images/default/check-disabled.svg new file mode 100644 index 0000000..53eb1f9 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/check-disabled.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/check.svg b/3rdparty/easyprofiler/profiler_gui/images/default/check.svg new file mode 100644 index 0000000..cc1eb65 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/check.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/close-white-hover.svg b/3rdparty/easyprofiler/profiler_gui/images/default/close-white-hover.svg new file mode 100644 index 0000000..d78f3d0 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/close-white-hover.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/close-white-pressed.svg b/3rdparty/easyprofiler/profiler_gui/images/default/close-white-pressed.svg new file mode 100644 index 0000000..6602d1c --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/close-white-pressed.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/close-white.svg b/3rdparty/easyprofiler/profiler_gui/images/default/close-white.svg new file mode 100644 index 0000000..cec4875 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/close-white.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/collapse.svg b/3rdparty/easyprofiler/profiler_gui/images/default/collapse.svg new file mode 100644 index 0000000..3bf9ed4 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/collapse.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/colors-black.svg b/3rdparty/easyprofiler/profiler_gui/images/default/colors-black.svg new file mode 100644 index 0000000..13c8182 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/colors-black.svg @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/colors.svg b/3rdparty/easyprofiler/profiler_gui/images/default/colors.svg new file mode 100644 index 0000000..a8d580a --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/colors.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/delete-old.svg b/3rdparty/easyprofiler/profiler_gui/images/default/delete-old.svg new file mode 100644 index 0000000..6502e27 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/delete-old.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/delete.svg b/3rdparty/easyprofiler/profiler_gui/images/default/delete.svg new file mode 100644 index 0000000..35a8016 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/delete.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/expand.svg b/3rdparty/easyprofiler/profiler_gui/images/default/expand.svg new file mode 100644 index 0000000..44ccaa3 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/expand.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/lan.svg b/3rdparty/easyprofiler/profiler_gui/images/default/lan.svg new file mode 100644 index 0000000..0f0124d --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/lan.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/lan_on.svg b/3rdparty/easyprofiler/profiler_gui/images/default/lan_on.svg new file mode 100644 index 0000000..41da421 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/lan_on.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/list.svg b/3rdparty/easyprofiler/profiler_gui/images/default/list.svg new file mode 100644 index 0000000..38ac6ed --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/list.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white-hover.svg b/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white-hover.svg new file mode 100644 index 0000000..92f8257 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white-hover.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white-pressed.svg b/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white-pressed.svg new file mode 100644 index 0000000..73a95d0 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white-pressed.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white.svg b/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white.svg new file mode 100644 index 0000000..d6d86c9 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/maximize-white.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white-hover.svg b/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white-hover.svg new file mode 100644 index 0000000..828901c --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white-hover.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white-pressed.svg b/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white-pressed.svg new file mode 100644 index 0000000..f575f28 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white-pressed.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white.svg b/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white.svg new file mode 100644 index 0000000..24c65de --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/minimize-white.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/off.svg b/3rdparty/easyprofiler/profiler_gui/images/default/off.svg new file mode 100644 index 0000000..57ce672 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/off.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/open-folder.svg b/3rdparty/easyprofiler/profiler_gui/images/default/open-folder.svg new file mode 100644 index 0000000..7500c16 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/open-folder.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/open-folder2.svg b/3rdparty/easyprofiler/profiler_gui/images/default/open-folder2.svg new file mode 100644 index 0000000..70cc9c5 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/open-folder2.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/play.svg b/3rdparty/easyprofiler/profiler_gui/images/default/play.svg new file mode 100644 index 0000000..8bbdf01 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/play.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/radio-indicator-disabled.svg b/3rdparty/easyprofiler/profiler_gui/images/default/radio-indicator-disabled.svg new file mode 100644 index 0000000..cb9540e --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/radio-indicator-disabled.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/radio-indicator.svg b/3rdparty/easyprofiler/profiler_gui/images/default/radio-indicator.svg new file mode 100644 index 0000000..76e0974 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/radio-indicator.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/reload-folder2.svg b/3rdparty/easyprofiler/profiler_gui/images/default/reload-folder2.svg new file mode 100644 index 0000000..1f6d7a6 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/reload-folder2.svg @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/reload.svg b/3rdparty/easyprofiler/profiler_gui/images/default/reload.svg new file mode 100644 index 0000000..796319e --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/reload.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/save.svg b/3rdparty/easyprofiler/profiler_gui/images/default/save.svg new file mode 100644 index 0000000..e81c739 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/save.svg @@ -0,0 +1,73 @@ + + +image/svg+xml \ No newline at end of file diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/search-next.svg b/3rdparty/easyprofiler/profiler_gui/images/default/search-next.svg new file mode 100644 index 0000000..e9ff44d --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/search-next.svg @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/search-prev.svg b/3rdparty/easyprofiler/profiler_gui/images/default/search-prev.svg new file mode 100644 index 0000000..8dbd6fb --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/search-prev.svg @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/settings.svg b/3rdparty/easyprofiler/profiler_gui/images/default/settings.svg new file mode 100644 index 0000000..783969c --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/settings.svg @@ -0,0 +1,39 @@ + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/statistics.svg b/3rdparty/easyprofiler/profiler_gui/images/default/statistics.svg new file mode 100644 index 0000000..bbb4428 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/statistics.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/statistics2.svg b/3rdparty/easyprofiler/profiler_gui/images/default/statistics2.svg new file mode 100644 index 0000000..3f5c3ad --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/statistics2.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/stop.svg b/3rdparty/easyprofiler/profiler_gui/images/default/stop.svg new file mode 100644 index 0000000..a914d1e --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/stop.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/wifi.svg b/3rdparty/easyprofiler/profiler_gui/images/default/wifi.svg new file mode 100644 index 0000000..cb04ace --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/wifi.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/default/wifi_on.svg b/3rdparty/easyprofiler/profiler_gui/images/default/wifi_on.svg new file mode 100644 index 0000000..461e2b7 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/default/wifi_on.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/images/logo.ico b/3rdparty/easyprofiler/profiler_gui/images/logo.ico new file mode 100644 index 0000000..9d72f93 Binary files /dev/null and b/3rdparty/easyprofiler/profiler_gui/images/logo.ico differ diff --git a/3rdparty/easyprofiler/profiler_gui/images/logo.svg b/3rdparty/easyprofiler/profiler_gui/images/logo.svg new file mode 100644 index 0000000..87756e5 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/images/logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/3rdparty/easyprofiler/profiler_gui/main.cpp b/3rdparty/easyprofiler/profiler_gui/main.cpp new file mode 100644 index 0000000..fb5a0bb --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/main.cpp @@ -0,0 +1,74 @@ +/************************************************************************ +* file name : main.cpp +* ----------------- : +* creation time : 2016/04/29 +* authors : Sergey Yagovtsev, Victor Zarubkin +* email : yse.sey@gmail.com, v.s.zarubkin@gmail.com +* ----------------- : +* description : Main file for EasyProfiler GUI. +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include +#include "main_window.h" +#include "globals.h" + +#if defined(_WIN32) && defined (_BUILD_RELEASE_) +#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") +#endif + +int main(int argc, char **argv) +{ + auto now = ::std::chrono::duration_cast(::std::chrono::system_clock::now().time_since_epoch()).count() >> 1; + srand((unsigned int)now); + + QApplication app(argc, argv); + + //Instanciate easy globals after QApplication to allow creation of global fonts, and on the main thread to avoid data races + profiler_gui::EasyGlobals::instance(); + + EasyMainWindow window; + window.show(); + + return app.exec(); +} diff --git a/3rdparty/easyprofiler/profiler_gui/main_window.cpp b/3rdparty/easyprofiler/profiler_gui/main_window.cpp new file mode 100644 index 0000000..be8c970 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/main_window.cpp @@ -0,0 +1,2982 @@ +/************************************************************************ +* file name : main_window.cpp +* ----------------- : +* creation time : 2016/06/26 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of MainWindow for easy_profiler GUI. +* ----------------- : +* change log : * 2016/06/26 Victor Zarubkin: Initial commit. +* : +* : * 2016/06/27 Victor Zarubkin: Passing blocks number to EasyTreeWidget::setTree(). +* : +* : * 2016/06/29 Victor Zarubkin: Added menu with tests. +* : +* : * 2016/06/30 Sergey Yagovtsev: Open file by command line argument +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main_window.h" +#include "blocks_tree_widget.h" +#include "blocks_graphics_view.h" +#include "descriptors_tree_widget.h" +#include "easy_frame_rate_viewer.h" +#include "globals.h" + +#include +#include + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +////////////////////////////////////////////////////////////////////////// + +#define EASY_DEFAULT_WINDOW_TITLE "EasyProfiler" + +const int LOADER_TIMER_INTERVAL = 40; +const auto NETWORK_CACHE_FILE = "easy_profiler_stream.cache"; + +////////////////////////////////////////////////////////////////////////// + +inline const QStringList& UI_themes() +{ + static const QStringList themes { + "default" + }; + + return themes; +} + +////////////////////////////////////////////////////////////////////////// + +inline void clear_stream(std::stringstream& _stream) +{ +#if defined(__GNUC__) && __GNUC__ < 5 + // gcc 4 has a known bug which has been solved in gcc 5: + // std::stringstream has no swap() method :( + _stream.str(std::string()); +#else + std::stringstream().swap(_stream); +#endif +} + +inline void loadTheme(const QString& _theme) +{ + QFile file(QStringLiteral(":/themes/") + _theme); + if (file.open(QFile::ReadOnly | QFile::Text)) + { + QTextStream in(&file); + QString style = in.readAll(); + if (!style.isEmpty()) + qApp->setStyleSheet(style); + } +} + +////////////////////////////////////////////////////////////////////////// + +EasyDockWidget::EasyDockWidget(const QString& title, QWidget* parent) : QDockWidget(title, parent) +{ + auto floatingButton = new QPushButton(); + floatingButton->setObjectName("EasyDockWidgetFloatButton"); + floatingButton->setProperty("floating", isFloating()); + connect(floatingButton, &QPushButton::clicked, [this, floatingButton] { + setFloating(!isFloating()); + floatingButton->setProperty("floating", isFloating()); + floatingButton->style()->unpolish(floatingButton); + floatingButton->style()->polish(floatingButton); + floatingButton->update(); + }); + + auto closeButton = new QPushButton(); + closeButton->setObjectName("EasyDockWidgetCloseButton"); + connect(closeButton, &QPushButton::clicked, [this] { + close(); + }); + + auto caption = new QWidget(this); + caption->setObjectName("EasyDockWidgetTitle"); + + auto lay = new QHBoxLayout(caption); + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(2); + lay->addWidget(new QLabel(title)); + lay->addStretch(100); + lay->addWidget(floatingButton); + lay->addWidget(closeButton); + + setTitleBarWidget(caption); +} + +EasyDockWidget::~EasyDockWidget() +{ +} + +EasyMainWindow::EasyMainWindow() : Parent(), m_theme("default"), m_lastAddress("localhost"), m_lastPort(::profiler::DEFAULT_PORT) +{ + { QIcon icon(":/images/logo"); if (!icon.isNull()) QApplication::setWindowIcon(icon); } + + setObjectName("ProfilerGUI_MainWindow"); + setWindowTitle(EASY_DEFAULT_WINDOW_TITLE); + setDockNestingEnabled(true); + setAcceptDrops(true); + resize(800, 600); + setStatusBar(nullptr); + + loadSettings(); + loadTheme(m_theme); + + m_graphicsView = new EasyDockWidget("Diagram", this); + m_graphicsView->setObjectName("ProfilerGUI_Diagram"); + m_graphicsView->setMinimumHeight(50); + m_graphicsView->setAllowedAreas(Qt::AllDockWidgetAreas); + + auto graphicsView = new EasyGraphicsViewWidget(this); + m_graphicsView->setWidget(graphicsView); + + m_treeWidget = new EasyDockWidget("Hierarchy", this); + m_treeWidget->setObjectName("ProfilerGUI_Hierarchy"); + m_treeWidget->setMinimumHeight(50); + m_treeWidget->setAllowedAreas(Qt::AllDockWidgetAreas); + + auto treeWidget = new EasyHierarchyWidget(this); + m_treeWidget->setWidget(treeWidget); + + m_fpsViewer = new EasyDockWidget("FPS Monitor", this); + m_fpsViewer->setObjectName("ProfilerGUI_FPS"); + m_fpsViewer->setWidget(new EasyFrameRateViewer(this)); + m_fpsViewer->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); + + addDockWidget(Qt::TopDockWidgetArea, m_graphicsView); + addDockWidget(Qt::BottomDockWidgetArea, m_treeWidget); + addDockWidget(Qt::TopDockWidgetArea, m_fpsViewer); + +#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 + auto descTree = new EasyDescWidget(); + m_descTreeWidget = new EasyDockWidget("Blocks"); + m_descTreeWidget->setObjectName("ProfilerGUI_Blocks"); + m_descTreeWidget->setMinimumHeight(50); + m_descTreeWidget->setAllowedAreas(Qt::AllDockWidgetAreas); + m_descTreeWidget->setWidget(descTree); + addDockWidget(Qt::BottomDockWidgetArea, m_descTreeWidget); +#endif + + + auto toolbar = addToolBar("FileToolbar"); + toolbar->setIconSize(::profiler_gui::ICONS_SIZE); + toolbar->setObjectName("ProfilerGUI_FileToolbar"); + toolbar->setContentsMargins(1, 0, 1, 0); + + m_loadActionMenu = new QMenu(this); + auto action = m_loadActionMenu->menuAction(); + action->setText("Open file"); + action->setIcon(QIcon(imagePath("open"))); + connect(action, &QAction::triggered, this, &This::onOpenFileClicked); + toolbar->addAction(action); + + for (const auto& f : m_lastFiles) + { + action = new QAction(f, this); + connect(action, &QAction::triggered, this, &This::onOpenFileClicked); + m_loadActionMenu->addAction(action); + } + + m_saveAction = toolbar->addAction(QIcon(imagePath("save")), tr("Save"), this, SLOT(onSaveFileClicked(bool))); + m_deleteAction = toolbar->addAction(QIcon(imagePath("delete")), tr("Clear all"), this, SLOT(onDeleteClicked(bool))); + + m_saveAction->setEnabled(false); + m_deleteAction->setEnabled(false); + + + + toolbar = addToolBar("ProfileToolbar"); + toolbar->setIconSize(::profiler_gui::ICONS_SIZE); + toolbar->setObjectName("ProfilerGUI_ProfileToolbar"); + toolbar->setContentsMargins(1, 0, 1, 0); + + toolbar->addAction(QIcon(imagePath("list")), tr("Blocks"), this, SLOT(onEditBlocksClicked(bool))); + m_captureAction = toolbar->addAction(QIcon(imagePath("start")), tr("Capture"), this, SLOT(onCaptureClicked(bool))); + m_captureAction->setEnabled(false); + + toolbar->addSeparator(); + m_connectAction = toolbar->addAction(QIcon(imagePath("connect")), tr("Connect"), this, SLOT(onConnectClicked(bool))); + + auto lbl = new QLabel("Address:", toolbar); + lbl->setContentsMargins(5, 0, 2, 0); + toolbar->addWidget(lbl); + m_addressEdit = new QLineEdit(); + m_addressEdit->setToolTip("Enter IP-address or host name"); + //QRegExp rx("^0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})(\\.0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})){3}$"); + //m_addressEdit->setValidator(new QRegExpValidator(rx, m_addressEdit)); + m_addressEdit->setText(m_lastAddress); + m_addressEdit->setFixedWidth((m_addressEdit->fontMetrics().width(QString("255.255.255.255")) * 3) / 2); + toolbar->addWidget(m_addressEdit); + + lbl = new QLabel("Port:", toolbar); + lbl->setContentsMargins(5, 0, 2, 0); + toolbar->addWidget(lbl); + m_portEdit = new QLineEdit(); + m_portEdit->setValidator(new QIntValidator(1, 65535, m_portEdit)); + m_portEdit->setText(QString::number(m_lastPort)); + m_portEdit->setFixedWidth(m_portEdit->fontMetrics().width(QString("000000")) + 10); + toolbar->addWidget(m_portEdit); + + connect(m_addressEdit, &QLineEdit::returnPressed, [this] { onConnectClicked(true); }); + connect(m_portEdit, &QLineEdit::returnPressed, [this] { onConnectClicked(true); }); + + + + toolbar = addToolBar("SetupToolbar"); + toolbar->setIconSize(::profiler_gui::ICONS_SIZE); + toolbar->setObjectName("ProfilerGUI_SetupToolbar"); + toolbar->setContentsMargins(1, 0, 1, 0); + + toolbar->addAction(QIcon(imagePath("expand")), "Expand all", this, SLOT(onExpandAllClicked(bool))); + toolbar->addAction(QIcon(imagePath("collapse")), "Collapse all", this, SLOT(onCollapseAllClicked(bool))); + + toolbar->addSeparator(); + auto menu = new QMenu("Settings", this); + menu->setToolTipsVisible(true); + + QToolButton* toolButton = new QToolButton(toolbar); + toolButton->setIcon(QIcon(imagePath("settings"))); + toolButton->setMenu(menu); + toolButton->setPopupMode(QToolButton::InstantPopup); + toolbar->addWidget(toolButton); + + action = menu->addAction("Statistics enabled"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.enable_statistics); + connect(action, &QAction::triggered, this, &This::onEnableDisableStatistics); + if (EASY_GLOBALS.enable_statistics) + { + auto f = action->font(); + f.setBold(true); + action->setFont(f); + action->setIcon(QIcon(imagePath("stats"))); + } + else + { + action->setText("Statistics disabled"); + action->setIcon(QIcon(imagePath("stats-off"))); + } + + + action = menu->addAction("Only frames on histogram"); + action->setToolTip("Display only top-level blocks on histogram."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.display_only_frames_on_histogram); + connect(action, &QAction::triggered, [this](bool _checked) + { + EASY_GLOBALS.display_only_frames_on_histogram = _checked; + emit EASY_GLOBALS.events.displayOnlyFramesOnHistogramChanged(); + }); + + + menu->addSeparator(); + auto submenu = menu->addMenu("View"); + submenu->setToolTipsVisible(true); + action = submenu->addAction("Draw items' borders"); + action->setToolTip("Draw borders for blocks on diagram.\nThis reduces performance."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.draw_graphics_items_borders); + connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.draw_graphics_items_borders = _checked; refreshDiagram(); }); + + action = submenu->addAction("Overlap narrow children"); + action->setToolTip("Children blocks will be overlaped by narrow\nparent blocks. See also \'Blocks narrow size\'.\nThis improves performance."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.hide_narrow_children); + connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.hide_narrow_children = _checked; refreshDiagram(); }); + + action = submenu->addAction("Hide min-size blocks"); + action->setToolTip("Hides blocks which screen size\nis less than \'Min blocks size\'."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.hide_minsize_blocks); + connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.hide_minsize_blocks = _checked; refreshDiagram(); }); + + action = submenu->addAction("Build hierarchy only for current thread"); + action->setToolTip("Hierarchy tree will be built\nfor blocks from current thread only.\nThis improves performance\nand saves a lot of memory."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.only_current_thread_hierarchy); + connect(action, &QAction::triggered, this, &This::onHierarchyFlagChange); + + action = submenu->addAction("Add zero blocks to hierarchy"); + action->setToolTip("Zero duration blocks will be added into hierarchy tree.\nThis reduces performance and increases memory consumption."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.add_zero_blocks_to_hierarchy); + connect(action, &QAction::triggered, [this](bool _checked) + { + EASY_GLOBALS.add_zero_blocks_to_hierarchy = _checked; + emit EASY_GLOBALS.events.hierarchyFlagChanged(_checked); + }); + + action = submenu->addAction("Enable zero duration blocks on diagram"); + action->setToolTip("If checked then allows diagram to paint zero duration blocks\nwith 1px width on each scale. Otherwise, such blocks will be resized\nto 250ns duration."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.enable_zero_length); + connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.enable_zero_length = _checked; refreshDiagram(); }); + + action = submenu->addAction("Highlight similar blocks"); + action->setToolTip("Highlight all visible blocks which are similar\nto the current selected block.\nThis reduces performance."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.highlight_blocks_with_same_id); + connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.highlight_blocks_with_same_id = _checked; refreshDiagram(); }); + + action = submenu->addAction("Collapse blocks on tree reset"); + action->setToolTip("This collapses all blocks on diagram\nafter hierarchy tree reset."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.collapse_items_on_tree_close); + connect(action, &QAction::triggered, this, &This::onCollapseItemsAfterCloseChanged); + + action = submenu->addAction("Expand all on file open"); + action->setToolTip("If checked then all blocks on diagram\nwill be initially expanded."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.all_items_expanded_by_default); + connect(action, &QAction::triggered, this, &This::onAllItemsExpandedByDefaultChange); + + action = submenu->addAction("Bind diagram and tree expand"); + action->setToolTip("Expanding/collapsing blocks at diagram expands/collapses\nblocks at hierarchy tree and wise versa."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.bind_scene_and_tree_expand_status); + connect(action, &QAction::triggered, this, &This::onBindExpandStatusChange); + + action = submenu->addAction("Selecting block changes current thread"); + action->setToolTip("Automatically select thread while selecting a block.\nIf not checked then you will have to select current thread\nmanually double clicking on thread name on a diagram."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.selecting_block_changes_thread); + connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.selecting_block_changes_thread = _checked; }); + + action = submenu->addAction("Draw event markers"); + action->setToolTip("Display event markers under the blocks\n(even if event-blocks are not visible).\nThis slightly reduces performance."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.enable_event_markers); + connect(action, &QAction::triggered, [this](bool _checked) + { + EASY_GLOBALS.enable_event_markers = _checked; + refreshDiagram(); + }); + + action = submenu->addAction("Automatically adjust histogram height"); + action->setToolTip("You do not need to adjust boundaries manually,\nbut this restricts you from adjusting boundaries at all (zoom mode).\nYou can still adjust boundaries in overview mode though."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.auto_adjust_histogram_height); + connect(action, &QAction::triggered, [](bool _checked) + { + EASY_GLOBALS.auto_adjust_histogram_height = _checked; + emit EASY_GLOBALS.events.autoAdjustHistogramChanged(); + }); + + action = submenu->addAction("Use decorated thread names"); + action->setToolTip("Add \'Thread\' word into thread name if there is no one already.\nExamples: \'Render\' will change to \'Render Thread\'\n\'WorkerThread\' will not change."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.use_decorated_thread_name); + connect(action, &QAction::triggered, [this](bool _checked) + { + EASY_GLOBALS.use_decorated_thread_name = _checked; + emit EASY_GLOBALS.events.threadNameDecorationChanged(); + }); + + action = submenu->addAction("Display hex thread id"); + action->setToolTip("Display hex thread id instead of decimal."); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.hex_thread_id); + connect(action, &QAction::triggered, [this](bool _checked) + { + EASY_GLOBALS.hex_thread_id = _checked; + emit EASY_GLOBALS.events.hexThreadIdChanged(); + }); + + submenu->addSeparator(); + auto actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + action = new QAction("Chrono text at top", actionGroup); + action->setToolTip("Draw duration of selected interval\nat the top of the screen."); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::ChronoTextPosition_Top)); + if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Top) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); + + action = new QAction("Chrono text at center", actionGroup); + action->setToolTip("Draw duration of selected interval\nat the center of the screen."); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::ChronoTextPosition_Center)); + if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Center) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); + + action = new QAction("Chrono text at bottom", actionGroup); + action->setToolTip("Draw duration of selected interval\nat the bottom of the screen."); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::ChronoTextPosition_Bottom)); + if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Bottom) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); + + submenu->addSeparator(); + auto w = new QWidget(submenu); + auto l = new QHBoxLayout(w); + l->setContentsMargins(26, 1, 16, 1); + l->addWidget(new QLabel("Min blocks spacing, px", w), 0, Qt::AlignLeft); + auto spinbox = new QSpinBox(w); + spinbox->setRange(0, 400); + spinbox->setValue(EASY_GLOBALS.blocks_spacing); + spinbox->setFixedWidth(70); + connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onSpacingChange(int))); + l->addWidget(spinbox); + w->setLayout(l); + auto waction = new QWidgetAction(submenu); + waction->setDefaultWidget(w); + submenu->addAction(waction); + + w = new QWidget(submenu); + l = new QHBoxLayout(w); + l->setContentsMargins(26, 1, 16, 1); + l->addWidget(new QLabel("Min blocks size, px", w), 0, Qt::AlignLeft); + spinbox = new QSpinBox(w); + spinbox->setRange(1, 400); + spinbox->setValue(EASY_GLOBALS.blocks_size_min); + spinbox->setFixedWidth(70); + connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onMinSizeChange(int))); + l->addWidget(spinbox); + w->setLayout(l); + waction = new QWidgetAction(submenu); + waction->setDefaultWidget(w); + submenu->addAction(waction); + + w = new QWidget(submenu); + l = new QHBoxLayout(w); + l->setContentsMargins(26, 1, 16, 1); + l->addWidget(new QLabel("Blocks narrow size, px", w), 0, Qt::AlignLeft); + spinbox = new QSpinBox(w); + spinbox->setRange(1, 400); + spinbox->setValue(EASY_GLOBALS.blocks_narrow_size); + spinbox->setFixedWidth(70); + connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onNarrowSizeChange(int))); + l->addWidget(spinbox); + w->setLayout(l); + waction = new QWidgetAction(submenu); + waction->setDefaultWidget(w); + submenu->addAction(waction); + + + + + submenu = menu->addMenu("FPS Monitor"); + w = new QWidget(submenu); + l = new QHBoxLayout(w); + l->setContentsMargins(26, 1, 16, 1); + l->addWidget(new QLabel("Request interval, ms", w), 0, Qt::AlignLeft); + spinbox = new QSpinBox(w); + spinbox->setRange(1, 600000); + spinbox->setValue(EASY_GLOBALS.fps_timer_interval); + spinbox->setFixedWidth(70); + connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onFpsIntervalChange(int))); + l->addWidget(spinbox); + w->setLayout(l); + waction = new QWidgetAction(submenu); + waction->setDefaultWidget(w); + submenu->addAction(waction); + + w = new QWidget(submenu); + l = new QHBoxLayout(w); + l->setContentsMargins(26, 1, 16, 1); + l->addWidget(new QLabel("Max history size", w), 0, Qt::AlignLeft); + spinbox = new QSpinBox(w); + spinbox->setRange(2, 200); + spinbox->setValue(EASY_GLOBALS.max_fps_history); + spinbox->setFixedWidth(70); + connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onFpsHistoryChange(int))); + l->addWidget(spinbox); + w->setLayout(l); + waction = new QWidgetAction(submenu); + waction->setDefaultWidget(w); + submenu->addAction(waction); + + w = new QWidget(submenu); + l = new QHBoxLayout(w); + l->setContentsMargins(26, 1, 16, 1); + l->addWidget(new QLabel("Line width, px", w), 0, Qt::AlignLeft); + spinbox = new QSpinBox(w); + spinbox->setRange(1, 6); + spinbox->setValue(EASY_GLOBALS.fps_widget_line_width); + spinbox->setFixedWidth(70); + connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onFpsMonitorLineWidthChange(int))); + l->addWidget(spinbox); + w->setLayout(l); + waction = new QWidgetAction(submenu); + waction->setDefaultWidget(w); + submenu->addAction(waction); + + + + + submenu = menu->addMenu("Units"); + actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + action = new QAction("Auto", actionGroup); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::TimeUnits_auto)); + if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_auto) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onUnitsChanged); + + action = new QAction("Milliseconds", actionGroup); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::TimeUnits_ms)); + if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ms) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onUnitsChanged); + + action = new QAction("Microseconds", actionGroup); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::TimeUnits_us)); + if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_us) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onUnitsChanged); + + action = new QAction("Nanoseconds", actionGroup); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::TimeUnits_ns)); + if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ns) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onUnitsChanged); + + + submenu = menu->addMenu("Remote"); + m_eventTracingEnableAction = submenu->addAction("Event tracing enabled"); + m_eventTracingEnableAction->setCheckable(true); + m_eventTracingEnableAction->setEnabled(false); + connect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange); + + m_eventTracingPriorityAction = submenu->addAction("Low priority event tracing"); + m_eventTracingPriorityAction->setCheckable(true); + m_eventTracingPriorityAction->setChecked(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING); + m_eventTracingPriorityAction->setEnabled(false); + connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange); + + + submenu = menu->addMenu("Encoding"); + actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + auto default_codec_mib = QTextCodec::codecForLocale()->mibEnum(); + { + QList actions; + + for (int mib : QTextCodec::availableMibs()) + { + auto codec = QTextCodec::codecForMib(mib)->name(); + + action = new QAction(codec, actionGroup); + action->setData(mib); + action->setCheckable(true); + if (mib == default_codec_mib) + action->setChecked(true); + + actions.push_back(action); + connect(action, &QAction::triggered, this, &This::onEncodingChanged); + } + + qSort(actions.begin(), actions.end(), [](QAction* lhs, QAction* rhs) { + return lhs->text().compare(rhs->text(), Qt::CaseInsensitive) < 0; + }); + + submenu->addActions(actions); + } + + + + menu->addSeparator(); + submenu = menu->addMenu("Theme"); + actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + for (const auto& theme : UI_themes()) + { + action = new QAction(theme, actionGroup); + action->setCheckable(true); + action->setChecked(action->text() == EASY_GLOBALS.theme); + connect(action, &QAction::triggered, this, &EasyMainWindow::onThemeChange); + submenu->addAction(action); + } + + + auto tb_height = toolbar->height() + 4; + toolbar = addToolBar("FrameToolbar"); + toolbar->setIconSize(::profiler_gui::ICONS_SIZE); + toolbar->setObjectName("ProfilerGUI_FrameToolbar"); + toolbar->setContentsMargins(1, 0, 1, 0); + toolbar->setMinimumHeight(tb_height); + + lbl = new QLabel("Expected frame time:", toolbar); + lbl->setContentsMargins(5, 2, 2, 2); + toolbar->addWidget(lbl); + + m_frameTimeEdit = new QLineEdit(); + m_frameTimeEdit->setFixedWidth(70); + auto val = new QDoubleValidator(m_frameTimeEdit); + val->setLocale(QLocale::c()); + val->setBottom(0); + m_frameTimeEdit->setValidator(val); + m_frameTimeEdit->setText(QString::number(EASY_GLOBALS.frame_time * 1e-3)); + connect(m_frameTimeEdit, &QLineEdit::editingFinished, this, &This::onFrameTimeEditFinish); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, this, &This::onFrameTimeChanged); + toolbar->addWidget(m_frameTimeEdit); + + lbl = new QLabel("ms", toolbar); + lbl->setContentsMargins(5, 2, 1, 1); + toolbar->addWidget(lbl); + + + connect(graphicsView->view(), &EasyGraphicsView::intervalChanged, treeWidget->tree(), &EasyTreeWidget::setTreeBlocks); + connect(&m_readerTimer, &QTimer::timeout, this, &This::onFileReaderTimeout); + connect(&m_listenerTimer, &QTimer::timeout, this, &This::onListenerTimerTimeout); + connect(&m_fpsRequestTimer, &QTimer::timeout, this, &This::onFrameTimeRequestTimeout); + + + loadGeometry(); + + if(QCoreApplication::arguments().size() > 1) + { + auto opened_filename = QCoreApplication::arguments().at(1); + loadFile(opened_filename); + } + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blockStatusChanged, this, &This::onBlockStatusChange); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blocksRefreshRequired, this, &This::onGetBlockDescriptionsClicked); +} + +EasyMainWindow::~EasyMainWindow() +{ +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::dragEnterEvent(QDragEnterEvent* drag_event) +{ + if (drag_event->mimeData()->hasUrls()) + drag_event->acceptProposedAction(); +} + +void EasyMainWindow::dragMoveEvent(QDragMoveEvent* drag_event) +{ + if (drag_event->mimeData()->hasUrls()) + drag_event->acceptProposedAction(); +} + +void EasyMainWindow::dragLeaveEvent(QDragLeaveEvent* drag_event) +{ + drag_event->accept(); +} + +void EasyMainWindow::dropEvent(QDropEvent* drop_event) +{ + const auto& urls = drop_event->mimeData()->urls(); + if (!urls.empty()) + { + if (m_bNetworkFileRegime) + { + // Warn user about unsaved network information and suggest to save + auto result = QMessageBox::question(this, "Unsaved session", "You have unsaved data!\nSave before opening new file?", QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel); + if (result == QMessageBox::Yes) + { + onSaveFileClicked(true); + } + else if (result != QMessageBox::No) + { + // User cancelled opening new file + return; + } + } + + loadFile(urls.front().toLocalFile()); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onThemeChange(bool) +{ + auto action = qobject_cast(sender()); + if (action == nullptr) + return; + + auto newTheme = action->text(); + if (m_theme != newTheme) + { + m_theme = std::move(newTheme); + QMessageBox::information(this, "Theme", "You should restart the application to apply the theme."); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onOpenFileClicked(bool) +{ + auto action = qobject_cast(sender()); + if (action == nullptr) + return; + + QString filename; + + if (action == m_loadActionMenu->menuAction()) + filename = QFileDialog::getOpenFileName(this, "Open EasyProfiler File", m_lastFiles.empty() ? QString() : m_lastFiles.front(), "EasyProfiler File (*.prof);;All Files (*.*)"); + else + filename = action->text(); + + if (!filename.isEmpty()) + { + if (m_bNetworkFileRegime) + { + // Warn user about unsaved network information and suggest to save + auto result = QMessageBox::question(this, "Unsaved session", "You have unsaved data!\nSave before opening new file?", QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel); + if (result == QMessageBox::Yes) + { + onSaveFileClicked(true); + } + else if (result != QMessageBox::No) + { + // User cancelled opening new file + return; + } + } + + loadFile(filename); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::addFileToList(const QString& filename) +{ + m_lastFiles.push_front(filename); + + auto action = new QAction(filename, this); + connect(action, &QAction::triggered, this, &This::onOpenFileClicked); + auto fileActions = m_loadActionMenu->actions(); + if (fileActions.empty()) + m_loadActionMenu->addAction(action); + else + m_loadActionMenu->insertAction(fileActions.front(), action); + + if (m_lastFiles.size() > 10) + { + // Keep 10 files at the list + m_lastFiles.pop_back(); + m_loadActionMenu->removeAction(fileActions.back()); + delete fileActions.back(); + } + + m_bOpenedCacheFile = filename.contains(NETWORK_CACHE_FILE); + + if (m_bOpenedCacheFile) + setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1] - UNSAVED network cache file").arg(m_lastFiles.front())); + else + setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1]").arg(m_lastFiles.front())); +} + +void EasyMainWindow::loadFile(const QString& filename) +{ + const auto i = filename.lastIndexOf(QChar('/')); + const auto j = filename.lastIndexOf(QChar('\\')); + + createProgressDialog(QString("Loading %1...").arg(filename.mid(::std::max(i, j) + 1))); + + m_readerTimer.start(LOADER_TIMER_INTERVAL); + m_reader.load(filename); +} + +void EasyMainWindow::readStream(::std::stringstream& data) +{ + createProgressDialog(tr("Reading from stream...")); + m_readerTimer.start(LOADER_TIMER_INTERVAL); + m_reader.load(data); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onSaveFileClicked(bool) +{ + if (m_serializedBlocks.empty()) + return; + + QString lastFile = m_lastFiles.empty() ? QString() : m_lastFiles.front(); + + const auto i = lastFile.lastIndexOf(QChar('/')); + const auto j = lastFile.lastIndexOf(QChar('\\')); + auto k = ::std::max(i, j); + + QString dir; + if (k > 0) + dir = lastFile.mid(0, ++k); + + if (m_bNetworkFileRegime) + { + // Current file is network cache file, use current system time as output file name + + if (!dir.isEmpty()) + dir += QDateTime::currentDateTime().toString("/yyyy-MM-dd_HH-mm-ss.prof"); + else + dir = QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss.prof"); + } + else if (m_bOpenedCacheFile) + { + // Opened old network cache file, use it's last modification time as output file name + + QFileInfo fileInfo(lastFile); + if (!fileInfo.exists()) + { + // Can not open the file! + + QMessageBox::warning(this, "Warning", "Cannot open source file.\nSaving incomplete.", QMessageBox::Close); + + m_lastFiles.pop_front(); + auto action = m_loadActionMenu->actions().front(); + m_loadActionMenu->removeAction(action); + delete action; + + return; + } + + if (!dir.isEmpty()) + dir += fileInfo.lastModified().toString("/yyyy-MM-dd_HH-mm-ss.prof"); + else + dir = fileInfo.lastModified().toString("yyyy-MM-dd_HH-mm-ss.prof"); + } + else + { + dir = lastFile; + } + + auto filename = QFileDialog::getSaveFileName(this, "Save EasyProfiler File", dir, "EasyProfiler File (*.prof);;All Files (*.*)"); + if (!filename.isEmpty()) + { + // Check if the same file has been selected + { + QFileInfo fileInfo1(m_bNetworkFileRegime ? QString(NETWORK_CACHE_FILE) : lastFile), fileInfo2(filename); + if (fileInfo1.exists() && fileInfo2.exists() && fileInfo1 == fileInfo2) + { + // Selected the same file - do nothing + return; + } + } + + bool inOk = false, outOk = false; + int8_t retry1 = -1; + while (++retry1 < 4) + { + ::std::ifstream inFile(m_bNetworkFileRegime ? NETWORK_CACHE_FILE : lastFile.toStdString().c_str(), ::std::fstream::binary); + if (!inFile.is_open()) + { + ::std::this_thread::sleep_for(::std::chrono::milliseconds(500)); + continue; + } + + inOk = true; + + int8_t retry2 = -1; + while (++retry2 < 4) + { + ::std::ofstream outFile(filename.toStdString(), ::std::fstream::binary); + if (!outFile.is_open()) + { + ::std::this_thread::sleep_for(::std::chrono::milliseconds(500)); + continue; + } + + outFile << inFile.rdbuf(); + outOk = true; + break; + } + + break; + } + + if (outOk) + { + if (m_bNetworkFileRegime) + { + // Remove temporary network cahche file + QFile::remove(QString(NETWORK_CACHE_FILE)); + } + else if (m_bOpenedCacheFile) + { + // Remove old temporary network cahche file + + QFile::remove(lastFile.toStdString().c_str()); + + m_lastFiles.pop_front(); + auto action = m_loadActionMenu->actions().front(); + m_loadActionMenu->removeAction(action); + delete action; + } + + addFileToList(filename); + + m_bNetworkFileRegime = false; + } + else if (inOk) + { + QMessageBox::warning(this, "Warning", "Cannot open destination file.\nSaving incomplete.", QMessageBox::Close); + } + else + { + if (m_bNetworkFileRegime) + QMessageBox::warning(this, "Warning", "Cannot open network cache file.\nSaving incomplete.", QMessageBox::Close); + else + QMessageBox::warning(this, "Warning", "Cannot open source file.\nSaving incomplete.", QMessageBox::Close); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::clear() +{ + static_cast(m_treeWidget->widget())->clear(true); + static_cast(m_graphicsView->widget())->clear(); + +#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 + static_cast(m_descTreeWidget->widget())->clear(); +#endif + if (m_dialogDescTree != nullptr) + m_dialogDescTree->clear(); + + EASY_GLOBALS.selected_thread = 0; + ::profiler_gui::set_max(EASY_GLOBALS.selected_block); + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + EASY_GLOBALS.profiler_blocks.clear(); + EASY_GLOBALS.descriptors.clear(); + EASY_GLOBALS.gui_blocks.clear(); + + m_serializedBlocks.clear(); + m_serializedDescriptors.clear(); + + m_saveAction->setEnabled(false); + m_deleteAction->setEnabled(false); + + if (m_bNetworkFileRegime) + QFile::remove(QString(NETWORK_CACHE_FILE)); + + m_bNetworkFileRegime = false; + m_bOpenedCacheFile = false; + + setWindowTitle(EASY_DEFAULT_WINDOW_TITLE); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::refreshDiagram() +{ + static_cast(m_graphicsView->widget())->view()->scene()->update(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onDeleteClicked(bool) +{ + int button = QMessageBox::Yes; + if (m_bNetworkFileRegime) + button = QMessageBox::question(this, "Clear all profiled data", "All profiled data and network cache file\nare going to be deleted!\nContinue?", QMessageBox::Yes, QMessageBox::No); + + if (button == QMessageBox::Yes) + clear(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onExitClicked(bool) +{ + close(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onEncodingChanged(bool) +{ + auto action = qobject_cast(sender()); + if (action == nullptr) + return; + + const int mib = action->data().toInt(); + auto codec = QTextCodec::codecForMib(mib); + if (codec != nullptr) + QTextCodec::setCodecForLocale(codec); +} + +void EasyMainWindow::onChronoTextPosChanged(bool) +{ + auto _sender = qobject_cast(sender()); + EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(_sender->data().toInt()); + refreshDiagram(); +} + +void EasyMainWindow::onUnitsChanged(bool) +{ + auto _sender = qobject_cast(sender()); + EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(_sender->data().toInt()); +} + +void EasyMainWindow::onEnableDisableStatistics(bool _checked) +{ + EASY_GLOBALS.enable_statistics = _checked; + + auto action = qobject_cast(sender()); + if (action != nullptr) + { + auto f = action->font(); + f.setBold(_checked); + action->setFont(f); + + if (_checked) + { + action->setText("Statistics enabled"); + action->setIcon(QIcon(imagePath("stats"))); + } + else + { + action->setText("Statistics disabled"); + action->setIcon(QIcon(imagePath("stats-off"))); + } + } +} + +void EasyMainWindow::onCollapseItemsAfterCloseChanged(bool _checked) +{ + EASY_GLOBALS.collapse_items_on_tree_close = _checked; +} + +void EasyMainWindow::onAllItemsExpandedByDefaultChange(bool _checked) +{ + EASY_GLOBALS.all_items_expanded_by_default = _checked; +} + +void EasyMainWindow::onBindExpandStatusChange(bool _checked) +{ + EASY_GLOBALS.bind_scene_and_tree_expand_status = _checked; +} + +void EasyMainWindow::onHierarchyFlagChange(bool _checked) +{ + EASY_GLOBALS.only_current_thread_hierarchy = _checked; + emit EASY_GLOBALS.events.hierarchyFlagChanged(_checked); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onExpandAllClicked(bool) +{ + for (auto& block : EASY_GLOBALS.gui_blocks) + block.expanded = true; + + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + + auto tree = static_cast(m_treeWidget->widget())->tree(); + const QSignalBlocker b(tree); + tree->expandAll(); +} + +void EasyMainWindow::onCollapseAllClicked(bool) +{ + for (auto& block : EASY_GLOBALS.gui_blocks) + block.expanded = false; + + emit EASY_GLOBALS.events.itemsExpandStateChanged(); + + auto tree = static_cast(m_treeWidget->widget())->tree(); + const QSignalBlocker b(tree); + tree->collapseAll(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onSpacingChange(int _value) +{ + EASY_GLOBALS.blocks_spacing = _value; + refreshDiagram(); +} + +void EasyMainWindow::onMinSizeChange(int _value) +{ + EASY_GLOBALS.blocks_size_min = _value; + refreshDiagram(); +} + +void EasyMainWindow::onNarrowSizeChange(int _value) +{ + EASY_GLOBALS.blocks_narrow_size = _value; + refreshDiagram(); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onFpsIntervalChange(int _value) +{ + EASY_GLOBALS.fps_timer_interval = _value; + + if (m_fpsRequestTimer.isActive()) + m_fpsRequestTimer.stop(); + + if (EASY_GLOBALS.connected) + m_fpsRequestTimer.start(_value); +} + +void EasyMainWindow::onFpsHistoryChange(int _value) +{ + EASY_GLOBALS.max_fps_history = _value; +} + +void EasyMainWindow::onFpsMonitorLineWidthChange(int _value) +{ + EASY_GLOBALS.fps_widget_line_width = _value; +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onEditBlocksClicked(bool) +{ + if (m_descTreeDialog != nullptr) + { + m_descTreeDialog->raise(); + return; + } + + m_descTreeDialog = new QDialog(); + m_descTreeDialog->setAttribute(Qt::WA_DeleteOnClose, true); + m_descTreeDialog->setWindowTitle(EASY_DEFAULT_WINDOW_TITLE); + m_descTreeDialog->resize(800, 600); + connect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose); + + auto l = new QVBoxLayout(m_descTreeDialog); + m_dialogDescTree = new EasyDescWidget(m_descTreeDialog); + l->addWidget(m_dialogDescTree); + m_descTreeDialog->setLayout(l); + + m_dialogDescTree->build(); + m_descTreeDialog->show(); +} + +void EasyMainWindow::onDescTreeDialogClose(int) +{ + disconnect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose); + m_dialogDescTree = nullptr; + m_descTreeDialog = nullptr; +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::closeEvent(QCloseEvent* close_event) +{ + if (m_bNetworkFileRegime) + { + // Warn user about unsaved network information and suggest to save + if (QMessageBox::Yes == QMessageBox::question(this, "Unsaved session", "You have unsaved data!\nSave before exit?", QMessageBox::Yes, QMessageBox::No)) + { + onSaveFileClicked(true); + } + } + + saveSettingsAndGeometry(); + + if (m_descTreeDialog != nullptr) + { + m_descTreeDialog->reject(); + m_descTreeDialog = nullptr; + m_dialogDescTree = nullptr; + } + + Parent::closeEvent(close_event); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::loadSettings() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("main"); + + auto last_files = settings.value("last_files"); + if (!last_files.isNull()) + m_lastFiles = last_files.toStringList(); + + auto last_addr = settings.value("ip_address"); + if (!last_addr.isNull()) + m_lastAddress = last_addr.toString(); + + auto last_port = settings.value("port"); + if (!last_port.isNull()) + m_lastPort = (uint16_t)last_port.toUInt(); + + + auto val = settings.value("chrono_text_position"); + if (!val.isNull()) + EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(val.toInt()); + + val = settings.value("time_units"); + if (!val.isNull()) + EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(val.toInt()); + + + val = settings.value("frame_time"); + if (!val.isNull()) + EASY_GLOBALS.frame_time = val.toFloat(); + + val = settings.value("blocks_spacing"); + if (!val.isNull()) + EASY_GLOBALS.blocks_spacing = val.toInt(); + + val = settings.value("blocks_size_min"); + if (!val.isNull()) + EASY_GLOBALS.blocks_size_min = val.toInt(); + + val = settings.value("blocks_narrow_size"); + if (!val.isNull()) + EASY_GLOBALS.blocks_narrow_size = val.toInt(); + + + auto flag = settings.value("draw_graphics_items_borders"); + if (!flag.isNull()) + EASY_GLOBALS.draw_graphics_items_borders = flag.toBool(); + + flag = settings.value("hide_narrow_children"); + if (!flag.isNull()) + EASY_GLOBALS.hide_narrow_children = flag.toBool(); + + flag = settings.value("hide_minsize_blocks"); + if (!flag.isNull()) + EASY_GLOBALS.hide_minsize_blocks = flag.toBool(); + + flag = settings.value("collapse_items_on_tree_close"); + if (!flag.isNull()) + EASY_GLOBALS.collapse_items_on_tree_close = flag.toBool(); + + flag = settings.value("all_items_expanded_by_default"); + if (!flag.isNull()) + EASY_GLOBALS.all_items_expanded_by_default = flag.toBool(); + + flag = settings.value("only_current_thread_hierarchy"); + if (!flag.isNull()) + EASY_GLOBALS.only_current_thread_hierarchy = flag.toBool(); + + flag = settings.value("enable_zero_length"); + if (!flag.isNull()) + EASY_GLOBALS.enable_zero_length = flag.toBool(); + + flag = settings.value("add_zero_blocks_to_hierarchy"); + if (!flag.isNull()) + EASY_GLOBALS.add_zero_blocks_to_hierarchy = flag.toBool(); + + + flag = settings.value("highlight_blocks_with_same_id"); + if (!flag.isNull()) + EASY_GLOBALS.highlight_blocks_with_same_id = flag.toBool(); + + flag = settings.value("bind_scene_and_tree_expand_status"); + if (!flag.isNull()) + EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool(); + + flag = settings.value("selecting_block_changes_thread"); + if (!flag.isNull()) + EASY_GLOBALS.selecting_block_changes_thread = flag.toBool(); + + flag = settings.value("enable_event_indicators"); + if (!flag.isNull()) + EASY_GLOBALS.enable_event_markers = flag.toBool(); + + flag = settings.value("auto_adjust_histogram_height"); + if (!flag.isNull()) + EASY_GLOBALS.auto_adjust_histogram_height = flag.toBool(); + + flag = settings.value("display_only_frames_on_histogram"); + if (!flag.isNull()) + EASY_GLOBALS.display_only_frames_on_histogram = flag.toBool(); + + flag = settings.value("use_decorated_thread_name"); + if (!flag.isNull()) + EASY_GLOBALS.use_decorated_thread_name = flag.toBool(); + + flag = settings.value("hex_thread_id"); + if (!flag.isNull()) + EASY_GLOBALS.hex_thread_id = flag.toBool(); + + flag = settings.value("fps_timer_interval"); + if (!flag.isNull()) + EASY_GLOBALS.fps_timer_interval = flag.toInt(); + + flag = settings.value("max_fps_history"); + if (!flag.isNull()) + EASY_GLOBALS.max_fps_history = flag.toInt(); + + flag = settings.value("fps_widget_line_width"); + if (!flag.isNull()) + EASY_GLOBALS.fps_widget_line_width = flag.toInt(); + + flag = settings.value("enable_statistics"); + if (!flag.isNull()) + EASY_GLOBALS.enable_statistics = flag.toBool(); + + QString encoding = settings.value("encoding", "UTF-8").toString(); + auto default_codec_mib = QTextCodec::codecForName(encoding.toStdString().c_str())->mibEnum(); + auto default_codec = QTextCodec::codecForMib(default_codec_mib); + QTextCodec::setCodecForLocale(default_codec); + + auto theme = settings.value("theme"); + if (theme.isValid()) + { + EASY_GLOBALS.theme = m_theme = theme.toString(); + } + else + { + m_theme = EASY_GLOBALS.theme; + } + + settings.endGroup(); +} + +void EasyMainWindow::loadGeometry() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("main"); + + auto geometry = settings.value("geometry").toByteArray(); + if (!geometry.isEmpty()) + restoreGeometry(geometry); + + auto state = settings.value("windowState").toByteArray(); + if (!state.isEmpty()) + restoreState(state); + + settings.endGroup(); +} + +void EasyMainWindow::saveSettingsAndGeometry() +{ + QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + settings.beginGroup("main"); + + settings.setValue("geometry", this->saveGeometry()); + settings.setValue("windowState", this->saveState()); + settings.setValue("last_files", m_lastFiles); + settings.setValue("ip_address", m_lastAddress); + settings.setValue("port", (quint32)m_lastPort); + settings.setValue("chrono_text_position", static_cast(EASY_GLOBALS.chrono_text_position)); + settings.setValue("time_units", static_cast(EASY_GLOBALS.time_units)); + settings.setValue("frame_time", EASY_GLOBALS.frame_time); + settings.setValue("blocks_spacing", EASY_GLOBALS.blocks_spacing); + settings.setValue("blocks_size_min", EASY_GLOBALS.blocks_size_min); + settings.setValue("blocks_narrow_size", EASY_GLOBALS.blocks_narrow_size); + settings.setValue("draw_graphics_items_borders", EASY_GLOBALS.draw_graphics_items_borders); + settings.setValue("hide_narrow_children", EASY_GLOBALS.hide_narrow_children); + settings.setValue("hide_minsize_blocks", EASY_GLOBALS.hide_minsize_blocks); + settings.setValue("collapse_items_on_tree_close", EASY_GLOBALS.collapse_items_on_tree_close); + settings.setValue("all_items_expanded_by_default", EASY_GLOBALS.all_items_expanded_by_default); + settings.setValue("only_current_thread_hierarchy", EASY_GLOBALS.only_current_thread_hierarchy); + settings.setValue("enable_zero_length", EASY_GLOBALS.enable_zero_length); + settings.setValue("add_zero_blocks_to_hierarchy", EASY_GLOBALS.add_zero_blocks_to_hierarchy); + settings.setValue("highlight_blocks_with_same_id", EASY_GLOBALS.highlight_blocks_with_same_id); + settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status); + settings.setValue("selecting_block_changes_thread", EASY_GLOBALS.selecting_block_changes_thread); + settings.setValue("enable_event_indicators", EASY_GLOBALS.enable_event_markers); + settings.setValue("auto_adjust_histogram_height", EASY_GLOBALS.auto_adjust_histogram_height); + settings.setValue("display_only_frames_on_histogram", EASY_GLOBALS.display_only_frames_on_histogram); + settings.setValue("use_decorated_thread_name", EASY_GLOBALS.use_decorated_thread_name); + settings.setValue("hex_thread_id", EASY_GLOBALS.hex_thread_id); + settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics); + settings.setValue("fps_timer_interval", EASY_GLOBALS.fps_timer_interval); + settings.setValue("max_fps_history", EASY_GLOBALS.max_fps_history); + settings.setValue("fps_widget_line_width", EASY_GLOBALS.fps_widget_line_width); + settings.setValue("encoding", QTextCodec::codecForLocale()->name()); + settings.setValue("theme", m_theme); + + settings.endGroup(); +} + +void EasyMainWindow::destroyProgressDialog() +{ + if (m_progress != nullptr) + { + m_progress->setValue(100); + m_progress->deleteLater(); + m_progress = nullptr; + } +} + +void EasyMainWindow::createProgressDialog(const QString& text) +{ + destroyProgressDialog(); + + m_progress = new QProgressDialog(text, QStringLiteral("Cancel"), 0, 100, this); + connect(m_progress, &QProgressDialog::canceled, this, &This::onFileReaderCancel); + + m_progress->setFixedWidth(300); + m_progress->setWindowTitle(EASY_DEFAULT_WINDOW_TITLE); + m_progress->setModal(true); + m_progress->setValue(0); + m_progress->show(); +} + +void EasyMainWindow::setDisconnected(bool _showMessage) +{ + if (m_fpsRequestTimer.isActive()) + m_fpsRequestTimer.stop(); + + if (_showMessage) + QMessageBox::warning(this, "Warning", "Connection was lost", QMessageBox::Close); + + EASY_GLOBALS.connected = false; + m_captureAction->setEnabled(false); + m_connectAction->setIcon(QIcon(imagePath("connect"))); + m_connectAction->setText(tr("Connect")); + + m_eventTracingEnableAction->setEnabled(false); + m_eventTracingPriorityAction->setEnabled(false); + + m_addressEdit->setEnabled(true); + m_portEdit->setEnabled(true); + + emit EASY_GLOBALS.events.connectionChanged(false); + +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onFrameTimeRequestTimeout() +{ + if (EASY_GLOBALS.fps_enabled && EASY_GLOBALS.connected && (m_listener.regime() == LISTENER_IDLE || m_listener.regime() == LISTENER_CAPTURE)) + { + if (m_listener.requestFrameTime()) + { + QTimer::singleShot(100, this, &This::checkFrameTimeReady); + } + else if (!m_listener.connected()) + { + m_listener.closeSocket(); + setDisconnected(); + } + } +} + +void EasyMainWindow::checkFrameTimeReady() +{ + if (EASY_GLOBALS.fps_enabled && EASY_GLOBALS.connected && (m_listener.regime() == LISTENER_IDLE || m_listener.regime() == LISTENER_CAPTURE)) + { + uint32_t maxTime = 0, avgTime = 0; + if (m_listener.frameTime(maxTime, avgTime)) + { + static_cast(m_fpsViewer->widget())->addPoint(maxTime, avgTime); + } + else if (m_fpsRequestTimer.isActive()) + { + QTimer::singleShot(100, this, &This::checkFrameTimeReady); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onListenerTimerTimeout() +{ + if (!m_listener.connected()) + { + if (m_listener.regime() == LISTENER_CAPTURE_RECEIVE) + m_listener.finalizeCapture(); + if (m_listenerDialog) + m_listenerDialog->reject(); + } + else if (m_listener.regime() == LISTENER_CAPTURE_RECEIVE) + { + if (m_listener.captured()) + { + if (m_listenerTimer.isActive()) + m_listenerTimer.stop(); + + m_listener.finalizeCapture(); + + m_listenerDialog->accept(); + m_listenerDialog = nullptr; + + if (m_listener.size() != 0) + { + readStream(m_listener.data()); + m_listener.clearData(); + } + } + } +} + +void EasyMainWindow::onListenerDialogClose(int _result) +{ + if (m_listener.regime() != LISTENER_CAPTURE_RECEIVE || !m_listener.connected()) + { + if (m_listenerTimer.isActive()) + m_listenerTimer.stop(); + } + + disconnect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose); + m_listenerDialog = nullptr; + + switch (m_listener.regime()) + { + case LISTENER_CAPTURE: + { + m_listenerDialog = new QMessageBox(QMessageBox::Information, "Receiving data...", "This process may take some time.", QMessageBox::Cancel, this); + m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true); + m_listenerDialog->show(); + + m_listener.stopCapture(); + + if (m_listener.regime() != LISTENER_CAPTURE_RECEIVE) + { + m_listenerDialog->reject(); + m_listenerDialog = nullptr; + } + else + { + connect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose); + m_listenerTimer.start(250); + } + + break; + } + + case LISTENER_CAPTURE_RECEIVE: + { + if (!m_listener.captured()) + { + if (_result == QDialog::Accepted) + { + m_listenerDialog = new QMessageBox(QMessageBox::Information, "Receiving data...", "This process may take some time.", QMessageBox::Cancel, this); + connect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose); + m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true); + m_listenerDialog->show(); + } + else + { + m_listener.finalizeCapture(); + m_listener.clearData(); + + if (m_listener.connected()) + { + // make reconnect to clear socket buffers + const std::string address = m_listener.address(); + const auto port = m_listener.port(); + + profiler::net::EasyProfilerStatus reply(false, false, false); + if (m_listener.reconnect(address.c_str(), port, reply)) + { + disconnect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange); + disconnect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange); + + m_eventTracingEnableAction->setChecked(reply.isEventTracingEnabled); + m_eventTracingPriorityAction->setChecked(reply.isLowPriorityEventTracing); + + connect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange); + connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange); + + if (reply.isProfilerEnabled) + { + // Connected application is already profiling. + // Show capture dialog immediately + onCaptureClicked(true); + } + } + } + } + + break; + } + + if (m_listenerTimer.isActive()) + m_listenerTimer.stop(); + + m_listener.finalizeCapture(); + + if (m_listener.size() != 0) + { + readStream(m_listener.data()); + m_listener.clearData(); + } + + break; + } + + case LISTENER_DESCRIBE: + { + break; + } + + default: + return; + } + + if (!m_listener.connected()) + { + m_listener.closeSocket(); + setDisconnected(); + } +} + + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onFileReaderTimeout() +{ + if (m_reader.done()) + { + auto nblocks = m_reader.size(); + if (nblocks != 0) + { + static_cast(m_treeWidget->widget())->clear(true); + + ::profiler::SerializedData serialized_blocks; + ::profiler::SerializedData serialized_descriptors; + ::profiler::descriptors_list_t descriptors; + ::profiler::blocks_t blocks; + ::profiler::thread_blocks_tree_t threads_map; + QString filename; + uint32_t descriptorsNumberInFile = 0; + uint32_t version = 0; + m_reader.get(serialized_blocks, serialized_descriptors, descriptors, blocks, threads_map, descriptorsNumberInFile, version, filename); + + if (threads_map.size() > 0xff) + { + if (m_reader.isFile()) + qWarning() << "Warning: file " << filename << " contains " << threads_map.size() << " threads!"; + else + qWarning() << "Warning: input stream contains " << threads_map.size() << " threads!"; + qWarning() << "Warning: Currently, maximum number of displayed threads is 255! Some threads will not be displayed."; + } + + m_bNetworkFileRegime = !m_reader.isFile(); + if (!m_bNetworkFileRegime) + { + auto index = m_lastFiles.indexOf(filename, 0); + if (index == -1) + { + // This file is totally new. Add it to the list. + addFileToList(filename); + } + else + { + if (index != 0) + { + // This file has been already loaded. Move it to the front. + m_lastFiles.move(index, 0); + auto fileActions = m_loadActionMenu->actions(); + auto action = fileActions.at(index); + m_loadActionMenu->removeAction(action); + m_loadActionMenu->insertAction(fileActions.front(), action); + } + + m_bOpenedCacheFile = filename.contains(NETWORK_CACHE_FILE); + + if (m_bOpenedCacheFile) + setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1] - UNSAVED network cache file").arg(filename)); + else + setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1]").arg(filename)); + } + } + else + { + m_bOpenedCacheFile = false; + setWindowTitle(EASY_DEFAULT_WINDOW_TITLE " - UNSAVED network cache"); + } + + m_serializedBlocks = ::std::move(serialized_blocks); + m_serializedDescriptors = ::std::move(serialized_descriptors); + m_descriptorsNumberInFile = descriptorsNumberInFile; + EASY_GLOBALS.selected_thread = 0; + EASY_GLOBALS.version = version; + ::profiler_gui::set_max(EASY_GLOBALS.selected_block); + ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + EASY_GLOBALS.profiler_blocks.swap(threads_map); + EASY_GLOBALS.descriptors.swap(descriptors); + + EASY_GLOBALS.gui_blocks.clear(); + EASY_GLOBALS.gui_blocks.resize(nblocks); + memset(EASY_GLOBALS.gui_blocks.data(), 0, sizeof(::profiler_gui::EasyBlock) * nblocks); + for (decltype(nblocks) i = 0; i < nblocks; ++i) { + auto& guiblock = EASY_GLOBALS.gui_blocks[i]; + guiblock.tree = ::std::move(blocks[i]); +#ifdef EASY_TREE_WIDGET__USE_VECTOR + ::profiler_gui::set_max(guiblock.tree_item); +#endif + } + + static_cast(m_graphicsView->widget())->view()->setTree(EASY_GLOBALS.profiler_blocks); + +#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 + static_cast(m_descTreeWidget->widget())->build(); +#endif + if (m_dialogDescTree != nullptr) + m_dialogDescTree->build(); + + m_saveAction->setEnabled(true); + m_deleteAction->setEnabled(true); + } + else + { + QMessageBox::warning(this, "Warning", QString("Cannot read profiled blocks.\n\nReason:\n%1").arg(m_reader.getError()), QMessageBox::Close); + + if (m_reader.isFile()) + { + auto index = m_lastFiles.indexOf(m_reader.filename(), 0); + if (index >= 0) + { + // Remove unexisting file from list + m_lastFiles.removeAt(index); + auto action = m_loadActionMenu->actions().at(index); + m_loadActionMenu->removeAction(action); + delete action; + } + } + } + + m_reader.interrupt(); + + m_readerTimer.stop(); + destroyProgressDialog(); + + if (EASY_GLOBALS.all_items_expanded_by_default) + { + onExpandAllClicked(true); + } + } + else if (m_progress != nullptr) + { + m_progress->setValue(m_reader.progress()); + } +} + +void EasyMainWindow::onFileReaderCancel() +{ + m_readerTimer.stop(); + m_reader.interrupt(); + destroyProgressDialog(); +} + +////////////////////////////////////////////////////////////////////////// + +EasyFileReader::EasyFileReader() +{ + +} + +EasyFileReader::~EasyFileReader() +{ + interrupt(); +} + +const bool EasyFileReader::isFile() const +{ + return m_isFile; +} + +bool EasyFileReader::done() const +{ + return m_bDone.load(::std::memory_order_acquire); +} + +int EasyFileReader::progress() const +{ + return m_progress.load(::std::memory_order_acquire); +} + +unsigned int EasyFileReader::size() const +{ + return m_size.load(::std::memory_order_acquire); +} + +const QString& EasyFileReader::filename() const +{ + return m_filename; +} + +void EasyFileReader::load(const QString& _filename) +{ + interrupt(); + + m_isFile = true; + m_filename = _filename; + m_thread = ::std::thread([this](bool _enableStatistics) { + m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors, + m_descriptors, m_blocks, m_blocksTree, m_descriptorsNumberInFile, m_version, _enableStatistics, m_errorMessage), ::std::memory_order_release); + m_progress.store(100, ::std::memory_order_release); + m_bDone.store(true, ::std::memory_order_release); + }, EASY_GLOBALS.enable_statistics); +} + +void EasyFileReader::load(::std::stringstream& _stream) +{ + interrupt(); + + m_isFile = false; + m_filename.clear(); + +#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__llvm__) + // gcc 4 has a known bug which has been solved in gcc 5: + // std::stringstream has no swap() method :( + // have to copy all contents... Use gcc 5 or higher! +#pragma message "Warning: in gcc 4 and lower std::stringstream has no swap()! Memory consumption may increase! Better use gcc 5 or higher instead." + m_stream.str(_stream.str()); +#else + m_stream.swap(_stream); +#endif + + m_thread = ::std::thread([this](bool _enableStatistics) { + ::std::ofstream cache_file(NETWORK_CACHE_FILE, ::std::fstream::binary); + if (cache_file.is_open()) { + cache_file << m_stream.str(); + cache_file.close(); + } + m_size.store(fillTreesFromStream(m_progress, m_stream, m_serializedBlocks, m_serializedDescriptors, m_descriptors, + m_blocks, m_blocksTree, m_descriptorsNumberInFile, m_version, _enableStatistics, m_errorMessage), ::std::memory_order_release); + m_progress.store(100, ::std::memory_order_release); + m_bDone.store(true, ::std::memory_order_release); + }, EASY_GLOBALS.enable_statistics); +} + +void EasyFileReader::interrupt() +{ + m_progress.store(-100, ::std::memory_order_release); + if (m_thread.joinable()) + m_thread.join(); + + m_bDone.store(false, ::std::memory_order_release); + m_progress.store(0, ::std::memory_order_release); + m_size.store(0, ::std::memory_order_release); + m_serializedBlocks.clear(); + m_serializedDescriptors.clear(); + m_descriptors.clear(); + m_blocks.clear(); + m_blocksTree.clear(); + m_descriptorsNumberInFile = 0; + m_version = 0; + + clear_stream(m_stream); + clear_stream(m_errorMessage); +} + +void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors, + ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& _tree, uint32_t& _descriptorsNumberInFile, uint32_t& _version, QString& _filename) +{ + if (done()) + { + m_serializedBlocks.swap(_serializedBlocks); + m_serializedDescriptors.swap(_serializedDescriptors); + ::profiler::descriptors_list_t(::std::move(m_descriptors)).swap(_descriptors); + m_blocks.swap(_blocks); + m_blocksTree.swap(_tree); + m_filename.swap(_filename); + _descriptorsNumberInFile = m_descriptorsNumberInFile; + _version = m_version; + } +} + +QString EasyFileReader::getError() +{ + return QString(m_errorMessage.str().c_str()); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onEventTracingPriorityChange(bool _checked) +{ + if (EASY_GLOBALS.connected) + m_listener.send(profiler::net::BoolMessage(profiler::net::MessageType::Change_Event_Tracing_Priority, _checked)); +} + +void EasyMainWindow::onEventTracingEnableChange(bool _checked) +{ + if (EASY_GLOBALS.connected) + m_listener.send(profiler::net::BoolMessage(profiler::net::MessageType::Change_Event_Tracing_Status, _checked)); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onFrameTimeEditFinish() +{ + auto text = m_frameTimeEdit->text(); + if (text.contains(QChar(','))) + { + text.remove(QChar('.')).replace(QChar(','), QChar('.')); + m_frameTimeEdit->setText(text); + } + + EASY_GLOBALS.frame_time = text.toFloat() * 1e3f; + + disconnect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, + this, &This::onFrameTimeChanged); + + emit EASY_GLOBALS.events.expectedFrameTimeChanged(); + + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, + this, &This::onFrameTimeChanged); +} + +void EasyMainWindow::onFrameTimeChanged() +{ + m_frameTimeEdit->setText(QString::number(EASY_GLOBALS.frame_time * 1e-3)); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onConnectClicked(bool) +{ + if (EASY_GLOBALS.connected) + { + // Disconnect if already connected + m_listener.disconnect(); + setDisconnected(false); + return; + } + + QString address = m_addressEdit->text(); + const decltype(m_lastPort) port = m_portEdit->text().toUShort(); + + const bool isSameAddress = (EASY_GLOBALS.connected && m_listener.port() == port && + address.toStdString() == m_listener.address()); + + profiler::net::EasyProfilerStatus reply(false, false, false); + if (!m_listener.connect(address.toStdString().c_str(), port, reply)) + { + QMessageBox::warning(this, "Warning", QString("Cannot connect to %1").arg(address), QMessageBox::Close); + if (EASY_GLOBALS.connected) + { + m_listener.closeSocket(); + setDisconnected(false); + } + + if (!isSameAddress) + { + m_lastAddress = ::std::move(address); + m_lastPort = port; + } + + return; + } + + m_lastAddress = ::std::move(address); + m_lastPort = port; + + qInfo() << "Connected successfully"; + EASY_GLOBALS.connected = true; + m_captureAction->setEnabled(true); + m_connectAction->setIcon(QIcon(imagePath("connected"))); + m_connectAction->setText(tr("Disconnect")); + + if (m_fpsViewer->isVisible()) + static_cast(m_fpsViewer->widget())->clear(); + + if (!m_fpsRequestTimer.isActive()) + m_fpsRequestTimer.start(EASY_GLOBALS.fps_timer_interval); + + disconnect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange); + disconnect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange); + + m_eventTracingEnableAction->setEnabled(true); + m_eventTracingPriorityAction->setEnabled(true); + + m_eventTracingEnableAction->setChecked(reply.isEventTracingEnabled); + m_eventTracingPriorityAction->setChecked(reply.isLowPriorityEventTracing); + + connect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange); + connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange); + + m_addressEdit->setEnabled(false); + m_portEdit->setEnabled(false); + + emit EASY_GLOBALS.events.connectionChanged(true); + + if (reply.isProfilerEnabled) + { + // Connected application is already profiling. + // Show capture dialog immediately + onCaptureClicked(true); + } +} + +void EasyMainWindow::onCaptureClicked(bool) +{ + if (!EASY_GLOBALS.connected) + { + QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close); + return; + } + + if (m_listener.regime() != LISTENER_IDLE) + { + if (m_listener.regime() == LISTENER_CAPTURE || m_listener.regime() == LISTENER_CAPTURE_RECEIVE) + QMessageBox::warning(this, "Warning", "Already capturing frames.\nFinish old capturing session first.", QMessageBox::Close); + else + QMessageBox::warning(this, "Warning", "Capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close); + return; + } + + if (!m_listener.startCapture()) + { + // Connection lost. Try to restore connection. + + profiler::net::EasyProfilerStatus reply(false, false, false); + if (!m_listener.connect(m_lastAddress.toStdString().c_str(), m_lastPort, reply)) + { + m_listener.closeSocket(); + setDisconnected(); + return; + } + + if (!m_listener.startCapture()) + { + m_listener.closeSocket(); + setDisconnected(); + return; + } + } + + m_listenerTimer.start(250); + + m_listenerDialog = new QMessageBox(QMessageBox::Information, "Capturing frames...", "Close this dialog to stop capturing.", QMessageBox::NoButton, this); + + auto button = new QToolButton(m_listenerDialog); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + button->setIconSize(::profiler_gui::ICONS_SIZE); + button->setIcon(QIcon(imagePath("stop"))); + button->setText("Stop"); + m_listenerDialog->addButton(button, QMessageBox::AcceptRole); + + m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true); + connect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose); + m_listenerDialog->show(); +} + +void EasyMainWindow::onGetBlockDescriptionsClicked(bool) +{ + if (!EASY_GLOBALS.connected) + { + QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close); + return; + } + + if (m_listener.regime() != LISTENER_IDLE) + { + if (m_listener.regime() == LISTENER_DESCRIBE) + QMessageBox::warning(this, "Warning", "Already capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close); + else + QMessageBox::warning(this, "Warning", "Already capturing frames.\nFinish old capturing session first.", QMessageBox::Close); + return; + } + + m_listenerDialog = new QMessageBox(QMessageBox::Information, "Waiting for blocks...", "This may take some time.", QMessageBox::NoButton, this); + m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true); + m_listenerDialog->show(); + + m_listener.requestBlocksDescription(); + + m_listenerDialog->reject(); + m_listenerDialog = nullptr; + + if (m_listener.size() != 0) + { + // Read descriptions from stream + + decltype(EASY_GLOBALS.descriptors) descriptors; + decltype(m_serializedDescriptors) serializedDescriptors; + ::std::stringstream errorMessage; + if (readDescriptionsFromStream(m_listener.data(), serializedDescriptors, descriptors, errorMessage)) + { + // Merge old and new descriptions + + bool cancel = false; + const bool doFlush = m_descriptorsNumberInFile > descriptors.size(); + if (doFlush && !m_serializedBlocks.empty()) + { + auto button = QMessageBox::question(this, "Information", + QString("New blocks description number = %1\nis less than the old one = %2.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?") + .arg(descriptors.size()) + .arg(m_descriptorsNumberInFile), + QMessageBox::Yes, QMessageBox::No); + + if (button == QMessageBox::Yes) + clear(); // Clear all contents because new descriptors list conflicts with old one + else + cancel = true; + } + + if (!cancel) + { + if (!doFlush && m_descriptorsNumberInFile < EASY_GLOBALS.descriptors.size()) + { + // There are dynamically added descriptors, add them to the new list too + + auto newnumber = static_cast(descriptors.size()); + auto size = static_cast(EASY_GLOBALS.descriptors.size()); + auto diff = newnumber - size; + decltype(newnumber) failnumber = 0; + + descriptors.reserve(descriptors.size() + EASY_GLOBALS.descriptors.size() - m_descriptorsNumberInFile); + for (auto i = m_descriptorsNumberInFile; i < size; ++i) + { + auto id = EASY_GLOBALS.descriptors[i]->id(); + if (id < newnumber) + descriptors.push_back(descriptors[id]); + else + ++failnumber; + } + + if (failnumber != 0) + { + // There are some errors... + + // revert changes + descriptors.resize(newnumber); + + // clear all profiled data to avoid conflicts + auto button = QMessageBox::question(this, "Information", + "There are errors while merging block descriptions lists.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?", + QMessageBox::Yes, QMessageBox::No); + + if (button == QMessageBox::Yes) + clear(); // Clear all contents because new descriptors list conflicts with old one + else + cancel = true; + } + + if (!cancel && diff != 0) + { + for (auto& b : EASY_GLOBALS.gui_blocks) + { + if (b.tree.node->id() >= m_descriptorsNumberInFile) + b.tree.node->setId(b.tree.node->id() + diff); + } + + m_descriptorsNumberInFile = newnumber; + } + } + + if (!cancel) + { + EASY_GLOBALS.descriptors.swap(descriptors); + m_serializedDescriptors.swap(serializedDescriptors); + m_descriptorsNumberInFile = static_cast(EASY_GLOBALS.descriptors.size()); + + if (m_descTreeDialog != nullptr) + { +#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 + static_cast(m_descTreeWidget->widget())->build(); +#endif + m_dialogDescTree->build(); + m_descTreeDialog->raise(); + } + else + { + onEditBlocksClicked(true); + } + } + } + } + else + { + QMessageBox::warning(this, "Warning", QString("Cannot read blocks description from stream.\n\nReason:\n%1").arg(errorMessage.str().c_str()), QMessageBox::Close); + } + + m_listener.clearData(); + } + + if (!m_listener.connected()) + { + m_listener.closeSocket(); + setDisconnected(); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status) +{ + if (EASY_GLOBALS.connected) + m_listener.send(profiler::net::BlockStatusMessage(_id, static_cast(_status))); +} + +////////////////////////////////////////////////////////////////////////// + +EasySocketListener::EasySocketListener() : m_receivedSize(0), m_port(0), m_regime(LISTENER_IDLE) +{ + m_bInterrupt = ATOMIC_VAR_INIT(false); + m_bConnected = ATOMIC_VAR_INIT(false); + m_bStopReceive = ATOMIC_VAR_INIT(false); + m_bFrameTimeReady = ATOMIC_VAR_INIT(false); + m_bCaptureReady = ATOMIC_VAR_INIT(false); + m_frameMax = ATOMIC_VAR_INIT(0); + m_frameAvg = ATOMIC_VAR_INIT(0); +} + +EasySocketListener::~EasySocketListener() +{ + m_bInterrupt.store(true, ::std::memory_order_release); + if (m_thread.joinable()) + m_thread.join(); +} + +bool EasySocketListener::connected() const +{ + return m_bConnected.load(::std::memory_order_acquire); +} + +bool EasySocketListener::captured() const +{ + return m_bCaptureReady.load(::std::memory_order_acquire); +} + +EasyListenerRegime EasySocketListener::regime() const +{ + return m_regime; +} + +uint64_t EasySocketListener::size() const +{ + return m_receivedSize; +} + +::std::stringstream& EasySocketListener::data() +{ + return m_receivedData; +} + +const ::std::string& EasySocketListener::address() const +{ + return m_address; +} + +uint16_t EasySocketListener::port() const +{ + return m_port; +} + +void EasySocketListener::clearData() +{ + clear_stream(m_receivedData); + m_receivedSize = 0; +} + +void EasySocketListener::disconnect() +{ + if (connected()) + { + m_bInterrupt.store(true, ::std::memory_order_release); + if (m_thread.joinable()) + m_thread.join(); + + m_bConnected.store(false, ::std::memory_order_release); + m_bInterrupt.store(false, ::std::memory_order_release); + m_bCaptureReady.store(false, ::std::memory_order_release); + m_bStopReceive.store(false, ::std::memory_order_release); + } + + m_address.clear(); + m_port = 0; + + closeSocket(); +} + +void EasySocketListener::closeSocket() +{ + m_easySocket.flush(); + m_easySocket.init(); +} + +bool EasySocketListener::connect(const char* _ipaddress, uint16_t _port, profiler::net::EasyProfilerStatus& _reply, bool _disconnectFirst) +{ + if (connected()) + { + m_bInterrupt.store(true, ::std::memory_order_release); + if (m_thread.joinable()) + m_thread.join(); + + m_bConnected.store(false, ::std::memory_order_release); + m_bInterrupt.store(false, ::std::memory_order_release); + m_bCaptureReady.store(false, ::std::memory_order_release); + m_bStopReceive.store(false, ::std::memory_order_release); + } + + m_address.clear(); + m_port = 0; + + if (_disconnectFirst) + closeSocket(); + + int res = m_easySocket.setAddress(_ipaddress, _port); + res = m_easySocket.connect(); + + const bool isConnected = res == 0; + if (isConnected) + { + static const size_t buffer_size = sizeof(profiler::net::EasyProfilerStatus) << 1; + char buffer[buffer_size] = {}; + int bytes = 0; + + while (true) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.isDisconnected()) + return false; + bytes = 0; + continue; + } + + break; + } + + if (bytes == 0) + { + m_address = _ipaddress; + m_port = _port; + m_bConnected.store(isConnected, ::std::memory_order_release); + return isConnected; + } + + size_t seek = bytes; + while (seek < sizeof(profiler::net::EasyProfilerStatus)) + { + bytes = m_easySocket.receive(buffer + seek, buffer_size - seek); + + if (bytes == -1) + { + if (m_easySocket.isDisconnected()) + return false; + break; + } + + seek += bytes; + } + + auto message = reinterpret_cast(buffer); + if (message->isEasyNetMessage() && message->type == profiler::net::MessageType::Connection_Accepted) + _reply = *message; + + m_address = _ipaddress; + m_port = _port; + } + + m_bConnected.store(isConnected, ::std::memory_order_release); + return isConnected; +} + +bool EasySocketListener::reconnect(const char* _ipaddress, uint16_t _port, ::profiler::net::EasyProfilerStatus& _reply) +{ + return connect(_ipaddress, _port, _reply, true); +} + +bool EasySocketListener::startCapture() +{ + //if (m_thread.joinable()) + //{ + // m_bInterrupt.store(true, ::std::memory_order_release); + // m_thread.join(); + // m_bInterrupt.store(false, ::std::memory_order_release); + //} + + clearData(); + + profiler::net::Message request(profiler::net::MessageType::Request_Start_Capture); + m_easySocket.send(&request, sizeof(request)); + + if (m_easySocket.isDisconnected()) { + m_bConnected.store(false, ::std::memory_order_release); + return false; + } + + m_regime = LISTENER_CAPTURE; + m_bCaptureReady.store(false, ::std::memory_order_release); + //m_thread = ::std::thread(&EasySocketListener::listenCapture, this); + + return true; +} + +void EasySocketListener::stopCapture() +{ + //if (!m_thread.joinable() || m_regime != LISTENER_CAPTURE) + // return; + + if (m_regime != LISTENER_CAPTURE) + return; + + //m_bStopReceive.store(true, ::std::memory_order_release); + profiler::net::Message request(profiler::net::MessageType::Request_Stop_Capture); + m_easySocket.send(&request, sizeof(request)); + + //m_thread.join(); + + if (m_easySocket.isDisconnected()) { + m_bConnected.store(false, ::std::memory_order_release); + m_bStopReceive.store(false, ::std::memory_order_release); + m_regime = LISTENER_IDLE; + m_bCaptureReady.store(true, ::std::memory_order_release); + return; + } + + m_regime = LISTENER_CAPTURE_RECEIVE; + if (m_thread.joinable()) + { + m_bInterrupt.store(true, ::std::memory_order_release); + m_thread.join(); + m_bInterrupt.store(false, ::std::memory_order_release); + } + + m_thread = ::std::thread(&EasySocketListener::listenCapture, this); + + //m_regime = LISTENER_IDLE; + //m_bStopReceive.store(false, ::std::memory_order_release); +} + +void EasySocketListener::finalizeCapture() +{ + if (m_thread.joinable()) + { + m_bInterrupt.store(true, ::std::memory_order_release); + m_thread.join(); + m_bInterrupt.store(false, ::std::memory_order_release); + } + + m_regime = LISTENER_IDLE; + m_bCaptureReady.store(false, ::std::memory_order_release); + m_bStopReceive.store(false, ::std::memory_order_release); +} + +void EasySocketListener::requestBlocksDescription() +{ + if (m_thread.joinable()) + { + m_bInterrupt.store(true, ::std::memory_order_release); + m_thread.join(); + m_bInterrupt.store(false, ::std::memory_order_release); + } + + clearData(); + + profiler::net::Message request(profiler::net::MessageType::Request_Blocks_Description); + m_easySocket.send(&request, sizeof(request)); + + if(m_easySocket.isDisconnected() ){ + m_bConnected.store(false, ::std::memory_order_release); + } + + m_regime = LISTENER_DESCRIBE; + listenDescription(); + m_regime = LISTENER_IDLE; +} + +bool EasySocketListener::frameTime(uint32_t& _maxTime, uint32_t& _avgTime) +{ + if (m_bFrameTimeReady.exchange(false, ::std::memory_order_acquire)) + { + _maxTime = m_frameMax.load(::std::memory_order_acquire); + _avgTime = m_frameAvg.load(::std::memory_order_acquire); + return true; + } + + return false; +} + +bool EasySocketListener::requestFrameTime() +{ + if (m_regime != LISTENER_IDLE && m_regime != LISTENER_CAPTURE) + return false; + + if (m_thread.joinable()) + { + m_bInterrupt.store(true, ::std::memory_order_release); + m_thread.join(); + m_bInterrupt.store(false, ::std::memory_order_release); + } + + profiler::net::Message request(profiler::net::MessageType::Request_MainThread_FPS); + m_easySocket.send(&request, sizeof(request)); + + if (m_easySocket.isDisconnected()) + { + m_bConnected.store(false, ::std::memory_order_release); + return false; + } + + m_bFrameTimeReady.store(false, ::std::memory_order_release); + m_thread = ::std::thread(&EasySocketListener::listenFrameTime, this); + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +void EasySocketListener::listenCapture() +{ + EASY_STATIC_CONSTEXPR int buffer_size = 8 * 1024 * 1024; + + char* buffer = new char[buffer_size]; + int seek = 0, bytes = 0; + auto timeBegin = ::std::chrono::system_clock::now(); + + bool isListen = true, disconnected = false; + while (isListen && !m_bInterrupt.load(::std::memory_order_acquire)) + { + if (m_bStopReceive.load(::std::memory_order_acquire)) + { + profiler::net::Message request(profiler::net::MessageType::Request_Stop_Capture); + m_easySocket.send(&request, sizeof(request)); + m_bStopReceive.store(false, ::std::memory_order_release); + } + + if ((bytes - seek) == 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.isDisconnected()) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + } + + seek = 0; + bytes = 0; + + continue; + } + + seek = 0; + } + + if (bytes == 0) + { + isListen = false; + break; + } + + char* buf = buffer + seek; + + if (bytes > 0) + { + auto message = reinterpret_cast(buf); + if (!message->isEasyNetMessage()) + continue; + + switch (message->type) + { + case profiler::net::MessageType::Connection_Accepted: + { + qInfo() << "Receive MessageType::Connection_Accepted"; + //m_easySocket.send(&request, sizeof(request)); + seek += sizeof(profiler::net::Message); + break; + } + + case profiler::net::MessageType::Reply_Capturing_Started: + { + qInfo() << "Receive MessageType::Reply_Capturing_Started"; + seek += sizeof(profiler::net::Message); + break; + } + + case profiler::net::MessageType::Reply_Blocks_End: + { + qInfo() << "Receive MessageType::Reply_Blocks_End"; + seek += sizeof(profiler::net::Message); + + const auto dt = ::std::chrono::duration_cast(::std::chrono::system_clock::now() - timeBegin); + const auto bytesNumber = m_receivedData.str().size(); + qInfo() << "recieved " << bytesNumber << " bytes, " << dt.count() << " ms, average speed = " << double(bytesNumber) * 1e3 / double(dt.count()) / 1024. << " kBytes/sec"; + + seek = 0; + bytes = 0; + + isListen = false; + + break; + } + + case profiler::net::MessageType::Reply_Blocks: + { + qInfo() << "Receive MessageType::Reply_Blocks"; + + seek += sizeof(profiler::net::DataMessage); + auto dm = (profiler::net::DataMessage*)message; + timeBegin = std::chrono::system_clock::now(); + + int neededSize = dm->size; + + + buf = buffer + seek; + auto bytesNumber = ::std::min((int)dm->size, bytes - seek); + m_receivedSize += bytesNumber; + m_receivedData.write(buf, bytesNumber); + neededSize -= bytesNumber; + + if (neededSize == 0) + seek += bytesNumber; + else + { + seek = 0; + bytes = 0; + } + + + int loaded = 0; + while (neededSize > 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.isDisconnected()) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + neededSize = 0; + } + + break; + } + + buf = buffer; + int toWrite = ::std::min(bytes, neededSize); + m_receivedSize += toWrite; + m_receivedData.write(buf, toWrite); + neededSize -= toWrite; + loaded += toWrite; + seek = toWrite; + } + + if (m_bStopReceive.load(::std::memory_order_acquire)) + { + profiler::net::Message request(profiler::net::MessageType::Request_Stop_Capture); + m_easySocket.send(&request, sizeof(request)); + m_bStopReceive.store(false, ::std::memory_order_release); + } + + break; + } + + default: + //qInfo() << "Receive unknown " << message->type; + break; + } + } + } + + if (disconnected) + clearData(); + + delete [] buffer; + + m_bCaptureReady.store(true, ::std::memory_order_release); +} + +void EasySocketListener::listenDescription() +{ + EASY_STATIC_CONSTEXPR int buffer_size = 8 * 1024 * 1024; + + char* buffer = new char[buffer_size]; + int seek = 0, bytes = 0; + + bool isListen = true, disconnected = false; + while (isListen && !m_bInterrupt.load(::std::memory_order_acquire)) + { + if ((bytes - seek) == 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.isDisconnected()) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + } + + seek = 0; + bytes = 0; + + continue; + } + + seek = 0; + } + + if (bytes == 0) + { + isListen = false; + break; + } + + char* buf = buffer + seek; + + if (bytes > 0) + { + auto message = reinterpret_cast(buf); + if (!message->isEasyNetMessage()) + continue; + + switch (message->type) + { + case profiler::net::MessageType::Connection_Accepted: + { + qInfo() << "Receive MessageType::Connection_Accepted"; + seek += sizeof(profiler::net::Message); + break; + } + + case profiler::net::MessageType::Reply_Blocks_Description_End: + { + qInfo() << "Receive MessageType::Reply_Blocks_Description_End"; + seek += sizeof(profiler::net::Message); + + seek = 0; + bytes = 0; + + isListen = false; + + break; + } + + case profiler::net::MessageType::Reply_Blocks_Description: + { + qInfo() << "Receive MessageType::Reply_Blocks_Description"; + + seek += sizeof(profiler::net::DataMessage); + auto dm = (profiler::net::DataMessage*)message; + int neededSize = dm->size; + + buf = buffer + seek; + auto bytesNumber = ::std::min((int)dm->size, bytes - seek); + m_receivedSize += bytesNumber; + m_receivedData.write(buf, bytesNumber); + neededSize -= bytesNumber; + + if (neededSize == 0) + seek += bytesNumber; + else{ + seek = 0; + bytes = 0; + } + + int loaded = 0; + while (neededSize > 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.isDisconnected()) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + neededSize = 0; + } + + break; + } + + buf = buffer; + int toWrite = ::std::min(bytes, neededSize); + m_receivedSize += toWrite; + m_receivedData.write(buf, toWrite); + neededSize -= toWrite; + loaded += toWrite; + seek = toWrite; + } + + break; + } + + default: + break; + } + } + } + + if (disconnected) + clearData(); + + delete[] buffer; +} + +void EasySocketListener::listenFrameTime() +{ + EASY_STATIC_CONSTEXPR size_t buffer_size = sizeof(::profiler::net::TimestampMessage) << 2; + + char buffer[buffer_size] = {}; + int seek = 0, bytes = 0; + + bool isListen = true; + while (isListen && !m_bInterrupt.load(::std::memory_order_acquire)) + { + if ((bytes - seek) == 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.isDisconnected()) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + } + + seek = 0; + bytes = 0; + + continue; + } + + seek = 0; + } + + if (bytes == 0) + { + isListen = false; + break; + } + + char* buf = buffer + seek; + + if (bytes > 0) + { + auto message = reinterpret_cast(buf); + if (!message->isEasyNetMessage()) + continue; + + switch (message->type) + { + case profiler::net::MessageType::Connection_Accepted: + case profiler::net::MessageType::Reply_Capturing_Started: + { + seek += sizeof(profiler::net::Message); + break; + } + + case profiler::net::MessageType::Reply_MainThread_FPS: + { + //qInfo() << "Receive MessageType::Reply_MainThread_FPS"; + + seek += sizeof(profiler::net::TimestampMessage); + if (seek <= buffer_size) + { + auto timestampMessage = (profiler::net::TimestampMessage*)message; + m_frameMax.store(timestampMessage->maxValue, ::std::memory_order_release); + m_frameAvg.store(timestampMessage->avgValue, ::std::memory_order_release); + m_bFrameTimeReady.store(true, ::std::memory_order_release); + } + + isListen = false; + break; + } + + default: + break; + } + } + } +} + +////////////////////////////////////////////////////////////////////////// + diff --git a/3rdparty/easyprofiler/profiler_gui/main_window.h b/3rdparty/easyprofiler/profiler_gui/main_window.h new file mode 100644 index 0000000..474b972 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/main_window.h @@ -0,0 +1,335 @@ +/************************************************************************ +* file name : main_window.h +* ----------------- : +* creation time : 2016/06/26 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of MainWindow for easy_profiler GUI. +* ----------------- : +* change log : * 2016/06/26 Victor Zarubkin: initial commit. +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_PROFILER_GUI__MAIN_WINDOW__H +#define EASY_PROFILER_GUI__MAIN_WINDOW__H + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +////////////////////////////////////////////////////////////////////////// + +#define EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW 0 + +namespace profiler { namespace net { struct EasyProfilerStatus; } } + +////////////////////////////////////////////////////////////////////////// + +class EasyFileReader Q_DECL_FINAL +{ + ::profiler::SerializedData m_serializedBlocks; ///< + ::profiler::SerializedData m_serializedDescriptors; ///< + ::profiler::descriptors_list_t m_descriptors; ///< + ::profiler::blocks_t m_blocks; ///< + ::profiler::thread_blocks_tree_t m_blocksTree; ///< + ::std::stringstream m_stream; ///< + ::std::stringstream m_errorMessage; ///< + QString m_filename; ///< + uint32_t m_descriptorsNumberInFile = 0; ///< + uint32_t m_version = 0; ///< + ::std::thread m_thread; ///< + ::std::atomic_bool m_bDone; ///< + ::std::atomic m_progress; ///< + ::std::atomic m_size; ///< + bool m_isFile = false; ///< + +public: + + EasyFileReader(); + ~EasyFileReader(); + + const bool isFile() const; + bool done() const; + int progress() const; + unsigned int size() const; + const QString& filename() const; + + void load(const QString& _filename); + void load(::std::stringstream& _stream); + void interrupt(); + void get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors, + ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& _tree, + uint32_t& _descriptorsNumberInFile, uint32_t& _version, QString& _filename); + + QString getError(); + +}; // END of class EasyFileReader. + +////////////////////////////////////////////////////////////////////////// + +enum EasyListenerRegime : uint8_t +{ + LISTENER_IDLE = 0, + LISTENER_CAPTURE, + LISTENER_CAPTURE_RECEIVE, + LISTENER_DESCRIBE +}; + +class EasySocketListener Q_DECL_FINAL +{ + EasySocket m_easySocket; ///< + ::std::string m_address; ///< + ::std::stringstream m_receivedData; ///< + ::std::thread m_thread; ///< + uint64_t m_receivedSize; ///< + uint16_t m_port; ///< + ::std::atomic m_frameMax; ///< + ::std::atomic m_frameAvg; ///< + ::std::atomic_bool m_bInterrupt; ///< + ::std::atomic_bool m_bConnected; ///< + ::std::atomic_bool m_bStopReceive; ///< + ::std::atomic_bool m_bCaptureReady; ///< + ::std::atomic_bool m_bFrameTimeReady; ///< + EasyListenerRegime m_regime; ///< + +public: + + EasySocketListener(); + ~EasySocketListener(); + + bool connected() const; + bool captured() const; + EasyListenerRegime regime() const; + uint64_t size() const; + const ::std::string& address() const; + uint16_t port() const; + + ::std::stringstream& data(); + void clearData(); + + void disconnect(); + void closeSocket(); + bool connect(const char* _ipaddress, uint16_t _port, ::profiler::net::EasyProfilerStatus& _reply, bool _disconnectFirst = false); + bool reconnect(const char* _ipaddress, uint16_t _port, ::profiler::net::EasyProfilerStatus& _reply); + + bool startCapture(); + void stopCapture(); + void finalizeCapture(); + void requestBlocksDescription(); + + bool frameTime(uint32_t& _maxTime, uint32_t& _avgTime); + bool requestFrameTime(); + + template + inline void send(const T& _message) + { + m_easySocket.send(&_message, sizeof(T)); + } + +private: + + void listenCapture(); + void listenDescription(); + void listenFrameTime(); + +}; // END of class EasySocketListener. + +////////////////////////////////////////////////////////////////////////// + +class EasyDockWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit EasyDockWidget(const QString& title, QWidget* parent = nullptr); + ~EasyDockWidget() override; +}; + +class EasyMainWindow : public QMainWindow +{ + Q_OBJECT + +protected: + + typedef EasyMainWindow This; + typedef QMainWindow Parent; + + QStringList m_lastFiles; + QString m_theme; + QString m_lastAddress; + QDockWidget* m_treeWidget = nullptr; + QDockWidget* m_graphicsView = nullptr; + QDockWidget* m_fpsViewer = nullptr; + +#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 + QDockWidget* m_descTreeWidget = nullptr; +#endif + + class QProgressDialog* m_progress = nullptr; + class QDialog* m_descTreeDialog = nullptr; + class EasyDescWidget* m_dialogDescTree = nullptr; + class QMessageBox* m_listenerDialog = nullptr; + QTimer m_readerTimer; + QTimer m_listenerTimer; + QTimer m_fpsRequestTimer; + ::profiler::SerializedData m_serializedBlocks; + ::profiler::SerializedData m_serializedDescriptors; + EasyFileReader m_reader; + EasySocketListener m_listener; + + class QLineEdit* m_addressEdit = nullptr; + class QLineEdit* m_portEdit = nullptr; + class QLineEdit* m_frameTimeEdit = nullptr; + + class QMenu* m_loadActionMenu = nullptr; + class QAction* m_saveAction = nullptr; + class QAction* m_deleteAction = nullptr; + + class QAction* m_captureAction = nullptr; + class QAction* m_connectAction = nullptr; + class QAction* m_eventTracingEnableAction = nullptr; + class QAction* m_eventTracingPriorityAction = nullptr; + + uint32_t m_descriptorsNumberInFile = 0; + uint16_t m_lastPort = 0; + bool m_bNetworkFileRegime = false; + bool m_bOpenedCacheFile = false; + +public: + + explicit EasyMainWindow(); + ~EasyMainWindow() override; + + // Public virtual methods + + void closeEvent(QCloseEvent* close_event) override; + void dragEnterEvent(QDragEnterEvent* drag_event) override; + void dragMoveEvent(QDragMoveEvent* drag_event) override; + void dragLeaveEvent(QDragLeaveEvent* drag_event) override; + void dropEvent(QDropEvent* drop_event) override; + +protected slots: + + void onThemeChange(bool); + void onOpenFileClicked(bool); + void onSaveFileClicked(bool); + void onDeleteClicked(bool); + void onExitClicked(bool); + void onEncodingChanged(bool); + void onChronoTextPosChanged(bool); + void onUnitsChanged(bool); + void onEnableDisableStatistics(bool); + void onCollapseItemsAfterCloseChanged(bool); + void onAllItemsExpandedByDefaultChange(bool); + void onBindExpandStatusChange(bool); + void onHierarchyFlagChange(bool); + void onExpandAllClicked(bool); + void onCollapseAllClicked(bool); + void onSpacingChange(int _value); + void onMinSizeChange(int _value); + void onNarrowSizeChange(int _value); + void onFpsIntervalChange(int _value); + void onFpsHistoryChange(int _value); + void onFpsMonitorLineWidthChange(int _value); + void onFileReaderTimeout(); + void onFrameTimeRequestTimeout(); + void onListenerTimerTimeout(); + void onFileReaderCancel(); + void onEditBlocksClicked(bool); + void onDescTreeDialogClose(int); + void onListenerDialogClose(int); + void onCaptureClicked(bool); + void onGetBlockDescriptionsClicked(bool); + void onConnectClicked(bool); + void onEventTracingPriorityChange(bool _checked); + void onEventTracingEnableChange(bool _checked); + void onFrameTimeEditFinish(); + void onFrameTimeChanged(); + + void onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status); + + void checkFrameTimeReady(); + +private: + + // Private non-virtual methods + + void clear(); + + void refreshDiagram(); + + void addFileToList(const QString& filename); + void loadFile(const QString& filename); + void readStream(::std::stringstream& data); + + void loadSettings(); + void loadGeometry(); + void saveSettingsAndGeometry(); + + void setDisconnected(bool _showMessage = true); + + void destroyProgressDialog(); + void createProgressDialog(const QString& text); + +}; // END of class EasyMainWindow. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER_GUI__MAIN_WINDOW__H diff --git a/3rdparty/easyprofiler/profiler_gui/resources.qrc b/3rdparty/easyprofiler/profiler_gui/resources.qrc new file mode 100644 index 0000000..74dffc4 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/resources.qrc @@ -0,0 +1,51 @@ + + + themes/default.css + + + images/logo.svg + + + images/default/off.svg + images/default/open-folder2.svg + images/default/reload-folder2.svg + images/default/reload.svg + images/default/expand.svg + images/default/collapse.svg + images/default/save.svg + images/default/statistics.svg + images/default/statistics2.svg + images/default/lan.svg + images/default/lan_on.svg + images/default/wifi.svg + images/default/wifi_on.svg + images/default/lan.svg + images/default/lan_on.svg + images/default/play.svg + images/default/stop.svg + images/default/delete.svg + images/default/list.svg + images/default/search-next.svg + images/default/search-prev.svg + images/default/settings.svg + images/default/check.svg + images/default/check-disabled.svg + images/default/radio-indicator.svg + images/default/radio-indicator-disabled.svg + images/default/maximize-white.svg + images/default/maximize-white-hover.svg + images/default/maximize-white-pressed.svg + images/default/minimize-white.svg + images/default/minimize-white-hover.svg + images/default/minimize-white-pressed.svg + images/default/close-white.svg + images/default/close-white-hover.svg + images/default/close-white-pressed.svg + images/default/arrow-up.svg + images/default/arrow-up-hover.svg + images/default/arrow-up-disabled.svg + images/default/arrow-down.svg + images/default/arrow-down-hover.svg + images/default/arrow-down-disabled.svg + + diff --git a/3rdparty/easyprofiler/profiler_gui/resources.rc b/3rdparty/easyprofiler/profiler_gui/resources.rc new file mode 100644 index 0000000..fdc5adb --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/resources.rc @@ -0,0 +1,33 @@ +IDI_ICON1 ICON DISCARDABLE "images/logo.ico" +1 VERSIONINFO +FILEVERSION EASY_PROFILER_VERSION_MAJOR, EASY_PROFILER_VERSION_MINOR, EASY_PROFILER_VERSION_PATCH +PRODUCTVERSION EASY_PROFILER_VERSION_MAJOR, EASY_PROFILER_VERSION_MINOR, EASY_PROFILER_VERSION_PATCH + +# define EASY_STRINGIFY(a) #a +# define EASY_STRINGIFICATION(a) EASY_STRINGIFY(a) + +#define EASY_PROFILER_PRODUCT_VERSION "v" EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MAJOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MINOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_PATCH) + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "CompanyName", "EasySolutions" + VALUE "FileDescription", "EasyProfiler" + VALUE "InternalName", "profiler_gui" + VALUE "LegalCopyright", "Copyright (C) 2016-2017 Victor Zarubkin, Sergey Yagovtsev" + VALUE "LegalTrademarks1", "All Rights Reserved" + VALUE "LegalTrademarks2", "All Rights Reserved" + VALUE "OriginalFilename", "profiler_gui.exe" + VALUE "ProductName", "easy_profiler gui application" + VALUE "ProductVersion", EASY_PROFILER_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END diff --git a/3rdparty/easyprofiler/profiler_gui/themes/default.css b/3rdparty/easyprofiler/profiler_gui/themes/default.css new file mode 100644 index 0000000..4d944f7 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/themes/default.css @@ -0,0 +1,358 @@ +/********************************** +* * +* Light theme for EasyProfiler. * +* * +* Automatically generated from * +* default.scss by pysassc tool * +* * +***********************************/ +/* ****************************************************************************************************************** */ +/* Functions */ +/* ****************************************************************************************************************** */ +/* Constants */ +/* ****************************************************************************************************************** */ +/* StyleSheet */ +* { + font-family: "DejaVu Sans"; + font-size: 13px; + color: #504040; } + +*:disabled { + color: #a08888; } + +EasyMainWindow, QToolBar, QDialog { + background-color: #f8f2f2; } + +QToolTip { + background-color: #ffeccc; + border: 1px solid #cccccc; } + +QGraphicsView { + border: 1px solid #cccccc; } + +/* ****************************************************************************************************************** */ +QLineEdit, QComboBox, QSpinBox { + height: 24px; + border: 1px solid #cccccc; + background-color: white; + selection-background-color: rgba(152, 222, 152, 0.5); + selection-color: #504040; } + +QLineEdit:disabled, QComboBox:disabled, QSpinBox:disabled { + background-color: #f0f0f0; + color: #a08888; + selection-background-color: rgba(152, 222, 152, 0.5); + selection-color: #a08888; } + +QLineEdit:focus { + border: 1px solid #ffbcbc; } + +/* ****************************************************************************************************************** */ +QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 24px; + border: none; + margin-left: 0; } + +QComboBox::down-arrow { + image: url(":/images/default/arrow-down"); + height: 8px; + width: 8px; } + +QComboBox::down-arrow:hover { + image: url(":/images/default/arrow-down-hover"); } + +QComboBox::down-arrow:disabled { + image: url(":/images/default/arrow-down-disabled"); } + +/* ****************************************************************************************************************** */ +QSpinBox::up-button { + subcontrol-origin: padding; + subcontrol-position: top right; + margin-left: 5px; + width: 24px; + border-left: 1px solid #cccccc; + border-bottom: 1px solid #cccccc; } + +QSpinBox::down-button { + subcontrol-origin: padding; + subcontrol-position: bottom right; + margin-left: 5px; + width: 24px; + border-left: 1px solid #cccccc; } + +QSpinBox::up-button:pressed, QSpinBox::down-button:pressed { + background-color: #f4f4f4; } + +QSpinBox::up-arrow { + image: url(":/images/default/arrow-up"); + height: 8px; + width: 8px; } + +QSpinBox::up-arrow:hover { + image: url(":/images/default/arrow-up-hover"); } + +QSpinBox::up-arrow:disabled { + image: url(":/images/default/arrow-up-disabled"); } + +QSpinBox::down-arrow { + image: url(":/images/default/arrow-down"); + height: 8px; + width: 8px; } + +QSpinBox::down-arrow:hover { + image: url(":/images/default/arrow-down-hover"); } + +QSpinBox::down-arrow:disabled { + image: url(":/images/default/arrow-down-disabled"); } + +/* ****************************************************************************************************************** */ +QPushButton { + height: 24px; + min-width: 50px; + border: 1px solid #cccccc; + background-color: white; + padding: 0 5px 0 5px; } + +QPushButton:disabled { + background-color: #f0f0f0; + color: #a08888; } + +QPushButton:hover { + border: 1px solid #ffbcbc; + color: #922c2c; } + +QPushButton:pressed { + border: 1px solid #922c2c; + color: #370400; } + +/* ****************************************************************************************************************** */ +QListView { + background-color: white; + border: 1px solid #cccccc; } + +QListView, QTableView, QTreeView { + alternate-background-color: #e4e4ec; + selection-background-color: rgba(152, 222, 152, 0.8); + selection-color: #504040; } + +QListView::item, QTableView::item, QTreeView::item { + height: 26px; + border-bottom: 1px solid #cccccc; } + +QListView::item:selected, QTableView::item:selected, QTreeView::item:selected { + background-color: rgba(152, 222, 152, 0.8); } + +QTreeView::indicator { + width: 14px; + height: 14px; + background-color: transparent; + border: 1px solid transparent; + padding: 1px; + margin: 0; } + +QTreeView::indicator:hover, QTreeView::indicator:checked { + background-color: white; + border: 1px solid #cccccc; } + +QTreeView::indicator:checked { + image: url(":/images/default/check"); } + +QTreeView::indicator:checked:disabled { + image: url(":/images/default/check-disabled"); } + +/* ****************************************************************************************************************** */ +QMenu { + background-color: white; + border: 1px solid #cccccc; + padding-top: 4px; + padding-bottom: 4px; } + +QMenu::item { + height: 24px; + padding: 0 16px 0 25px; + border: 1px solid transparent; + /* reserve space for selection border */ } + +QMenu::item:selected { + border: 1px solid rgba(152, 222, 152, 0.5); + background-color: rgba(152, 222, 152, 0.5); } + +QMenu::icon { + width: 14px; + height: 14px; + background: none; + border: 1px inset transparent; + padding: 1px; + margin-left: 2px; } + +QMenu::icon:checked { + /* appearance of a 'checked' icon */ + background-color: #dddddd; + border: 1px inset #aaaaaa; } + +QMenu::separator { + height: 1px; + background: #cccccc; + margin-left: 5px; + margin-right: 5px; } + +QMenu::indicator { + width: 14px; + height: 14px; + background-color: white; + border: 1px solid #cccccc; + margin-left: 2px; + padding: 1px; } + +QMenu::indicator:non-exclusive:checked { + image: url(":/images/default/check"); } + +QMenu::indicator:non-exclusive:checked:disabled { + image: url(":/images/default/check-disabled"); } + +QMenu::indicator:exclusive { + border-radius: 8px; } + +QMenu::indicator:exclusive:checked { + image: url(":/images/default/radio-check"); } + +QMenu::indicator:exclusive:checked:disabled { + image: url(":/images/default/radio-check-disabled"); } + +/* ****************************************************************************************************************** */ +/*QToolButton { + border: 1px solid transparent; + background: none; + padding: 2px; +} + +QToolButton:hover { + border: 1px solid $BorderColor; +} + +QToolButton[popupMode="1"] { + padding-right: 13px; +} + +QToolButton:pressed { + background-color: #808080; +} + +QToolButton::menu-button { + border: none; + border-left: 1px solid transparent; + width: 12px; +} + +QToolButton::menu-button:hover { + border-left: 1px solid $BorderColor; + background-color: #bbbbbb; +} + +QToolButton::menu-button:pressed { + border-left: 1px solid $BorderColor; + background-color: #808080; +}*/ +/* ****************************************************************************************************************** */ +QHeaderView::section { + height: 28px; + width: 96px; + min-width: 64px; + background: #eeeeee; } + +/* ****************************************************************************************************************** */ +EasyDockWidget QWidget#EasyDockWidgetTitle { + background-color: #686464; } + EasyDockWidget QWidget#EasyDockWidgetTitle QLabel { + color: white; + margin-left: 4px; } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton { + background: none; + border: none; + max-height: 12px; + min-width: 12px; + max-width: 12px; + margin-right: 4px; + padding: 0; } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetFloatButton { + image: url(":/images/default/dock-maximize-white"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetFloatButton:hover { + image: url(":/images/default/dock-maximize-white-hover"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetFloatButton:pressed { + image: url(":/images/default/dock-maximize-white-pressed"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetFloatButton[floating=true] { + image: url(":/images/default/dock-minimize-white"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetFloatButton[floating=true]:hover { + image: url(":/images/default/dock-minimize-white-hover"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetFloatButton[floating=true]:pressed { + image: url(":/images/default/dock-minimize-white-pressed"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetCloseButton { + image: url(":/images/default/dock-close-white"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetCloseButton:hover { + image: url(":/images/default/dock-close-white-hover"); } + EasyDockWidget QWidget#EasyDockWidgetTitle QPushButton#EasyDockWidgetCloseButton:pressed { + image: url(":/images/default/dock-close-white-pressed"); } + +/* ****************************************************************************************************************** */ +QWidget#DiagramPopup, QWidget#ThreadsPopup { + background-color: white; + border: 1px solid #cccccc; } + +/* ****************************************************************************************************************** */ +QProgressBar { + height: 24px; + background-color: white; + border: 1px solid #cccccc; + color: #0B530B; + text-align: center; } + +QProgressBar::chunk { + background-color: #98DE98; + width: 2px; + margin: 0; } + +/* ****************************************************************************************************************** */ +QScrollBar { + background-color: transparent; + border: none; + padding: 0; } + +QScrollBar:hover { + background-color: rgba(0, 0, 0, 0.1); } + +QScrollBar:horizontal { + margin: 0; + height: 8px; } + +QScrollBar:vertical { + margin: 0; + width: 8px; } + +QScrollBar::handle { + background-color: rgba(0, 0, 0, 0.4); + border: none; + margin: 0; + padding: 0; } + +QScrollBar::handle:pressed { + background-color: rgba(0, 0, 0, 0.6); } + +QScrollBar::handle:vertical { + min-height: 30px; + margin-left: 4px; } + +QScrollBar::handle:vertical:hover, QScrollBar::handle:vertical:pressed { + margin-left: 0; } + +QScrollBar::handle:horizontal { + min-width: 30px; + margin-top: 4px; } + +QScrollBar::handle:horizontal:hover, QScrollBar::handle:horizontal:pressed { + margin-top: 0; } + +QScrollBar::add-line, QScrollBar::sub-line { + background: none; + border: none; } diff --git a/3rdparty/easyprofiler/profiler_gui/themes/default.scss b/3rdparty/easyprofiler/profiler_gui/themes/default.scss new file mode 100644 index 0000000..2bd248c --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/themes/default.scss @@ -0,0 +1,391 @@ +/********************************** +* * +* Light theme for EasyProfiler. * +* * +* Automatically generated from * +* default.scss by pysassc tool * +* * +***********************************/ + +/* ****************************************************************************************************************** */ +/* Functions */ +@function rgb_a($color, $opacity) { + @return fade_out($color, 1.0 - $opacity); +} + +/* ****************************************************************************************************************** */ +/* Constants */ +$TextColor: #504040; +$DisabledTextColor: #a08888; +$BorderColor: #cccccc; +$MainColor: #f44336; +$HoveredMenuRowColor: rgb_a(#98DE98, 0.5); +$BackgroundColor: white; +$DisabledBackgroundColor: #f0f0f0; +$ButtonHoverColor: #922c2c;//#d77d7d; +$ButtonPressedColor: #370400;//#922c2c; +$FocusBorderColor: #ffbcbc; +$DefaultHeight: 24px; +$ComboBoxArrowSize: 8px; +$SpinBoxArrowSize: 8px; + +/* ****************************************************************************************************************** */ +/* StyleSheet */ + +* { + font-family: "DejaVu Sans"; + font-size: 13px; + color: $TextColor; +} + +*:disabled { + color: $DisabledTextColor; +} + +EasyMainWindow, QToolBar, QDialog { + background-color: #f8f2f2; +} + +QToolTip { + background-color: #ffeccc; + border: 1px solid $BorderColor; +} + +QGraphicsView { + border: 1px solid $BorderColor; +} + +/* ****************************************************************************************************************** */ +QLineEdit, QComboBox, QSpinBox { + height: $DefaultHeight; + border: 1px solid $BorderColor; + background-color: $BackgroundColor; + selection-background-color: $HoveredMenuRowColor; + selection-color: $TextColor; +} + +QLineEdit:disabled, QComboBox:disabled, QSpinBox:disabled { + background-color: $DisabledBackgroundColor; + color: $DisabledTextColor; + selection-background-color: $HoveredMenuRowColor; + selection-color: $DisabledTextColor; +} + +QLineEdit:focus { border: 1px solid $FocusBorderColor; } + +/* ****************************************************************************************************************** */ +QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: $DefaultHeight; + border: none; + margin-left: 0; +} + +QComboBox::down-arrow { image: url(":/images/default/arrow-down"); height: $ComboBoxArrowSize; width: $ComboBoxArrowSize; } +QComboBox::down-arrow:hover { image: url(":/images/default/arrow-down-hover"); } +QComboBox::down-arrow:disabled { image: url(":/images/default/arrow-down-disabled"); } + +/* ****************************************************************************************************************** */ +QSpinBox::up-button { + subcontrol-origin: padding; + subcontrol-position: top right; + margin-left: 5px; + width: $DefaultHeight; + border-left: 1px solid $BorderColor; + border-bottom: 1px solid $BorderColor; +} + +QSpinBox::down-button { + subcontrol-origin: padding; + subcontrol-position: bottom right; + margin-left: 5px; + width: $DefaultHeight; + border-left: 1px solid $BorderColor; +} + +QSpinBox::up-button:pressed, QSpinBox::down-button:pressed { + background-color: #f4f4f4; +} + +QSpinBox::up-arrow { image: url(":/images/default/arrow-up"); height: $SpinBoxArrowSize; width: $SpinBoxArrowSize; } +QSpinBox::up-arrow:hover { image: url(":/images/default/arrow-up-hover"); } +QSpinBox::up-arrow:disabled { image: url(":/images/default/arrow-up-disabled"); } + +QSpinBox::down-arrow { image: url(":/images/default/arrow-down"); height: $SpinBoxArrowSize; width: $SpinBoxArrowSize; } +QSpinBox::down-arrow:hover { image: url(":/images/default/arrow-down-hover"); } +QSpinBox::down-arrow:disabled { image: url(":/images/default/arrow-down-disabled"); } + +/* ****************************************************************************************************************** */ +QPushButton { + height: $DefaultHeight; + min-width: 50px; + border: 1px solid $BorderColor; + background-color: $BackgroundColor; + padding: 0 5px 0 5px; +} + +QPushButton:disabled { + background-color: $DisabledBackgroundColor; + color: $DisabledTextColor; +} + +QPushButton:hover { + border: 1px solid $FocusBorderColor; + color: $ButtonHoverColor; +} + +QPushButton:pressed { + border: 1px solid $ButtonHoverColor; + color: $ButtonPressedColor; +} + +/* ****************************************************************************************************************** */ +QListView { + background-color: $BackgroundColor; + border: 1px solid $BorderColor; +} + +QListView, QTableView, QTreeView { + alternate-background-color: #e4e4ec; + selection-background-color: rgb_a(#98DE98, 0.8); + selection-color: $TextColor; +} + +QListView::item, QTableView::item, QTreeView::item { + height: $DefaultHeight + 2px; + border-bottom: 1px solid $BorderColor; +} + +QListView::item:selected, QTableView::item:selected, QTreeView::item:selected { + background-color: rgb_a(#98DE98, 0.8); +} + + +QTreeView::indicator { + width: 14px; + height: 14px; + background-color: transparent; + border: 1px solid transparent; + padding: 1px; + margin: 0; +} + +QTreeView::indicator:hover, QTreeView::indicator:checked { + background-color: $BackgroundColor; + border: 1px solid $BorderColor; +} + +QTreeView::indicator:checked { image: url(":/images/default/check"); } +QTreeView::indicator:checked:disabled { image: url(":/images/default/check-disabled"); } + +/* ****************************************************************************************************************** */ +QMenu { + background-color: $BackgroundColor; + border: 1px solid $BorderColor; + padding-top: 4px; + padding-bottom: 4px; +} + +QMenu::item { + height: $DefaultHeight; + padding: 0 16px 0 25px; + border: 1px solid transparent; /* reserve space for selection border */ +} + +QMenu::item:selected { + border: 1px solid $HoveredMenuRowColor; + background-color: $HoveredMenuRowColor; +} + +QMenu::icon { + width: 14px; + height: 14px; + background: none; + border: 1px inset transparent; + padding: 1px; + margin-left: 2px; +} + +QMenu::icon:checked { /* appearance of a 'checked' icon */ + background-color: #dddddd; + border: 1px inset #aaaaaa; +} + +QMenu::separator { + height: 1px; + background: $BorderColor; + margin-left: 5px; + margin-right: 5px; +} + +QMenu::indicator { + width: 14px; + height: 14px; + background-color: $BackgroundColor; + border: 1px solid $BorderColor; + margin-left: 2px; + padding: 1px; +} + +QMenu::indicator:non-exclusive:checked { image: url(":/images/default/check"); } +QMenu::indicator:non-exclusive:checked:disabled { image: url(":/images/default/check-disabled"); } + +QMenu::indicator:exclusive { border-radius: 8px; } +QMenu::indicator:exclusive:checked { image: url(":/images/default/radio-check"); } +QMenu::indicator:exclusive:checked:disabled { image: url(":/images/default/radio-check-disabled"); } + + + + +/* ****************************************************************************************************************** */ +/*QToolButton { + border: 1px solid transparent; + background: none; + padding: 2px; +} + +QToolButton:hover { + border: 1px solid $BorderColor; +} + +QToolButton[popupMode="1"] { + padding-right: 13px; +} + +QToolButton:pressed { + background-color: #808080; +} + +QToolButton::menu-button { + border: none; + border-left: 1px solid transparent; + width: 12px; +} + +QToolButton::menu-button:hover { + border-left: 1px solid $BorderColor; + background-color: #bbbbbb; +} + +QToolButton::menu-button:pressed { + border-left: 1px solid $BorderColor; + background-color: #808080; +}*/ + + + + + + +/* ****************************************************************************************************************** */ +QHeaderView::section { + height: 28px; + width: 96px; + min-width: 64px; + background: #eeeeee; +} + + + + + +/* ****************************************************************************************************************** */ +EasyDockWidget +{ + QWidget#EasyDockWidgetTitle + { + background-color: #686464; + + QLabel { + color: white; + margin-left: 4px; + } + + QPushButton { + background: none; + border: none; + max-height: 12px; + min-width: 12px; + max-width: 12px; + margin-right: 4px; + padding: 0; + } + + QPushButton#EasyDockWidgetFloatButton { image: url(":/images/default/dock-maximize-white"); } + QPushButton#EasyDockWidgetFloatButton:hover { image: url(":/images/default/dock-maximize-white-hover"); } + QPushButton#EasyDockWidgetFloatButton:pressed { image: url(":/images/default/dock-maximize-white-pressed"); } + + QPushButton#EasyDockWidgetFloatButton[floating=true] { image: url(":/images/default/dock-minimize-white"); } + QPushButton#EasyDockWidgetFloatButton[floating=true]:hover { image: url(":/images/default/dock-minimize-white-hover"); } + QPushButton#EasyDockWidgetFloatButton[floating=true]:pressed { image: url(":/images/default/dock-minimize-white-pressed"); } + + QPushButton#EasyDockWidgetCloseButton { image: url(":/images/default/dock-close-white"); } + QPushButton#EasyDockWidgetCloseButton:hover { image: url(":/images/default/dock-close-white-hover"); } + QPushButton#EasyDockWidgetCloseButton:pressed { image: url(":/images/default/dock-close-white-pressed"); } + } +} + +/* ****************************************************************************************************************** */ +QWidget#DiagramPopup, QWidget#ThreadsPopup { + background-color: $BackgroundColor; + border: 1px solid $BorderColor; +} + +/* ****************************************************************************************************************** */ +QProgressBar { + height: $DefaultHeight; + background-color: $BackgroundColor; + border: 1px solid $BorderColor;//#64BC64; + color: #0B530B; + text-align: center; +} + +QProgressBar::chunk { + background-color: #98DE98; + width: 2px; + margin: 0; +} + +/* ****************************************************************************************************************** */ +QScrollBar { + background-color: transparent; + border: none; + padding: 0; +} + +QScrollBar:hover { + background-color: rgb_a(#000000, 0.1); +} + +QScrollBar:horizontal { + margin: 0; + height: 8px; +} + +QScrollBar:vertical { + margin: 0; + width: 8px; +} + +QScrollBar::handle { + background-color: rgb_a(#000000, 0.4); + border: none; + margin: 0; + padding: 0; +} + +QScrollBar::handle:pressed { + background-color: rgb_a(#000000, 0.6); +} + +QScrollBar::handle:vertical { min-height: 30px; margin-left: 4px; } +QScrollBar::handle:vertical:hover, QScrollBar::handle:vertical:pressed { margin-left: 0; } + +QScrollBar::handle:horizontal { min-width: 30px; margin-top: 4px; } +QScrollBar::handle:horizontal:hover, QScrollBar::handle:horizontal:pressed { margin-top: 0; } + +QScrollBar::add-line, QScrollBar::sub-line { + background: none; + border: none; +} diff --git a/3rdparty/easyprofiler/profiler_gui/tree_widget_item.cpp b/3rdparty/easyprofiler/profiler_gui/tree_widget_item.cpp new file mode 100644 index 0000000..4fd2459 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/tree_widget_item.cpp @@ -0,0 +1,431 @@ +/************************************************************************ +* file name : tree_widget_item.cpp +* ----------------- : +* creation time : 2016/08/18 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of EasyTreeWidgetItem. +* ----------------- : +* change log : * 2016/08/18 Victor Zarubkin: Moved sources from blocks_tree_widget.cpp +* : and renamed classes from Prof* to Easy*. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include "tree_widget_item.h" +#include "globals.h" +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// + +EASY_CONSTEXPR int BlockColorRole = Qt::UserRole + 1; + +////////////////////////////////////////////////////////////////////////// + +EASY_CONSTEXPR int ColumnBit[COL_COLUMNS_NUMBER] = { + -1 // COL_NAME = 0, + + , 0 // COL_BEGIN, + + , 1 // COL_DURATION, + , 2 // COL_SELF_DURATION, + , 3 // COL_DURATION_SUM_PER_PARENT, + , 4 // COL_DURATION_SUM_PER_FRAME, + , 5 // COL_DURATION_SUM_PER_THREAD, + + , -1 // COL_SELF_DURATION_PERCENT, + , -1 // COL_PERCENT_PER_PARENT, + , -1 // COL_PERCENT_PER_FRAME, + , -1 // COL_PERCENT_SUM_PER_PARENT, + , -1 // COL_PERCENT_SUM_PER_FRAME, + , -1 // COL_PERCENT_SUM_PER_THREAD, + + , 6 // COL_END, + + , 7 // COL_MIN_PER_FRAME, + , 8 // COL_MAX_PER_FRAME, + , 9 // COL_AVERAGE_PER_FRAME, + , -1 // COL_NCALLS_PER_FRAME, + + , 10 // COL_MIN_PER_THREAD, + , 11 // COL_MAX_PER_THREAD, + , 12 // COL_AVERAGE_PER_THREAD, + , -1 // COL_NCALLS_PER_THREAD, + + , 13 // COL_MIN_PER_PARENT, + , 14 // COL_MAX_PER_PARENT, + , 15 // COL_AVERAGE_PER_PARENT, + , -1 // COL_NCALLS_PER_PARENT, + + , 16 // COL_ACTIVE_TIME, + , -1 // COL_ACTIVE_PERCENT, +}; + +////////////////////////////////////////////////////////////////////////// + +EasyTreeWidgetItem::EasyTreeWidgetItem(const ::profiler::block_index_t _treeBlock, Parent* _parent) + : Parent(_parent, QTreeWidgetItem::UserType) + , m_block(_treeBlock) + , m_customBGColor(0) + , m_bMain(false) +{ + +} + +EasyTreeWidgetItem::~EasyTreeWidgetItem() +{ +} + +bool EasyTreeWidgetItem::operator < (const Parent& _other) const +{ + const auto col = treeWidget()->sortColumn(); + + switch (col) + { + //case COL_UNKNOWN: + case COL_NAME: + { + if (parent() == nullptr) + return false; // Do not sort topLevelItems by name + return Parent::operator < (_other); + } + + case COL_NCALLS_PER_THREAD: + case COL_NCALLS_PER_PARENT: + case COL_NCALLS_PER_FRAME: + { + return data(col, Qt::UserRole).toUInt() < _other.data(col, Qt::UserRole).toUInt(); + } + + case COL_SELF_DURATION_PERCENT: + case COL_PERCENT_PER_PARENT: + case COL_PERCENT_PER_FRAME: + case COL_PERCENT_SUM_PER_PARENT: + case COL_PERCENT_SUM_PER_FRAME: + case COL_PERCENT_SUM_PER_THREAD: + { + return data(col, Qt::UserRole).toInt() < _other.data(col, Qt::UserRole).toInt(); + } + + case COL_ACTIVE_PERCENT: + { + return data(col, Qt::UserRole).toDouble() < _other.data(col, Qt::UserRole).toDouble(); + } + + default: + { + // durations min, max, average + return data(col, Qt::UserRole).toULongLong() < _other.data(col, Qt::UserRole).toULongLong(); + } + } + + return false; +} + +bool EasyTreeWidgetItem::hasToolTip(int _column) const +{ + const int bit = ColumnBit[_column]; + return bit < 0 ? false : m_bHasToolTip.test(static_cast(bit)); +} + +void EasyTreeWidgetItem::setHasToolTip(int _column) +{ + const int bit = ColumnBit[_column]; + if (bit >= 0) + m_bHasToolTip.set(static_cast(bit), true); +} + +QVariant EasyTreeWidgetItem::data(int _column, int _role) const +{ + if (_column == COL_NAME) + { + if (_role == Qt::SizeHintRole) + { +#ifdef _WIN32 + const float k = m_font.bold() ? 1.2f : 1.f; +#else + const float k = m_font.bold() ? 1.15f : 1.f; +#endif + return QSize(static_cast(QFontMetrics(m_font).width(text(COL_NAME)) * k) + 20, 26); + } + + if (_role == BlockColorRole) + { + if (parent() != nullptr || m_bMain) + return QBrush(QColor::fromRgba(m_customBGColor)); + return QVariant(); + } + } + + switch (_role) + { + case Qt::FontRole: + return m_font; + + case Qt::ForegroundRole: + return m_bMain ? QVariant::fromValue(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_FOREGROUND)) : QVariant(); + + case Qt::ToolTipRole: + return hasToolTip(_column) ? + QVariant::fromValue(QString("%1 ns").arg(QTreeWidgetItem::data(_column, Qt::UserRole).toULongLong())) : + QVariant(); + + default: + return QTreeWidgetItem::data(_column, _role); + } +} + +::profiler::block_index_t EasyTreeWidgetItem::block_index() const +{ + return m_block; +} + +::profiler_gui::EasyBlock& EasyTreeWidgetItem::guiBlock() +{ + return easyBlock(m_block); +} + +const ::profiler::BlocksTree& EasyTreeWidgetItem::block() const +{ + return easyBlocksTree(m_block); +} + +::profiler::timestamp_t EasyTreeWidgetItem::duration() const +{ + if (parent() != nullptr) + return block().node->duration(); + return data(COL_DURATION, Qt::UserRole).toULongLong(); +} + +::profiler::timestamp_t EasyTreeWidgetItem::selfDuration() const +{ + return data(COL_SELF_DURATION, Qt::UserRole).toULongLong(); +} + +void EasyTreeWidgetItem::setTimeSmart(int _column, ::profiler_gui::TimeUnits _units, const ::profiler::timestamp_t& _time, const QString& _prefix) +{ + const ::profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time); + + setData(_column, Qt::UserRole, (quint64)nanosecondsTime); + setHasToolTip(_column); + setText(_column, QString("%1%2").arg(_prefix).arg(::profiler_gui::timeStringRealNs(_units, nanosecondsTime, 3))); + +// if (_time < 1e3) +// { +// setText(_column, QString("%1%2 ns").arg(_prefix).arg(nanosecondsTime)); +// } +// else if (_time < 1e6) +// { +// setText(_column, QString("%1%2 us").arg(_prefix).arg(double(nanosecondsTime) * 1e-3, 0, 'f', 3)); +// } +// else if (_time < 1e9) +// { +// setText(_column, QString("%1%2 ms").arg(_prefix).arg(double(nanosecondsTime) * 1e-6, 0, 'f', 3)); +// } +// else +// { +// setText(_column, QString("%1%2 s").arg(_prefix).arg(double(nanosecondsTime) * 1e-9, 0, 'f', 3)); +// } +} + +void EasyTreeWidgetItem::setTimeSmart(int _column, ::profiler_gui::TimeUnits _units, const ::profiler::timestamp_t& _time) +{ + const ::profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time); + + setData(_column, Qt::UserRole, (quint64)nanosecondsTime); + setHasToolTip(_column); + setText(_column, ::profiler_gui::timeStringRealNs(_units, nanosecondsTime, 3)); +} + +void EasyTreeWidgetItem::setTimeMs(int _column, const ::profiler::timestamp_t& _time) +{ + const ::profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time); + setData(_column, Qt::UserRole, (quint64)nanosecondsTime); + setHasToolTip(_column); + setText(_column, QString::number(double(nanosecondsTime) * 1e-6, 'g', 9)); +} + +void EasyTreeWidgetItem::setTimeMs(int _column, const ::profiler::timestamp_t& _time, const QString& _prefix) +{ + const ::profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time); + setData(_column, Qt::UserRole, (quint64)nanosecondsTime); + setHasToolTip(_column); + setText(_column, QString("%1%2").arg(_prefix).arg(double(nanosecondsTime) * 1e-6, 0, 'g', 9)); +} + +void EasyTreeWidgetItem::setBackgroundColor(QRgb _color) +{ + m_customBGColor = _color; +} + +void EasyTreeWidgetItem::setMain(bool _main) +{ + m_bMain = _main; +} + +void EasyTreeWidgetItem::collapseAll() +{ + for (int i = 0, childrenNumber = childCount(); i < childrenNumber; ++i) + { + static_cast(child(i))->collapseAll(); + } + + setExpanded(false); + if (parent() != nullptr) + guiBlock().expanded = false; +} + +void EasyTreeWidgetItem::expandAll() +{ + for (int i = 0, childrenNumber = childCount(); i < childrenNumber; ++i) + { + static_cast(child(i))->expandAll(); + } + + setExpanded(true); + if (parent() != nullptr) + guiBlock().expanded = true; +} + +void EasyTreeWidgetItem::setBold(bool _bold) +{ + m_font.setBold(_bold); +} + +////////////////////////////////////////////////////////////////////////// + +EasyItemDelegate::EasyItemDelegate(QTreeWidget* parent) : QStyledItemDelegate(parent), m_treeWidget(parent) +{ + +} + +EasyItemDelegate::~EasyItemDelegate() +{ + +} + +void EasyItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + auto brushData = m_treeWidget->model()->data(index, BlockColorRole); + if (brushData.isNull()) + { +#ifdef _WIN32 + const auto currentTreeIndex = m_treeWidget->currentIndex(); + if (index.parent() == currentTreeIndex.parent() && index.row() == currentTreeIndex.row()) + { + // Draw selection background for selected row + painter->save(); + painter->setBrush(QColor::fromRgba(0xCC98DE98)); + painter->setPen(Qt::NoPen); + painter->drawRect(QRect(0, option.rect.top(), option.rect.left() + 16, option.rect.height())); + painter->restore(); + } +#endif + + // Draw item as usual + QStyledItemDelegate::paint(painter, option, index); + + // Draw line under tree indicator + const auto bottomLeft = option.rect.bottomLeft(); + if (bottomLeft.x() > 0) + { + painter->save(); + painter->setBrush(Qt::NoBrush); + painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); + painter->drawLine(QPoint(0, bottomLeft.y()), bottomLeft); + painter->restore(); + } + + return; + } + + const auto currentTreeIndex = m_treeWidget->currentIndex(); + if (index.parent() == currentTreeIndex.parent() && index.row() == currentTreeIndex.row()) + { + // Draw selection background for selected row + + painter->save(); + + painter->setBrush(QColor::fromRgba(0xCC98DE98)); + painter->setPen(Qt::NoPen); + +#ifdef _WIN32 + painter->drawRect(QRect(0, option.rect.top(), option.rect.left() + 16, option.rect.height())); +#else + painter->drawRect(QRect(option.rect.left(), option.rect.top(), 16, option.rect.height())); +#endif + + painter->restore(); + } + + // Adjust rect size for drawing color marker + QStyleOptionViewItem opt = option; + opt.rect.adjust(16, 0, 0, 0); + + // Draw item as usual + QStyledItemDelegate::paint(painter, opt, index); + + painter->save(); + + // Draw color marker with block color + const auto brush = m_treeWidget->model()->data(index, Qt::UserRole + 1).value(); + painter->setBrush(brush); + painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); + painter->drawRect(QRect(option.rect.left(), option.rect.top() + 5, 16, option.rect.height() - 10)); + + // Draw line under tree indicator + const auto bottomLeft = opt.rect.bottomLeft(); + if (bottomLeft.x() > 0) + { + painter->setBrush(Qt::NoBrush); + painter->drawLine(QPoint(0, bottomLeft.y()), bottomLeft); + } + + painter->restore(); +} diff --git a/3rdparty/easyprofiler/profiler_gui/tree_widget_item.h b/3rdparty/easyprofiler/profiler_gui/tree_widget_item.h new file mode 100644 index 0000000..b164907 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/tree_widget_item.h @@ -0,0 +1,184 @@ +/************************************************************************ +* file name : tree_widget_item.h +* ----------------- : +* creation time : 2016/08/18 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of EasyTreeWidgetItem +* : for displyaing EasyProfiler blocks tree. +* ----------------- : +* change log : * 2016/08/18 Victor Zarubkin: moved sources from blocks_tree_widget.h +* : and renamed classes from Prof* to Easy*. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_TREE_WIDGET_ITEM_H +#define EASY_TREE_WIDGET_ITEM_H + +#include +#include +#include +#include +#include + +#include "common_functions.h" + +////////////////////////////////////////////////////////////////////////// + +enum EasyColumnsIndexes +{ + COL_UNKNOWN = -1, + + COL_NAME = 0, + + COL_BEGIN, + + COL_DURATION, + COL_SELF_DURATION, + COL_DURATION_SUM_PER_PARENT, + COL_DURATION_SUM_PER_FRAME, + COL_DURATION_SUM_PER_THREAD, + + COL_SELF_DURATION_PERCENT, + COL_PERCENT_PER_PARENT, + COL_PERCENT_PER_FRAME, + COL_PERCENT_SUM_PER_PARENT, + COL_PERCENT_SUM_PER_FRAME, + COL_PERCENT_SUM_PER_THREAD, + + COL_END, + + COL_MIN_PER_FRAME, + COL_MAX_PER_FRAME, + COL_AVERAGE_PER_FRAME, + COL_NCALLS_PER_FRAME, + + COL_MIN_PER_THREAD, + COL_MAX_PER_THREAD, + COL_AVERAGE_PER_THREAD, + COL_NCALLS_PER_THREAD, + + COL_MIN_PER_PARENT, + COL_MAX_PER_PARENT, + COL_AVERAGE_PER_PARENT, + COL_NCALLS_PER_PARENT, + + COL_ACTIVE_TIME, + COL_ACTIVE_PERCENT, + + COL_COLUMNS_NUMBER +}; + +////////////////////////////////////////////////////////////////////////// + +class EasyTreeWidgetItem : public QTreeWidgetItem +{ + using Parent = QTreeWidgetItem; + using This = EasyTreeWidgetItem; + + QFont m_font; + const ::profiler::block_index_t m_block; + QRgb m_customBGColor; + std::bitset<17> m_bHasToolTip; + bool m_bMain; + +public: + + explicit EasyTreeWidgetItem(const ::profiler::block_index_t _treeBlock = ::profiler_gui::numeric_max(), Parent* _parent = nullptr); + virtual ~EasyTreeWidgetItem(); + + bool operator < (const Parent& _other) const override; + QVariant data(int _column, int _role) const override; + +public: + + ::profiler::block_index_t block_index() const; + ::profiler_gui::EasyBlock& guiBlock(); + const ::profiler::BlocksTree& block() const; + + ::profiler::timestamp_t duration() const; + ::profiler::timestamp_t selfDuration() const; + + void setTimeSmart(int _column, ::profiler_gui::TimeUnits _units, const ::profiler::timestamp_t& _time, const QString& _prefix); + void setTimeSmart(int _column, ::profiler_gui::TimeUnits _units, const ::profiler::timestamp_t& _time); + + void setTimeMs(int _column, const ::profiler::timestamp_t& _time); + void setTimeMs(int _column, const ::profiler::timestamp_t& _time, const QString& _prefix); + + void setBackgroundColor(QRgb _color); + + void setMain(bool _main); + + void collapseAll(); + + void expandAll(); + + void setBold(bool _bold); + +private: + + bool hasToolTip(int _column) const; + void setHasToolTip(int _column); + +}; // END of class EasyTreeWidgetItem. + +////////////////////////////////////////////////////////////////////////// + +class EasyItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + QTreeWidget* m_treeWidget; + +public: + + explicit EasyItemDelegate(QTreeWidget* parent = nullptr); + ~EasyItemDelegate() override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +}; // END of class EasyItemDelegate. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_TREE_WIDGET_ITEM_H diff --git a/3rdparty/easyprofiler/profiler_gui/tree_widget_loader.cpp b/3rdparty/easyprofiler/profiler_gui/tree_widget_loader.cpp new file mode 100644 index 0000000..c6ae0b8 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/tree_widget_loader.cpp @@ -0,0 +1,1031 @@ +/************************************************************************ +* file name : tree_widget_loader.h +* ----------------- : +* creation time : 2016/08/18 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains implementation of EasyTreeWidgetLoader which aim is +* : to load EasyProfiler blocks hierarchy in separate thread. +* ----------------- : +* change log : * 2016/08/18 Victor Zarubkin: moved sources from blocks_tree_widget.h/.cpp +* : and renamed Prof* to Easy*. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#include "tree_widget_loader.h" +#include "tree_widget_item.h" +#include "globals.h" + +#ifdef _WIN32 +#include + +#ifdef __MINGW32__ +#include +#endif + +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +////////////////////////////////////////////////////////////////////////// + +EasyTreeWidgetLoader::EasyTreeWidgetLoader() + : m_bDone(ATOMIC_VAR_INIT(false)) + , m_bInterrupt(ATOMIC_VAR_INIT(false)) + , m_progress(ATOMIC_VAR_INIT(0)) + , m_mode(EasyTreeMode_Full) +{ +} + +EasyTreeWidgetLoader::~EasyTreeWidgetLoader() +{ + interrupt(true); +} + +bool EasyTreeWidgetLoader::done() const +{ + return m_bDone.load(); +} + +void EasyTreeWidgetLoader::setDone() +{ + m_bDone.store(true); + //m_progress.store(100); +} + +void EasyTreeWidgetLoader::setProgress(int _progress) +{ + m_progress.store(_progress); +} + +bool EasyTreeWidgetLoader::interrupted() const +{ + return m_bInterrupt.load(); +} + +int EasyTreeWidgetLoader::progress() const +{ + return m_progress.load(); +} + +void EasyTreeWidgetLoader::takeTopLevelItems(ThreadedItems& _output) +{ + if (done()) + { + _output = ::std::move(m_topLevelItems); + m_topLevelItems.clear(); + } +} + +void EasyTreeWidgetLoader::takeItems(Items& _output) +{ + if (done()) + { + _output = ::std::move(m_items); + m_items.clear(); + } +} + +void EasyTreeWidgetLoader::interrupt(bool _wait) +{ + m_bInterrupt.store(true); + if (m_thread.joinable()) + m_thread.join(); + + m_bInterrupt.store(false); + m_bDone.store(false); + m_progress.store(0); + + if (!_wait) + { + auto deleter_thread = ::std::thread([](decltype(m_topLevelItems) _items) + { +#ifdef _WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); +#endif + + for (auto item : _items) + delete item.second; + + }, ::std::move(m_topLevelItems)); + + deleter_thread.detach(); + } + else + { + for (auto item : m_topLevelItems) + delete item.second; + } + + m_items.clear(); + m_topLevelItems.clear(); + m_iditems.clear(); +} + +void EasyTreeWidgetLoader::fillTree(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, EasyTreeMode _mode) +{ + interrupt(); + m_mode = _mode; + m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal1, this, + ::std::ref(_beginTime), _blocksNumber, ::std::ref(_blocksTree), EASY_GLOBALS.add_zero_blocks_to_hierarchy, + EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.hex_thread_id, EASY_GLOBALS.time_units); +} + +void EasyTreeWidgetLoader::fillTreeBlocks(const::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _beginTime, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, EasyTreeMode _mode) +{ + interrupt(); + m_mode = _mode; + m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal2, this, + _beginTime, ::std::ref(_blocks), _left, _right, _strict, EASY_GLOBALS.add_zero_blocks_to_hierarchy, + EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.hex_thread_id, EASY_GLOBALS.time_units); +} + +////////////////////////////////////////////////////////////////////////// + +void EasyTreeWidgetLoader::setTreeInternal1(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units) +{ + m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks + + ::profiler::timestamp_t finishtime = 0; + for (const auto& threadTree : _blocksTree) + { + const auto node_block = easyBlocksTree(threadTree.second.children.front()).node; + const auto startTime = node_block->begin(); + const auto endTime = node_block->end(); + + if (_beginTime > startTime) + _beginTime = startTime; + + if (finishtime < endTime) + finishtime = endTime; + } + + //const QSignalBlocker b(this); + const auto u_thread = ::profiler_gui::toUnicode("thread"); + int i = 0; + const int total = static_cast(_blocksTree.size()); + for (const auto& threadTree : _blocksTree) + { + if (interrupted()) + break; + + const auto& root = threadTree.second; + auto item = new EasyTreeWidgetItem(); + item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, root, u_thread, _hexThreadId)); + + ::profiler::timestamp_t duration = 0; + if (!root.children.empty()) + duration = easyBlocksTree(root.children.back()).node->end() - easyBlocksTree(root.children.front()).node->begin(); + + item->setTimeSmart(COL_DURATION, _units, duration); + item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); + + //_items.push_back(item); + + item->setTimeSmart(COL_SELF_DURATION, _units, root.profiled_time); + + ::profiler::timestamp_t children_duration = 0; + const auto children_items_number = setTreeInternal(root, 0, _beginTime, root.children, item, nullptr, + _beginTime, finishtime + 1000000000ULL, false, + children_duration, _addZeroBlocks, _units); + + if (children_items_number > 0) + { + //total_items += children_items_number + 1; + //addTopLevelItem(item); + //m_roots[threadTree.first] = item; + m_topLevelItems.emplace_back(root.thread_id, item); + } + else + { + //_items.pop_back(); + delete item; + } + + setProgress((100 * ++i) / total); + } + + setDone(); + //return total_items; +} + +////////////////////////////////////////////////////////////////////////// + +// auto calculateTotalChildrenNumber(const ::profiler::BlocksTree& _tree) -> decltype(_tree.children.size()) +// { +// auto children_number = _tree.children.size(); +// for (auto i : _tree.children) +// children_number += calculateTotalChildrenNumber(easyBlocksTree(i)); +// return children_number; +// } + +using BeginEndIndicesMap = ::std::unordered_map<::profiler::thread_id_t, ::profiler::block_index_t, + ::estd::hash<::profiler::thread_id_t> >; + +void EasyTreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTime, + const ::profiler_gui::TreeBlocks& _blocks, + ::profiler::timestamp_t _left, + ::profiler::timestamp_t _right, + bool _strict, + bool _addZeroBlocks, + bool _decoratedThreadNames, + bool _hexThreadId, + ::profiler_gui::TimeUnits _units) +{ + //size_t blocksNumber = 0; + //for (const auto& block : _blocks) + // blocksNumber += calculateTotalChildrenNumber(*block.tree); + // //blocksNumber += block.tree->total_children_number; + //m_items.reserve(blocksNumber + _blocks.size()); // blocksNumber does not include root blocks + + BeginEndIndicesMap beginEndMap; + RootsMap threadsMap; + + auto const setTree = (m_mode == EasyTreeMode_Full) ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain; + + const auto u_thread = ::profiler_gui::toUnicode("thread"); + int i = 0, total = static_cast(_blocks.size()); + //const QSignalBlocker b(this); + for (const auto& block : _blocks) + { + if (interrupted()) + break; + + auto& gui_block = easyBlock(block.tree); + const auto startTime = gui_block.tree.node->begin(); + const auto endTime = gui_block.tree.node->end(); + if (startTime > _right || endTime < _left) + { + setProgress((90 * ++i) / total); + continue; + } + + ::profiler::timestamp_t duration = 0; + EasyTreeWidgetItem* thread_item = nullptr; + ::profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id]; + auto thread_item_it = threadsMap.find(block.root->thread_id); + if (thread_item_it != threadsMap.end()) + { + thread_item = thread_item_it->second; + } + else + { + thread_item = new EasyTreeWidgetItem(); + thread_item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread, _hexThreadId)); + + if (!block.root->children.empty()) + duration = easyBlocksTree(block.root->children.back()).node->end() - easyBlocksTree(block.root->children.front()).node->begin(); + + thread_item->setTimeSmart(COL_DURATION, _units, duration); + thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); + + // Sum of all children durations: + thread_item->setTimeSmart(COL_SELF_DURATION, _units, block.root->profiled_time); + + threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item)); + + firstCswitch = 0; + auto it = ::std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [](::profiler::block_index_t ind, decltype(_left) _val) + { + return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val; + }); + + if (it != block.root->sync.end()) + { + firstCswitch = it - block.root->sync.begin(); + if (firstCswitch > 0) + --firstCswitch; + } + else + { + firstCswitch = static_cast<::profiler::block_index_t>(block.root->sync.size()); + } + } + + bool hasContextSwitch = false; + ::profiler::timestamp_t idleTime = 0; + for (::profiler::block_index_t ind = firstCswitch, ncs = static_cast<::profiler::block_index_t>(block.root->sync.size()); ind < ncs; ++ind) + { + auto cs_index = block.root->sync[ind]; + const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + + if (cs->begin() > endTime) + { + if (!hasContextSwitch) + firstCswitch = ind; + break; + } + + if (startTime <= cs->begin() && cs->end() <= endTime) + { + if (!hasContextSwitch) + { + firstCswitch = ind; + hasContextSwitch = true; + } + + idleTime += cs->duration(); + } + } + + auto item = new EasyTreeWidgetItem(block.tree, thread_item); + duration = endTime - startTime; + + auto name = *gui_block.tree.node->name() != 0 ? gui_block.tree.node->name() : easyDescriptor(gui_block.tree.node->id()).name(); + item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); + item->setTimeSmart(COL_DURATION, _units, duration); + + auto active_time = duration - idleTime; + auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + + item->setTimeMs(COL_BEGIN, startTime - _beginTime); + item->setTimeMs(COL_END, endTime - _beginTime); + + item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); + + auto percentage_per_thread = ::profiler_gui::percent(duration, block.root->profiled_time); + item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); + + if (gui_block.tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also + { + const ::profiler::BlockStatistics* per_thread_stats = gui_block.tree.per_thread_stats; + const ::profiler::BlockStatistics* per_parent_stats = gui_block.tree.per_parent_stats; + const ::profiler::BlockStatistics* per_frame_stats = gui_block.tree.per_frame_stats; + + + if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); + item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); + } + + item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); + item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); + + percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); + + + if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration()); + item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration); + } + + item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number); + item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number)); + + + if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); + item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration); + } + + item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); + item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); + } + else + { + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); + item->setText(COL_PERCENT_SUM_PER_THREAD, ""); + } + + const auto color = easyDescriptor(gui_block.tree.node->id()).color(); + item->setBackgroundColor(color); + +#ifdef EASY_TREE_WIDGET__USE_VECTOR + auto item_index = static_cast(m_items.size()); + m_items.push_back(item); +#endif + + size_t children_items_number = 0; + ::profiler::timestamp_t children_duration = 0; + if (!gui_block.tree.children.empty()) + { + m_iditems.clear(); + + children_items_number = (this->*setTree)(*block.root, firstCswitch, _beginTime, gui_block.tree.children, + item, item, _left, _right, _strict, children_duration, + _addZeroBlocks, _units); + + if (interrupted()) + break; + } + + int percentage = 100; + auto self_duration = duration - children_duration; + if (children_duration > 0 && duration > 0) + { + percentage = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration)); + } + + item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); + item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + + if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) + { + //total_items += children_items_number + 1; +#ifdef EASY_TREE_WIDGET__USE_VECTOR + gui_block.tree_item = item_index; +#endif + + if (gui_block.expanded) + item->setExpanded(true); + +#ifndef EASY_TREE_WIDGET__USE_VECTOR + m_items.insert(::std::make_pair(block.tree, item)); +#endif + } + else + { +#ifdef EASY_TREE_WIDGET__USE_VECTOR + m_items.pop_back(); +#endif + delete item; + } + + setProgress((90 * ++i) / total); + } + + i = 0; + total = static_cast(threadsMap.size()); + for (auto& it : threadsMap) + { + auto item = it.second; + + if (item->childCount() > 0) + { + //addTopLevelItem(item); + //m_roots[it.first] = item; + + //_items.push_back(item); + m_topLevelItems.emplace_back(it.first, item); + + //++total_items; + } + else + { + delete item; + } + + setProgress(90 + (10 * ++i) / total); + } + + setDone(); + //return total_items; +} + +////////////////////////////////////////////////////////////////////////// + +size_t EasyTreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _threadRoot, + ::profiler::block_index_t _firstCswitch, + const ::profiler::timestamp_t& _beginTime, + const ::profiler::BlocksTree::children_t& _children, + EasyTreeWidgetItem* _parent, + EasyTreeWidgetItem* _frame, + ::profiler::timestamp_t _left, + ::profiler::timestamp_t _right, + bool _strict, + ::profiler::timestamp_t& _duration, + bool _addZeroBlocks, + ::profiler_gui::TimeUnits _units) +{ + auto const setTree = m_mode == EasyTreeMode_Full ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain; + + size_t total_items = 0; + for (auto child_index : _children) + { + if (interrupted()) + break; + + auto& gui_block = easyBlock(child_index); + const auto& child = gui_block.tree; + const auto startTime = child.node->begin(); + const auto endTime = child.node->end(); + const auto duration = endTime - startTime; + + if (duration == 0 && !_addZeroBlocks) + continue; + + _duration += duration; + + if (startTime > _right || endTime < _left) + continue; + + bool hasContextSwitch = false; + ::profiler::timestamp_t idleTime = 0; + for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) + { + auto cs_index = _threadRoot.sync[ind]; + const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + + if (cs->begin() > endTime) + { + if (!hasContextSwitch) + _firstCswitch = ind; + break; + } + + if (startTime <= cs->begin() && cs->end() <= endTime) + { + if (!hasContextSwitch) + { + _firstCswitch = ind; + hasContextSwitch = true; + } + + idleTime += cs->duration(); + } + } + + auto item = new EasyTreeWidgetItem(child_index, _parent); + + auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name(); + item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); + item->setTimeSmart(COL_DURATION, _units, duration); + + auto active_time = duration - idleTime; + auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + + item->setTimeMs(COL_BEGIN, startTime - _beginTime); + item->setTimeMs(COL_END, endTime - _beginTime); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); + + if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also + { + const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats; + const ::profiler::BlockStatistics* per_parent_stats = child.per_parent_stats; + const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats; + + auto parent_duration = _parent->duration(); + auto percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration); + auto percentage_sum = ::profiler_gui::percent(per_parent_stats->total_duration, parent_duration); + item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage); + item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage)); + item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum); + item->setText(COL_PERCENT_SUM_PER_PARENT, QString::number(percentage_sum)); + + if (_frame != nullptr) + { + if (_parent != _frame) + { + parent_duration = _frame->duration(); + percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration); + percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, parent_duration); + } + + item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage); + item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage)); + item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, percentage_sum); + item->setText(COL_PERCENT_SUM_PER_FRAME, QString::number(percentage_sum)); + } + else + { + item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); + item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, 0); + + auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time); + item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); + } + + + if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); + item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); + } + + item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); + item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); + + auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); + + + if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration()); + item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration); + } + + item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number); + item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number)); + + + if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); + item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration); + } + + item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); + item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); + } + else + { + if (_frame == nullptr) + { + auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time); + item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); + } + else + { + item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, 0); + } + + item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, 0); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); + } + + const auto color = easyDescriptor(child.node->id()).color(); + item->setBackgroundColor(color); + +#ifdef EASY_TREE_WIDGET__USE_VECTOR + auto item_index = static_cast(m_items.size()); + m_items.push_back(item); +#endif + + size_t children_items_number = 0; + ::profiler::timestamp_t children_duration = 0; + if (!child.children.empty()) + { + m_iditems.clear(); + + children_items_number = (this->*setTree)(_threadRoot, _firstCswitch, _beginTime, child.children, item, + _frame ? _frame : item, _left, _right, _strict, children_duration, + _addZeroBlocks, _units); + + if (interrupted()) + break; + } + + int percentage = 100; + auto self_duration = duration - children_duration; + if (children_duration > 0 && duration > 0) + { + percentage = ::profiler_gui::percent(self_duration, duration); + } + + item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); + item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + + if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) + { + total_items += children_items_number + 1; +#ifdef EASY_TREE_WIDGET__USE_VECTOR + gui_block.tree_item = item_index; +#endif + + if (gui_block.expanded) + item->setExpanded(true); + +#ifndef EASY_TREE_WIDGET__USE_VECTOR + m_items.insert(::std::make_pair(child_index, item)); +#endif + } + else + { +#ifdef EASY_TREE_WIDGET__USE_VECTOR + m_items.pop_back(); +#endif + delete item; + } + } + + return total_items; +} + +////////////////////////////////////////////////////////////////////////// + +::profiler::timestamp_t EasyTreeWidgetLoader::calculateChildrenDurationRecursive(const ::profiler::BlocksTree::children_t& _children, ::profiler::block_id_t _id) +{ + ::profiler::timestamp_t total_duration = 0; + + for (auto child_index : _children) + { + if (interrupted()) + break; + + const auto& gui_block = easyBlock(child_index); + total_duration += gui_block.tree.node->duration(); + if (gui_block.tree.node->id() == _id) + total_duration += calculateChildrenDurationRecursive(gui_block.tree.children, _id); + } + + return total_duration; +} + +size_t EasyTreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& _threadRoot, + ::profiler::block_index_t _firstCswitch, + const ::profiler::timestamp_t& _beginTime, + const ::profiler::BlocksTree::children_t& _children, + EasyTreeWidgetItem*, + EasyTreeWidgetItem* _frame, + ::profiler::timestamp_t _left, + ::profiler::timestamp_t _right, + bool _strict, + ::profiler::timestamp_t& _duration, + bool _addZeroBlocks, + ::profiler_gui::TimeUnits _units) +{ + size_t total_items = 0; + + for (auto child_index : _children) + { + if (interrupted()) + break; + + const auto& gui_block = easyBlock(child_index); + const auto& child = gui_block.tree; + const auto startTime = child.node->begin(); + const auto endTime = child.node->end(); + const auto duration = endTime - startTime; + + _duration += duration; + + auto it = m_iditems.find(child.node->id()); + if (it != m_iditems.end()) + { + ++total_items; + + ::profiler::timestamp_t children_duration = 0; + if (!child.children.empty()) + { + setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, + _right, _strict, children_duration, _addZeroBlocks, _units); + + if (interrupted()) + break; + } + + if (it->second != nullptr && child.per_frame_stats != nullptr) + { + auto item = it->second; + + //auto children_duration = calculateChildrenDurationRecursive(child.children, it->first); + if (children_duration != 0) + { + auto self_duration = item->data(COL_SELF_DURATION, Qt::UserRole).toULongLong() - children_duration; + + int percentage = 100; + if (child.per_frame_stats->total_duration > 0) + percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); + + item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); + item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + } + + bool hasContextSwitch = false; + ::profiler::timestamp_t idleTime = 0; + for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) + { + auto cs_index = _threadRoot.sync[ind]; + const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + + if (cs->begin() > endTime) + { + if (!hasContextSwitch) + _firstCswitch = ind; + break; + } + + if (startTime <= cs->begin() && cs->end() <= endTime) + { + if (!hasContextSwitch) + { + _firstCswitch = ind; + hasContextSwitch = true; + } + + idleTime += cs->duration(); + } + } + + auto active_time = item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime; + auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + } + + continue; + } + + if (startTime > _right || endTime < _left) + continue; + + bool hasContextSwitch = false; + ::profiler::timestamp_t idleTime = 0; + for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) + { + auto cs_index = _threadRoot.sync[ind]; + const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; + + if (cs->begin() > endTime) + { + if (!hasContextSwitch) + _firstCswitch = ind; + break; + } + + if (startTime <= cs->begin() && cs->end() <= endTime) + { + if (!hasContextSwitch) + { + _firstCswitch = ind; + hasContextSwitch = true; + } + + idleTime += cs->duration(); + } + } + + auto item = new EasyTreeWidgetItem(child_index, _frame); + + auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name(); + item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); + + if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also + { + const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats; + if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); + } + + item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); + item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); + item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); + + auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time); + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); + item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); + + const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats; + const auto percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, _frame->duration()); + item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage_sum); + item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum)); + + if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) + { + item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); + item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); + item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); + } + + item->setTimeSmart(COL_DURATION, _units, per_frame_stats->total_duration); + item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); + item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); + } + else + { + item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); + item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); + } + + const auto color = easyDescriptor(child.node->id()).color(); + item->setBackgroundColor(color); + +#ifdef EASY_TREE_WIDGET__USE_VECTOR + auto item_index = static_cast(m_items.size()); + m_items.push_back(item); +#endif + m_iditems[child.node->id()] = nullptr; + + size_t children_items_number = 0; + ::profiler::timestamp_t children_duration = 0; + if (!child.children.empty()) + { + children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, + _frame, _left, _right, _strict, children_duration, + _addZeroBlocks, _units); + + if (interrupted()) + break; + } + + m_iditems[child.node->id()] = item; + + if (child.per_frame_stats != nullptr) + { + int percentage = 100; + auto self_duration = child.per_frame_stats->total_duration - children_duration; + if (child.per_frame_stats->total_duration > 0) + percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); + + item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); + item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); + item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); + + auto active_time = child.per_frame_stats->total_duration - idleTime; + auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); + item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); + item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); + item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); + } + + if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) + { + total_items += children_items_number + 1; +#ifdef EASY_TREE_WIDGET__USE_VECTOR + gui_block.tree_item = item_index; +#endif + + if (gui_block.expanded) + item->setExpanded(true); + +#ifndef EASY_TREE_WIDGET__USE_VECTOR + m_items.insert(::std::make_pair(child_index, item)); +#endif + } + else + { +#ifdef EASY_TREE_WIDGET__USE_VECTOR + m_items.pop_back(); +#endif + delete item; + m_iditems.erase(gui_block.tree.node->id()); + } + } + + return total_items; +} + +////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/easyprofiler/profiler_gui/tree_widget_loader.h b/3rdparty/easyprofiler/profiler_gui/tree_widget_loader.h new file mode 100644 index 0000000..5e0dc09 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/tree_widget_loader.h @@ -0,0 +1,134 @@ +/************************************************************************ +* file name : tree_widget_loader.h +* ----------------- : +* creation time : 2016/08/18 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of EasyTreeWidgetLoader which aim is +* : to load EasyProfiler blocks hierarchy in separate thread. +* ----------------- : +* change log : * 2016/08/18 Victor Zarubkin: moved sources from blocks_tree_widget.h/.cpp +* : and renamed Prof* to Easy*. +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin +* : +* : Licensed under either of +* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) +* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* : at your option. +* : +* : The MIT License +* : +* : 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. +* : +* : The Apache License, Version 2.0 (the "License") +* : +* : You may not use this file except in compliance with the License. +* : You may obtain a copy of the License at +* : +* : http://www.apache.org/licenses/LICENSE-2.0 +* : +* : Unless required by applicable law or agreed to in writing, software +* : distributed under the License is distributed on an "AS IS" BASIS, +* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* : See the License for the specific language governing permissions and +* : limitations under the License. +************************************************************************/ + +#ifndef EASY_TREE_WIDGET_LOADER_H +#define EASY_TREE_WIDGET_LOADER_H + +#include +#include +#include +#include +#include +#include "common_types.h" + +////////////////////////////////////////////////////////////////////////// + +class EasyTreeWidgetItem; + +#ifndef EASY_TREE_WIDGET__USE_VECTOR +using Items = ::std::unordered_map<::profiler::block_index_t, EasyTreeWidgetItem*, ::estd::hash<::profiler::block_index_t> >; +#else +using Items = ::std::vector; +#endif + +using ThreadedItems = ::std::vector<::std::pair<::profiler::thread_id_t, EasyTreeWidgetItem*> >; +using RootsMap = ::std::unordered_map<::profiler::thread_id_t, EasyTreeWidgetItem*, ::estd::hash<::profiler::thread_id_t> >; +using IdItems = ::std::unordered_map<::profiler::block_id_t, EasyTreeWidgetItem*, ::estd::hash<::profiler::block_index_t> >; + +////////////////////////////////////////////////////////////////////////// + +enum EasyTreeMode : uint8_t +{ + EasyTreeMode_Full, + EasyTreeMode_Plain +}; + +////////////////////////////////////////////////////////////////////////// + +class EasyTreeWidgetLoader Q_DECL_FINAL +{ + ThreadedItems m_topLevelItems; ///< + Items m_items; ///< + IdItems m_iditems; ///< + ::std::thread m_thread; ///< + ::std::atomic_bool m_bDone; ///< + ::std::atomic_bool m_bInterrupt; ///< + ::std::atomic m_progress; ///< + EasyTreeMode m_mode; ///< + +public: + + EasyTreeWidgetLoader(); + ~EasyTreeWidgetLoader(); + + int progress() const; + bool done() const; + + void takeTopLevelItems(ThreadedItems& _output); + void takeItems(Items& _output); + + void interrupt(bool _wait = false); + void fillTree(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, EasyTreeMode _mode); + void fillTreeBlocks(const::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _beginTime, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, EasyTreeMode _mode); + +private: + + bool interrupted() const; + void setDone(); + void setProgress(int _progress); + + void setTreeInternal1(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units); + void setTreeInternal2(const ::profiler::timestamp_t& _beginTime, const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units); + size_t setTreeInternal(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem* _parent, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units); + size_t setTreeInternalPlain(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem* _parent, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units); + + ::profiler::timestamp_t calculateChildrenDurationRecursive(const ::profiler::BlocksTree::children_t& _children, ::profiler::block_id_t _id); + +}; // END of class EasyTreeWidgetLoader. + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_TREE_WIDGET_LOADER_H diff --git a/3rdparty/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp b/3rdparty/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp new file mode 100644 index 0000000..bc61555 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/treeview_first_column_delegate.cpp @@ -0,0 +1,33 @@ + + +#include +#include +#include "treeview_first_column_delegate.h" +#include "globals.h" + +EasyTreeViewFirstColumnItemDelegate::EasyTreeViewFirstColumnItemDelegate(QObject* parent) : QStyledItemDelegate(parent) +{ + +} + +EasyTreeViewFirstColumnItemDelegate::~EasyTreeViewFirstColumnItemDelegate() +{ + +} + +void EasyTreeViewFirstColumnItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + // Draw item as usual + QStyledItemDelegate::paint(painter, option, index); + + // Draw line under tree indicator + const auto bottomLeft = option.rect.bottomLeft(); + if (bottomLeft.x() > 0) + { + painter->save(); + painter->setBrush(Qt::NoBrush); + painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR); + painter->drawLine(QPoint(0, bottomLeft.y()), bottomLeft); + painter->restore(); + } +} \ No newline at end of file diff --git a/3rdparty/easyprofiler/profiler_gui/treeview_first_column_delegate.h b/3rdparty/easyprofiler/profiler_gui/treeview_first_column_delegate.h new file mode 100644 index 0000000..d55c056 --- /dev/null +++ b/3rdparty/easyprofiler/profiler_gui/treeview_first_column_delegate.h @@ -0,0 +1,23 @@ + + + + + +#ifndef EASY_PROFILER_GUI_TREEVIEW_FIRST_COLUMN_DELEGATE_H +#define EASY_PROFILER_GUI_TREEVIEW_FIRST_COLUMN_DELEGATE_H + +#include + +class EasyTreeViewFirstColumnItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + + explicit EasyTreeViewFirstColumnItemDelegate(QObject* parent = nullptr); + ~EasyTreeViewFirstColumnItemDelegate() override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +}; // END of class EasyTreeViewFirstColumnItemDelegate. + +#endif // EASY_PROFILER_GUI_TREEVIEW_FIRST_COLUMN_DELEGATE_H diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/CMakeDirectoryInformation.cmake b/3rdparty/easyprofiler/reader/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..9ecfcf5 --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/alex/Work/C++Projects/easyprofiler") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/alex/Work/C++Projects/easyprofiler") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/DependInfo.cmake b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/DependInfo.cmake new file mode 100644 index 0000000..235700c --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/DependInfo.cmake @@ -0,0 +1,31 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/home/alex/Work/C++Projects/easyprofiler/reader/main.cpp" "/home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/profiler_reader.dir/main.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BUILD_WITH_EASY_PROFILER=1" + "EASY_DEFAULT_PORT=28077" + "EASY_PROFILER_VERSION_MAJOR=1" + "EASY_PROFILER_VERSION_MINOR=3" + "EASY_PROFILER_VERSION_PATCH=0" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "easy_profiler_core/include" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake" + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/build.make b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/build.make new file mode 100644 index 0000000..604c854 --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/build.make @@ -0,0 +1,114 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/alex/Work/C++Projects/easyprofiler + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/alex/Work/C++Projects/easyprofiler + +# Include any dependencies generated for this target. +include reader/CMakeFiles/profiler_reader.dir/depend.make + +# Include the progress variables for this target. +include reader/CMakeFiles/profiler_reader.dir/progress.make + +# Include the compile flags for this target's objects. +include reader/CMakeFiles/profiler_reader.dir/flags.make + +reader/CMakeFiles/profiler_reader.dir/main.cpp.o: reader/CMakeFiles/profiler_reader.dir/flags.make +reader/CMakeFiles/profiler_reader.dir/main.cpp.o: reader/main.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object reader/CMakeFiles/profiler_reader.dir/main.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/reader && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_reader.dir/main.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/reader/main.cpp + +reader/CMakeFiles/profiler_reader.dir/main.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_reader.dir/main.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/reader && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/reader/main.cpp > CMakeFiles/profiler_reader.dir/main.cpp.i + +reader/CMakeFiles/profiler_reader.dir/main.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_reader.dir/main.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/reader && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/reader/main.cpp -o CMakeFiles/profiler_reader.dir/main.cpp.s + +reader/CMakeFiles/profiler_reader.dir/main.cpp.o.requires: + +.PHONY : reader/CMakeFiles/profiler_reader.dir/main.cpp.o.requires + +reader/CMakeFiles/profiler_reader.dir/main.cpp.o.provides: reader/CMakeFiles/profiler_reader.dir/main.cpp.o.requires + $(MAKE) -f reader/CMakeFiles/profiler_reader.dir/build.make reader/CMakeFiles/profiler_reader.dir/main.cpp.o.provides.build +.PHONY : reader/CMakeFiles/profiler_reader.dir/main.cpp.o.provides + +reader/CMakeFiles/profiler_reader.dir/main.cpp.o.provides.build: reader/CMakeFiles/profiler_reader.dir/main.cpp.o + + +# Object files for target profiler_reader +profiler_reader_OBJECTS = \ +"CMakeFiles/profiler_reader.dir/main.cpp.o" + +# External object files for target profiler_reader +profiler_reader_EXTERNAL_OBJECTS = + +bin/profiler_reader: reader/CMakeFiles/profiler_reader.dir/main.cpp.o +bin/profiler_reader: reader/CMakeFiles/profiler_reader.dir/build.make +bin/profiler_reader: bin/libeasy_profiler.so +bin/profiler_reader: reader/CMakeFiles/profiler_reader.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX executable ../bin/profiler_reader" + cd /home/alex/Work/C++Projects/easyprofiler/reader && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/profiler_reader.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +reader/CMakeFiles/profiler_reader.dir/build: bin/profiler_reader + +.PHONY : reader/CMakeFiles/profiler_reader.dir/build + +reader/CMakeFiles/profiler_reader.dir/requires: reader/CMakeFiles/profiler_reader.dir/main.cpp.o.requires + +.PHONY : reader/CMakeFiles/profiler_reader.dir/requires + +reader/CMakeFiles/profiler_reader.dir/clean: + cd /home/alex/Work/C++Projects/easyprofiler/reader && $(CMAKE_COMMAND) -P CMakeFiles/profiler_reader.dir/cmake_clean.cmake +.PHONY : reader/CMakeFiles/profiler_reader.dir/clean + +reader/CMakeFiles/profiler_reader.dir/depend: + cd /home/alex/Work/C++Projects/easyprofiler && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/reader /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/reader /home/alex/Work/C++Projects/easyprofiler/reader/CMakeFiles/profiler_reader.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : reader/CMakeFiles/profiler_reader.dir/depend + diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/cmake_clean.cmake b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/cmake_clean.cmake new file mode 100644 index 0000000..93ff1ac --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/cmake_clean.cmake @@ -0,0 +1,10 @@ +file(REMOVE_RECURSE + "CMakeFiles/profiler_reader.dir/main.cpp.o" + "../bin/profiler_reader.pdb" + "../bin/profiler_reader" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/profiler_reader.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/depend.make b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/depend.make new file mode 100644 index 0000000..e5f4bcd --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/depend.make @@ -0,0 +1,2 @@ +# Empty dependencies file for profiler_reader. +# This may be replaced when dependencies are built. diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/flags.make b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/flags.make new file mode 100644 index 0000000..a02c938 --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -O3 -DNDEBUG -DEASY_CHRONO_STEADY_CLOCK=0 -DEASY_CHRONO_HIGHRES_CLOCK=0 -DEASY_OPTION_START_LISTEN_ON_STARTUP=0 -DEASY_OPTION_MEASURE_STORAGE_EXPAND=0 -DEASY_OPTION_STORAGE_EXPAND_BLOCKS_ON=0 -DEASY_OPTION_IMPLICIT_THREAD_REGISTRATION=1 -DEASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS=0 -DEASY_OPTION_LOG_ENABLED=0 -DEASY_OPTION_PRETTY_PRINT_FUNCTIONS=0 -DEASY_OPTION_BUILTIN_COLORS=1 -std=gnu++11 + +CXX_DEFINES = -DBUILD_WITH_EASY_PROFILER=1 -DEASY_DEFAULT_PORT=28077 -DEASY_PROFILER_VERSION_MAJOR=1 -DEASY_PROFILER_VERSION_MINOR=3 -DEASY_PROFILER_VERSION_PATCH=0 + +CXX_INCLUDES = -I/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include + diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/link.txt b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/link.txt new file mode 100644 index 0000000..73eee0f --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/link.txt @@ -0,0 +1 @@ +/usr/bin/c++ -O3 -DNDEBUG CMakeFiles/profiler_reader.dir/main.cpp.o -o ../bin/profiler_reader -rdynamic ../bin/libeasy_profiler.so -lpthread -Wl,-rpath,/home/alex/Work/C++Projects/easyprofiler/bin diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/progress.make b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/progress.make new file mode 100644 index 0000000..335ef43 --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/profiler_reader.dir/progress.make @@ -0,0 +1,3 @@ +CMAKE_PROGRESS_1 = 30 +CMAKE_PROGRESS_2 = 31 + diff --git a/3rdparty/easyprofiler/reader/CMakeFiles/progress.marks b/3rdparty/easyprofiler/reader/CMakeFiles/progress.marks new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeFiles/progress.marks @@ -0,0 +1 @@ +10 diff --git a/3rdparty/easyprofiler/reader/CMakeLists.txt b/3rdparty/easyprofiler/reader/CMakeLists.txt new file mode 100644 index 0000000..903c910 --- /dev/null +++ b/3rdparty/easyprofiler/reader/CMakeLists.txt @@ -0,0 +1,3 @@ + +add_executable(profiler_reader main.cpp) +target_link_libraries(profiler_reader easy_profiler) diff --git a/3rdparty/easyprofiler/reader/cmake_install.cmake b/3rdparty/easyprofiler/reader/cmake_install.cmake new file mode 100644 index 0000000..bf2dcfa --- /dev/null +++ b/3rdparty/easyprofiler/reader/cmake_install.cmake @@ -0,0 +1,34 @@ +# Install script for directory: /home/alex/Work/C++Projects/easyprofiler/reader + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Release") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + diff --git a/3rdparty/easyprofiler/reader/main.cpp b/3rdparty/easyprofiler/reader/main.cpp new file mode 100644 index 0000000..34a9360 --- /dev/null +++ b/3rdparty/easyprofiler/reader/main.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class TreePrinter +{ + struct Info{ + std::string name; + std::string info; + }; + std::vector m_rows; + +public: + TreePrinter(){ + + } + void addNewRow(int level) + { + + } + + void printTree() + { + for (auto& row : m_rows){ + std::cout << row.name << " " << row.info << std::endl; + } + } +}; + + +void printTree(TreePrinter& printer, const ::profiler::BlocksTree& tree, int level = 0, profiler::timestamp_t parent_dur = 0, profiler::timestamp_t root_dur = 0) +{ + // + //if (tree.node){ + // auto duration = tree.node->block()->duration(); + // float duration_ms = duration / 1e6f; + // float percent = parent_dur ? float(duration) / float(parent_dur)*100.0f : 100.0f; + // float rpercent = root_dur ? float(duration) / float(root_dur)*100.0f : 100.0f; + // std::cout << std::string(level, '\t') << tree.node->getName() + // << std::string(5 - level, '\t') + // /*<< std::string(level, ' ')*/ << percent << "%| " + // << rpercent << "%| " + // << duration_ms << " ms" + // << std::endl; + // if (root_dur == 0){ + // root_dur = tree.node->block()->duration(); + // } + //} + //else{ + // root_dur = 0; + //} + // + + //for (const auto& i : tree.children){ + + // printTree(printer, i, level + 1, tree.node ? tree.node->block()->duration() : 0, root_dur); + //} +} + +int main(int argc, char* argv[]) +{ + + ::profiler::thread_blocks_tree_t threaded_trees; + + ::std::string filename;// = "test.prof"; + if (argc > 1 && argv[1]) + { + filename = argv[1]; + } + else + { + std::cout << "Specify prof file: "; + std::getline(std::cin, filename); + //return 255; + } + + ::std::string dump_filename; + if (argc > 2 && argv[2]) + { + dump_filename = argv[2]; + } + else + { + std::cout << "Specify output prof file: "; + std::getline(std::cin, dump_filename); + } + + if (dump_filename.size() > 2) + { + EASY_PROFILER_ENABLE; + std::cout << "Will dump reader prof file to " << dump_filename << std::endl; + } + else + { + dump_filename.clear(); + } + + + auto start = std::chrono::system_clock::now(); + + ::profiler::SerializedData serialized_blocks, serialized_descriptors; + ::profiler::descriptors_list_t descriptors; + ::profiler::blocks_t blocks; + ::std::stringstream errorMessage; + uint32_t descriptorsNumberInFile = 0; + uint32_t version = 0; + auto blocks_counter = fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, descriptors, blocks, + threaded_trees, descriptorsNumberInFile, version, true, errorMessage); + if (blocks_counter == 0) + std::cout << "Can not read blocks from file " << filename.c_str() << "\nReason: " << errorMessage.str(); + + auto end = std::chrono::system_clock::now(); + + std::cout << "Blocks count: " << blocks_counter << std::endl; + std::cout << "dT = " << std::chrono::duration_cast(end - start).count() << " usec" << std::endl; + //for (const auto & i : threaded_trees){ + // TreePrinter p; + // std::cout << std::string(20, '=') << " thread "<< i.first << " "<< std::string(20, '=') << std::endl; + // printTree(p, i.second.tree,-1); + //} + + if (!dump_filename.empty()) + { + auto bcount = profiler::dumpBlocksToFile(dump_filename.c_str()); + + std::cout << "Blocks count for reader: " << bcount << std::endl; + } + + //char c; + //::std::cin >> c; + + + return 0; +} diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/CMakeDirectoryInformation.cmake b/3rdparty/easyprofiler/sample/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..9ecfcf5 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/alex/Work/C++Projects/easyprofiler") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/alex/Work/C++Projects/easyprofiler") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/DependInfo.cmake b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/DependInfo.cmake new file mode 100644 index 0000000..60f0791 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/DependInfo.cmake @@ -0,0 +1,31 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/home/alex/Work/C++Projects/easyprofiler/sample/main.cpp" "/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/profiler_sample.dir/main.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BUILD_WITH_EASY_PROFILER=1" + "EASY_DEFAULT_PORT=28077" + "EASY_PROFILER_VERSION_MAJOR=1" + "EASY_PROFILER_VERSION_MINOR=3" + "EASY_PROFILER_VERSION_PATCH=0" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "easy_profiler_core/include" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake" + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/build.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/build.make new file mode 100644 index 0000000..3e96c75 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/build.make @@ -0,0 +1,114 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/alex/Work/C++Projects/easyprofiler + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/alex/Work/C++Projects/easyprofiler + +# Include any dependencies generated for this target. +include sample/CMakeFiles/profiler_sample.dir/depend.make + +# Include the progress variables for this target. +include sample/CMakeFiles/profiler_sample.dir/progress.make + +# Include the compile flags for this target's objects. +include sample/CMakeFiles/profiler_sample.dir/flags.make + +sample/CMakeFiles/profiler_sample.dir/main.cpp.o: sample/CMakeFiles/profiler_sample.dir/flags.make +sample/CMakeFiles/profiler_sample.dir/main.cpp.o: sample/main.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object sample/CMakeFiles/profiler_sample.dir/main.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/sample && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_sample.dir/main.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/sample/main.cpp + +sample/CMakeFiles/profiler_sample.dir/main.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_sample.dir/main.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/sample && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/sample/main.cpp > CMakeFiles/profiler_sample.dir/main.cpp.i + +sample/CMakeFiles/profiler_sample.dir/main.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_sample.dir/main.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/sample && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/sample/main.cpp -o CMakeFiles/profiler_sample.dir/main.cpp.s + +sample/CMakeFiles/profiler_sample.dir/main.cpp.o.requires: + +.PHONY : sample/CMakeFiles/profiler_sample.dir/main.cpp.o.requires + +sample/CMakeFiles/profiler_sample.dir/main.cpp.o.provides: sample/CMakeFiles/profiler_sample.dir/main.cpp.o.requires + $(MAKE) -f sample/CMakeFiles/profiler_sample.dir/build.make sample/CMakeFiles/profiler_sample.dir/main.cpp.o.provides.build +.PHONY : sample/CMakeFiles/profiler_sample.dir/main.cpp.o.provides + +sample/CMakeFiles/profiler_sample.dir/main.cpp.o.provides.build: sample/CMakeFiles/profiler_sample.dir/main.cpp.o + + +# Object files for target profiler_sample +profiler_sample_OBJECTS = \ +"CMakeFiles/profiler_sample.dir/main.cpp.o" + +# External object files for target profiler_sample +profiler_sample_EXTERNAL_OBJECTS = + +bin/profiler_sample: sample/CMakeFiles/profiler_sample.dir/main.cpp.o +bin/profiler_sample: sample/CMakeFiles/profiler_sample.dir/build.make +bin/profiler_sample: bin/libeasy_profiler.so +bin/profiler_sample: sample/CMakeFiles/profiler_sample.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX executable ../bin/profiler_sample" + cd /home/alex/Work/C++Projects/easyprofiler/sample && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/profiler_sample.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +sample/CMakeFiles/profiler_sample.dir/build: bin/profiler_sample + +.PHONY : sample/CMakeFiles/profiler_sample.dir/build + +sample/CMakeFiles/profiler_sample.dir/requires: sample/CMakeFiles/profiler_sample.dir/main.cpp.o.requires + +.PHONY : sample/CMakeFiles/profiler_sample.dir/requires + +sample/CMakeFiles/profiler_sample.dir/clean: + cd /home/alex/Work/C++Projects/easyprofiler/sample && $(CMAKE_COMMAND) -P CMakeFiles/profiler_sample.dir/cmake_clean.cmake +.PHONY : sample/CMakeFiles/profiler_sample.dir/clean + +sample/CMakeFiles/profiler_sample.dir/depend: + cd /home/alex/Work/C++Projects/easyprofiler && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/sample /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/sample /home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/profiler_sample.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : sample/CMakeFiles/profiler_sample.dir/depend + diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/cmake_clean.cmake b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/cmake_clean.cmake new file mode 100644 index 0000000..4b8b8c5 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/cmake_clean.cmake @@ -0,0 +1,10 @@ +file(REMOVE_RECURSE + "CMakeFiles/profiler_sample.dir/main.cpp.o" + "../bin/profiler_sample.pdb" + "../bin/profiler_sample" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/profiler_sample.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/depend.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/depend.make new file mode 100644 index 0000000..6b5fc0b --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/depend.make @@ -0,0 +1,2 @@ +# Empty dependencies file for profiler_sample. +# This may be replaced when dependencies are built. diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/flags.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/flags.make new file mode 100644 index 0000000..a02c938 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -O3 -DNDEBUG -DEASY_CHRONO_STEADY_CLOCK=0 -DEASY_CHRONO_HIGHRES_CLOCK=0 -DEASY_OPTION_START_LISTEN_ON_STARTUP=0 -DEASY_OPTION_MEASURE_STORAGE_EXPAND=0 -DEASY_OPTION_STORAGE_EXPAND_BLOCKS_ON=0 -DEASY_OPTION_IMPLICIT_THREAD_REGISTRATION=1 -DEASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS=0 -DEASY_OPTION_LOG_ENABLED=0 -DEASY_OPTION_PRETTY_PRINT_FUNCTIONS=0 -DEASY_OPTION_BUILTIN_COLORS=1 -std=gnu++11 + +CXX_DEFINES = -DBUILD_WITH_EASY_PROFILER=1 -DEASY_DEFAULT_PORT=28077 -DEASY_PROFILER_VERSION_MAJOR=1 -DEASY_PROFILER_VERSION_MINOR=3 -DEASY_PROFILER_VERSION_PATCH=0 + +CXX_INCLUDES = -I/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include + diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/link.txt b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/link.txt new file mode 100644 index 0000000..d0949c3 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/link.txt @@ -0,0 +1 @@ +/usr/bin/c++ -O3 -DNDEBUG CMakeFiles/profiler_sample.dir/main.cpp.o -o ../bin/profiler_sample -L/home/alex/Work/C++Projects/easyprofiler/../bin -rdynamic ../bin/libeasy_profiler.so -lpthread -Wl,-rpath,/home/alex/Work/C++Projects/easyprofiler/../bin:/home/alex/Work/C++Projects/easyprofiler/bin diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/progress.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/progress.make new file mode 100644 index 0000000..e1615c1 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample.dir/progress.make @@ -0,0 +1,3 @@ +CMAKE_PROGRESS_1 = 32 +CMAKE_PROGRESS_2 = 33 + diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/DependInfo.cmake b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/DependInfo.cmake new file mode 100644 index 0000000..228a991 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/DependInfo.cmake @@ -0,0 +1,32 @@ +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + "CXX" + ) +# The set of files for implicit dependencies of each language: +set(CMAKE_DEPENDS_CHECK_CXX + "/home/alex/Work/C++Projects/easyprofiler/sample/main.cpp" "/home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o" + ) +set(CMAKE_CXX_COMPILER_ID "GNU") + +# Preprocessor definitions for this target. +set(CMAKE_TARGET_DEFINITIONS_CXX + "BUILD_WITH_EASY_PROFILER=1" + "DISABLE_EASY_PROFILER" + "EASY_DEFAULT_PORT=28077" + "EASY_PROFILER_VERSION_MAJOR=1" + "EASY_PROFILER_VERSION_MINOR=3" + "EASY_PROFILER_VERSION_PATCH=0" + ) + +# The include file search paths: +set(CMAKE_CXX_TARGET_INCLUDE_PATH + "easy_profiler_core/include" + ) + +# Targets to which this target links. +set(CMAKE_TARGET_LINKED_INFO_FILES + "/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/CMakeFiles/easy_profiler.dir/DependInfo.cmake" + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/build.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/build.make new file mode 100644 index 0000000..17502cf --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/build.make @@ -0,0 +1,114 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/alex/Work/C++Projects/easyprofiler + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/alex/Work/C++Projects/easyprofiler + +# Include any dependencies generated for this target. +include sample/CMakeFiles/profiler_sample_disabled_profiler.dir/depend.make + +# Include the progress variables for this target. +include sample/CMakeFiles/profiler_sample_disabled_profiler.dir/progress.make + +# Include the compile flags for this target's objects. +include sample/CMakeFiles/profiler_sample_disabled_profiler.dir/flags.make + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o: sample/CMakeFiles/profiler_sample_disabled_profiler.dir/flags.make +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o: sample/main.cpp + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o" + cd /home/alex/Work/C++Projects/easyprofiler/sample && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o -c /home/alex/Work/C++Projects/easyprofiler/sample/main.cpp + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.i" + cd /home/alex/Work/C++Projects/easyprofiler/sample && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/alex/Work/C++Projects/easyprofiler/sample/main.cpp > CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.i + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.s" + cd /home/alex/Work/C++Projects/easyprofiler/sample && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/alex/Work/C++Projects/easyprofiler/sample/main.cpp -o CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.s + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.requires: + +.PHONY : sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.requires + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.provides: sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.requires + $(MAKE) -f sample/CMakeFiles/profiler_sample_disabled_profiler.dir/build.make sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.provides.build +.PHONY : sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.provides + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.provides.build: sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o + + +# Object files for target profiler_sample_disabled_profiler +profiler_sample_disabled_profiler_OBJECTS = \ +"CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o" + +# External object files for target profiler_sample_disabled_profiler +profiler_sample_disabled_profiler_EXTERNAL_OBJECTS = + +bin/profiler_sample_disabled_profiler: sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o +bin/profiler_sample_disabled_profiler: sample/CMakeFiles/profiler_sample_disabled_profiler.dir/build.make +bin/profiler_sample_disabled_profiler: bin/libeasy_profiler.so +bin/profiler_sample_disabled_profiler: sample/CMakeFiles/profiler_sample_disabled_profiler.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/home/alex/Work/C++Projects/easyprofiler/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX executable ../bin/profiler_sample_disabled_profiler" + cd /home/alex/Work/C++Projects/easyprofiler/sample && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/profiler_sample_disabled_profiler.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/build: bin/profiler_sample_disabled_profiler + +.PHONY : sample/CMakeFiles/profiler_sample_disabled_profiler.dir/build + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/requires: sample/CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o.requires + +.PHONY : sample/CMakeFiles/profiler_sample_disabled_profiler.dir/requires + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/clean: + cd /home/alex/Work/C++Projects/easyprofiler/sample && $(CMAKE_COMMAND) -P CMakeFiles/profiler_sample_disabled_profiler.dir/cmake_clean.cmake +.PHONY : sample/CMakeFiles/profiler_sample_disabled_profiler.dir/clean + +sample/CMakeFiles/profiler_sample_disabled_profiler.dir/depend: + cd /home/alex/Work/C++Projects/easyprofiler && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/sample /home/alex/Work/C++Projects/easyprofiler /home/alex/Work/C++Projects/easyprofiler/sample /home/alex/Work/C++Projects/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/DependInfo.cmake --color=$(COLOR) +.PHONY : sample/CMakeFiles/profiler_sample_disabled_profiler.dir/depend + diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/cmake_clean.cmake b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/cmake_clean.cmake new file mode 100644 index 0000000..38c16f6 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/cmake_clean.cmake @@ -0,0 +1,10 @@ +file(REMOVE_RECURSE + "CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o" + "../bin/profiler_sample_disabled_profiler.pdb" + "../bin/profiler_sample_disabled_profiler" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/profiler_sample_disabled_profiler.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/depend.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/depend.make new file mode 100644 index 0000000..6291d48 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/depend.make @@ -0,0 +1,2 @@ +# Empty dependencies file for profiler_sample_disabled_profiler. +# This may be replaced when dependencies are built. diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/flags.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/flags.make new file mode 100644 index 0000000..084b973 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.5 + +# compile CXX with /usr/bin/c++ +CXX_FLAGS = -O3 -DNDEBUG -DEASY_CHRONO_STEADY_CLOCK=0 -DEASY_CHRONO_HIGHRES_CLOCK=0 -DEASY_OPTION_START_LISTEN_ON_STARTUP=0 -DEASY_OPTION_MEASURE_STORAGE_EXPAND=0 -DEASY_OPTION_STORAGE_EXPAND_BLOCKS_ON=0 -DEASY_OPTION_IMPLICIT_THREAD_REGISTRATION=1 -DEASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS=0 -DEASY_OPTION_LOG_ENABLED=0 -DEASY_OPTION_PRETTY_PRINT_FUNCTIONS=0 -DEASY_OPTION_BUILTIN_COLORS=1 -std=gnu++11 + +CXX_DEFINES = -DBUILD_WITH_EASY_PROFILER=1 -DDISABLE_EASY_PROFILER -DEASY_DEFAULT_PORT=28077 -DEASY_PROFILER_VERSION_MAJOR=1 -DEASY_PROFILER_VERSION_MINOR=3 -DEASY_PROFILER_VERSION_PATCH=0 + +CXX_INCLUDES = -I/home/alex/Work/C++Projects/easyprofiler/easy_profiler_core/include + diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/link.txt b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/link.txt new file mode 100644 index 0000000..caada90 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/link.txt @@ -0,0 +1 @@ +/usr/bin/c++ -O3 -DNDEBUG CMakeFiles/profiler_sample_disabled_profiler.dir/main.cpp.o -o ../bin/profiler_sample_disabled_profiler -L/home/alex/Work/C++Projects/easyprofiler/../bin -rdynamic ../bin/libeasy_profiler.so -lpthread -Wl,-rpath,/home/alex/Work/C++Projects/easyprofiler/../bin:/home/alex/Work/C++Projects/easyprofiler/bin diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/progress.make b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/progress.make new file mode 100644 index 0000000..30c3091 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/profiler_sample_disabled_profiler.dir/progress.make @@ -0,0 +1,3 @@ +CMAKE_PROGRESS_1 = 34 +CMAKE_PROGRESS_2 = 35 + diff --git a/3rdparty/easyprofiler/sample/CMakeFiles/progress.marks b/3rdparty/easyprofiler/sample/CMakeFiles/progress.marks new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeFiles/progress.marks @@ -0,0 +1 @@ +12 diff --git a/3rdparty/easyprofiler/sample/CMakeLists.txt b/3rdparty/easyprofiler/sample/CMakeLists.txt new file mode 100644 index 0000000..dbc31a3 --- /dev/null +++ b/3rdparty/easyprofiler/sample/CMakeLists.txt @@ -0,0 +1,16 @@ +set(CPP_FILES + main.cpp +) + +set(SOURCES + ${CPP_FILES} +) + +link_directories(${CMAKE_SOURCE_DIR}/../bin) + +add_executable(profiler_sample ${SOURCES}) +target_link_libraries(profiler_sample easy_profiler) + +add_executable(profiler_sample_disabled_profiler ${SOURCES}) +target_link_libraries(profiler_sample_disabled_profiler easy_profiler) +target_compile_definitions(profiler_sample_disabled_profiler PRIVATE DISABLE_EASY_PROFILER) diff --git a/3rdparty/easyprofiler/sample/build_express_test.sh b/3rdparty/easyprofiler/sample/build_express_test.sh new file mode 100755 index 0000000..638ee2a --- /dev/null +++ b/3rdparty/easyprofiler/sample/build_express_test.sh @@ -0,0 +1,21 @@ +#!/bin/bash +TEMP_FILE_ENABLE="enable.info" +TEMP_FILE_DISABLE="disable.info" +OBJECTS="1000" + +$CXX_COMPILER -O3 -std=c++11 -I../easy_profiler_core/include/ -L../bin/ -leasy_profiler express_sample.cpp -o express_test_disabled +$CXX_COMPILER -O3 -std=c++11 -I../easy_profiler_core/include/ -L../bin/ -leasy_profiler -DBUILD_WITH_EASY_PROFILER express_sample.cpp -o express_test_enabled + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../bin + +./express_test_disabled $OBJECTS > $TEMP_FILE_DISABLE +./express_test_enabled $OBJECTS > $TEMP_FILE_ENABLE + +DT_ENA=`cat $TEMP_FILE_ENABLE | grep Elapsed| awk '{print $3}'` +N_ENA=`cat $TEMP_FILE_ENABLE | grep Blocks| awk '{print $3}'` +DT_DIS=`cat $TEMP_FILE_DISABLE | grep Elapsed| awk '{print $3}'` + +DELTA=$(($DT_ENA-$DT_DIS)) +USEC_BLOCK=`awk "BEGIN{print $DELTA/$N_ENA}"` + +echo "~" $USEC_BLOCK "usec/block" diff --git a/3rdparty/easyprofiler/sample/cmake_install.cmake b/3rdparty/easyprofiler/sample/cmake_install.cmake new file mode 100644 index 0000000..a3eabe7 --- /dev/null +++ b/3rdparty/easyprofiler/sample/cmake_install.cmake @@ -0,0 +1,34 @@ +# Install script for directory: /home/alex/Work/C++Projects/easyprofiler/sample + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Release") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + diff --git a/3rdparty/easyprofiler/sample/express_sample.cpp b/3rdparty/easyprofiler/sample/express_sample.cpp new file mode 100644 index 0000000..4212c48 --- /dev/null +++ b/3rdparty/easyprofiler/sample/express_sample.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int OBJECTS = 500; + +void modellingThread(){ + EASY_THREAD("Modelling"); + + static const int N = OBJECTS; + + volatile double *pos[N]; + for (int i = 0; i < N; ++i) + { + pos[i] = new volatile double[3]; + } + + { + EASY_BLOCK("Collisions"); + volatile int i, j; + volatile double dist; + for (i = 0; i < N; ++i) + { + for (j = i + 1; j < N; ++j) + { + EASY_BLOCK("Check"); + volatile double v[3]; + v[0] = pos[i][0] - pos[j][0]; + v[1] = pos[i][1] - pos[j][1]; + v[2] = pos[i][2] - pos[j][2]; + dist = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; + if (dist < 10000) + { + dist *= dist; + } + } + } + } + + for (int i = 0; i < N; ++i) + { + delete [] pos[i]; + } +} + +////////////////////////////////////////////////////////////////////////// + +int main(int argc, char* argv[]) +{ + if (argc > 1 && argv[1]){ + OBJECTS = std::atoi(argv[1]); + } + + std::cout << "Objects count: " << OBJECTS << std::endl; + + auto start = std::chrono::system_clock::now(); + + + EASY_PROFILER_ENABLE; + EASY_MAIN_THREAD; + + + modellingThread(); + + auto end = std::chrono::system_clock::now(); + auto elapsed = + std::chrono::duration_cast(end - start); + + std::cout << "Elapsed time: " << elapsed.count() << " usec" << std::endl; + + auto blocks_count = profiler::dumpBlocksToFile("test.prof"); + + std::cout << "Blocks count: " << blocks_count << std::endl; + + return 0; +} diff --git a/3rdparty/easyprofiler/sample/main.cpp b/3rdparty/easyprofiler/sample/main.cpp new file mode 100644 index 0000000..b2e694f --- /dev/null +++ b/3rdparty/easyprofiler/sample/main.cpp @@ -0,0 +1,289 @@ +//#define FULL_DISABLE_PROFILER +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +std::condition_variable cv; +std::mutex cv_m; +int g_i = 0; + +int OBJECTS = 500; +int MODELLING_STEPS = 1500; +int RENDER_STEPS = 1500; +int RESOURCE_LOADING_COUNT = 50; + +#define SAMPLE_NETWORK_TEST + +void localSleep(int magic=200000) +{ + //PROFILER_BEGIN_FUNCTION_BLOCK_GROUPED(profiler::colors::Blue); + volatile int i = 0; + for (; i < magic; ++i); +} + +void loadingResources(){ + EASY_FUNCTION(profiler::colors::DarkCyan); + localSleep(); +// std::this_thread::sleep_for(std::chrono::milliseconds(50)); +} + +void prepareMath(){ + EASY_FUNCTION(profiler::colors::Green); + uint64_t sum = 0; + int* intarray = new int[OBJECTS]; + for (int i = 0; i < OBJECTS; ++i) + { + intarray[i] = i * i; + sum += i * i; + } + delete[] intarray; + //std::this_thread::sleep_for(std::chrono::milliseconds(3)); + + EASY_VALUE("sum", sum, profiler::colors::Blue); +} + +void calcIntersect(){ + EASY_FUNCTION(profiler::colors::Gold); + //int* intarray = new int[OBJECTS * OBJECTS]; + int* intarray = new int[OBJECTS]; + for (int i = 0; i < OBJECTS; ++i) + { + for (int j = i; j < OBJECTS; ++j) + //intarray[i * OBJECTS + j] = i * j - i / 2 + (OBJECTS - j) * 5; + intarray[j] = i * j - i / 2 + (OBJECTS - j) * 5; + } + delete[] intarray; + //std::this_thread::sleep_for(std::chrono::milliseconds(4)); +} + +double multModel(double i) +{ + EASY_FUNCTION(profiler::colors::PaleGold); + return i * sin(i) * cos(i); +} + +void calcPhys(){ + EASY_FUNCTION(profiler::colors::Amber); + double* intarray = new double[OBJECTS]; + for (int i = 0; i < OBJECTS; ++i) + intarray[i] = multModel(double(i)) + double(i / 3) - double((OBJECTS - i) / 2); + calcIntersect(); + delete[] intarray; +} + +double calcSubbrain(int i) +{ + EASY_FUNCTION(profiler::colors::Navy); + auto val = i * i * i - i / 10 + (OBJECTS - i) * 7 ; + EASY_VALUE("subbrainResult", val, profiler::colors::DarkRed); + return val; +} + +void calcBrain(){ + EASY_FUNCTION(profiler::colors::LightBlue); + double* intarray = new double[OBJECTS]; + for (int i = 0; i < OBJECTS; ++i) + intarray[i] = calcSubbrain(i) + double(i * 180 / 3); + delete[] intarray; + //std::this_thread::sleep_for(std::chrono::milliseconds(3)); +} + +void calculateBehavior(){ + EASY_FUNCTION(profiler::colors::Blue); + calcPhys(); + calcBrain(); +} + +void modellingStep(){ + EASY_FUNCTION(); + prepareMath(); + calculateBehavior(); +} + +void prepareRender(){ + EASY_FUNCTION(profiler::colors::Brick); + localSleep(); + //std::this_thread::sleep_for(std::chrono::milliseconds(8)); + +} + +int multPhys(int i) +{ + EASY_FUNCTION(profiler::colors::Red700, profiler::ON); + return i * i * i * i / 100; +} + +int calcPhysicForObject(int i) +{ + EASY_FUNCTION(profiler::colors::Red); + return multPhys(i) + i / 3 - (OBJECTS - i) * 15; +} + +void calculatePhysics(){ + EASY_FUNCTION(profiler::colors::Red); + unsigned int* intarray = new unsigned int[OBJECTS]; + for (int i = 0; i < OBJECTS; ++i) + intarray[i] = calcPhysicForObject(i); + delete[] intarray; + //std::this_thread::sleep_for(std::chrono::milliseconds(8)); +} + +void frame(){ + EASY_FUNCTION(profiler::colors::Magenta); + prepareRender(); + calculatePhysics(); +} + +void loadingResourcesThread(){ + //std::unique_lock lk(cv_m); + //cv.wait(lk, []{return g_i == 1; }); + EASY_THREAD("Resource loading"); +#ifdef SAMPLE_NETWORK_TEST + while (true) { +#else + for(int i = 0; i < RESOURCE_LOADING_COUNT; i++){ +#endif + loadingResources(); + EASY_EVENT("Resources Loading!", profiler::colors::Cyan); + localSleep(1200000); + //std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } +} + +void modellingThread(){ + //std::unique_lock lk(cv_m); + //cv.wait(lk, []{return g_i == 1; }); + EASY_THREAD("Modelling"); + uint64_t step = 0; +#ifdef SAMPLE_NETWORK_TEST + while (true) { +#else + for (int i = 0; i < MODELLING_STEPS; i++){ +#endif + EASY_END_BLOCK; + EASY_NONSCOPED_BLOCK("Frame", true, 15., profiler::ON, -5.f, profiler::colors::Red); + modellingStep(); + + localSleep(1200000); + + ++step; + EASY_VALUE("step", step, profiler::colors::Gold); + if (step > 10000000) + step = 0; + + EASY_TEXT("Test String", "Some short text. Hey!", profiler::colors::Red); + //std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + EASY_END_BLOCK; +} + +void renderThread(){ + //std::unique_lock lk(cv_m); + //cv.wait(lk, []{return g_i == 1; }); + EASY_THREAD("Render"); +#ifdef SAMPLE_NETWORK_TEST + while (true) { +#else + for (int i = 0; i < RENDER_STEPS; i++){ +#endif + frame(); + localSleep(1200000); + //std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } +} + +////////////////////////////////////////////////////////////////////////// + +int main(int argc, char* argv[]) +{ + if (argc > 1 && argv[1]){ + OBJECTS = std::atoi(argv[1]); + } + if (argc > 2 && argv[2]){ + MODELLING_STEPS = std::atoi(argv[2]); + } + if (argc > 3 && argv[3]){ + RENDER_STEPS = std::atoi(argv[3]); + } + if (argc > 4 && argv[4]){ + RESOURCE_LOADING_COUNT = std::atoi(argv[4]); + } + + std::cout << "Objects count: " << OBJECTS << std::endl; + std::cout << "Render steps: " << MODELLING_STEPS << std::endl; + std::cout << "Modelling steps: " << RENDER_STEPS << std::endl; + std::cout << "Resource loading count: " << RESOURCE_LOADING_COUNT << std::endl; + + auto start = std::chrono::system_clock::now(); + +#ifndef SAMPLE_NETWORK_TEST + EASY_PROFILER_ENABLE; +#endif + + EASY_MAIN_THREAD; + profiler::startListen(); + +#ifdef EASY_CONSTEXPR_AVAILABLE + constexpr int grrr[] {2, -3, 4}; + auto pppp = &grrr; + EASY_ARRAY("threads count", grrr, 3, false, true, "blabla", profiler::colors::Blue/*, EASY_VIN("threads count")*/, profiler::OFF); +#endif + + int* intPtr = new int(2); + EASY_VALUE("count", *intPtr); + + std::vector threads; + //for (int i=0; i < 3; i++) + { + threads.emplace_back(loadingResourcesThread); + threads.emplace_back(renderThread); + threads.emplace_back(modellingThread); + } + + cv_m.lock(); + g_i = 1; + cv_m.unlock(); + cv.notify_all(); + +#ifndef SAMPLE_NETWORK_TEST + std::atomic_bool stop = ATOMIC_VAR_INIT(false); + auto frame_time_printer_thread = std::thread([&stop]() + { + while (!stop.load(std::memory_order_acquire)) + { + std::cout << "Frame time: max " << profiler::main_thread::frameTimeLocalMax() << " us // avg " << profiler::main_thread::frameTimeLocalAvg() << " us\n"; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + }); +#endif + + modellingThread(); + +#ifndef SAMPLE_NETWORK_TEST + stop.store(true, std::memory_order_release); + frame_time_printer_thread.join(); +#endif + + for(auto& t : threads) + t.join(); + + auto end = std::chrono::system_clock::now(); + auto elapsed = + std::chrono::duration_cast(end - start); + + std::cout << "Elapsed time: " << elapsed.count() << " usec" << std::endl; + + auto blocks_count = profiler::dumpBlocksToFile("test.prof"); + + std::cout << "Blocks count: " << blocks_count << std::endl; + + return 0; +} diff --git a/3rdparty/easyprofiler/sample/main_clock.cpp b/3rdparty/easyprofiler/sample/main_clock.cpp new file mode 100644 index 0000000..95fa008 --- /dev/null +++ b/3rdparty/easyprofiler/sample/main_clock.cpp @@ -0,0 +1,249 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +static inline uint64_t getCurrentTime() +{ +#if defined(__i386__) + int64_t ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + return ret; +#elif defined(__x86_64__) || defined(__amd64__) + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return (high << 32) | low; +#endif +} + +int OBJECTS = 500; +#define STR(x) #x + +int64_t calculate_cpu_frequency()//per sec +{ + double g_TicksPerNanoSec; + struct timespec begints, endts; + uint64_t begin = 0, end = 0; + clock_gettime(CLOCK_MONOTONIC, &begints); + begin = getCurrentTime(); + volatile uint64_t i; + for (i = 0; i < 100000000; i++); /* must be CPU intensive */ + end = getCurrentTime(); + clock_gettime(CLOCK_MONOTONIC, &endts); + struct timespec tmpts; + const int NANO_SECONDS_IN_SEC = 1000000000; + tmpts.tv_sec = endts.tv_sec - begints.tv_sec; + tmpts.tv_nsec = endts.tv_nsec - begints.tv_nsec; + if (tmpts.tv_nsec < 0) + { + tmpts.tv_sec--; + tmpts.tv_nsec += NANO_SECONDS_IN_SEC; + } + + uint64_t nsecElapsed = tmpts.tv_sec * 1000000000LL + tmpts.tv_nsec; + g_TicksPerNanoSec = (double)(end - begin) / (double)nsecElapsed; + + int64_t cpu_frequency = int(g_TicksPerNanoSec * 1000); + + return cpu_frequency; +} + +const auto CPU_FREQUENCY = calculate_cpu_frequency(); + +# define TICKS_TO_US(ticks) ticks / CPU_FREQUENCY + +void localSleep(int magic=200000) +{ + volatile int i = 0; + for (; i < magic; ++i); +} + +template +auto calcDelta(int magic=200000) -> decltype(Clock::now().time_since_epoch().count()) +{ + auto start = Clock::now().time_since_epoch().count(); + localSleep(magic); + auto end = Clock::now().time_since_epoch().count(); + return end - start; +} + +template +double calcDuration(int objects) +{ + const auto frequency = Clock::period::den / Clock::period::num; + + auto start = Clock::now(); + + decltype(Clock::now().time_since_epoch().count()) summ = 0; + + for (int i=0; i < objects; i++) + { + summ += calcDelta(); + } + + summ = summ * 1000000LL / frequency; + + auto end = Clock::now(); + auto elapsed = + std::chrono::duration_cast(end - start); + + + return (elapsed.count()-summ)/double(objects)/2.0; +} + +uint64_t calcDeltaRdtsc(int magic=200000) +{ + auto start = getCurrentTime(); + localSleep(magic); + auto end = getCurrentTime(); + return end - start; +} + +double calcDurationByRdtsc(int objects) +{ + auto start = getCurrentTime(); + + uint64_t summ = 0; + + for (int i=0; i < objects; i++) + { + summ += calcDeltaRdtsc(); + } + + auto end = getCurrentTime(); + return TICKS_TO_US((end - start - summ))/double(objects)/2.0; +} + +uint64_t calcDeltaSysCall(int magic, int type) +{ + timespec tp0,tp1; + syscall(SYS_clock_gettime, type, &tp0); + auto start = tp0.tv_sec*1000000000+tp0.tv_nsec; + localSleep(magic); + syscall(SYS_clock_gettime, type, &tp1); + auto end = tp1.tv_sec*1000000000+tp1.tv_nsec; + return end - start; +} + +double calcDurationBySyscall(int objects, int type) +{ + timespec tp0,tp1; + syscall(SYS_clock_gettime, type, &tp0); + auto start = tp0.tv_sec*1000000000+tp0.tv_nsec; + + uint64_t summ = 0; + + for (int i=0; i < objects; i++) + { + summ += calcDeltaSysCall(200000,type); + } + + syscall(SYS_clock_gettime, type, &tp1); + auto end = tp1.tv_sec*1000000000+tp1.tv_nsec; + return (end - start - summ)/double(objects)/2.0/1000.0; +} + +uint64_t calcDeltaSysGetTime(int magic, int type) +{ + timespec tp0,tp1; + clock_gettime(type, &tp0); + auto start = tp0.tv_sec*1000000000+tp0.tv_nsec; + localSleep(magic); + clock_gettime(type, &tp1); + auto end = tp1.tv_sec*1000000000+tp1.tv_nsec; + return end - start; +} + +double calcDurationByGetTime(int objects, int type) +{ + timespec tp0,tp1; + clock_gettime(type, &tp0); + auto start = tp0.tv_sec*1000000000+tp0.tv_nsec; + + uint64_t summ = 0; + + for (int i=0; i < objects; i++) + { + summ += calcDeltaSysGetTime(200000,type); + } + + clock_gettime(type, &tp1); + auto end = tp1.tv_sec*1000000000+tp1.tv_nsec; + return (end - start - summ)/double(objects)/2.0/1000.0; +} + +uint64_t calcDeltaSysGetTimeOfDay(int magic=200000) +{ + timeval tv0,tv1; + gettimeofday(&tv0,0); + auto start = tv0.tv_sec*1000000+tv0.tv_usec; + localSleep(magic); + gettimeofday(&tv1, 0); + auto end = tv1.tv_sec*1000000+tv1.tv_usec; + return end - start; +} + +double calcDurationByGetTimeOfDay(int objects) +{ + timeval tv0,tv1; + gettimeofday(&tv0,0); + auto start = tv0.tv_sec*1000000+tv0.tv_usec; + + uint64_t summ = 0; + + for (int i=0; i < objects; i++) + { + summ += calcDeltaSysGetTimeOfDay(); + } + + gettimeofday(&tv1, 0); + auto end = tv1.tv_sec*1000000+tv1.tv_usec; + return (end - start - summ)/double(objects)/2.0; +} + +int main(int argc, char* argv[]) +{ + if (argc > 1 && argv[1]){ + OBJECTS = std::atoi(argv[1]); + } + + + std::cout << STR(std::chrono::steady_clock) << ": "<(OBJECTS) << " usec\n"; + std::cout << STR(std::chrono::high_resolution_clock)<< ": " << calcDuration(OBJECTS) << " usec\n"; + std::cout << STR(std::chrono::system_clock)<< ": " << calcDuration(OBJECTS) << " usec\n"; + + std::cout << "\n"; + + std::cout << "rdtsc: " << calcDurationByRdtsc(OBJECTS) << " usec\n"; + + std::cout << "\n"; + + std::cout << "syscall(SYS_clock_gettime, CLOCK_MONOTONIC): " << calcDurationBySyscall(OBJECTS,CLOCK_MONOTONIC) << " usec\n"; + std::cout << "syscall(SYS_clock_gettime, CLOCK_REALTIME): " << calcDurationBySyscall(OBJECTS,CLOCK_REALTIME) << " usec\n"; + std::cout << "syscall(SYS_clock_gettime, CLOCK_MONOTONIC_RAW): " << calcDurationBySyscall(OBJECTS,CLOCK_MONOTONIC_RAW) << " usec\n"; + std::cout << "syscall(SYS_clock_gettime, CLOCK_MONOTONIC_COARSE): " << calcDurationBySyscall(OBJECTS,CLOCK_MONOTONIC_COARSE) << " usec\n"; + std::cout << "syscall(SYS_clock_gettime, CLOCK_REALTIME_COARSE): " << calcDurationBySyscall(OBJECTS,CLOCK_REALTIME_COARSE) << " usec\n"; + + std::cout << "\n"; + + std::cout << "clock_gettime(CLOCK_MONOTONIC): " << calcDurationByGetTime(OBJECTS,CLOCK_MONOTONIC) << " usec\n"; + std::cout << "clock_gettime(CLOCK_REALTIME): " << calcDurationByGetTime(OBJECTS,CLOCK_REALTIME) << " usec\n"; + std::cout << "clock_gettime(CLOCK_MONOTONIC_RAW): " << calcDurationByGetTime(OBJECTS,CLOCK_MONOTONIC_RAW) << " usec\n"; + std::cout << "clock_gettime(CLOCK_MONOTONIC_COARSE): " << calcDurationByGetTime(OBJECTS,CLOCK_MONOTONIC_COARSE) << " usec\n"; + std::cout << "clock_gettime(CLOCK_REALTIME_COARSE): " << calcDurationByGetTime(OBJECTS,CLOCK_REALTIME_COARSE) << " usec\n"; + + std::cout << "\n"; + + std::cout << "gettimeofday(): " << calcDurationByGetTimeOfDay(OBJECTS) << " usec\n"; + + return 0; +} diff --git a/3rdparty/easyprofiler/scripts/context_switch_logger.stp b/3rdparty/easyprofiler/scripts/context_switch_logger.stp new file mode 100644 index 0000000..fd10537 --- /dev/null +++ b/3rdparty/easyprofiler/scripts/context_switch_logger.stp @@ -0,0 +1,37 @@ +global target_pid +global target_name + +probe scheduler.ctxswitch { + + if (target_pid != 0 + && next_pid != target_pid + && prev_pid != target_pid) + next + + if (target_name != "" + && prev_task_name != target_name + && next_task_name != target_name) + next + + //printf("Switch from %d(%s) to %d(%s) at %d\n",prev_tid, prev_task_name,next_tid,next_task_name, gettimeofday_ns()) + printf("%d %d %d %s %d\n",gettimeofday_ns(),prev_tid, next_tid, next_task_name,next_pid ) + //printf("%d %d %d\n",gettimeofday_ns(),prev_tid, next_tid ) +} + +probe begin +{ + target_pid = 0 + target_name = "" + + %( $# == 1 || $# > 2 %? + log("Wrong number of arguments, use none, 'pid nr' or 'name proc'") + exit() + %) + + %( $# == 2 %? + if(@1 == "pid") + target_pid = strtol(@2, 10) + if(@1 == "name") + target_name = @2 + %) +} diff --git a/3rdparty/easyprofiler/scripts/make_style.sh b/3rdparty/easyprofiler/scripts/make_style.sh new file mode 100755 index 0000000..6646ccd --- /dev/null +++ b/3rdparty/easyprofiler/scripts/make_style.sh @@ -0,0 +1,19 @@ +if [ "$#" -ne 1 ]; then + echo -e "Usage: \n$0 DIRECTORY\n\twhere DIRECTORY is a derectory with sources for styling" + exit 1 +fi + +if ! [ -x "$(command -v clang-format)" ]; then + echo 'Error: clang-format is not installed. Please install clang-format with minimal version 3.8' >&2 + exit 1 +fi + +DIR=$1 + +FILES=`find $DIR -name "*.h" -or -name "*.cpp"` + +for FILE in $FILES +do + echo "Set style for $FILE" + clang-format -i $FILE +done diff --git a/3rdparty/easyprofiler/scripts/test.sh b/3rdparty/easyprofiler/scripts/test.sh new file mode 100755 index 0000000..4072207 --- /dev/null +++ b/3rdparty/easyprofiler/scripts/test.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +unamestr=`uname` +SUBDIR="./bin" +if [[ ! "$unamestr" == 'Linux' ]]; then + SUBDIR="./bin/Release/" +fi + +DISABLED_PROF=$SUBDIR/profiler_sample_disabled_profiler +ENABLED_PROF=$SUBDIR/profiler_sample + +TEMP_FILE_ENABLE="enable.info" +TEMP_FILE_DISABLE="disable.info" +RESULT_FILE="result.csv" +RESULT_FILE_TMP="result.csv.tmp" + +HEADER="Blocks count, dT prof enabled usec, dT prof disabled usec,delta, usec/block" + +#echo "Blocks count, dT prof enabled usec, dT prof disabled usec,delta, usec/block" > $RESULT_FILE + +rm -rf $RESULT_FILE + +for i in {1..9} +do + OBJECTS_COUNT=$(($i*100)) + for j in {10..15} + do + RENDER_COUNT=$(($j*100)) + for k in {10..15} + do + MODELLING_COUNT=$(($k*100)) + $ENABLED_PROF $OBJECTS_COUNT $RENDER_COUNT $MODELLING_COUNT > $TEMP_FILE_ENABLE + $DISABLED_PROF $OBJECTS_COUNT $RENDER_COUNT $MODELLING_COUNT > $TEMP_FILE_DISABLE + DT_ENA=`cat $TEMP_FILE_ENABLE | grep Elapsed| awk '{print $3}'` + N_ENA=`cat $TEMP_FILE_ENABLE | grep Blocks| awk '{print $3}'` + N_DIS=`cat $TEMP_FILE_DISABLE | grep Elapsed| awk '{print $3}'` + + DELTA=$(($DT_ENA-$N_DIS)) + USEC_BLOCK=`awk "BEGIN{print $DELTA/$N_ENA}"` + + echo $N_ENA,$DT_ENA,$N_DIS,$DELTA,$USEC_BLOCK >> $RESULT_FILE + done + done + echo $i + +done + +cat $RESULT_FILE | sort > $RESULT_FILE_TMP + +echo $HEADER > $RESULT_FILE +cat $RESULT_FILE_TMP >> $RESULT_FILE + +rm -rf $TEMP_FILE_ENABLE +rm -rf $TEMP_FILE_DISABLE +rm -rf $RESULT_FILE_TMP + +echo "See result in $RESULT_FILE" diff --git a/3rdparty/zint-2.6.1/backend/emf.c b/3rdparty/zint-2.6.1/backend/emf.c index 94c1425..8e9ba0f 100644 --- a/3rdparty/zint-2.6.1/backend/emf.c +++ b/3rdparty/zint-2.6.1/backend/emf.c @@ -35,7 +35,10 @@ #include #include #include +#include +#ifdef _MSC_VER #include +#endif #include "common.h" #include "emf.h" diff --git a/common.pri b/common.pri index cfdac1c..cef5fe8 100644 --- a/common.pri +++ b/common.pri @@ -1,15 +1,41 @@ CONFIG *= build_translations +#CONFIG *= easy_profiler !contains(CONFIG, no_zint){ CONFIG *= zint } -!contains(CONFIG, qtscriptengine): -greaterThan(QT_MAJOR_VERSION, 4): -greaterThan(QT_MINOR_VERSION, 5){ - CONFIG *= qjsengine +INCLUDEPATH += $$PWD/3rdparty/easyprofiler/easy_profiler_core/include +DEPENDPATH += $$PWD/3rdparty/easyprofiler/easy_profiler_core/include + +contains(CONFIG, easy_profiler){ + message(EasyProfiler) + unix|win32: LIBS += -L$$PWD/3rdparty/easyprofiler/build/bin/ -leasy_profiler + greaterThan(QT_MAJOR_VERSION, 4){ + DEFINES += BUILD_WITH_EASY_PROFILER + } } +!contains(CONFIG, qtscriptengine){ +greaterThan(QT_MAJOR_VERSION, 4){ + CONFIG *= qjsengine +#greaterThan(QT_MINOR_VERSION, 5){ +# CONFIG *= qjsengine +#} +#lessThan(QT_MINOR_VERSION, 6){ +# CONFIG *= qtscriptengine +#} +} +lessThan(QT_MAJOR_VERSION, 5){ + CONFIG *= qtscriptengine +} +} + +contains(CONFIG, qtscriptengine){ + CONFIG -= qjsengine + QT *= script + message(qtscriptengine) +} !contains(CONFIG, no_formdesigner){ CONFIG *= dialogdesigner @@ -28,6 +54,7 @@ contains(CONFIG,zint){ greaterThan(QT_MAJOR_VERSION, 4) { QT *= uitools } + lessThan(QT_MAJOR_VERSION, 5){ CONFIG *= uitools } @@ -78,12 +105,13 @@ RCC_DIR = $${ARCH_DIR}/$${BUILD_TYPE}/rcc LIMEREPORT_VERSION_MAJOR = 1 LIMEREPORT_VERSION_MINOR = 4 -LIMEREPORT_VERSION_RELEASE = 63 +LIMEREPORT_VERSION_RELEASE = 78 LIMEREPORT_VERSION = '$${LIMEREPORT_VERSION_MAJOR}.$${LIMEREPORT_VERSION_MINOR}.$${LIMEREPORT_VERSION_RELEASE}' DEFINES *= LIMEREPORT_VERSION_STR=\\\"$${LIMEREPORT_VERSION}\\\" -QT *= script xml sql +QT *= xml sql + REPORT_PATH = $$PWD/limereport TRANSLATIONS_PATH = $$PWD/translations @@ -107,5 +135,3 @@ lessThan(QT_MAJOR_VERSION, 5){ DEFINES *= HAVE_UI_LOADER } } - - diff --git a/demo_r1/mainwindow.cpp b/demo_r1/mainwindow.cpp index 1c82c3a..7f35de3 100644 --- a/demo_r1/mainwindow.cpp +++ b/demo_r1/mainwindow.cpp @@ -38,6 +38,14 @@ #include #include +#ifdef BUILD_WITH_EASY_PROFILER +#include "easy/profiler.h" +#else +# define EASY_BLOCK(...) +# define EASY_END_BLOCK +# define EASY_PROFILER_ENABLE +#endif + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), m_progressDialog(0), m_customers(0), m_orders(0) @@ -110,22 +118,36 @@ MainWindow::~MainWindow() void MainWindow::on_pushButton_clicked() { + EASY_PROFILER_ENABLE; + EASY_BLOCK("design report"); report->dataManager()->clearUserVariables(); if (!ui->leVariableName->text().isEmpty() && !ui->leVariableValue->text().isEmpty()){ report->dataManager()->setReportVariable(ui->leVariableName->text(), ui->leVariableValue->text()); } report->setShowProgressDialog(false); report->designReport(); + EASY_END_BLOCK; +#ifdef BUILD_WITH_EASY_PROFILER + profiler::dumpBlocksToFile("test.prof"); +#endif } void MainWindow::on_pushButton_2_clicked() { QString fileName = QFileDialog::getOpenFileName(this,"Select report file",QApplication::applicationDirPath()+"/demo_reports/","*.lrxml"); if (!fileName.isEmpty()) { + EASY_PROFILER_ENABLE; + EASY_BLOCK("Load file"); report->loadFromFile(fileName); + EASY_END_BLOCK; + EASY_BLOCK("Set report variable"); if (!ui->leVariableName->text().isEmpty() && !ui->leVariableValue->text().isEmpty()){ report->dataManager()->setReportVariable(ui->leVariableName->text(), ui->leVariableValue->text()); } + EASY_END_BLOCK; +#ifdef BUILD_WITH_EASY_PROFILER + profiler::dumpBlocksToFile("test.prof"); +#endif report->previewReport(); } } diff --git a/designer/designer.pro b/designer/designer.pro index 4062e6f..d5abd1b 100644 --- a/designer/designer.pro +++ b/designer/designer.pro @@ -8,7 +8,8 @@ contains(CONFIG,release) { } TEMPLATE = app -SOURCES += main.cpp +SOURCES += main.cpp \ + designersettingmanager.cpp INCLUDEPATH += $$PWD/../include DEPENDPATH += $$PWD/../include @@ -61,3 +62,6 @@ win32 { } } +HEADERS += \ + designersettingmanager.h + diff --git a/designer/designersettingmanager.cpp b/designer/designersettingmanager.cpp new file mode 100644 index 0000000..6a353ab --- /dev/null +++ b/designer/designersettingmanager.cpp @@ -0,0 +1,39 @@ +#include "designersettingmanager.h" + +DesignerSettingManager::DesignerSettingManager(QObject *parent) : QObject(parent) +{ + m_setting = new QSettings("LimeReport",QCoreApplication::applicationName()); +} + +DesignerSettingManager::~DesignerSettingManager() +{ + delete m_setting; +} + +void DesignerSettingManager::getAviableLanguages(QList* languages) +{ + languages->append(QLocale::Russian); + languages->append(QLocale::English); + languages->append(QLocale::Arabic); + languages->append(QLocale::French); + languages->append(QLocale::Chinese); +} + +QLocale::Language DesignerSettingManager::getCurrentDefaultLanguage() +{ + m_setting->beginGroup("ReportDesigner"); + QVariant v = m_setting->value("DesignerLanguage"); + m_setting->endGroup(); + if (v.isValid()){ + return static_cast(v.toInt()) ; + } else { + return QLocale::system().language(); + } +} + +void DesignerSettingManager::currentDefaulLanguageChanged(QLocale::Language language) +{ + m_setting->beginGroup("ReportDesigner"); + m_setting->setValue("DesignerLanguage", (int)language); + m_setting->endGroup(); +} diff --git a/designer/designersettingmanager.h b/designer/designersettingmanager.h new file mode 100644 index 0000000..2f07d79 --- /dev/null +++ b/designer/designersettingmanager.h @@ -0,0 +1,27 @@ +#ifndef DESIGNERSETTINGMANAGER_H +#define DESIGNERSETTINGMANAGER_H + +#include +#include +#include +#include + +class DesignerSettingManager : public QObject +{ + Q_OBJECT +public: + explicit DesignerSettingManager(QObject *parent = 0); + ~DesignerSettingManager(); + void setApplicationInstance(QApplication* application); +signals: + +public slots: + void getAviableLanguages(QList* languages); + QLocale::Language getCurrentDefaultLanguage(); + void currentDefaulLanguageChanged(QLocale::Language language); +private: + QApplication* m_app; + QSettings* m_setting; +}; + +#endif // DESIGNERSETTINGMANAGER_H diff --git a/designer/main.cpp b/designer/main.cpp index 9132b47..3fcb4a9 100644 --- a/designer/main.cpp +++ b/designer/main.cpp @@ -1,25 +1,49 @@ #include #include #include +#include +#include "designersettingmanager.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); - - QTranslator limeReportTranslator; - QString translationPath = QApplication::applicationDirPath(); - translationPath.append("/languages"); - limeReportTranslator.load("limereport_"+QLocale::system().name(),translationPath); - a.installTranslator(&limeReportTranslator); - - QTranslator qtTranslator; - qtTranslator.load("qt_" + QLocale::system().name(),translationPath); - a.installTranslator(&qtTranslator); + DesignerSettingManager manager; LimeReport::ReportEngine report; + + QTranslator limeReportTranslator; + QTranslator qtTranslator; + QString translationPath = QApplication::applicationDirPath(); + translationPath.append("/languages"); + + QString designerTranslation = QLocale(manager.getCurrentDefaultLanguage()).name(); + + if (limeReportTranslator.load("limereport_"+designerTranslation, translationPath)){ + + qtTranslator.load("qt_" + designerTranslation, translationPath); + a.installTranslator(&qtTranslator); + a.installTranslator(&limeReportTranslator); + + Qt::LayoutDirection layoutDirection = QLocale(manager.getCurrentDefaultLanguage()).textDirection(); + + a.setLayoutDirection(layoutDirection); + report.setPreviewLayoutDirection(layoutDirection); + } + + if (a.arguments().count()>1){ report.loadFromFile(a.arguments().at(1)); } + QObject::connect(&report, SIGNAL(getAviableLanguages(QList*)), + &manager, SLOT(getAviableLanguages(QList*))); + + QObject::connect(&report, SIGNAL(getCurrentDefaultLanguage()), + &manager, SLOT(getCurrentDefaultLanguage())); + + QObject::connect(&report, SIGNAL(currentDefaulLanguageChanged(QLocale::Language)), + &manager, SLOT(currentDefaulLanguageChanged(QLocale::Language))); + report.designReport(); return a.exec(); } + diff --git a/include/lrglobal.cpp b/include/lrglobal.cpp index b4eb854..55ab31b 100644 --- a/include/lrglobal.cpp +++ b/include/lrglobal.cpp @@ -76,4 +76,13 @@ QVector normalizeCaptures(const QRegExp& reg){ return result; } +bool isColorDark(QColor color){ + qreal darkness = 1-(0.299*color.red() + 0.587*color.green() + 0.114*color.blue())/255; + if(darkness<0.5){ + return false; + } else { + return true; + } +} + } //namespace LimeReport diff --git a/include/lrglobal.h b/include/lrglobal.h index 4829390..b32d263 100644 --- a/include/lrglobal.h +++ b/include/lrglobal.h @@ -85,7 +85,7 @@ namespace Const{ const QString VARIABLE_RX = "\\$V\\s*\\{\\s*([^{}]*)\\s*\\}"; const QString NAMED_VARIABLE_RX = "\\$V\\s*\\{\\s*(%1)\\s*\\}"; const QString SCRIPT_RX = "\\$S\\s*\\{(.*)\\}"; - const QString GROUP_FUNCTION_PARAM_RX = "\\(\\s*((?:(?:\\\")|(?:))(?:(?:\\$(?:(?:D\\{\\s*\\w*.\\w*\\s*\\})|(?:V\\{\\s*\\w*\\s*\\})|(?:S\\{.+\\})))|(?:\\w*))(?:(?:\\\")|(?:)))(?:(?:\\s*,\\s*(?:\\\"(\\w*)\\\"))|(?:))\\)"; + const QString GROUP_FUNCTION_PARAM_RX = "\\(\\s*((?:(?:\\\")|(?:))(?:(?:\\$(?:(?:D\\{\\s*\\w*.\\w*\\s*\\})|(?:V\\{\\s*\\w*\\s*\\})|(?:S\\{.+\\})))|(?:\\w*))(?:(?:\\\")|(?:)))(?:(?:\\s*,\\s*(?:\\\"(\\w*)\\\"))|(?:))(?:(?:\\s*,\\s*(?:(\\w*)))|(?:))\\)"; const int DATASOURCE_INDEX = 3; const int VALUE_INDEX = 2; const int EXPRESSION_ARGUMENT_INDEX = 1; @@ -101,6 +101,7 @@ namespace Const{ QString escapeSimbols(const QString& value); QString replaceHTMLSymbols(const QString &value); QVector normalizeCaptures(const QRegExp ®); + bool isColorDark(QColor color); enum ExpandType {EscapeSymbols, NoEscapeSymbols, ReplaceHTMLSymbols}; enum RenderPass {FirstPass = 1, SecondPass = 2}; @@ -137,13 +138,12 @@ namespace Const{ #endif #ifdef USE_QJSENGINE - typedef QQmlEngine ScriptEngineType; + typedef QJSEngine ScriptEngineType; typedef QJSValue ScriptValueType; template - static inline QJSValue getCppOwnedJSValue(QJSEngine &e, T *p) + static inline QJSValue getJSValue(QJSEngine &e, T *p) { QJSValue res = e.newQObject(p); - QQmlEngine::setObjectOwnership(p, QQmlEngine::CppOwnership); return res; } #else diff --git a/include/lrreportengine.h b/include/lrreportengine.h index 589e359..a4c1158 100644 --- a/include/lrreportengine.h +++ b/include/lrreportengine.h @@ -113,6 +113,8 @@ public: bool setReportLanguage(QLocale::Language language); Qt::LayoutDirection previewLayoutDirection(); void setPreviewLayoutDirection(const Qt::LayoutDirection& previewLayoutDirection); + QList designerLanguages(); + QLocale::Language currentDesignerLanguage(); signals: void renderStarted(); void renderFinished(); @@ -120,6 +122,14 @@ signals: void onLoad(bool& loaded); void onSave(); void saveFinished(); + + void loaded(); + void printedToPDF(QString fileName); + + void getAviableLanguages(QList* languages); + void currentDefaulLanguageChanged(QLocale::Language); + QLocale::Language getCurrentDefaultLanguage(); + public slots: void cancelRender(); protected: diff --git a/limereport/bands/lrdataband.cpp b/limereport/bands/lrdataband.cpp index 4a67e58..d6a4b94 100644 --- a/limereport/bands/lrdataband.cpp +++ b/limereport/bands/lrdataband.cpp @@ -83,7 +83,7 @@ void DataBand::preparePopUpMenu(QMenu &menu) { DataBandDesignIntf::preparePopUpMenu(menu); - QAction* autoSplittableAction = menu.addAction(tr("useAlternateBackgroundColor")); + QAction* autoSplittableAction = menu.addAction(tr("Use alternate background color")); autoSplittableAction->setCheckable(true); autoSplittableAction->setChecked(useAlternateBackgroundColor()); } @@ -91,7 +91,7 @@ void DataBand::preparePopUpMenu(QMenu &menu) void DataBand::processPopUpAction(QAction *action) { DataBandDesignIntf::processPopUpAction(action); - if (action->text().compare(tr("useAlternateBackgroundColor")) == 0){ + if (action->text().compare(tr("Use alternate background color")) == 0){ setProperty("useAlternateBackgroundColor",action->isChecked()); } } diff --git a/limereport/bands/lrpagefooter.cpp b/limereport/bands/lrpagefooter.cpp index c5be0fb..a6b304f 100644 --- a/limereport/bands/lrpagefooter.cpp +++ b/limereport/bands/lrpagefooter.cpp @@ -30,6 +30,7 @@ #include "lrpagefooter.h" #include "lrdesignelementsfactory.h" #include "lrglobal.h" +#include "lrpagedesignintf.h" const QString xmlTag ="PageFooter"; @@ -62,7 +63,29 @@ BaseDesignIntf *PageFooter::createSameTypeItem(QObject *owner, QGraphicsItem *pa QColor PageFooter::bandColor() const { - return QColor(246,120,12); + return QColor(246,120,12); +} + +void PageFooter::preparePopUpMenu(QMenu &menu) +{ + QAction* action = menu.addAction(tr("Print on first page")); + action->setCheckable(true); + action->setChecked(printOnFirstPage()); + + action = menu.addAction(tr("Print on last page")); + action->setCheckable(true); + action->setChecked(printOnLastPage()); + +} + +void PageFooter::processPopUpAction(QAction *action) +{ + if (action->text().compare(tr("Print on first page")) == 0){ + page()->setPropertyToSelectedItems("printOnFirstPage",action->isChecked()); + } + if (action->text().compare(tr("Print on last page")) == 0){ + page()->setPropertyToSelectedItems("printOnLastPage",action->isChecked()); + } } bool PageFooter::printOnFirstPage() const @@ -72,7 +95,12 @@ bool PageFooter::printOnFirstPage() const void PageFooter::setPrintOnFirstPage(bool printOnFirstPage) { - m_printOnFirstPage = printOnFirstPage; + if (m_printOnFirstPage != printOnFirstPage){ + bool oldValue = m_printOnFirstPage; + m_printOnFirstPage = printOnFirstPage; + update(); + notify("printOnFirstPage",oldValue,printOnFirstPage); + } } bool PageFooter::printOnLastPage() const @@ -82,7 +110,12 @@ bool PageFooter::printOnLastPage() const void PageFooter::setPrintOnLastPage(bool printOnLastPage) { - m_printOnLastPage = printOnLastPage; + if (m_printOnLastPage != printOnLastPage){ + bool oldValue = m_printOnLastPage; + m_printOnLastPage = printOnLastPage; + update(); + notify("printOnLastPage",oldValue,printOnLastPage); + } } } // namespace LimeReport diff --git a/limereport/bands/lrpagefooter.h b/limereport/bands/lrpagefooter.h index d6678a3..2f634a5 100644 --- a/limereport/bands/lrpagefooter.h +++ b/limereport/bands/lrpagefooter.h @@ -50,7 +50,9 @@ public: void setPrintOnFirstPage(bool printOnFirstPage); protected: - QColor bandColor() const; + QColor bandColor() const; + void preparePopUpMenu(QMenu &menu); + void processPopUpAction(QAction *action); private: bool m_printOnFirstPage; bool m_printOnLastPage; diff --git a/limereport/databrowser/lrdatabrowser.cpp b/limereport/databrowser/lrdatabrowser.cpp index 3dcd795..50eaa6a 100644 --- a/limereport/databrowser/lrdatabrowser.cpp +++ b/limereport/databrowser/lrdatabrowser.cpp @@ -96,31 +96,10 @@ void DataBrowser::slotAddConnection() void DataBrowser::slotSQLEditingFinished(SQLEditResult result) { if (result.dialogMode==SQLEditDialog::AddMode) { - switch (result.resultMode) { - case SQLEditResult::Query: - addQuery(result); - break; - case SQLEditResult::SubQuery: - addSubQuery(result); - break; - case SQLEditResult::SubProxy: - addProxy(result); - default: - break; - } + addDatasource(result); } else { - switch(result.resultMode){ - case SQLEditResult::Query: - changeQuery(result); - break; - case SQLEditResult::SubQuery: - changeSubQuery(result); - break; - case SQLEditResult::SubProxy: - changeProxy(result); - } + applyChanges(result); } - updateDataTree(); } @@ -661,6 +640,50 @@ void DataBrowser::changeProxy(SQLEditResult result) } } +SQLEditResult::ResultMode DataBrowser::currentDatasourceType(const QString& datasourceName) +{ + if (m_report->dataManager()->isQuery(datasourceName)) return SQLEditResult::Query; + if (m_report->dataManager()->isSubQuery(datasourceName)) return SQLEditResult::SubQuery; + if (m_report->dataManager()->isProxy(datasourceName)) return SQLEditResult::SubProxy; + return SQLEditResult::Undefined; +} + + +void DataBrowser::applyChanges(SQLEditResult result) +{ + if (result.resultMode == currentDatasourceType(result.datasourceName)){ + switch(result.resultMode){ + case SQLEditResult::Query: + changeQuery(result); + break; + case SQLEditResult::SubQuery: + changeSubQuery(result); + break; + case SQLEditResult::SubProxy: + changeProxy(result); + } + } else { + removeDatasource(result.datasourceName); + addDatasource(result); + } +} + +void DataBrowser::addDatasource(SQLEditResult result) +{ + switch (result.resultMode) { + case SQLEditResult::Query: + addQuery(result); + break; + case SQLEditResult::SubQuery: + addSubQuery(result); + break; + case SQLEditResult::SubProxy: + addProxy(result); + default: + break; + } +} + void DataBrowser::addConnectionDesc(ConnectionDesc *connection) { m_report->dataManager()->addConnectionDesc(connection); diff --git a/limereport/databrowser/lrdatabrowser.h b/limereport/databrowser/lrdatabrowser.h index 7d0a91e..3e4bfd3 100644 --- a/limereport/databrowser/lrdatabrowser.h +++ b/limereport/databrowser/lrdatabrowser.h @@ -106,6 +106,11 @@ private: void addProxy(SQLEditResult result); void changeProxy(SQLEditResult result); + + SQLEditResult::ResultMode currentDatasourceType(const QString& datasourceName); + void applyChanges(SQLEditResult result); + void addDatasource(SQLEditResult result); + void addConnectionDesc(ConnectionDesc *connection); void changeConnectionDesc(ConnectionDesc *connection); bool checkConnectionDesc(ConnectionDesc *connection); diff --git a/limereport/databrowser/lrsqleditdialog.cpp b/limereport/databrowser/lrsqleditdialog.cpp index f74ae6c..ae0d297 100644 --- a/limereport/databrowser/lrsqleditdialog.cpp +++ b/limereport/databrowser/lrsqleditdialog.cpp @@ -179,7 +179,7 @@ void SQLEditDialog::setDataSources(LimeReport::DataSourceManager *dataSources, Q { m_datasources=dataSources; if (!datasourceName.isEmpty()){ - ui->cbSubdetail->setEnabled(false); + ui->cbSubdetail->setEnabled(true); initQueryMode(); m_oldDatasourceName=datasourceName; ui->leDatasourceName->setText(datasourceName); @@ -278,7 +278,6 @@ void SQLEditDialog::initSubQueryMode() ui->leMaster->setVisible(true); ui->leMaster->setEnabled(true); ui->lbMaster->setVisible(true); - } void SQLEditDialog::initProxyMode() diff --git a/limereport/databrowser/lrsqleditdialog.h b/limereport/databrowser/lrsqleditdialog.h index 269f3f4..0f6759a 100644 --- a/limereport/databrowser/lrsqleditdialog.h +++ b/limereport/databrowser/lrsqleditdialog.h @@ -95,7 +95,7 @@ private: }; struct SQLEditResult{ - enum ResultMode{Query,SubQuery,SubProxy}; + enum ResultMode{Query, SubQuery, SubProxy, Undefined}; QString connectionName; QString datasourceName; QString oldDatasourceName; diff --git a/limereport/images/object.png b/limereport/images/object.png new file mode 100644 index 0000000..807c2a8 Binary files /dev/null and b/limereport/images/object.png differ diff --git a/limereport/images/property.png b/limereport/images/property.png new file mode 100644 index 0000000..4e4efaa Binary files /dev/null and b/limereport/images/property.png differ diff --git a/limereport/images/signal.png b/limereport/images/signal.png new file mode 100644 index 0000000..864c27d Binary files /dev/null and b/limereport/images/signal.png differ diff --git a/limereport/items/editors/lrfonteditorwidget.cpp b/limereport/items/editors/lrfonteditorwidget.cpp index 28212f5..be54949 100644 --- a/limereport/items/editors/lrfonteditorwidget.cpp +++ b/limereport/items/editors/lrfonteditorwidget.cpp @@ -106,9 +106,9 @@ bool FontEditorWidget::ignoreSlots() const } -void FontEditorWidget::slotFontChanged(const QFont /*&font*/) +void FontEditorWidget::slotFontChanged(const QFont& /*font*/) { - // if (page()) page()->setFont(font); + //if (page()) page()->setFont(font); } void FontEditorWidget::slotFontSizeChanged(const QString &value) @@ -122,6 +122,7 @@ void FontEditorWidget::slotFontAttribsChanged(bool) { if (m_ignoreSlots) return; m_resFont = m_fontNameEditor->currentFont(); + m_resFont.setPointSize(m_fontSizeEditor->currentText().toInt()); m_resFont.setBold(m_fontBold->isChecked()); m_resFont.setItalic(m_fontItalic->isChecked()); m_resFont.setUnderline(m_fontUnderline->isChecked()); diff --git a/limereport/items/editors/lrfonteditorwidget.h b/limereport/items/editors/lrfonteditorwidget.h index c5de288..d319ff0 100644 --- a/limereport/items/editors/lrfonteditorwidget.h +++ b/limereport/items/editors/lrfonteditorwidget.h @@ -53,7 +53,7 @@ protected: QFontComboBox* fontNameEditor(){return m_fontNameEditor;} virtual void initEditor(); protected slots: - virtual void slotFontChanged(const QFont); + virtual void slotFontChanged(const QFont&); virtual void slotFontSizeChanged(const QString& value); virtual void slotFontAttribsChanged(bool); void slotPropertyChanged(const QString& objectName, const QString& property, const QVariant &oldValue, const QVariant &newValue); diff --git a/limereport/items/lrimageitem.cpp b/limereport/items/lrimageitem.cpp index 1233ac0..a271cda 100644 --- a/limereport/items/lrimageitem.cpp +++ b/limereport/items/lrimageitem.cpp @@ -31,6 +31,7 @@ #include "lrdesignelementsfactory.h" #include "lrglobal.h" #include "lrdatasourcemanager.h" +#include "lrpagedesignintf.h" namespace{ @@ -54,6 +55,42 @@ BaseDesignIntf *ImageItem::createSameTypeItem(QObject *owner, QGraphicsItem *par return new ImageItem(owner,parent); } +void ImageItem::loadPictureFromVariant(QVariant& data){ + if (data.isValid()){ + if (data.type()==QVariant::Image){ + m_picture = data.value(); + } else { + switch (m_format) { + default: + case Binary: + m_picture.loadFromData(data.toByteArray()); + break; + case Hex: + m_picture.loadFromData(QByteArray::fromHex(data.toByteArray())); + break; + case Base64: + m_picture.loadFromData(QByteArray::fromBase64(data.toByteArray())); + break; + } + } + + } +} + +void ImageItem::preparePopUpMenu(QMenu &menu) +{ + QAction* action = menu.addAction(tr("Watermark")); + action->setCheckable(true); + action->setChecked(isWatermark()); +} + +void ImageItem::processPopUpAction(QAction *action) +{ + if (action->text().compare(tr("Watermark")) == 0){ + page()->setPropertyToSelectedItems("watermark",action->isChecked()); + } +} + void ImageItem::updateItemSize(DataSourceManager* dataManager, RenderPass pass, int maxHeight) { @@ -62,28 +99,13 @@ void ImageItem::updateItemSize(DataSourceManager* dataManager, RenderPass pass, IDataSource* ds = dataManager->dataSource(m_datasource); if (ds) { QVariant data = ds->data(m_field); - if (data.isValid()){ - if (data.type()==QVariant::Image){ - m_picture = data.value(); - } else { - switch (m_format) { - default: - case Binary: - m_picture.loadFromData(data.toByteArray()); - break; - case Hex: - m_picture.loadFromData(QByteArray::fromHex(data.toByteArray())); - break; - case Base64: - m_picture.loadFromData(QByteArray::fromBase64(data.toByteArray())); - break; - } - } - - } + loadPictureFromVariant(data); } } else if (!m_resourcePath.isEmpty()){ m_picture = QImage(m_resourcePath); + } else if (!m_variable.isEmpty()){ + QVariant data = dataManager->variable(m_variable); + loadPictureFromVariant(data); } } if (m_autoSize){ @@ -112,6 +134,16 @@ qreal ImageItem::minHeight() const{ } } +void ImageItem::setVariable(const QString& content) +{ + if (m_variable!=content){ + QString oldValue = m_variable; + m_variable=content; + update(); + notify("content", oldValue, m_variable); + } +} + bool ImageItem::center() const { return m_center; diff --git a/limereport/items/lrimageitem.h b/limereport/items/lrimageitem.h index 18b236f..f6713df 100644 --- a/limereport/items/lrimageitem.h +++ b/limereport/items/lrimageitem.h @@ -47,6 +47,8 @@ class ImageItem : public LimeReport::ItemDesignIntf Q_PROPERTY(bool keepAspectRatio READ keepAspectRatio WRITE setKeepAspectRatio) Q_PROPERTY(bool center READ center WRITE setCenter) Q_PROPERTY(QString resourcePath READ resourcePath WRITE setResourcePath) + Q_PROPERTY(QString variable READ variable WRITE setVariable) + Q_PROPERTY(bool watermark READ isWatermark WRITE setWatermark) public: enum Format { Binary = 0, @@ -75,14 +77,18 @@ public: void setCenter(bool center); Format format() const; void setFormat(Format format); - qreal minHeight() const; + QString variable(){ return m_variable;} + void setVariable(const QString& variable); protected: BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent); void updateItemSize(DataSourceManager *dataManager, RenderPass pass, int maxHeight); bool isNeedUpdateSize(RenderPass) const; bool drawDesignBorders() const {return m_picture.isNull();} + void loadPictureFromVariant(QVariant& data); + void preparePopUpMenu(QMenu &menu); + void processPopUpAction(QAction *action); private: QImage m_picture; QString m_resourcePath; @@ -93,6 +99,8 @@ private: bool m_keepAspectRatio; bool m_center; Format m_format; + QString m_variable; + }; } diff --git a/limereport/items/lrsubitemparentpropitem.cpp b/limereport/items/lrsubitemparentpropitem.cpp index 1587bc9..b02cfa7 100644 --- a/limereport/items/lrsubitemparentpropitem.cpp +++ b/limereport/items/lrsubitemparentpropitem.cpp @@ -77,8 +77,8 @@ void LimeReport::ItemLocationPropItem::setPropertyEditorData(QWidget *propertyEd } void LimeReport::ItemLocationPropItem::setModelData(QWidget *propertyEditor, QAbstractItemModel *model, const QModelIndex &index){ - object()->setProperty(propertyName().toLatin1(),stringToLocation(qobject_cast(propertyEditor)->text())); model->setData(index,object()->property(propertyName().toLatin1())); + setValueToObject(propertyName(), propertyValue()); } QString LimeReport::ItemLocationPropItem::locationToString(LimeReport::ItemDesignIntf::LocationType location) const{ @@ -92,6 +92,5 @@ LimeReport::ItemDesignIntf::LocationType LimeReport::ItemLocationPropItem::strin void LimeReport::ItemLocationPropItem::slotLocationChanged(const QString &text){ if ( locationToString(object()->property(propertyName().toLatin1()).toInt())!=text){ object()->setProperty(propertyName().toLatin1(),stringToLocation(text)); - dynamic_cast(sender())->setTextValue(locationToString(object()->property(propertyName().toLatin1()).toInt())); } } diff --git a/limereport/items/lrtextitem.cpp b/limereport/items/lrtextitem.cpp index 9bd5b0e..a022147 100644 --- a/limereport/items/lrtextitem.cpp +++ b/limereport/items/lrtextitem.cpp @@ -29,7 +29,9 @@ ****************************************************************************/ #include #include +#ifndef USE_QJSENGINE #include +#endif #include #include #include @@ -109,6 +111,9 @@ void TextItem::preparePopUpMenu(QMenu &menu) action->setCheckable(true); action->setChecked(backgroundMode() == TransparentMode); + action = menu.addAction(tr("Watermark")); + action->setCheckable(true); + action->setChecked(isWatermark()); } void TextItem::processPopUpAction(QAction *action) @@ -135,6 +140,9 @@ void TextItem::processPopUpAction(QAction *action) setProperty("backgroundMode",OpaqueMode); } } + if (action->text().compare(tr("Watermark")) == 0){ + page()->setPropertyToSelectedItems("watermark",action->isChecked()); + } } void TextItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* style, QWidget* widget) { @@ -553,6 +561,15 @@ void TextItem::setTextLayoutDirection(const Qt::LayoutDirection &textLayoutDirec } } +void TextItem::setWatermark(bool watermark) +{ + if (watermark){ + setBackgroundMode(TransparentMode); + } + BaseDesignIntf::setWatermark(watermark); + +} + QString TextItem::followTo() const { diff --git a/limereport/items/lrtextitem.h b/limereport/items/lrtextitem.h index a892202..778bfb7 100644 --- a/limereport/items/lrtextitem.h +++ b/limereport/items/lrtextitem.h @@ -72,6 +72,7 @@ class TextItem : public LimeReport::ContentItemDesignIntf, IPageInit { Q_PROPERTY(qreal textIndent READ textIndent WRITE setTextIndent) Q_PROPERTY(Qt::LayoutDirection textLayoutDirection READ textLayoutDirection WRITE setTextLayoutDirection) Q_PROPERTY(bool fillInSecondPass READ fillInSecondPass WRITE setFillInSecondPass) + Q_PROPERTY(bool watermark READ isWatermark WRITE setWatermark) public: enum AutoWidth{NoneAutoWidth,MaxWordLength,MaxStringLength}; @@ -164,6 +165,8 @@ public: void setTextIndent(const qreal &textIndent); Qt::LayoutDirection textLayoutDirection() const; void setTextLayoutDirection(const Qt::LayoutDirection &textLayoutDirection); + + void setWatermark(bool watermark); protected: void updateLayout(); diff --git a/limereport/limereport.pro b/limereport/limereport.pro index e97c92b..f9a6480 100644 --- a/limereport/limereport.pro +++ b/limereport/limereport.pro @@ -28,7 +28,7 @@ DEFINES += LIMEREPORT_EXPORTS contains(CONFIG, staticlib){ DEFINES += HAVE_STATIC_BUILD - message(Static Build) + message(STATIC_BUILD) DEFINES -= LIMEREPORT_EXPORTS } @@ -90,7 +90,7 @@ contains(CONFIG,zint){ ####Automatically build required translation files (*.qm) contains(CONFIG,build_translations){ - LANGUAGES = ru es_ES ar fr + LANGUAGES = ru es_ES ar fr zh defineReplace(prependAll) { for(a,$$1):result += $$2$${a}$$3 @@ -102,10 +102,10 @@ contains(CONFIG,build_translations){ qtPrepareTool(LUPDATE, lupdate) greaterThan(QT_MAJOR_VERSION, 4) { - ts.commands = $$LUPDATE $$shell_quote($$PWD) -ts $$TRANSLATIONS + ts.commands = $$LUPDATE $$shell_quote($$PWD) -no-obsolete -ts $$TRANSLATIONS } lessThan(QT_MAJOR_VERSION, 5){ - ts.commands = $$LUPDATE $$quote($$PWD) -ts $$TRANSLATIONS + ts.commands = $$LUPDATE $$quote($$PWD) -no-obsolete -ts $$TRANSLATIONS } TRANSLATIONS_FILES = qtPrepareTool(LRELEASE, lrelease) diff --git a/limereport/lrbanddesignintf.cpp b/limereport/lrbanddesignintf.cpp index 1d790cc..af9e324 100644 --- a/limereport/lrbanddesignintf.cpp +++ b/limereport/lrbanddesignintf.cpp @@ -191,14 +191,27 @@ void BandDesignIntf::copyBookmarks(BandDesignIntf* sourceBand) } } +void BandDesignIntf::setBackgroundModeProperty(BaseDesignIntf::BGMode value) +{ + if (value!=backgroundMode()){ + BaseDesignIntf::BGMode oldValue = backgroundMode(); + setBackgroundMode(value); + notify("backgroundMode",oldValue,value); + } +} + +void BandDesignIntf::setBackgroundOpacity(int value) +{ + if (opacity()!=value){ + int oldValue = opacity(); + setOpacity(value); + notify("backgroundOpacity",oldValue,value); + } +} + void BandDesignIntf::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - - if ( !(backgroundColor() == Qt::white && backgroundBrushStyle() == SolidPattern) ) { - QBrush brush(backgroundColor(), static_cast(backgroundBrushStyle())); - brush.setTransform(painter->worldTransform().inverted()); - painter->fillRect(rect(), brush); - } + prepareRect(painter, option, widget); if (itemMode() & DesignMode){ painter->save(); @@ -614,7 +627,7 @@ BaseDesignIntf *BandDesignIntf::cloneBottomPart(int height, QObject *owner, QGra BaseDesignIntf* tmpItem = item->cloneItem(item->itemMode(),bottomPart,bottomPart); tmpItem->setPos(tmpItem->pos().x(), (tmpItem->pos().y()-height)+borderLineSize()); } - else if ((item->geometry().top()geometry().bottom()>height)){ + else if ((item->geometry().top()<=height) && (item->geometry().bottom()>height)){ int sliceHeight = height-item->geometry().top(); if (item->isSplittable() && item->canBeSplitted(sliceHeight)) { BaseDesignIntf* tmpItem=item->cloneBottomPart(sliceHeight,bottomPart,bottomPart); @@ -626,7 +639,7 @@ BaseDesignIntf *BandDesignIntf::cloneBottomPart(int height, QObject *owner, QGra moveItemsDown(item->pos().y()+item->height(), sizeOffset + bottomOffset); } } else { - if ((item->geometry().bottom()-height)>height){ + if ((item->geometry().bottom()-height)>=height){ BaseDesignIntf* tmpItem = item->cloneItem(item->itemMode(),bottomPart,bottomPart); tmpItem->setPos(tmpItem->pos().x(),borderLineSize()); tmpItem->setHeight((this->height()-height)); diff --git a/limereport/lrbanddesignintf.h b/limereport/lrbanddesignintf.h index 44def94..f3750b0 100644 --- a/limereport/lrbanddesignintf.h +++ b/limereport/lrbanddesignintf.h @@ -106,6 +106,8 @@ class BandDesignIntf : public ItemsContainerDesignInft Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) Q_PROPERTY(BrushStyle backgroundBrushStyle READ backgroundBrushStyle WRITE setBackgroundBrushStyle) Q_PROPERTY(bool printIfEmpty READ printIfEmpty WRITE setPrintIfEmpty) + Q_PROPERTY(BGMode backgroundMode READ backgroundMode WRITE setBackgroundModeProperty) + Q_PROPERTY(int backgroundOpacity READ opacity WRITE setBackgroundOpacity) Q_ENUMS(BandColumnsLayoutType) friend class BandMarker; friend class BandNameLabel; @@ -243,6 +245,8 @@ public: void setUseAlternateBackgroundColor(bool useAlternateBackgroundColor); void replaceGroupsFunction(BandDesignIntf *band); qreal bottomSpace() const; + void setBackgroundModeProperty(BGMode value); + void setBackgroundOpacity(int value); void addBookmark(const QString& key, const QVariant& value){ m_bookmarks.insert(key, value);} QList bookmarks(){ return m_bookmarks.keys();} diff --git a/limereport/lrbasedesignintf.cpp b/limereport/lrbasedesignintf.cpp index 6ef5a34..520b893 100644 --- a/limereport/lrbasedesignintf.cpp +++ b/limereport/lrbasedesignintf.cpp @@ -80,7 +80,8 @@ BaseDesignIntf::BaseDesignIntf(const QString &storageTypeName, QObject *owner, Q m_reportSettings(0), m_patternName(""), m_patternItem(0), - m_fillInSecondPass(false) + m_fillInSecondPass(false), + m_watermark(false) { setGeometry(QRectF(0, 0, m_width, m_height)); if (BaseDesignIntf *item = dynamic_cast(parent)) { @@ -399,19 +400,21 @@ void BaseDesignIntf::prepareRect(QPainter *painter, const QStyleOptionGraphicsIt { painter->save(); + QRectF r = rect().adjusted(0, 0, borderLineSize(), borderLineSize()); QBrush brush(m_backgroundColor,static_cast(m_backgroundBrushStyle)); brush.setTransform(painter->worldTransform().inverted()); if (isSelected() && (opacity() == 100) && (m_BGMode!=TransparentMode)) { - painter->fillRect(rect(), brush); + painter->fillRect(r, brush); } else { if (m_BGMode == OpaqueMode) { - painter->setOpacity(qreal(m_opacity) / 100); - painter->fillRect(rect(), brush); + qreal o = (itemMode() & DesignMode) ? 0.5 : qreal(m_opacity) / 100; + painter->setOpacity(o); + painter->fillRect(r, brush); } else if (itemMode() & DesignMode){ painter->setOpacity(0.1); - painter->fillRect(rect(), QBrush(QPixmap(":/report/images/empty"))); + painter->fillRect(r, QBrush(QPixmap(":/report/images/empty"))); } } painter->restore(); @@ -716,6 +719,19 @@ void BaseDesignIntf::setFillInSecondPass(bool fillInSecondPass) } +bool BaseDesignIntf::isWatermark() const +{ + return m_watermark; +} + +void BaseDesignIntf::setWatermark(bool watermark) +{ + if (m_watermark != watermark){ + m_watermark = watermark; + notify("watermark",!watermark,watermark); + } +} + QString BaseDesignIntf::patternName() const { return (m_patternName.isEmpty()) ? objectName() : m_patternName; @@ -1169,6 +1185,7 @@ void BaseDesignIntf::showEditorDialog(){ dialog->layout()->setContentsMargins(2,2,2,2); dialog->layout()->addWidget(editor); connect(editor,SIGNAL(destroyed()),dialog,SLOT(close())); + dialog->setWindowTitle(editor->windowTitle()); dialog->exec(); #endif } @@ -1186,6 +1203,7 @@ void BaseDesignIntf::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) void BaseDesignIntf::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + if (!(flags() & QGraphicsItem::ItemIsSelectable)) return; PageDesignIntf* page = dynamic_cast(scene()); if (!page->selectedItems().contains(this)){ page->clearSelection(); diff --git a/limereport/lrbasedesignintf.h b/limereport/lrbasedesignintf.h index 3f41318..af80022 100644 --- a/limereport/lrbasedesignintf.h +++ b/limereport/lrbasedesignintf.h @@ -279,6 +279,8 @@ public: virtual QMap getStringForTranslation(); bool fillInSecondPass() const; void setFillInSecondPass(bool fillInSecondPass); + bool isWatermark() const; + virtual void setWatermark(bool watermark); Q_INVOKABLE QString setItemWidth(qreal width); Q_INVOKABLE QString setItemHeight(qreal height); @@ -408,6 +410,7 @@ private: QString m_patternName; BaseDesignIntf* m_patternItem; bool m_fillInSecondPass; + bool m_watermark; signals: void geometryChanged(QObject* object, QRectF newGeometry, QRectF oldGeometry); diff --git a/limereport/lrdatadesignintf.cpp b/limereport/lrdatadesignintf.cpp index f3add56..2cde1d9 100644 --- a/limereport/lrdatadesignintf.cpp +++ b/limereport/lrdatadesignintf.cpp @@ -70,7 +70,7 @@ bool QueryHolder::runQuery(IDataSource::DatasourceMode mode) m_mode = mode; QSqlDatabase db = QSqlDatabase::database(m_connectionName); - QSqlQuery* query = new QSqlQuery(db); + QSqlQuery query(db); if (!db.isValid()) { setLastError(QObject::tr("Invalid connection! %1").arg(m_connectionName)); @@ -82,13 +82,12 @@ bool QueryHolder::runQuery(IDataSource::DatasourceMode mode) if (!m_prepared) return false; } - query->prepare(m_preparedSQL); - - fillParams(query); - query->exec(); + query.prepare(m_preparedSQL); + fillParams(&query); + query.exec(); QSqlQueryModel *model = new QSqlQueryModel; - model->setQuery(*query); + model->setQuery(query); while (model->canFetchMore()) model->fetchMore(); @@ -508,7 +507,7 @@ QObject *ProxyDesc::elementAt(const QString &collectionName, int index) ProxyHolder::ProxyHolder(ProxyDesc* desc, DataSourceManager* dataManager) :m_model(0), m_desc(desc), m_lastError(""), m_mode(IDataSource::RENDER_MODE), - m_invalid(false), m_dataManger(dataManager) + m_invalid(false), m_dataManager(dataManager) {} QString ProxyHolder::masterDatasource() diff --git a/limereport/lrdatadesignintf.h b/limereport/lrdatadesignintf.h index 3486549..2336ad5 100644 --- a/limereport/lrdatadesignintf.h +++ b/limereport/lrdatadesignintf.h @@ -337,7 +337,7 @@ public: void invalidate(IDataSource::DatasourceMode mode, bool dbWillBeClosed = false); void update(){} void clearErrors(){m_lastError = "";} - DataSourceManager* dataManager() const {return m_dataManger;} + DataSourceManager* dataManager() const {return m_dataManager;} private slots: void slotChildModelDestoroyed(); private: @@ -347,7 +347,7 @@ private: QString m_lastError; IDataSource::DatasourceMode m_mode; bool m_invalid; - DataSourceManager* m_dataManger; + DataSourceManager* m_dataManager; }; class ModelToDataSource : public QObject, public IDataSource{ diff --git a/limereport/lrdatasourcemanager.cpp b/limereport/lrdatasourcemanager.cpp index 1c14081..ad1cd58 100644 --- a/limereport/lrdatasourcemanager.cpp +++ b/limereport/lrdatasourcemanager.cpp @@ -37,6 +37,13 @@ #include #include +#ifdef BUILD_WITH_EASY_PROFILER +#include "easy/profiler.h" +#else +# define EASY_BLOCK(...) +# define EASY_END_BLOCK +#endif + namespace LimeReport{ DataNode::~DataNode() @@ -217,7 +224,7 @@ void DataSourceModel::updateModel() } DataSourceManager::DataSourceManager(QObject *parent) : - QObject(parent), m_lastError(""), m_designTime(true), m_needUpdate(false), m_dbCredentialsProvider(0) + QObject(parent), m_lastError(""), m_designTime(false), m_needUpdate(false), m_dbCredentialsProvider(0) { m_groupFunctionFactory.registerFunctionCreator(QLatin1String("COUNT"),new ConstructorGroupFunctionCreator); m_groupFunctionFactory.registerFunctionCreator(QLatin1String("SUM"),new ConstructorGroupFunctionCreator); @@ -1104,11 +1111,11 @@ QObject* DataSourceManager::elementAt(const QString &collectionName, int index) void DataSourceManager::collectionLoadFinished(const QString &collectionName) { - + EASY_BLOCK("DataSourceManager::collectionLoadFinished"); if (collectionName.compare("connections",Qt::CaseInsensitive) == 0){ } - + EASY_BLOCK("queryes"); if (collectionName.compare("queries",Qt::CaseInsensitive) == 0){ QMutableListIterator it(m_queries); @@ -1125,7 +1132,8 @@ void DataSourceManager::collectionLoadFinished(const QString &collectionName) } } - + EASY_END_BLOCK; + EASY_BLOCK("subqueries") if (collectionName.compare("subqueries",Qt::CaseInsensitive) == 0){ QMutableListIterator it(m_subqueries); @@ -1147,7 +1155,8 @@ void DataSourceManager::collectionLoadFinished(const QString &collectionName) } } - + EASY_END_BLOCK; + EASY_BLOCK("subproxies"); if (collectionName.compare("subproxies",Qt::CaseInsensitive) == 0){ QMutableListIterator it(m_proxies); while (it.hasNext()){ @@ -1160,7 +1169,8 @@ void DataSourceManager::collectionLoadFinished(const QString &collectionName) } } } - + EASY_END_BLOCK; + EASY_BLOCK("variables"); if (collectionName.compare("variables",Qt::CaseInsensitive) == 0){ foreach (VarDesc* item, m_tempVars) { if (!m_reportVariables.containsVariable(item->name())){ @@ -1172,9 +1182,16 @@ void DataSourceManager::collectionLoadFinished(const QString &collectionName) } m_tempVars.clear(); } - - emit datasourcesChanged(); + EASY_END_BLOCK; + if (designTime()){ + EASY_BLOCK("emit datasourcesChanged()"); + emit datasourcesChanged(); + EASY_END_BLOCK; + } + EASY_BLOCK("emit loadCollectionFinished(collectionName)"); emit loadCollectionFinished(collectionName); + EASY_END_BLOCK; + EASY_END_BLOCK; } void DataSourceManager::addVariable(const QString &name, const QVariant &value, VarDesc::VarType type, RenderPass pass) @@ -1184,8 +1201,11 @@ void DataSourceManager::addVariable(const QString &name, const QVariant &value, } else { m_reportVariables.addVariable(name,value,type,pass); } - if (designTime()) - emit datasourcesChanged(); + if (designTime()){ + EASY_BLOCK("DataSourceManager::addVariable emit ds changed"); + emit datasourcesChanged(); + EASY_END_BLOCK; + } } void DataSourceManager::deleteVariable(const QString& name) diff --git a/limereport/lrglobal.cpp b/limereport/lrglobal.cpp index b4eb854..55ab31b 100644 --- a/limereport/lrglobal.cpp +++ b/limereport/lrglobal.cpp @@ -76,4 +76,13 @@ QVector normalizeCaptures(const QRegExp& reg){ return result; } +bool isColorDark(QColor color){ + qreal darkness = 1-(0.299*color.red() + 0.587*color.green() + 0.114*color.blue())/255; + if(darkness<0.5){ + return false; + } else { + return true; + } +} + } //namespace LimeReport diff --git a/limereport/lrglobal.h b/limereport/lrglobal.h index 4829390..b32d263 100644 --- a/limereport/lrglobal.h +++ b/limereport/lrglobal.h @@ -85,7 +85,7 @@ namespace Const{ const QString VARIABLE_RX = "\\$V\\s*\\{\\s*([^{}]*)\\s*\\}"; const QString NAMED_VARIABLE_RX = "\\$V\\s*\\{\\s*(%1)\\s*\\}"; const QString SCRIPT_RX = "\\$S\\s*\\{(.*)\\}"; - const QString GROUP_FUNCTION_PARAM_RX = "\\(\\s*((?:(?:\\\")|(?:))(?:(?:\\$(?:(?:D\\{\\s*\\w*.\\w*\\s*\\})|(?:V\\{\\s*\\w*\\s*\\})|(?:S\\{.+\\})))|(?:\\w*))(?:(?:\\\")|(?:)))(?:(?:\\s*,\\s*(?:\\\"(\\w*)\\\"))|(?:))\\)"; + const QString GROUP_FUNCTION_PARAM_RX = "\\(\\s*((?:(?:\\\")|(?:))(?:(?:\\$(?:(?:D\\{\\s*\\w*.\\w*\\s*\\})|(?:V\\{\\s*\\w*\\s*\\})|(?:S\\{.+\\})))|(?:\\w*))(?:(?:\\\")|(?:)))(?:(?:\\s*,\\s*(?:\\\"(\\w*)\\\"))|(?:))(?:(?:\\s*,\\s*(?:(\\w*)))|(?:))\\)"; const int DATASOURCE_INDEX = 3; const int VALUE_INDEX = 2; const int EXPRESSION_ARGUMENT_INDEX = 1; @@ -101,6 +101,7 @@ namespace Const{ QString escapeSimbols(const QString& value); QString replaceHTMLSymbols(const QString &value); QVector normalizeCaptures(const QRegExp ®); + bool isColorDark(QColor color); enum ExpandType {EscapeSymbols, NoEscapeSymbols, ReplaceHTMLSymbols}; enum RenderPass {FirstPass = 1, SecondPass = 2}; @@ -137,13 +138,12 @@ namespace Const{ #endif #ifdef USE_QJSENGINE - typedef QQmlEngine ScriptEngineType; + typedef QJSEngine ScriptEngineType; typedef QJSValue ScriptValueType; template - static inline QJSValue getCppOwnedJSValue(QJSEngine &e, T *p) + static inline QJSValue getJSValue(QJSEngine &e, T *p) { QJSValue res = e.newQObject(p); - QQmlEngine::setObjectOwnership(p, QQmlEngine::CppOwnership); return res; } #else diff --git a/limereport/lrgroupfunctions.cpp b/limereport/lrgroupfunctions.cpp index c265dfc..b3ea189 100644 --- a/limereport/lrgroupfunctions.cpp +++ b/limereport/lrgroupfunctions.cpp @@ -32,6 +32,7 @@ #include "lrbanddesignintf.h" #include "lritemdesignintf.h" #include "lrscriptenginemanager.h" +#include "lrpageitemdesignintf.h" #include @@ -50,6 +51,7 @@ void GroupFunction::slotBandRendered(BandDesignIntf *band) QString field = rxField.cap(1); if (m_dataManager->containsField(field)){ m_values.push_back(m_dataManager->fieldData(field)); + m_valuesByBand.insert(band, m_dataManager->fieldData(field)); } else { setInvalid(tr("Field \"%1\" not found").arg(m_data)); } @@ -60,6 +62,7 @@ void GroupFunction::slotBandRendered(BandDesignIntf *band) QString var = rxVar.cap(1); if (m_dataManager->containsVariable(var)){ m_values.push_back(m_dataManager->variable(var)); + m_valuesByBand.insert(band, m_dataManager->variable(var)); } else { setInvalid(tr("Variable \"%1\" not found").arg(m_data)); } @@ -70,6 +73,7 @@ void GroupFunction::slotBandRendered(BandDesignIntf *band) QVariant value = sm.evaluateScript(m_data); if (value.isValid()){ m_values.push_back(value); + m_valuesByBand.insert(band, value); } else { setInvalid(tr("Wrong script syntax \"%1\" ").arg(m_data)); } @@ -78,10 +82,12 @@ void GroupFunction::slotBandRendered(BandDesignIntf *band) case ContentItem:{ QString itemName = m_data; ContentItemDesignIntf* item = dynamic_cast(band->childByName(itemName.remove('"'))); - if (item) + if (item){ m_values.push_back(item->content()); - else if (m_name.compare("COUNT",Qt::CaseInsensitive) == 0) { + m_valuesByBand.insert(band, item->content()); + } else if (m_name.compare("COUNT",Qt::CaseInsensitive) == 0) { m_values.push_back(1); + m_valuesByBand.insert(band, 1); } else setInvalid(tr("Item \"%1\" not found").arg(m_data)); break; @@ -153,20 +159,32 @@ GroupFunctionFactory::~GroupFunctionFactory() m_creators.clear(); } -QVariant SumGroupFunction::calculate() +QVariant SumGroupFunction::calculate(PageItemDesignIntf *page) { - QVariant res=0; - foreach(QVariant value,values()){ - res=addition(res,value); + QVariant res = 0; + if (!page){ + foreach(QVariant value,values()){ + res = addition(res,value); + } + } else { + foreach (BandDesignIntf* band, page->bands()) { + res = addition(res, m_valuesByBand.value(band)); + } } return res; } -QVariant AvgGroupFunction::calculate() +QVariant AvgGroupFunction::calculate(PageItemDesignIntf *page) { - QVariant res=QVariant(); - foreach(QVariant value,values()){ - res=addition(res,value); + QVariant res = QVariant(); + if (!page){ + foreach(QVariant value,values()){ + res=addition(res,value); + } + } else { + foreach (BandDesignIntf* band, page->bands()) { + res = addition(res, m_valuesByBand.value(band)); + } } if (!res.isNull()&&(values().count()>0)){ res=division(res,values().count()); @@ -174,29 +192,58 @@ QVariant AvgGroupFunction::calculate() return res; } -QVariant MinGroupFunction::calculate() +QVariant MinGroupFunction::calculate(PageItemDesignIntf *page) { //TODO: check variant type QVariant res = QVariant(); - if (!values().empty()) res = values().at(0); - foreach(QVariant value, values()){ - if (res.toDouble()>value.toDouble()) res = value; + if (!page){ + if (!values().empty()) res = values().at(0); + foreach(QVariant value, values()){ + if (res.toDouble() > value.toDouble()) res = value; + } + } else { + if (!page->bands().empty()) res = m_valuesByBand.value(page->bands().at(0)); + foreach (BandDesignIntf* band, page->bands()) { + if (res.toDouble() > m_valuesByBand.value(band).toDouble()) res = m_valuesByBand.value(band); + } } return res; } -QVariant MaxGroupFunction::calculate() +QVariant MaxGroupFunction::calculate(PageItemDesignIntf *page) { //TODO: check variant type QVariant res = QVariant(); - if (!values().empty()) res = values().at(0); - foreach(QVariant value, values()){ - if (res.toDouble()bands().empty()) res = m_valuesByBand.value(page->bands().at(0)); + foreach (BandDesignIntf* band, page->bands()) { + if (res.toDouble() < m_valuesByBand.value(band).toDouble()) res = m_valuesByBand.value(band); + } } return res; } +QVariant CountGroupFunction::calculate(PageItemDesignIntf *page){ + if (!page){ + return values().count(); + } else { + int res = 0; + foreach (BandDesignIntf* band, page->bands()) { + if (!m_valuesByBand.value(band).isNull()){ + res++; + } + } + return res; + } +} + } //namespace LimeReport diff --git a/limereport/lrgroupfunctions.h b/limereport/lrgroupfunctions.h index f85a0bd..28cc08b 100644 --- a/limereport/lrgroupfunctions.h +++ b/limereport/lrgroupfunctions.h @@ -38,6 +38,7 @@ namespace LimeReport{ class DataSourceManager; class BandDesignIntf; +class PageItemDesignIntf; class GroupFunction : public QObject{ Q_OBJECT @@ -50,8 +51,9 @@ public: const QString& data(){return m_data;} const QString& error(){return m_errorMessage;} QVector& values(){return m_values;} + QHash m_valuesByBand; const QString& dataBandName(){return m_dataBandName;} - virtual QVariant calculate()=0; + virtual QVariant calculate(PageItemDesignIntf* page = 0)=0; public slots: void slotBandRendered(BandDesignIntf* band); protected: @@ -95,7 +97,7 @@ public: CountGroupFunction(const QString& expression, const QString& dataBandName, DataSourceManager *dataManager) :GroupFunction(expression, dataBandName, dataManager){setName("COUNT");} protected: - virtual QVariant calculate(){return values().count();} + virtual QVariant calculate(PageItemDesignIntf* page = 0); }; class SumGroupFunction :public GroupFunction{ @@ -104,7 +106,7 @@ public: SumGroupFunction(const QString& expression, const QString& dataBandName, DataSourceManager *dataManager) :GroupFunction(expression, dataBandName, dataManager){setName("SUM");} protected: - virtual QVariant calculate(); + virtual QVariant calculate(PageItemDesignIntf* page = 0); }; class AvgGroupFunction :public GroupFunction{ @@ -113,7 +115,7 @@ public: AvgGroupFunction(const QString& expression, const QString& dataBandName, DataSourceManager *dataManager) :GroupFunction(expression, dataBandName, dataManager){setName("AVG");} protected: - virtual QVariant calculate(); + virtual QVariant calculate(PageItemDesignIntf* page = 0); }; class MinGroupFunction :public GroupFunction{ @@ -122,7 +124,7 @@ public: MinGroupFunction(const QString& expression, const QString& dataBandName, DataSourceManager *dataManager) :GroupFunction(expression, dataBandName, dataManager){setName("MIN");} protected: - virtual QVariant calculate(); + virtual QVariant calculate(PageItemDesignIntf* page = 0); }; class MaxGroupFunction :public GroupFunction{ @@ -131,7 +133,7 @@ public: MaxGroupFunction(const QString& expression, const QString& dataBandName, DataSourceManager *dataManager) :GroupFunction(expression, dataBandName, dataManager){setName("MAX");} protected: - virtual QVariant calculate(); + virtual QVariant calculate(PageItemDesignIntf* page = 0); }; template diff --git a/limereport/lrpageitemdesignintf.cpp b/limereport/lrpageitemdesignintf.cpp index fcccff0..2c22102 100644 --- a/limereport/lrpageitemdesignintf.cpp +++ b/limereport/lrpageitemdesignintf.cpp @@ -51,7 +51,7 @@ PageItemDesignIntf::PageItemDesignIntf(QObject *owner, QGraphicsItem *parent) : m_pageOrientaion(Portrait), m_pageSize(A4), m_sizeChainging(false), m_fullPage(false), m_oldPrintMode(false), m_resetPageNumber(false), m_isExtendedInDesignMode(false), m_extendedHeight(1000), m_isTOC(false), m_setPageSizeToPrinter(false), - m_endlessHeight(false) + m_endlessHeight(false), m_printable(true) { setFixedPos(true); setPossibleResizeDirectionFlags(Fixed); @@ -64,8 +64,8 @@ PageItemDesignIntf::PageItemDesignIntf(const PageSize pageSize, const QRectF &re m_topMargin(0), m_bottomMargin(0), m_leftMargin(0), m_rightMargin(0), m_pageOrientaion(Portrait), m_pageSize(pageSize), m_sizeChainging(false), m_fullPage(false), m_oldPrintMode(false), m_resetPageNumber(false), - m_isExtendedInDesignMode(false), m_extendedHeight(1000), m_isTOC(false), - m_endlessHeight(false) + m_isExtendedInDesignMode(false), m_extendedHeight(1000), m_isTOC(false), m_setPageSizeToPrinter(false), + m_endlessHeight(false), m_printable(true) { setFixedPos(true); setPossibleResizeDirectionFlags(Fixed); @@ -333,6 +333,16 @@ void PageItemDesignIntf::initColumnsPos(QVector &posByColumns, qreal pos, } } +bool PageItemDesignIntf::isPrintable() const +{ + return m_printable; +} + +void PageItemDesignIntf::setPrintable(bool printable) +{ + m_printable = printable; +} + bool PageItemDesignIntf::endlessHeight() const { return m_endlessHeight; diff --git a/limereport/lrpageitemdesignintf.h b/limereport/lrpageitemdesignintf.h index 93df860..4204d06 100644 --- a/limereport/lrpageitemdesignintf.h +++ b/limereport/lrpageitemdesignintf.h @@ -58,7 +58,7 @@ class PageItemDesignIntf : public LimeReport::ItemsContainerDesignInft Q_PROPERTY(bool pageIsTOC READ isTOC WRITE setIsTOC) Q_PROPERTY(bool setPageSizeToPrinter READ getSetPageSizeToPrinter WRITE setSetPageSizeToPrinter ) Q_PROPERTY(bool endlessHeight READ endlessHeight WRITE setEndlessHeight) - + Q_PROPERTY(bool printable READ isPrintable WRITE setPrintable) friend class ReportRender; public: enum Orientation { Portrait, Landscape }; @@ -138,6 +138,9 @@ public: bool endlessHeight() const; void setEndlessHeight(bool endlessHeight); + bool isPrintable() const; + void setPrintable(bool printable); + signals: void beforeFirstPageRendered(); void afterLastPageRendered(); @@ -175,6 +178,7 @@ private: bool m_isTOC; bool m_setPageSizeToPrinter; bool m_endlessHeight; + bool m_printable; }; typedef QList ReportPages; diff --git a/limereport/lrpreviewreportwidget.cpp b/limereport/lrpreviewreportwidget.cpp index 5443adf..5ef2693 100644 --- a/limereport/lrpreviewreportwidget.cpp +++ b/limereport/lrpreviewreportwidget.cpp @@ -200,6 +200,7 @@ void PreviewReportWidget::printToPDF() foreach(PageItemDesignIntf::Ptr pageItem, d_ptr->m_reportPages){ d_ptr->m_previewPage->reactivatePageItem(pageItem); } + d_ptr->m_report->emitPrintedToPDF(fileName); } } diff --git a/limereport/lrreportdesignwidget.cpp b/limereport/lrreportdesignwidget.cpp index b336429..96cd9eb 100644 --- a/limereport/lrreportdesignwidget.cpp +++ b/limereport/lrreportdesignwidget.cpp @@ -72,20 +72,15 @@ ReportDesignWidget::ReportDesignWidget(ReportEnginePrivateInterface* report, QMa mainLayout->addWidget(m_tabWidget); setLayout(mainLayout); -// if (!report) { -// m_report=new ReportEnginePrivate(this); -// m_report->setObjectName("report"); -// m_report->appendPage("page1"); -// } -// else { - m_report=report;//report->d_ptr; - if (!m_report->pageCount()) m_report->appendPage("page1"); -// } + m_report=report; + if (!m_report->pageCount()) m_report->appendPage("page1"); createTabs(); connect(dynamic_cast(m_report), SIGNAL(pagesLoadFinished()),this,SLOT(slotPagesLoadFinished())); - connect(dynamic_cast(m_report), SIGNAL(cleared()),this,SIGNAL(cleared())); + connect(dynamic_cast(m_report), SIGNAL(cleared()), this, SIGNAL(cleared())); + connect(dynamic_cast(m_report), SIGNAL(loaded()), this, SLOT(slotReportLoaded())); + connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabChanged(int))); #ifdef HAVE_UI_LOADER connect(m_report->scriptContext(), SIGNAL(dialogDeleted(QString)), this, SLOT(slotDialogDeleted(QString))); @@ -206,7 +201,10 @@ void ReportDesignWidget::applySettings() QString styleSheet = theme.readAll(); parentWidget()->setStyleSheet(styleSheet); m_report->setStyleSheet(styleSheet); - } else parentWidget()->setStyleSheet(""); + } else { + parentWidget()->setStyleSheet(""); + m_report->setStyleSheet(""); + } } void ReportDesignWidget::loadState(QSettings* settings) @@ -247,6 +245,7 @@ void ReportDesignWidget::loadState(QSettings* settings) void ReportDesignWidget::createTabs(){ + m_tabWidget->clear(); int pageIndex = -1; for (int i = 0; ipageCount();++i){ QGraphicsView* view = new QGraphicsView(qobject_cast(this)); @@ -462,14 +461,13 @@ bool ReportDesignWidget::save() bool ReportDesignWidget::loadFromFile(const QString &fileName) { if (m_report->loadFromFile(fileName,false)){ - QByteArray editorState = m_scriptEditor->saveState(); - createTabs(); - //connectPage(m_report->pageAt(0)); - m_scriptEditor->setPlainText(m_report->scriptContext()->initScript()); - m_scriptEditor->restoreState(editorState); - emit loaded(); - m_dialogChanged = false; - return true; +// QByteArray editorState = m_scriptEditor->saveState(); +// createTabs(); +// m_scriptEditor->setPlainText(m_report->scriptContext()->initScript()); +// m_scriptEditor->restoreState(editorState); +// emit loaded(); +// m_dialogChanged = false; +// return true; } else { QMessageBox::critical(this,tr("Error"),tr("Wrong file format")); return false; @@ -709,6 +707,7 @@ void ReportDesignWidget::editSetting() setting.setDefaultFont(m_defaultFont); setting.setSuppressAbsentFieldsAndVarsWarnings(m_report->suppressFieldAndVarError()); setting.setUseDarkTheme(m_useDarkTheme); + setting.setDesignerLanguages(m_report->designerLanguages(), m_report->currentDesignerLanguage()); if (setting.exec()){ m_horizontalGridStep = setting.horizontalGridStep(); @@ -716,6 +715,9 @@ void ReportDesignWidget::editSetting() m_defaultFont = setting.defaultFont(); m_useDarkTheme = setting.userDarkTheme(); m_report->setSuppressFieldAndVarError(setting.suppressAbsentFieldsAndVarsWarnings()); + if (m_report->currentDesignerLanguage() != setting.designerLanguage() ){ + m_report->setCurrentDesignerLanguage(setting.designerLanguage()); + } applySettings(); } } @@ -836,6 +838,16 @@ void ReportDesignWidget::slotCurrentTabChanged(int index) if (view) view->centerOn(0,0); } +void ReportDesignWidget::slotReportLoaded() +{ + QByteArray editorState = m_scriptEditor->saveState(); + createTabs(); + m_scriptEditor->setPlainText(m_report->scriptContext()->initScript()); + m_scriptEditor->restoreState(editorState); + emit loaded(); + m_dialogChanged = false; +} + #ifdef HAVE_QTDESIGNER_INTEGRATION void ReportDesignWidget::addNewDialog() diff --git a/limereport/lrreportdesignwidget.h b/limereport/lrreportdesignwidget.h index 2887621..8e812dc 100644 --- a/limereport/lrreportdesignwidget.h +++ b/limereport/lrreportdesignwidget.h @@ -168,6 +168,7 @@ private slots: void slotDatasourceCollectionLoaded(const QString&); void slotSceneRectChanged(QRectF); void slotCurrentTabChanged(int index); + void slotReportLoaded(); #ifdef HAVE_QTDESIGNER_INTEGRATION void slotDialogChanged(QString); void slotDialogNameChanged(QString oldName, QString newName); diff --git a/limereport/lrreportdesignwindow.h b/limereport/lrreportdesignwindow.h index 6f3dba6..1beb45a 100644 --- a/limereport/lrreportdesignwindow.h +++ b/limereport/lrreportdesignwindow.h @@ -71,7 +71,6 @@ public: QSettings* settings(); void restoreSetting(); void setShowProgressDialog(bool value){m_showProgressDialog = value;} - private slots: void slotNewReport(); void slotNewPage(); diff --git a/limereport/lrreportengine.cpp b/limereport/lrreportengine.cpp index 9123426..8c6c3f9 100644 --- a/limereport/lrreportengine.cpp +++ b/limereport/lrreportengine.cpp @@ -56,10 +56,18 @@ #include "lrpreviewreportwidget.h" #include "lrpreviewreportwidget_p.h" +#ifdef BUILD_WITH_EASY_PROFILER +#include "easy/profiler.h" +#else +# define EASY_BLOCK(...) +# define EASY_END_BLOCK +#endif + #ifdef HAVE_STATIC_BUILD #include "lrfactoryinitializer.h" #endif + namespace LimeReport{ QSettings* ReportEngine::m_settings = 0; @@ -223,6 +231,12 @@ void ReportEnginePrivate::slotPreviewWindowDestroyed(QObject* window) } } +void ReportEnginePrivate::slotDesignerWindowDestroyed(QObject *window) +{ + Q_UNUSED(window) + dataManager()->setDesignTime(false); +} + void ReportEnginePrivate::clearReport() { foreach(PageDesignIntf* page,m_pages) delete page; @@ -283,10 +297,6 @@ void ReportEnginePrivate::printReport(ReportPages pages, QPrinter &printer) qreal leftMargin, topMargin, rightMargin, bottomMargin; printer.getPageMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin, QPrinter::Millimeter); - QRectF printerPageRect = printer.pageRect(QPrinter::Millimeter); - printerPageRect = QRectF(0,0,(printerPageRect.size().width() + rightMargin + leftMargin) * Const::mmFACTOR, - (printerPageRect.size().height() + bottomMargin +topMargin) * Const::mmFACTOR); - foreach(PageItemDesignIntf::Ptr page, pages){ if ( @@ -334,7 +344,11 @@ void ReportEnginePrivate::printReport(ReportPages pages, QPrinter &printer) } else { isFirst=false; painter = new QPainter(&printer); - } + } + + QRectF printerPageRect = printer.pageRect(QPrinter::Millimeter); + printerPageRect = QRectF(0,0,(printerPageRect.size().width() + rightMargin + leftMargin) * Const::mmFACTOR, + (printerPageRect.size().height() + bottomMargin +topMargin) * Const::mmFACTOR); if (printerPageRect.width() < page->geometry().width()){ qreal pageWidth = page->geometry().width(); @@ -375,7 +389,7 @@ void ReportEnginePrivate::setReportTranslation(const QString &languageName) setReportLanguage(language); } } -}; +} bool ReportEnginePrivate::printReport(QPrinter* printer) { @@ -396,9 +410,10 @@ bool ReportEnginePrivate::printReport(QPrinter* printer) printer =(printer)?printer:m_printer.data(); if (printer&&printer->isValid()){ try{ + bool designTime = dataManager()->designTime(); dataManager()->setDesignTime(false); ReportPages pages = renderToPages(); - dataManager()->setDesignTime(true); + dataManager()->setDesignTime(designTime); if (pages.count()>0){ printReport(pages,*printer); } @@ -469,7 +484,9 @@ bool ReportEnginePrivate::printToPDF(const QString &fileName) QPrinter printer; printer.setOutputFileName(fn); printer.setOutputFormat(QPrinter::PdfFormat); - return printReport(&printer); + bool success = printReport(&printer); + if(success) emitPrintedToPDF(fileName); + return success; } return false; } @@ -585,6 +602,11 @@ void ReportEnginePrivate::emitSaveFinished() emit saveFinished(); } +void ReportEnginePrivate::emitPrintedToPDF(QString fileName) +{ + emit printedToPDF(fileName); +} + bool ReportEnginePrivate::isSaved() { foreach (PageDesignIntf* page, m_pages) { @@ -601,6 +623,7 @@ void ReportEnginePrivate::setCurrentReportsDir(const QString &dirName) bool ReportEnginePrivate::slotLoadFromFile(const QString &fileName) { + EASY_BLOCK("ReportEnginePrivate::slotLoadFromFile") PreviewReportWindow *currentPreview = qobject_cast(m_activePreview); if (!QFile::exists(fileName)) @@ -642,17 +665,20 @@ bool ReportEnginePrivate::slotLoadFromFile(const QString &fileName) } } } - + EASY_BLOCK("Connect auto connections") dataManager()->connectAutoConnections(); + EASY_END_BLOCK; if ( hasActivePreview() ) { currentPreview->reloadPreview(); } + EASY_END_BLOCK; return true; }; } m_lastError = reader->lastError(); + EASY_END_BLOCK; return false; } @@ -672,6 +698,8 @@ void ReportEnginePrivate::designReport() { ReportDesignWindowInterface* designerWindow = getDesignerWindow(); if (designerWindow){ + dataManager()->setDesignTime(true); + connect(designerWindow, SIGNAL(destroyed(QObject*)), this, SLOT(slotDesignerWindowDestroyed(QObject*))); #ifdef Q_OS_WIN designerWindow->setWindowModality(Qt::ApplicationModal); #endif @@ -709,6 +737,7 @@ QSettings*ReportEnginePrivate::settings() bool ReportEnginePrivate::loadFromFile(const QString &fileName, bool autoLoadPreviewOnChange) { // only watch one file at a time + if ( !m_fileWatcher->files().isEmpty() ) { m_fileWatcher->removePaths( m_fileWatcher->files() ); @@ -719,7 +748,11 @@ bool ReportEnginePrivate::loadFromFile(const QString &fileName, bool autoLoadPre m_fileWatcher->addPath( fileName ); } - return slotLoadFromFile( fileName ); + bool result = slotLoadFromFile( fileName ); + if (result) { + emit loaded(); + } + return result; } bool ReportEnginePrivate::loadFromByteArray(QByteArray* data, const QString &name){ @@ -731,6 +764,7 @@ bool ReportEnginePrivate::loadFromByteArray(QByteArray* data, const QString &nam if (reader->readItem(this)){ m_fileName = ""; m_reportName = name; + emit loaded(); return true; }; } @@ -747,6 +781,7 @@ bool ReportEnginePrivate::loadFromString(const QString &report, const QString &n if (reader->readItem(this)){ m_fileName = ""; m_reportName = name; + emit loaded(); return true; }; } @@ -842,13 +877,13 @@ QString ReportEnginePrivate::renderToString() if (m_pages.count()){ render.setDatasources(dataManager()); render.setScriptContext(scriptContext()); - return render.renderPageToString(m_pages.at(0)); + return render.renderPageToString(m_pages.at(0)->pageItem()); }else return QString(); } -PageDesignIntf* ReportEnginePrivate::getPageByName(const QString& pageName) +PageItemDesignIntf* ReportEnginePrivate::getPageByName(const QString& pageName) { - foreach(PageDesignIntf* page, m_pages){ + foreach(PageItemDesignIntf* page, m_renderingPages){ if ( page->objectName().compare(pageName, Qt::CaseInsensitive) == 0) return page; } @@ -915,10 +950,10 @@ void ReportEnginePrivate::activateLanguage(QLocale::Language language) ReportTranslation* translation = m_translations.value(language); foreach(PageTranslation* pageTranslation, translation->pagesTranslation()){ - PageDesignIntf* page = getPageByName(pageTranslation->pageName); + PageItemDesignIntf* page = getPageByName(pageTranslation->pageName); if (page){ foreach(ItemTranslation* itemTranslation, pageTranslation->itemsTranslation){ - BaseDesignIntf* item = page->pageItem()->childByName(itemTranslation->itemName); + BaseDesignIntf* item = page->childByName(itemTranslation->itemName); if (item) { foreach(PropertyTranslation* propertyTranslation, itemTranslation->propertyesTranslation){ item->setProperty(propertyTranslation->propertyName.toLatin1(), propertyTranslation->value); @@ -929,6 +964,27 @@ void ReportEnginePrivate::activateLanguage(QLocale::Language language) } } +QList ReportEnginePrivate::designerLanguages() +{ + + QList result; + emit getAviableLanguages(&result); + return result; +} + +QLocale::Language ReportEnginePrivate::currentDesignerLanguage() +{ + QLocale::Language result = emit getCurrentDefaultLanguage(); + return result; +} + +void ReportEnginePrivate::setCurrentDesignerLanguage(QLocale::Language language) +{ + m_currentDesignerLanguage = language; + QMessageBox::information(m_designerWindow, tr("Warning") ,tr("The language will change after the application is restarted")); + emit currentDefaulLanguageChanged(language); +} + QString ReportEnginePrivate::styleSheet() const { return m_styleSheet; @@ -1001,6 +1057,13 @@ void ReportEnginePrivate::setPreviewWindowIcon(const QIcon &previewWindowIcon) m_previewWindowIcon = previewWindowIcon; } +PageItemDesignIntf* ReportEnginePrivate::createRenderingPage(PageItemDesignIntf* page){ + PageItemDesignIntf* result = dynamic_cast(page->cloneItem(page->itemMode())); + ICollectionContainer* co = dynamic_cast(result); + if (co) co->collectionLoadFinished("children"); + return result; +} + ReportPages ReportEnginePrivate::renderToPages() { if (m_reportRendering) return ReportPages(); @@ -1015,6 +1078,7 @@ ReportPages ReportEnginePrivate::renderToPages() this, SIGNAL(renderPageFinished(int))); if (m_pages.count()){ + #ifdef HAVE_UI_LOADER m_scriptEngineContext->initDialogs(); #endif @@ -1025,7 +1089,9 @@ ReportPages ReportEnginePrivate::renderToPages() m_reportRender->setScriptContext(scriptContext()); foreach (PageDesignIntf* page, m_pages) { - scriptContext()->baseDesignIntfToScript(page->pageItem()->objectName(), page->pageItem()); + PageItemDesignIntf* rp = createRenderingPage(page->pageItem()); + m_renderingPages.append(rp); + scriptContext()->baseDesignIntfToScript(rp->objectName(), rp); } scriptContext()->qobjectToScript("engine",this); @@ -1035,27 +1101,25 @@ ReportPages ReportEnginePrivate::renderToPages() activateLanguage(m_reportLanguage); emit renderStarted(); - foreach(PageDesignIntf* page , m_pages){ - if (!page->pageItem()->isTOC()){ + foreach(PageItemDesignIntf* page , m_renderingPages){ + if (!page->isTOC() && page->isPrintable()){ page->setReportSettings(&m_reportSettings); result.append(m_reportRender->renderPageToPages(page)); } } -// m_reportRender->secondRenderPass(result); - - for (int i=0; ipageItem()->isTOC()){ + for (int i=0; iisTOC()){ page->setReportSettings(&m_reportSettings); if (i==0){ - PageDesignIntf* secondPage = 0; - if (m_pages.count()>1) secondPage = m_pages.at(1); + PageItemDesignIntf* secondPage = 0; + if (m_pages.count()>1) secondPage = m_renderingPages.at(1); ReportPages pages = m_reportRender->renderTOC( page, true, - secondPage && secondPage->pageItem()->resetPageNumber() + secondPage && secondPage->resetPageNumber() ); for (int j=0; j*)), + this, SIGNAL(getAviableLanguages(QList*))); + connect(d, SIGNAL(currentDefaulLanguageChanged(QLocale::Language)), + this, SIGNAL(currentDefaulLanguageChanged(QLocale::Language))); + connect(d, SIGNAL(getCurrentDefaultLanguage()), + this, SIGNAL(getCurrentDefaultLanguage())); + } ReportEngine::~ReportEngine() @@ -1143,7 +1223,7 @@ void ReportEngine::designReport() d->designReport(); } -ReportDesignWindowInterface*ReportEngine::getDesignerWindow() +ReportDesignWindowInterface* ReportEngine::getDesignerWindow() { Q_D(ReportEngine); return d->getDesignerWindow(); @@ -1203,6 +1283,30 @@ bool ReportEngine::setReportLanguage(QLocale::Language language) return d->setReportLanguage(language); } +Qt::LayoutDirection ReportEngine::previewLayoutDirection() +{ + Q_D(ReportEngine); + return d->previewLayoutDirection(); +} + +void ReportEngine::setPreviewLayoutDirection(const Qt::LayoutDirection& previewLayoutDirection) +{ + Q_D(ReportEngine); + d->setPreviewLayoutDirection(previewLayoutDirection); +} + +QList ReportEngine::designerLanguages() +{ + Q_D(ReportEngine); + return d->designerLanguages(); +} + +QLocale::Language ReportEngine::currentDesignerLanguage() +{ + Q_D(ReportEngine); + return d->currentDesignerLanguage(); +} + void ReportEngine::setShowProgressDialog(bool value) { Q_D(ReportEngine); diff --git a/limereport/lrreportengine.h b/limereport/lrreportengine.h index 589e359..a4c1158 100644 --- a/limereport/lrreportengine.h +++ b/limereport/lrreportengine.h @@ -113,6 +113,8 @@ public: bool setReportLanguage(QLocale::Language language); Qt::LayoutDirection previewLayoutDirection(); void setPreviewLayoutDirection(const Qt::LayoutDirection& previewLayoutDirection); + QList designerLanguages(); + QLocale::Language currentDesignerLanguage(); signals: void renderStarted(); void renderFinished(); @@ -120,6 +122,14 @@ signals: void onLoad(bool& loaded); void onSave(); void saveFinished(); + + void loaded(); + void printedToPDF(QString fileName); + + void getAviableLanguages(QList* languages); + void currentDefaulLanguageChanged(QLocale::Language); + QLocale::Language getCurrentDefaultLanguage(); + public slots: void cancelRender(); protected: diff --git a/limereport/lrreportengine_p.h b/limereport/lrreportengine_p.h index 9f7ebf5..a5c7fb4 100644 --- a/limereport/lrreportengine_p.h +++ b/limereport/lrreportengine_p.h @@ -33,6 +33,7 @@ #include #include #include +#include #include "lrreportengine.h" #include "lrcollection.h" #include "lrglobal.h" @@ -83,6 +84,9 @@ public: virtual void setSuppressFieldAndVarError(bool suppressFieldAndVarError) = 0; virtual void setStyleSheet(const QString& styleSheet) = 0; virtual QString styleSheet() const = 0; + virtual QList designerLanguages() = 0; + virtual QLocale::Language currentDesignerLanguage() = 0; + virtual void setCurrentDesignerLanguage(QLocale::Language language) = 0; }; class ReportEnginePrivate : public QObject, public ICollectionContainer, public ITranslationContainer, @@ -147,6 +151,7 @@ public: void emitSaveReport(); bool emitLoadReport(); void emitSaveFinished(); + void emitPrintedToPDF(QString fileName); bool isSaved(); void setCurrentReportsDir(const QString& dirName); QString currentReportsDir(){ return m_reportsDir;} @@ -178,7 +183,9 @@ public: void setPreviewLayoutDirection(const Qt::LayoutDirection& previewLayoutDirection); QString styleSheet() const; void setStyleSheet(const QString &styleSheet); - + QList designerLanguages(); + QLocale::Language currentDesignerLanguage(); + void setCurrentDesignerLanguage(QLocale::Language language); signals: void pagesLoadFinished(); void datasourceCollectionLoadFinished(const QString& collectionName); @@ -189,6 +196,14 @@ signals: void onLoad(bool& loaded); void onSave(); void saveFinished(); + + void loaded(); + void printedToPDF(QString fileName); + + void getAviableLanguages(QList* languages); + void currentDefaulLanguageChanged(QLocale::Language); + QLocale::Language getCurrentDefaultLanguage(); + public slots: bool slotLoadFromFile(const QString& fileName); void cancelRender(); @@ -197,7 +212,8 @@ protected: protected slots: void slotDataSourceCollectionLoaded(const QString& collectionName); private slots: - void slotPreviewWindowDestroyed(QObject *window); + void slotPreviewWindowDestroyed(QObject* window); + void slotDesignerWindowDestroyed(QObject* window); private: //ICollectionContainer virtual QObject* createElement(const QString&,const QString&); @@ -213,10 +229,12 @@ private: //ITranslationContainer ReportPages renderToPages(); QString renderToString(); - PageDesignIntf* getPageByName(const QString& pageName); + PageItemDesignIntf *getPageByName(const QString& pageName); ATranslationProperty fakeTranslationReader(){ return ATranslationProperty();} + PageItemDesignIntf *createRenderingPage(PageItemDesignIntf *page); private: QList m_pages; + QList m_renderingPages; DataSourceManager* m_datasources; ScriptEngineContext* m_scriptEngineContext; ReportRender::Ptr m_reportRender; @@ -244,6 +262,7 @@ private: Qt::LayoutDirection m_previewLayoutDirection; LimeReportPluginInterface* m_designerFactory; QString m_styleSheet; + QLocale::Language m_currentDesignerLanguage; }; } diff --git a/limereport/lrreportrender.cpp b/limereport/lrreportrender.cpp index 154ab61..a6679bf 100644 --- a/limereport/lrreportrender.cpp +++ b/limereport/lrreportrender.cpp @@ -187,10 +187,10 @@ void ReportRender::initDatasource(const QString& name){ } } -void ReportRender::renderPage(PageDesignIntf* patternPage, bool isTOC, bool isFirst, bool resetPageNumbers) +void ReportRender::renderPage(PageItemDesignIntf* patternPage, bool isTOC, bool isFirst, bool resetPageNumbers) { m_curentNameIndex = 0; - m_patternPageItem = patternPage->pageItem(); + m_patternPageItem = patternPage; m_renderingFirstTOC = isTOC && isFirst; if (m_patternPageItem->resetPageNumber() && m_pageCount>0 && !isTOC) { @@ -269,19 +269,19 @@ PageItemDesignIntf::Ptr ReportRender::pageAt(int index) else return m_renderedPages.at(index); } -QString ReportRender::renderPageToString(PageDesignIntf *patternPage) +QString ReportRender::renderPageToString(PageItemDesignIntf *patternPage) { renderPage(patternPage); return toString(); } -ReportPages ReportRender::renderPageToPages(PageDesignIntf *patternPage) +ReportPages ReportRender::renderPageToPages(PageItemDesignIntf *patternPage) { renderPage(patternPage); return m_renderedPages; } -ReportPages ReportRender::renderTOC(PageDesignIntf* patternPage, bool first, bool resetPages){ +ReportPages ReportRender::renderTOC(PageItemDesignIntf* patternPage, bool first, bool resetPages){ renderPage(patternPage, true, first, resetPages); return m_renderedPages; } @@ -294,6 +294,24 @@ void ReportRender::initRenderPage() m_renderPageItem->setItemMode(PreviewMode); m_renderPageItem->setPatternName(m_patternPageItem->objectName()); m_renderPageItem->setPatternItem(m_patternPageItem); + + ScriptValueType svCurrentPage; + ScriptEngineType* se = ScriptEngineManager::instance().scriptEngine(); + +#ifdef USE_QJSENGINE + svCurrentPage = getJSValue(*se, m_renderPageItem); + se->globalObject().setProperty("currentPage", svCurrentPage); +#else + svCurrentPage = se->globalObject().property("currentPage"); + if (svCurrentPage.isValid()){ + se->newQObject(svCurrentPage, m_renderPageItem); + } else { + svCurrentPage = se->newQObject(m_renderPageItem); + se->globalObject().setProperty("currentPage", svCurrentPage); + } +#endif + + } } @@ -384,7 +402,15 @@ void ReportRender::replaceGroupFunctionsInItem(ContentItemDesignIntf* contentIte QVector captures = normalizeCaptures(rx); if (captures.size() >= 3){ QString expressionIndex = datasources()->putGroupFunctionsExpressions(captures.at(Const::VALUE_INDEX)); - content.replace(captures.at(0),QString("%1(%2,%3)").arg(functionName).arg('"'+expressionIndex+'"').arg('"'+band->objectName()+'"')); + if (captures.size()<5){ + content.replace(captures.at(0),QString("%1(%2,%3)").arg(functionName).arg('"'+expressionIndex+'"').arg('"'+band->objectName()+'"')); + } else { + content.replace(captures.at(0),QString("%1(%2,%3,%4)") + .arg(functionName) + .arg('"'+expressionIndex+'"') + .arg('"'+band->objectName()+'"') + .arg(captures.at(4))); + } } pos += rx.matchedLength(); } @@ -650,7 +676,10 @@ void ReportRender::renderPageItems(PageItemDesignIntf* patternPage) m_renderPageItem->restoreLinks(); m_renderPageItem->updateSubItemsSize(FirstPass,m_datasources); foreach(BaseDesignIntf* item, pageItems){ - item->setZValue(item->zValue()-100000); + if (!item->isWatermark()) + item->setZValue(item->zValue()-100000); + else + item->setZValue(item->zValue()+100000); } } @@ -749,8 +778,8 @@ void ReportRender::renderGroupHeader(BandDesignIntf *parentBand, IDataSource* da foreach (BandDesignIntf* subBand, band->childrenByType(BandDesignIntf::GroupHeader)) { foreach(BandDesignIntf* footer, subBand->childrenByType(BandDesignIntf::GroupFooter)){ renderBand(footer, 0); - closeDataGroup(subBand); } + closeDataGroup(subBand); } foreach (BandDesignIntf* footer, band->childrenByType(BandDesignIntf::GroupFooter)) { @@ -1134,7 +1163,7 @@ BandDesignIntf *ReportRender::renderData(BandDesignIntf *patternBand) { BandDesignIntf* bandClone = dynamic_cast(patternBand->cloneItem(PreviewMode)); - m_scriptEngineContext->baseDesignIntfToScript(patternBand->page()->pageItem()->objectName(), bandClone); + m_scriptEngineContext->baseDesignIntfToScript(patternBand->parent()->objectName(), bandClone); m_scriptEngineContext->setCurrentBand(bandClone); emit(patternBand->beforeRender()); diff --git a/limereport/lrreportrender.h b/limereport/lrreportrender.h index e32d04a..8467656 100644 --- a/limereport/lrreportrender.h +++ b/limereport/lrreportrender.h @@ -80,9 +80,9 @@ public: DataSourceManager* datasources(){return m_datasources;} int pageCount(); PageItemDesignIntf::Ptr pageAt(int index); - QString renderPageToString(PageDesignIntf *patternPage); - ReportPages renderPageToPages(PageDesignIntf *patternPage); - ReportPages renderTOC(PageDesignIntf* patternPage, bool first, bool resetPages); + QString renderPageToString(PageItemDesignIntf *patternPage); + ReportPages renderPageToPages(PageItemDesignIntf *patternPage); + ReportPages renderTOC(PageItemDesignIntf *patternPage, bool first, bool resetPages); void secondRenderPass(ReportPages renderedPages); signals: void pageRendered(int renderedPageCount); @@ -96,7 +96,7 @@ private: void initGroups(); void clearPageMap(); - void renderPage(PageDesignIntf *patternPage, bool isTOC = false, bool isFirst = false, bool resetPageNumbers = false); + void renderPage(PageItemDesignIntf *patternPage, bool isTOC = false, bool isFirst = false, bool resetPageNumbers = false); BandDesignIntf* renderBand(BandDesignIntf *patternBand, BandDesignIntf *bandData, DataRenderMode mode = NotStartNewPage, bool isLast = false); void renderDataBand(BandDesignIntf* dataBand); void renderPageHeader(PageItemDesignIntf* patternPage); diff --git a/limereport/lrreporttranslation.cpp b/limereport/lrreporttranslation.cpp index 2df00eb..5c8fee8 100644 --- a/limereport/lrreporttranslation.cpp +++ b/limereport/lrreporttranslation.cpp @@ -32,7 +32,7 @@ ReportTranslation::~ReportTranslation() PageTranslation* ReportTranslation::createPageTranslation(PageDesignIntf* page) { PageTranslation* pageTranslation = new PageTranslation; - pageTranslation->pageName = page->objectName(); + pageTranslation->pageName = page->pageItem()->objectName(); foreach(BaseDesignIntf* item, page->pageItem()->allChildBaseItems()){ createItemTranslation(item, pageTranslation); } diff --git a/limereport/lrscriptenginemanager.cpp b/limereport/lrscriptenginemanager.cpp index 2bc89a5..fa78023 100644 --- a/limereport/lrscriptenginemanager.cpp +++ b/limereport/lrscriptenginemanager.cpp @@ -31,7 +31,9 @@ #include #include +#ifndef USE_QJSENGINE #include +#endif #include #ifdef HAVE_UI_LOADER #include @@ -41,16 +43,19 @@ #include "lrdatasourcemanager.h" #include "lrbasedesignintf.h" #include "lrbanddesignintf.h" +#include "lrpageitemdesignintf.h" Q_DECLARE_METATYPE(QColor) Q_DECLARE_METATYPE(QFont) Q_DECLARE_METATYPE(LimeReport::ScriptEngineManager *) +#ifndef USE_QJSENGINE QScriptValue constructColor(QScriptContext *context, QScriptEngine *engine) { QColor color(context->argument(0).toString()); return engine->toScriptValue(color); } +#endif namespace LimeReport{ @@ -257,7 +262,7 @@ bool ScriptEngineManager::addFunction(const JSFunctionDesc &functionDescriber) return false; } } else { - m_lastError = tr("Function manger with name \"%1\" already exists!"); + m_lastError = tr("Function manager with name \"%1\" already exists!"); return false; } @@ -340,8 +345,9 @@ void ScriptEngineManager::setDataManager(DataSourceManager *dataManager){ func+"(\""+tr("FieldName")+"\",\""+tr("BandName")+"\")", LimeReport::Const::FUNCTION_MANAGER_NAME, m_functionManager, - QString("function %1(fieldName,bandName){\ - return %2.calcGroupFunction(\"%1\",fieldName,bandName);}" + QString("function %1(fieldName, bandName, pageitem){\ + if (typeof pageitem == 'undefined') return %2.calcGroupFunction(\"%1\", fieldName, bandName); \ + else return %2.calcGroupFunction(\"%1\", fieldName, bandName, pageitem);}" ).arg(func) .arg(LimeReport::Const::FUNCTION_MANAGER_NAME) ); @@ -468,7 +474,7 @@ QString ScriptEngineManager::expandScripts(QString context, QVariant& varValue, ScriptValueType svThis; #ifdef USE_QJSENGINE - svThis = getCppOwnedJSValue(*se, reportItem); + svThis = getJSValue(*se, reportItem); se->globalObject().setProperty("THIS",svThis); #else svThis = se->globalObject().property("THIS"); @@ -1274,7 +1280,7 @@ void ScriptEngineContext::baseDesignIntfToScript(const QString& pageName, BaseDe ScriptEngineType* engine = ScriptEngineManager::instance().scriptEngine(); #ifdef USE_QJSENGINE - ScriptValueType sItem = getCppOwnedJSValue(*engine, item); + ScriptValueType sItem = getJSValue(*engine, item); QString on = item->patternName().compare(pageName) == 0 ? pageName : pageName+"_"+item->patternName(); engine->globalObject().setProperty(on, sItem); #else @@ -1297,7 +1303,7 @@ void ScriptEngineContext::qobjectToScript(const QString& name, QObject *item) { ScriptEngineType* engine = ScriptEngineManager::instance().scriptEngine(); #ifdef USE_QJSENGINE - ScriptValueType sItem = getCppOwnedJSValue(*engine, item); + ScriptValueType sItem = getJSValue(*engine, item); engine->globalObject().setProperty(name, sItem); #else ScriptValueType sItem = engine->globalObject().property(name); @@ -1444,14 +1450,15 @@ void JSFunctionDesc::setScriptWrapper(const QString &scriptWrapper) m_scriptWrapper = scriptWrapper; } -QVariant ScriptFunctionsManager::calcGroupFunction(const QString &name, const QString &expressionID, const QString &bandName) +QVariant ScriptFunctionsManager::calcGroupFunction(const QString &name, const QString &expressionID, const QString &bandName, QObject* currentPage) { if (m_scriptEngineManager->dataManager()){ + PageItemDesignIntf* pageItem = dynamic_cast(currentPage); QString expression = m_scriptEngineManager->dataManager()->getExpression(expressionID); GroupFunction* gf = m_scriptEngineManager->dataManager()->groupFunction(name,expression,bandName); if (gf){ if (gf->isValid()){ - return gf->calculate(); + return gf->calculate(pageItem); }else{ return gf->error(); } @@ -1464,6 +1471,11 @@ QVariant ScriptFunctionsManager::calcGroupFunction(const QString &name, const QS } } +QVariant ScriptFunctionsManager::calcGroupFunction(const QString& name, const QString& expressionID, const QString& bandName) +{ + return calcGroupFunction(name, expressionID, bandName, 0); +} + QVariant ScriptFunctionsManager::line(const QString &bandName) { QString varName = QLatin1String("line_")+bandName.toLower(); diff --git a/limereport/lrscriptenginemanager.h b/limereport/lrscriptenginemanager.h index dc30657..2241c85 100644 --- a/limereport/lrscriptenginemanager.h +++ b/limereport/lrscriptenginemanager.h @@ -29,14 +29,15 @@ ****************************************************************************/ #ifndef LRSCRIPTENGINEMANAGER_H #define LRSCRIPTENGINEMANAGER_H - +#ifndef USE_QJSENGINE #include +#include +#endif #include #include #include #include #include -#include #include #include @@ -231,10 +232,10 @@ public: const QString& functionCategory, const QString& functionDescription, const QString& functionManagerName, - QObject* functionManger, + QObject* functionManager, const QString& functionScriptWrapper ): m_name(functionName), m_category(functionCategory), m_description(functionDescription), - m_managerName(functionManagerName), m_manager(functionManger), m_scriptWrapper(functionScriptWrapper) + m_managerName(functionManagerName), m_manager(functionManager), m_scriptWrapper(functionScriptWrapper) {} QString name() const; void setName(const QString &name); @@ -305,6 +306,7 @@ public: ~ScriptFunctionsManager(){ foreach(IWrapperCreator* wrapper, m_wrappersFactory.values()){ delete wrapper;} m_wrappersFactory.clear(); } + Q_INVOKABLE QVariant calcGroupFunction(const QString& name, const QString& expressionID, const QString& bandName, QObject* currentPage); Q_INVOKABLE QVariant calcGroupFunction(const QString& name, const QString& expressionID, const QString& bandName); Q_INVOKABLE QVariant line(const QString& bandName); Q_INVOKABLE QVariant numberFormat(QVariant value, const char &format, int precision, const QString &locale); @@ -440,6 +442,7 @@ private: }; +#ifndef USE_QJSENGINE class QFontPrototype : public QObject, public QScriptable { Q_OBJECT Q_PROPERTY(QString family READ family) @@ -486,9 +489,9 @@ public: return qScriptValueFromValue(engine, font); } }; +#endif } - #ifndef USE_QJSENGINE Q_DECLARE_METATYPE(LimeReport::ComboBoxPrototype*) Q_DECLARE_METATYPE(QComboBox*) diff --git a/limereport/lrsettingdialog.cpp b/limereport/lrsettingdialog.cpp index ee34440..b05fa74 100644 --- a/limereport/lrsettingdialog.cpp +++ b/limereport/lrsettingdialog.cpp @@ -47,6 +47,15 @@ bool SettingDialog::suppressAbsentFieldsAndVarsWarnings() return ui->cbSuppressWarnings->isChecked(); } +QLocale::Language SettingDialog::designerLanguage() +{ + foreach (QLocale::Language language, m_aviableLanguages) { + if (ui->designerLanguage->currentText().compare(QLocale::languageToString(language)) == 0) + return language; + } + return QLocale().language(); +} + void SettingDialog::setSuppressAbsentFieldsAndVarsWarnings(bool value){ ui->cbSuppressWarnings->setChecked(value); } @@ -72,4 +81,22 @@ void SettingDialog::setUseDarkTheme(bool value) ui->cbbUseDarkTheme->setChecked(value); } +void SettingDialog::setDesignerLanguages(QList languages, QLocale::Language currentLanguage) +{ + m_aviableLanguages = languages; + m_currentLanguage = currentLanguage; + + if (languages.isEmpty()) { + ui->designerLanguage->setVisible(false); + ui->lblLanguage->setVisible(false); + return; + } + ui->designerLanguage->addItem(QLocale::languageToString(currentLanguage)); + foreach (QLocale::Language language, languages) { + if (language != currentLanguage) + ui->designerLanguage->addItem(QLocale::languageToString(language)); + } + ui->designerLanguage->setCurrentText(QLocale::languageToString(currentLanguage)); +} + } // namespace LimeReport diff --git a/limereport/lrsettingdialog.h b/limereport/lrsettingdialog.h index db908f9..d4c89e3 100644 --- a/limereport/lrsettingdialog.h +++ b/limereport/lrsettingdialog.h @@ -2,6 +2,7 @@ #define LRSETTINGDIALOG_H #include +#include namespace LimeReport{ @@ -21,13 +22,17 @@ public: QFont defaultFont(); bool userDarkTheme(); bool suppressAbsentFieldsAndVarsWarnings(); + QLocale::Language designerLanguage(); void setSuppressAbsentFieldsAndVarsWarnings(bool value); void setHorizontalGridStep(int value); void setVerticalGridStep(int value); void setDefaultFont(const QFont& value); void setUseDarkTheme(bool value); + void setDesignerLanguages(QList languages, QLocale::Language currentLanguage); private: Ui::SettingDialog *ui; + QList m_aviableLanguages; + QLocale::Language m_currentLanguage; }; } // namespace LimeReport diff --git a/limereport/lrsettingdialog.ui b/limereport/lrsettingdialog.ui index b0dd1ed..19d27fe 100644 --- a/limereport/lrsettingdialog.ui +++ b/limereport/lrsettingdialog.ui @@ -6,14 +6,23 @@ 0 0 - 351 - 318 + 397 + 345 + + + 0 + 0 + + Designer setting + + QLayout::SetDefaultConstraint + @@ -108,6 +117,27 @@ + + + + + + Language + + + + + + + + 0 + 0 + + + + + + diff --git a/limereport/objectinspector/editors/lrcoloreditor.cpp b/limereport/objectinspector/editors/lrcoloreditor.cpp index e845d76..09c22d6 100644 --- a/limereport/objectinspector/editors/lrcoloreditor.cpp +++ b/limereport/objectinspector/editors/lrcoloreditor.cpp @@ -28,11 +28,14 @@ * GNU General Public License for more details. * ****************************************************************************/ #include "lrcoloreditor.h" +#include "lrglobal.h" #include #include #include #include +#include +#include namespace LimeReport{ @@ -94,17 +97,23 @@ void ColorEditor::slotClicked() emit(editingFinished()); } - void ColorIndicator::paintEvent(QPaintEvent* event) { QPainter painter(this); + + QStyle* style = QApplication::style(); painter.save(); painter.setBrush(m_color); - painter.setPen(Qt::gray); - QRect rect = event->rect().adjusted(3,3,-4,-4); - rect.setWidth(rect.height()); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawEllipse(rect); + QColor penColor = isColorDark(m_color) ? Qt::transparent : Qt::darkGray; + + painter.setPen(penColor); + int border = (event->rect().height() - style->pixelMetric(QStyle::PM_IndicatorWidth))/2; + + QRect rect(event->rect().x()+border, event->rect().y()+border, + style->pixelMetric(QStyle::PM_IndicatorWidth), + style->pixelMetric(QStyle::PM_IndicatorWidth));// = option.rect.adjusted(4,4,-4,-6); + + painter.drawRect(rect); painter.restore(); } diff --git a/limereport/objectinspector/lrobjectitemmodel.cpp b/limereport/objectinspector/lrobjectitemmodel.cpp index 07a8200..6fc1dab 100644 --- a/limereport/objectinspector/lrobjectitemmodel.cpp +++ b/limereport/objectinspector/lrobjectitemmodel.cpp @@ -147,6 +147,9 @@ void QObjectPropertyModel::translatePropertyName() tr("legendAlign"); tr("series"); tr("titleAlign"); + tr("watermark"); + tr("keepTopSpace"); + } void QObjectPropertyModel::clearObjectsList() diff --git a/limereport/objectinspector/lrpropertydelegate.cpp b/limereport/objectinspector/lrpropertydelegate.cpp index 80bd4d0..acb9c20 100644 --- a/limereport/objectinspector/lrpropertydelegate.cpp +++ b/limereport/objectinspector/lrpropertydelegate.cpp @@ -62,14 +62,12 @@ void LimeReport::PropertyDelegate::paint(QPainter *painter, const QStyleOptionVi painter->save(); painter->setPen(option.palette.color(QPalette::HighlightedText)); painter->setBackground(QBrush(option.palette.color(QPalette::Highlight))); - //drawBackground(painter,option,index); cellOpt.widget->style()->drawPrimitive(QStyle::PE_IndicatorBranch,&primitiveOpt,painter); cellOpt.rect.adjust(primitiveOpt.rect.width(),0,0,0); cellOpt.font.setBold(true); cellOpt.palette.setColor(QPalette::Text,cellOpt.palette.color(QPalette::BrightText)); cellOpt.text = LimeReport::extractClassName(node->propertyName()); style->drawControl(QStyle::CE_ItemViewItem, &cellOpt, painter, cellOpt.widget); - //drawDisplay(painter,cellOpt,cellOpt.rect,LimeReport::extractClassName(node->propertyName())); painter->restore(); } } else { @@ -92,13 +90,13 @@ void LimeReport::PropertyDelegate::paint(QPainter *painter, const QStyleOptionVi else so.palette.setColor(QPalette::Text,Qt::black); -// drawBackground(painter,option,index); - opt.text = ""; + opt.rect.setHeight(opt.rect.height()-1); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); if (!node->paint(painter,so,index)){ so.state &= ~QStyle::State_HasFocus; + so.rect.adjust(0,0,0,-1); QStyledItemDelegate::paint(painter, so, index); } @@ -117,6 +115,8 @@ void LimeReport::PropertyDelegate::paint(QPainter *painter, const QStyleOptionVi painter->drawLine(start,end); painter->restore(); } + + } } } diff --git a/limereport/objectinspector/propertyItems/lrboolpropitem.cpp b/limereport/objectinspector/propertyItems/lrboolpropitem.cpp index c6c9f37..6e462a2 100644 --- a/limereport/objectinspector/propertyItems/lrboolpropitem.cpp +++ b/limereport/objectinspector/propertyItems/lrboolpropitem.cpp @@ -67,23 +67,39 @@ void BoolPropItem::setModelData(QWidget *propertyEditor, QAbstractItemModel *mod setValueToObject(propertyName(),propertyValue()); } +QPixmap BoolPropItem::getIndicatorImage(const StyleOptionViewItem &option){ + QStyleOptionButton so; + so.state = option.state; + if (!isValueReadonly()) + so.state = QStyle::State_Enabled; + else + so.state &= ~QStyle::State_Enabled; + so.state |= propertyValue().toBool() ? QStyle::State_On : QStyle::State_Off; + so.rect = QRect(0,0, + QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth), + QApplication::style()->pixelMetric(QStyle::PM_IndicatorHeight)); + + QPixmap pixmap(so.rect.width(),so.rect.height()); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + option.widget->style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck,&so, &p); + return pixmap; +} + bool BoolPropItem::paint(QPainter *painter, const StyleOptionViewItem &option, const QModelIndex &index) { + + QStyle* style = option.widget ? option.widget->style() : QApplication::style(); + if (index.column()==1){ - QStyleOptionButton so; - int border = (option.rect.height() - QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth))/2; - so.rect = option.rect.adjusted(border,border,0,-border); - so.rect.setWidth(QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth)); - - if (!isValueReadonly()) - so.state = QStyle::State_Enabled; - else - so.state &= ~QStyle::State_Enabled; - - so.state |= propertyValue().toBool() ? QStyle::State_On : QStyle::State_Off; - - option.widget->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox,&so,painter); - + int border = (option.rect.height() - style->pixelMetric(QStyle::PM_IndicatorWidth))/2; +// QStyleOptionButton so; +// so.rect = option.rect.adjusted(border,border,0,-border); +// so.rect.setWidth(style->pixelMetric(QStyle::PM_IndicatorWidth)); +// so.rect.setHeight(style->pixelMetric(QStyle::PM_IndicatorHeight)); +// so.state |= propertyValue().toBool() ? QStyle::State_On : QStyle::State_Off; +// style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck,&so,painter); + painter->drawPixmap(option.rect.x()+border,option.rect.y()+border, getIndicatorImage(option)); return true; } else return false; } diff --git a/limereport/objectinspector/propertyItems/lrboolpropitem.h b/limereport/objectinspector/propertyItems/lrboolpropitem.h index 3407925..9331c3d 100644 --- a/limereport/objectinspector/propertyItems/lrboolpropitem.h +++ b/limereport/objectinspector/propertyItems/lrboolpropitem.h @@ -45,6 +45,8 @@ public: virtual void setPropertyEditorData(QWidget * propertyEditor, const QModelIndex &) const; virtual void setModelData(QWidget * propertyEditor, QAbstractItemModel * model, const QModelIndex & index); bool paint(QPainter *painter, const StyleOptionViewItem &option, const QModelIndex &index); +protected: + QPixmap getIndicatorImage(const StyleOptionViewItem &option); }; } // namespace LimeReport diff --git a/limereport/objectinspector/propertyItems/lrcolorpropitem.cpp b/limereport/objectinspector/propertyItems/lrcolorpropitem.cpp index 8201e22..a47f77c 100644 --- a/limereport/objectinspector/propertyItems/lrcolorpropitem.cpp +++ b/limereport/objectinspector/propertyItems/lrcolorpropitem.cpp @@ -28,8 +28,10 @@ * GNU General Public License for more details. * ****************************************************************************/ #include "lrcolorpropitem.h" +#include "lrglobal.h" #include "editors/lrcoloreditor.h" #include +#include namespace{ LimeReport::ObjectPropItem * createColorPropItem( @@ -58,22 +60,20 @@ bool ColorPropItem::paint(QPainter *painter, const StyleOptionViewItem &option, { if (index.column()==1){ painter->save(); + + QStyle* style = option.widget ? option.widget->style() : QApplication::style(); QPen pen; - - if (option.state & QStyle::State_Selected){ - pen.setWidth(2); - pen.setColor(option.palette.brightText().color()); - }else { - pen.setColor(Qt::gray); - } - + QColor penColor = isColorDark(propertyValue().value()) ? Qt::transparent : Qt::darkGray; + pen.setColor(penColor); painter->setPen(pen); - painter->setBrush(propertyValue().value()); - QRect rect = option.rect.adjusted(4,4,-4,-6); - rect.setWidth(rect.height()); - painter->setRenderHint(QPainter::Antialiasing); - painter->drawEllipse(rect); + int border = (option.rect.height() - style->pixelMetric(QStyle::PM_IndicatorWidth))/2; + + QRect rect(option.rect.x()+border,option.rect.y()+border, + style->pixelMetric(QStyle::PM_IndicatorWidth), + style->pixelMetric(QStyle::PM_IndicatorWidth)); + painter->drawRect(rect); + painter->restore(); return true; } else return false; diff --git a/limereport/objectsbrowser/lrobjectbrowser.cpp b/limereport/objectsbrowser/lrobjectbrowser.cpp index e657a81..a5ee464 100644 --- a/limereport/objectsbrowser/lrobjectbrowser.cpp +++ b/limereport/objectsbrowser/lrobjectbrowser.cpp @@ -35,7 +35,8 @@ namespace LimeReport{ ObjectBrowser::ObjectBrowser(QWidget *parent) - :QWidget(parent), m_report(NULL), m_mainWindow(NULL), m_changingItemSelection(false) + :QWidget(parent), m_report(NULL), m_mainWindow(NULL), + m_changingItemSelection(false), m_movingItem(false) { QVBoxLayout *layout = new QVBoxLayout(this); setLayout(layout); @@ -86,6 +87,7 @@ void ObjectBrowser::slotClear() } void ObjectBrowser::fillNode(QTreeWidgetItem* parentNode, BaseDesignIntf* reportItem, BaseDesignIntf *ignoredItem){ + foreach (BaseDesignIntf* item, reportItem->childBaseItems()) { if (item != ignoredItem){ ObjectBrowserNode* treeItem = new ObjectBrowserNode(parentNode); @@ -93,11 +95,11 @@ void ObjectBrowser::fillNode(QTreeWidgetItem* parentNode, BaseDesignIntf* report treeItem->setObject(item); treeItem->setIcon(0,QIcon(":/items/"+extractClassName(item->metaObject()->className()))); connect(item, SIGNAL(propertyObjectNameChanged(QString,QString)), - this, SLOT(slotPropertyObjectNameChanged(QString,QString))); + this, SLOT(slotPropertyObjectNameChanged(QString,QString)), Qt::UniqueConnection); ItemDesignIntf* i = dynamic_cast(item); if (i){ connect(i, SIGNAL(itemLocationChanged(BaseDesignIntf*,BaseDesignIntf*)), - this, SLOT(slotItemParentChanged(BaseDesignIntf*,BaseDesignIntf*))); + this, SLOT(slotItemParentChanged(BaseDesignIntf*,BaseDesignIntf*)), Qt::UniqueConnection); } m_itemsMap.insert(item,treeItem); parentNode->addChild(treeItem); @@ -283,7 +285,9 @@ void ObjectBrowser::slotActivePageUpdated(LimeReport::PageDesignIntf *) buildTree(); } -void ObjectBrowser::slotItemParentChanged(BaseDesignIntf* item, BaseDesignIntf* parent) + + +void ObjectBrowser::moveItemNode(BaseDesignIntf* item, BaseDesignIntf* parent) { if (m_itemsMap.contains(item) && m_itemsMap.contains(parent)){ m_itemsMap.value(item)->parent()->removeChild(m_itemsMap.value(item)); @@ -293,7 +297,24 @@ void ObjectBrowser::slotItemParentChanged(BaseDesignIntf* item, BaseDesignIntf* item->setSelected(true); m_changingItemSelection = false; } +} +void ObjectBrowser::slotItemParentChanged(BaseDesignIntf* item, BaseDesignIntf* parent) +{ + if (!m_movingItem){ + m_movingItem = true; + moveItemNode(item, parent); + m_movingItem = false; + foreach(QObject* di, m_defferedItems){ + BaseDesignIntf* b = dynamic_cast(di); + if (b) + moveItemNode(b, parent); + } + m_defferedItems.clear(); + } else { + if (!m_defferedItems.contains(item)) + m_defferedItems.append(item); + } } void ObjectBrowserNode::setObject(QObject *value) diff --git a/limereport/objectsbrowser/lrobjectbrowser.h b/limereport/objectsbrowser/lrobjectbrowser.h index 4bdcb52..9d9482b 100644 --- a/limereport/objectsbrowser/lrobjectbrowser.h +++ b/limereport/objectsbrowser/lrobjectbrowser.h @@ -60,6 +60,7 @@ protected: void buildTree(BaseDesignIntf *ignoredItem = 0); void removeItem(BaseDesignIntf* item); void findAndRemove(QTreeWidgetItem *node, BaseDesignIntf *item); + void moveItemNode(BaseDesignIntf* item, BaseDesignIntf* parent); private slots: // void slotObjectNameChanged(const QString& objectName); void slotPropertyObjectNameChanged(const QString& oldName, const QString& newName); @@ -82,6 +83,8 @@ private: QTreeWidget* m_treeView; QMap m_itemsMap; bool m_changingItemSelection; + bool m_movingItem; + QList m_defferedItems; }; } //namespace LimeReport diff --git a/limereport/report.qrc b/limereport/report.qrc index 7e5f56d..fe70d36 100644 --- a/limereport/report.qrc +++ b/limereport/report.qrc @@ -179,5 +179,8 @@ images/deleteDialog.png images/copy3.png images/paste2.png + images/property.png + images/signal.png + images/object.png diff --git a/limereport/scripteditor/lrscripteditor.cpp b/limereport/scripteditor/lrscripteditor.cpp index 1b84f25..f40068a 100644 --- a/limereport/scripteditor/lrscripteditor.cpp +++ b/limereport/scripteditor/lrscripteditor.cpp @@ -219,8 +219,6 @@ void ScriptEditor::slotOnCurrentChanged(const QModelIndex &to, const QModelIndex } } - - QString ReportStructureCompleater::pathFromIndex(const QModelIndex &index) const { QStringList dataList; @@ -274,7 +272,8 @@ void ReportStructureCompleater::updateCompleaterModel(ReportEnginePrivateInterfa { if (report){ m_model.clear(); - + QIcon signalIcon(":/report/images/signal"); + QIcon propertyIcon(":/report/images/property"); addAdditionalDatawords(report->dataManager()); for ( int i = 0; i < report->pageCount(); ++i){ @@ -282,17 +281,27 @@ void ReportStructureCompleater::updateCompleaterModel(ReportEnginePrivateInterfa QStandardItem* itemNode = new QStandardItem; itemNode->setText(page->pageItem()->objectName()); + itemNode->setIcon(QIcon(":/report/images/object")); m_model.invisibleRootItem()->appendRow(itemNode); - QStringList slotsNames = extractSlotNames(page->pageItem()); - foreach(QString slotName, slotsNames){ + QStringList items = extractSignalNames(page->pageItem()); + foreach(QString slotName, items){ QStandardItem* slotItem = new QStandardItem; slotItem->setText(slotName); + slotItem->setIcon(signalIcon); itemNode->appendRow(slotItem); } + items = extractProperties(page->pageItem()); + foreach(QString propertyName, items){ + QStandardItem* properyItem = new QStandardItem; + properyItem->setText(propertyName); + properyItem->setIcon(propertyIcon); + itemNode->appendRow(properyItem); + } foreach (BaseDesignIntf* item, page->pageItem()->childBaseItems()){ addChildItem(item, itemNode->text(), m_model.invisibleRootItem()); } + } } } @@ -303,7 +312,7 @@ void ReportStructureCompleater::updateCompleaterModel(DataSourceManager *dataMan addAdditionalDatawords(dataManager); } -QStringList ReportStructureCompleater::extractSlotNames(BaseDesignIntf *item) +QStringList ReportStructureCompleater::extractSignalNames(BaseDesignIntf *item) { QStringList result; if (!item) return result; @@ -312,7 +321,11 @@ QStringList ReportStructureCompleater::extractSlotNames(BaseDesignIntf *item) for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { if (mo->method(i).methodType() == QMetaMethod::Signal) { +#ifndef HAVE_QT4 result.append(QString::fromLatin1(mo->method(i).name())); +#else + result.append(QString::fromLatin1(mo->method(i).signature())); +#endif } } mo = mo->superClass(); @@ -321,25 +334,66 @@ QStringList ReportStructureCompleater::extractSlotNames(BaseDesignIntf *item) return result; } +QStringList ReportStructureCompleater::extractProperties(BaseDesignIntf *item) +{ + QStringList result; + if (!item) return result; + QMetaObject const * mo = item->metaObject(); + while (mo){ + for(int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) + { + result.append(QString::fromLatin1(mo->property(i).name())); + } + mo = mo->superClass(); + } + result.sort(); + return result; +} + void ReportStructureCompleater::addChildItem(BaseDesignIntf *item, const QString &pageName, QStandardItem *parent) { if (!item) return; + QIcon signalIcon(":/report/images/signal"); + QIcon propertyIcon(":/report/images/property"); + QStandardItem* itemNode = new QStandardItem; itemNode->setText(pageName+"_"+item->objectName()); + itemNode->setIcon(QIcon(":/report/images/object")); parent->appendRow(itemNode); - QStringList slotNames = extractSlotNames(item); - foreach(QString slotName, slotNames){ + QStringList items; + + if (!m_signals.contains(item->metaObject()->className())){ + items = extractSignalNames(item); + m_signals.insert(item->metaObject()->className(),items); + } else { + items = m_signals.value(item->metaObject()->className()); + } + + foreach(QString slotName, items){ QStandardItem* slotItem = new QStandardItem; slotItem->setText(slotName); + slotItem->setIcon(signalIcon); itemNode->appendRow(slotItem); } - //BandDesignIntf* band = dynamic_cast(item); - //if (band){ + + if (!m_properties.contains(item->metaObject()->className())){ + items = extractProperties(item); + m_properties.insert(item->metaObject()->className(),items); + } else { + items = m_properties.value(item->metaObject()->className()); + } + + foreach(QString propertyName, items){ + QStandardItem* properyItem = new QStandardItem; + properyItem->setText(propertyName); + properyItem->setIcon(propertyIcon); + itemNode->appendRow(properyItem); + } + foreach (BaseDesignIntf* child, item->childBaseItems()){ addChildItem(child, pageName, parent); } - //} } } // namespace LimeReport diff --git a/limereport/scripteditor/lrscripteditor.h b/limereport/scripteditor/lrscripteditor.h index 666dbc9..e9e0aa6 100644 --- a/limereport/scripteditor/lrscripteditor.h +++ b/limereport/scripteditor/lrscripteditor.h @@ -33,11 +33,14 @@ public: void updateCompleaterModel(ReportEnginePrivateInterface* report); void updateCompleaterModel(DataSourceManager* dataManager); protected: - QStringList extractSlotNames(BaseDesignIntf* item); + QStringList extractSignalNames(BaseDesignIntf* item); + QStringList extractProperties(BaseDesignIntf* item); void addChildItem(BaseDesignIntf *item, const QString &pageName, QStandardItem *parent); void addAdditionalDatawords(DataSourceManager *dataManager); private: QStandardItemModel m_model; + QMap m_properties; + QMap m_signals; }; class ScriptEditor : public QWidget diff --git a/limereport/scripteditor/lrscripthighlighter.cpp b/limereport/scripteditor/lrscripthighlighter.cpp index 1dac14b..0af09ba 100644 --- a/limereport/scripteditor/lrscripthighlighter.cpp +++ b/limereport/scripteditor/lrscripthighlighter.cpp @@ -1,4 +1,5 @@ #include "lrscripthighlighter.h" +#include "lrglobal.h" #include #include @@ -180,17 +181,11 @@ bool ScriptHighlighter::isKeyWord(const QString& word) return false; } -bool ScriptHighlighter::isDark(QColor color) -{ - double a = 1 - ( 0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255; - return (a < 0.5); -} - ScriptHighlighter::ScriptHighlighter(QTextDocument* parent): QSyntaxHighlighter(parent) { - if ( isDark(QPalette().background().color())){ + if ( isColorDark(QPalette().background().color())){ m_formats[NumberFormat].setForeground(Qt::darkBlue); m_formats[StringFormat].setForeground(Qt::darkGreen); m_formats[KeywordFormat].setForeground(Qt::darkYellow); @@ -205,6 +200,13 @@ ScriptHighlighter::ScriptHighlighter(QTextDocument* parent): } } +TextBlockData::~TextBlockData() +{ + foreach(ParenthesisInfo* info, m_parentheses){ + delete info; + } +} + QVector TextBlockData::parentheses() { return m_parentheses; diff --git a/limereport/scripteditor/lrscripthighlighter.h b/limereport/scripteditor/lrscripthighlighter.h index 4580484..46c4d24 100644 --- a/limereport/scripteditor/lrscripthighlighter.h +++ b/limereport/scripteditor/lrscripthighlighter.h @@ -23,6 +23,7 @@ class TextBlockData : public QTextBlockUserData { public: TextBlockData(){} + ~TextBlockData(); QVector parentheses(); void insert(ParenthesisInfo *info); @@ -41,7 +42,6 @@ protected: }; QTextCharFormat m_formats[FormatsCount]; bool isKeyWord(const QString& word); - bool isDark(QColor color); void createParentheisisInfo(const char& literal, TextBlockData *data, const QString& text); }; diff --git a/limereport/serializators/lrxmlreader.cpp b/limereport/serializators/lrxmlreader.cpp index 9b15b30..7439fc4 100644 --- a/limereport/serializators/lrxmlreader.cpp +++ b/limereport/serializators/lrxmlreader.cpp @@ -37,6 +37,13 @@ #include +#ifdef BUILD_WITH_EASY_PROFILER +#include "easy/profiler.h" +#else +# define EASY_BLOCK(...) +# define EASY_END_BLOCK +#endif + namespace LimeReport{ XMLReader::XMLReader() @@ -107,6 +114,7 @@ bool XMLReader::readItem(QObject *item) void XMLReader::readItemFromNode(QObject* item,QDomElement *node) { + EASY_BLOCK("readItemFromNode"); ObjectLoadingStateIntf* lf = dynamic_cast(item); if(lf) lf->objectLoadStarted(); for (int i=0;ichildNodes().count();i++){ @@ -129,6 +137,7 @@ void XMLReader::readItemFromNode(QObject* item,QDomElement *node) if (baseItem) baseItem->parentObjectLoadFinished(); } } + EASY_END_BLOCK; } QString XMLReader::lastError() @@ -183,13 +192,16 @@ QVariant XMLReader::getValue(QDomElement *node) void XMLReader::readQObject(QObject* item, QDomElement* node) { + EASY_BLOCK("readQObject"); QObject* childItem = qvariant_cast(item->property(node->nodeName().toLatin1())); if (childItem) readItemFromNode(childItem,node); + EASY_END_BLOCK; } void XMLReader::readCollection(QObject *item, QDomElement *node) { + EASY_BLOCK("readCollection") ICollectionContainer* collection = dynamic_cast(item); if (collection){ QString collectionName = node->nodeName(); @@ -201,6 +213,7 @@ void XMLReader::readCollection(QObject *item, QDomElement *node) } collection->collectionLoadFinished(collectionName); } + EASY_END_BLOCK; } void XMLReader::readTranslation(QObject* item, QDomElement* node) diff --git a/limereport/translationeditor/translationeditor.cpp b/limereport/translationeditor/translationeditor.cpp index 4db0a6a..f4f4ce6 100644 --- a/limereport/translationeditor/translationeditor.cpp +++ b/limereport/translationeditor/translationeditor.cpp @@ -78,8 +78,14 @@ void TranslationEditor::updateUi() ui->lvLanguages->addItem(QLocale::languageToString(language)); } if (!translations->keys().isEmpty()){ - ui->lvLanguages->item(0)->setSelected(true); - activateLanguage(getLanguageByName(ui->lvLanguages->item(0)->text())); + if (ui->lvLanguages->count()!=0){ + ui->lvLanguages->item(0)->setSelected(true); + activateLanguage(getLanguageByName(ui->lvLanguages->item(0)->text())); + } else { + //activateLanguage(QLocale::AnyLanguage); + ui->twPages->clear(); + ui->tbStrings->setRowCount(0); + } } } } diff --git a/translations/limereport_ru.ts b/translations/limereport_ru.ts index 7d0f7ef..d66a67a 100644 --- a/translations/limereport_ru.ts +++ b/translations/limereport_ru.ts @@ -1,6 +1,60 @@ + + $ClassName$ + + $ClassName$ + + + + + ChartItemEditor + + Series editor + + + + Series + + + + Add + + + + Delete + + + + Name + Имя + + + Values field + + + + Color + + + + Type + Тип + + + Labels field + + + + Ok + Ок + + + Series name + + + LRVariableDialog @@ -23,6 +77,21 @@ Attention Внимание + + Mandatory + + + + + LanguageSelectDialog + + Dialog + + + + Language + + LimeReport::AboutDialog @@ -319,6 +388,10 @@ p, li { white-space: pre-wrap; } Start new page Начинать новую страницу + + Keep top space + + LimeReport::BaseDesignIntf @@ -350,6 +423,10 @@ p, li { white-space: pre-wrap; } All borders Внешние границы + + Create Horizontal Layout + + LimeReport::ConnectionDesc @@ -440,6 +517,10 @@ p, li { white-space: pre-wrap; } already exists! уже существует! + + Port + + LimeReport::DataBand @@ -447,6 +528,10 @@ p, li { white-space: pre-wrap; } Data Данные + + Use alternate background color + + LimeReport::DataBrowser @@ -595,6 +680,37 @@ p, li { white-space: pre-wrap; } Внешние переменные + + LimeReport::DialogDesignerManager + + Edit Widgets + + + + Widget Box + + + + Object Inspector + Инспектор объектов + + + Property Editor + + + + Signals && Slots Editor + + + + Resource Editor + + + + Action Editor + + + LimeReport::EnumPropItem @@ -785,6 +901,42 @@ p, li { white-space: pre-wrap; } VerticalUniform Вертикально равномерно + + Pie + + + + VerticalBar + + + + HorizontalBar + + + + LegendAlignTop + + + + LegendAlignCenter + + + + LegendAlignBottom + + + + TitleAlignLeft + + + + TitleAlignRight + + + + TitleAlignCenter + + LimeReport::FlagsPropItem @@ -894,6 +1046,10 @@ p, li { white-space: pre-wrap; } Image Изображение + + Watermark + + LimeReport::ItemLocationPropItem @@ -1007,6 +1163,14 @@ p, li { white-space: pre-wrap; } Page Footer Нижний колонтитул + + Print on first page + + + + Print on last page + + LimeReport::PageHeader @@ -1021,6 +1185,22 @@ p, li { white-space: pre-wrap; } Paste Вставить + + Page is TOC + + + + Reset page number + + + + Full page + + + + Set page size to printer + + LimeReport::PreviewReportWidget @@ -1553,6 +1733,66 @@ p, li { white-space: pre-wrap; } Property value Значение + + endlessHeight + + + + extendedHeight + + + + isExtendedInDesignMode + + + + pageIsTOC + + + + setPageSizeToPrinter + + + + fillInSecondPass + + + + chartTitle + + + + chartType + + + + drawLegendBorder + + + + labelsField + + + + legendAlign + + + + series + + + + titleAlign + + + + watermark + + + + keepTopSpace + + LimeReport::RectMMPropItem @@ -1594,6 +1834,10 @@ p, li { white-space: pre-wrap; } Wrong file format Неверный формат файла + + Translations + + LimeReport::ReportDesignWindow @@ -1687,11 +1931,11 @@ p, li { white-space: pre-wrap; } Hide left panel - Спрятать левую панель + Спрятать левую панель Hide right panel - Спрятать правую панель + Спрятать правую панель Report Tools @@ -1837,6 +2081,46 @@ p, li { white-space: pre-wrap; } Report has been modified! Do you want save the report? Отчет был изменен! Сохранить изменения? + + Hide left panel | Alt+L + + + + Hide right panel | Alt+R + + + + Delete dialog + + + + Add new dialog + + + + Widget Box + + + + Property Editor + + + + Action Editor + + + + Resource Editor + + + + SignalSlot Editor + + + + Dialog Designer Tools + + LimeReport::ReportEnginePrivate @@ -1858,6 +2142,22 @@ p, li { white-space: pre-wrap; } This preview is no longer valid. Файл отчета "%1" изменил имя или был удален. + + Designer not found! + + + + Language %1 already exists + + + + Warning + Предупреждение + + + The language will change after the application is restarted + + LimeReport::ReportFooter @@ -2042,12 +2342,31 @@ This preview is no longer valid. Диалог %1 уже существует + + LimeReport::ScriptEditor + + Form + Форма + + + Data + Данные + + + Functions + Функции + + LimeReport::ScriptEngineContext Dialog with name: %1 can`t be created Диалог %1 не может быть создан + + Error + Ошибка + LimeReport::ScriptEngineManager @@ -2103,6 +2422,50 @@ This preview is no longer valid. GENERAL ОБЩИЕ + + Function manger with name "%1" already exists! + + + + FieldName + + + + Field %1 not found in %2! + + + + Datasource + Источник данных + + + ValueField + + + + KeyField + + + + KeyFieldValue + + + + Unique identifier + + + + Content + Содержимое + + + Indent + + + + datasourceName + + LimeReport::SettingDialog @@ -2138,6 +2501,14 @@ This preview is no longer valid. Suppress absent fields and variables warning Не выводить сообщения об отсутствии полей или переменных + + Language + + + + Use dark theme + + LimeReport::SubDetailBand @@ -2221,6 +2592,14 @@ This preview is no longer valid. TextItem " %1 " not found! Текстовый элемент %1 не найден! + + Transparent + + + + Watermark + + LimeReport::TextItemEditor @@ -2234,7 +2613,7 @@ This preview is no longer valid. Functions - Функции + Функции Editor settings @@ -2250,7 +2629,7 @@ This preview is no longer valid. Data - Данные + Данные ... @@ -2269,6 +2648,53 @@ This preview is no longer valid. + + LimeReport::TranslationEditor + + Form + Форма + + + Languages + + + + ... + ... + + + Pages + + + + Strings + + + + Source Text + + + + Translation + + + + Checked + + + + Report Item + + + + Property + + + + Source text + + + LimeReport::VariablesHolder @@ -2486,5 +2912,25 @@ This preview is no longer valid. Object with name %1 already exists! Объект %1 уже существует! + + Chart Item + + + + First + + + + Second + + + + Thrid + + + + Datasource manager not found + + diff --git a/translations/limereport_zh.ts b/translations/limereport_zh.ts new file mode 100644 index 0000000..dec023c --- /dev/null +++ b/translations/limereport_zh.ts @@ -0,0 +1,2507 @@ + + + + + LRVariableDialog + + Variable + 变量 + + + Name + 名称 + + + Value + + + + Type + 类型 + + + Attention + 注意 + + + + LimeReport::AboutDialog + + About + 关于 + + + Lime Report + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/report/images/logo_100.png" height="100" style="float: left;" /><span style=" font-size:12pt; font-weight:600;">Report engine for </span><span style=" font-size:12pt; font-weight:600; color:#7faa18;">Qt</span><span style=" font-size:12pt; font-weight:600;"> framework</span></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">LimeReport - multi-platform C++ library written using Qt framework and intended for software developers that would like to add into their application capability to form report or print forms generated using templates.</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">Official web site : </span><a href="www.limereport.ru"><span style=" font-size:11pt; text-decoration: underline; color:#0000ff;">www.limereport.ru</span></a></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt; text-decoration: underline; color:#0000ff;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600; color:#000000;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Copyright 2015 Arin Alexander. All rights reserved.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/report/images/logo_100.png" height="100" style="float: left;" /><span style=" font-size:12pt; font-weight:600; color:#7faa18;">Qt</span><span style=" font-size:12pt; font-weight:600;">报表引擎</span></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">LimeReport - QT框架多平台C++库,帮助开发者给应用增加基于模板生成报表及打印报表功能。</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">官方网站: </span><a href="www.limereport.ru"><span style=" font-size:11pt; text-decoration: underline; color:#0000ff;">www.limereport.ru</span></a></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt; text-decoration: underline; color:#0000ff;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">该库基于提供帮助目的发布,但不提供任何担保,不以任何形式提供其适销性或适用于某一特定用途的默示保证。</span></p> +<p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600; color:#000000;"><br /></p> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">版权 2015 Arin Alexander.所有权利保留.</span></p></body></html> + + + Author + 作者 + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Arin Alexander</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">arin_a@bk.ru</p></body></html> + + + + License + 许可 + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">(c) 2015 Arin Alexander arin_a@bk.ru</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="SEC1"></a><span style=" font-family:'sans-serif'; font-weight:600;">G</span><span style=" font-family:'sans-serif'; font-weight:600;">NU LESSER GENERAL PUBLIC LICENSE</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Version 2.1, February 1999</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">Copyright (C) 1991, 1999 Free Software Foundation, Inc.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">Everyone is permitted to copy and distribute verbatim copies</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">of this license document, but changing it is not allowed.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'monospace';"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">[This is the first released version of the Lesser GPL. It also counts</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';"> as the successor of the GNU Library Public License, version 2, hence</span></p> +<p style=" margin-top:0px; margin-bottom:15px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';"> the version number 2.1.]</span></p> +<p style=" margin-top:15px; margin-bottom:15px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="SEC2"></a><span style=" font-family:'sans-serif'; font-weight:600;">P</span><span style=" font-family:'sans-serif'; font-weight:600;">reamble</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">We call this license the &quot;Lesser&quot; General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a &quot;work based on the library&quot; and a &quot;work that uses the library&quot;. The former contains code derived from the library, whereas the latter must be combined with the library in order to run.</span></p> +<p style=" margin-top:15px; margin-bottom:15px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="SEC3"></a><span style=" font-family:'sans-serif'; font-weight:600; color:#333333;">T</span><span style=" font-family:'sans-serif'; font-weight:600; color:#333333;">ERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">0.</span><span style=" font-family:'sans-serif';"> This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called &quot;this License&quot;). Each licensee is addressed as &quot;you&quot;.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">A &quot;library&quot; means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">The &quot;Library&quot;, below, refers to any such software library or work which has been distributed under these terms. A &quot;work based on the Library&quot; means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term &quot;modification&quot;.)</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">&quot;Source code&quot; for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">1.</span><span style=" font-family:'sans-serif';"> You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">2.</span><span style=" font-family:'sans-serif';"> You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:</span></p> +<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'sans-serif';" style=" margin-top:19px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">a)</span><span style=" font-size:16px;"> The modified work must itself be a software library.</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">b)</span><span style=" font-size:16px;"> You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">c)</span><span style=" font-size:16px;"> You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:19px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">d)</span><span style=" font-size:16px;"> If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.</span></li></ul> +<p style=" margin-top:15px; margin-bottom:15px; margin-left:38px; margin-right:19px; -qt-block-indent:1; text-indent:0px;"><span style=" font-family:'sans-serif';">(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">3.</span><span style=" font-family:'sans-serif';"> You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">This option is useful when you wish to copy part of the code of the Library into a program that is not a library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">4.</span><span style=" font-family:'sans-serif';"> You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">5.</span><span style=" font-family:'sans-serif';"> A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a &quot;work that uses the Library&quot;. Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">However, linking a &quot;work that uses the Library&quot; with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a &quot;work that uses the library&quot;. The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">When a &quot;work that uses the Library&quot; uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">6.</span><span style=" font-family:'sans-serif';"> As an exception to the Sections above, you may also combine or link a &quot;work that uses the Library&quot; with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:</span></p> +<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'sans-serif';" style=" margin-top:19px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">a)</span><span style=" font-size:16px;"> Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable &quot;work that uses the Library&quot;, as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">b)</span><span style=" font-size:16px;"> Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">c)</span><span style=" font-size:16px;"> Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">d)</span><span style=" font-size:16px;"> If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:19px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">e)</span><span style=" font-size:16px;"> Verify that the user has already received a copy of these materials or that you have already sent this user a copy.</span></li></ul> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">For an executable, the required form of the &quot;work that uses the Library&quot; must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">7.</span><span style=" font-family:'sans-serif';"> You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:</span></p> +<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'sans-serif';" style=" margin-top:19px; margin-bottom:0px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">a)</span><span style=" font-size:16px;"> Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.</span></li> +<li style=" font-family:'sans-serif';" style=" margin-top:0px; margin-bottom:19px; margin-left:38px; margin-right:19px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16px; font-weight:600;">b)</span><span style=" font-size:16px;"> Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.</span></li></ul> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">8.</span><span style=" font-family:'sans-serif';"> You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">9.</span><span style=" font-family:'sans-serif';"> You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">10.</span><span style=" font-family:'sans-serif';"> Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">11.</span><span style=" font-family:'sans-serif';"> If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">12.</span><span style=" font-family:'sans-serif';"> If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">13.</span><span style=" font-family:'sans-serif';"> The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and &quot;any later version&quot;, you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">14.</span><span style=" font-family:'sans-serif';"> If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">NO WARRANTY</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">15.</span><span style=" font-family:'sans-serif';"> BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY &quot;AS IS&quot; WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600;">16.</span><span style=" font-family:'sans-serif';"> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</span></p> +<p style=" margin-top:15px; margin-bottom:15px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif'; font-weight:600; color:#333333;">END OF TERMS AND CONDITIONS</span></p> +<p style=" margin-top:15px; margin-bottom:15px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="SEC4"></a><span style=" font-family:'sans-serif'; font-weight:600; color:#333333;">H</span><span style=" font-family:'sans-serif'; font-weight:600; color:#333333;">ow to Apply These Terms to Your New Libraries</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the &quot;copyright&quot; line and a pointer to where the full notice is found.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace'; font-style:italic;">one line to give the library's name and an idea of what it does.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">Copyright (C) </span><span style=" font-family:'monospace'; font-style:italic;">year</span><span style=" font-family:'monospace';"> </span><span style=" font-family:'monospace'; font-style:italic;">name of author</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'monospace'; font-style:italic;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">This library is free software; you can redistribute it and/or</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">modify it under the terms of the GNU Lesser General Public</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">License as published by the Free Software Foundation; either</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">version 2.1 of the License, or (at your option) any later version.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'monospace';"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">This library is distributed in the hope that it will be useful,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">but WITHOUT ANY WARRANTY; without even the implied warranty of</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">Lesser General Public License for more details.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'monospace';"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">You should have received a copy of the GNU Lesser General Public</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">License along with this library; if not, write to the Free Software</span></p> +<p style=" margin-top:0px; margin-bottom:15px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">Also add information on how to contact you by electronic and paper mail.</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">You should also get your employer (if you work as a programmer) or your school, if any, to sign a &quot;copyright disclaimer&quot; for the library, if necessary. Here is a sample; alter the names:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">Yoyodyne, Inc., hereby disclaims all copyright interest in</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">the library `Frob' (a library for tweaking knobs) written</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">by James Random Hacker.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'monospace';"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace'; font-style:italic;">signature of Ty Coon</span><span style=" font-family:'monospace';">, 1 April 1990</span></p> +<p style=" margin-top:0px; margin-bottom:15px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'monospace';">Ty Coon, President of Vice</span></p> +<p style=" margin-top:19px; margin-bottom:19px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'sans-serif';">That's all there is to it!</span></p></body></html> + + + + Close + 关闭 + + + Version 1.1.1 + 版本 1.1.1 + + + + LimeReport::AlignmentPropItem + + Left + + + + Right + + + + Center + 居中 + + + Justify + 对齐 + + + Top + + + + Botom + + + + horizontal + 水平 + + + vertical + 垂直 + + + + LimeReport::BandDesignIntf + + DataBand + 数据带 + + + DataHeaderBand + 数据带头 + + + DataFooterBand + 数据带脚 + + + ReportHeader + 表头 + + + ReportFooter + 表脚 + + + PageHeader + 页眉 + + + PageFooter + 页脚 + + + SubDetailBand + 子细节带 + + + SubDetailHeaderBand + 子细节带头 + + + SubDetailFooterBand + 子细节带脚 + + + GroupBandHeader + 组带头 + + + GroupBandFooter + 组带脚 + + + TearOffBand + 分离带 + + + connected to + 连接到 + + + Bring to top + 置顶 + + + Send to back + 置底 + + + Auto height + 自动高度 + + + Splittable + 可拆分 + + + Keep bottom space + 保持底部距离 + + + Start from new page + 从新页开始 + + + Start new page + 开始新页 + + + + LimeReport::BaseDesignIntf + + Copy + 复制 + + + Cut + 剪切 + + + Paste + 粘贴 + + + Bring to top + 置顶 + + + Send to back + 置底 + + + No borders + 无边框 + + + All borders + 所有边框 + + + + LimeReport::ConnectionDesc + + defaultConnection + 默认连接 + + + + LimeReport::ConnectionDialog + + Connection + 数据连接 + + + Connection Name + 连接名称 + + + Use default application connection + 使用默认应用连接 + + + Driver + 驱动 + + + Server + 服务器 + + + Port + 端口 + + + User + 用户名 + + + Password + 密码 + + + Database + 数据库 + + + ... + + + + Auto connect + 自动连接 + + + Dont keep credentals in lrxml + 不在lrxml文件中保存凭证 + + + Check connection + 检查连接 + + + Cancel + 取消 + + + Ok + 确定 + + + Error + 错误 + + + Connection succsesfully established! + 连接成功建立! + + + Connection Name is empty + 连接名为空 + + + Connection with name + 连接 + + + already exists! + 已经存在! + + + defaultConnection + 默认连接 + + + + LimeReport::DataBand + + Data + 数据带 + + + + LimeReport::DataBrowser + + Datasources + 数据源 + + + Add database connection + 新建数据库连接 + + + ... + + + + Add new datasource + 新建数据源 + + + View data + 查看数据 + + + Change datasource + 编辑数据源 + + + Delete datasource + 删除数据源 + + + Show error + 显示错误 + + + Variables + 变量 + + + Add new variable + 新增变量 + + + Edit variable + 编辑变量 + + + Delete variable + 删除变量 + + + Grab variable + 取得变量 + + + Attention + 注意 + + + Do you really want to delete "%1" connection? + 是否确认删除"%1"连接? + + + Report variables + 报表变量 + + + System variables + 系统变量 + + + External variables + 外部变量 + + + Do you really want to delete "%1" datasource? + 是否确认删除"%1"数据源? + + + Do you really want to delete variable "%1"? + 是否确认删除变量"%1"? + + + Error + 错误 + + + + LimeReport::DataFooterBand + + DataFooter + 数据带脚 + + + + LimeReport::DataHeaderBand + + DataHeader + 数据带头 + + + + LimeReport::DataSourceManager + + Connection "%1" is not open + 连接"%1"没有打开 + + + Variable "%1" not found! + 未找到变量"%1"! + + + Datasource "%1" not found! + 未找到数据源"%1"! + + + Connection with name "%1" already exists! + 连接 "%1" 已存在! + + + Datasource with name "%1" already exists! + 数据源 "%1" 已存在! + + + Database "%1" not found + 未找到数据库 "%1" + + + invalid connection + 无效连接 + + + + LimeReport::DataSourceModel + + Datasources + 数据源 + + + Variables + 变量 + + + External variables + 外部变量 + + + + LimeReport::EnumPropItem + + Default + 默认 + + + Portrait + 纵向 + + + Landscape + 横向 + + + NoneAutoWidth + 无自动宽度 + + + MaxWordLength + 最大词长 + + + MaxStringLength + 最大字符串长 + + + TransparentMode + 透明模式 + + + OpaqueMode + 不透明模式 + + + Angle0 + 0度 + + + Angle90 + 90度 + + + Angle180 + 180度 + + + Angle270 + 270度 + + + Angle45 + 45度 + + + Angle315 + 315度 + + + DateTime + 日期时间 + + + Double + + + + NoBrush + + + + SolidPattern + 填充 + + + Dense1Pattern + 密集1 + + + Dense2Pattern + 密集2 + + + Dense3Pattern + 密集3 + + + Dense4Pattern + 密集4 + + + Dense5Pattern + 密集5 + + + Dense6Pattern + 密集6 + + + Dense7Pattern + 密集7 + + + HorPattern + 横条纹 + + + VerPattern + 竖条纹 + + + CrossPattern + 交叉条纹 + + + BDiagPattern + 斜条纹 + + + FDiagPattern + 反斜条纹 + + + LeftToRight + 从左到右 + + + RightToLeft + 从右到左 + + + LayoutDirectionAuto + 自动布局方向 + + + LeftItemAlign + 左对齐 + + + RightItemAlign + 右对齐 + + + CenterItemAlign + 居中对齐 + + + ParentWidthItemAlign + 上层宽度对齐 + + + DesignedItemAlign + 保持设计对齐 + + + HorizontalLine + 水平线 + + + VerticalLine + 垂直线 + + + Ellipse + 椭圆 + + + Rectangle + 矩形 + + + Page + + + + Band + + + + Horizontal + 水平 + + + Vertical + 垂直 + + + VerticalUniform + 均匀垂直 + + + + LimeReport::FlagsPropItem + + NoLine + 无边框 + + + TopLine + 顶边框 + + + BottomLine + 底边框 + + + LeftLine + 左边框 + + + RightLine + 右边框 + + + + LimeReport::FontEditorWidget + + Font bold + 粗体 + + + Font Italic + 斜体 + + + Font Underline + 下划线 + + + + LimeReport::FontPropItem + + bold + 粗体 + + + italic + 斜体 + + + underline + 下划线 + + + size + 字号 + + + family + 系列 + + + + LimeReport::GroupBandFooter + + GroupFooter + 组带脚 + + + + LimeReport::GroupBandHeader + + GroupHeader + 组带头 + + + Group field not found + 未找到组字段 + + + Datasource "%1" not found! + 未找到数据源 "%1"! + + + + LimeReport::GroupFunction + + Field "%1" not found + 未找到字段 "%1" + + + Variable "%1" not found + 未找到变量 "%1" + + + Wrong script syntax "%1" + 脚本语法错误 "%1" + + + Item "%1" not found + 未找到目标项 "%1" + + + + LimeReport::ImageItem + + Image + 图像 + + + + LimeReport::ItemLocationPropItem + + Band + + + + Page + + + + + LimeReport::ItemsAlignmentEditorWidget + + Bring to top + 置顶 + + + Send to back + 置底 + + + Align to left + 左对齐 + + + Align to right + 右对齐 + + + Align to vertical center + 居中对齐 + + + Align to top + 顶部对齐 + + + Align to bottom + 底部对齐 + + + Align to horizontal center + 水平居中 + + + Set same height + 相同高度 + + + Set same width + 相同宽度 + + + + LimeReport::ItemsBordersEditorWidget + + Top line + 顶边框 + + + Bottom line + 底边框 + + + Left line + 左边框 + + + Right line + 右边框 + + + No borders + 无边框 + + + All borders + 所有边框 + + + + LimeReport::MasterDetailProxyModel + + Field: "%1" not found in "%2" child datasource + 从数据源 "%2" 中未找到字段: "%1" + + + Field: "%1" not found in "%2" master datasource + 主数据源 "%2" 中未找到字段: "%1" + + + + LimeReport::ModelToDataSource + + model is destroyed + 数据模型已销毁 + + + + LimeReport::ObjectBrowser + + Objects + 对象 + + + + LimeReport::PageFooter + + Page Footer + 页脚 + + + + LimeReport::PageHeader + + Page Header + 页眉 + + + + LimeReport::PageItemDesignIntf + + Paste + 粘贴 + + + + LimeReport::PreviewReportWidget + + Form + 表格 + + + PDF file name + PDF 文件名 + + + Report file name + 报表文件名 + + + + LimeReport::PreviewReportWindow + + Preview + 预览 + + + View + 查看 + + + Report + 报表 + + + toolBar + 工具栏 + + + Print + 打印 + + + Ctrl+P + + + + Zoom In + 放大 + + + Zoom Out + 缩小 + + + Prior Page + 上一页 + + + Next Page + 下一页 + + + Close Preview + 关闭预览 + + + Esc + + + + Edit Mode + 编辑模式 + + + Save to file + 保存 + + + Show errors + 显示错误 + + + First Page + 首页 + + + First page + 首页 + + + Last Page + 末页 + + + Print To PDF + 打印到PDF文件 + + + Fit page width + 适合页宽 + + + Fit page + 适合页高 + + + One to one + 原始尺寸 + + + Show Toolbar + 显示工具栏 + + + Show toolbar + 显示工具栏 + + + Page: + 页数: + + + Font + 字体 + + + Text align + 文本对齐 + + + of %1 + / %1 + + + + LimeReport::ProxyHolder + + Datasource has been invalidated + 数据源已失效 + + + + LimeReport::QObjectPropertyModel + + leftMargin + 左边距 + + + rightMargin + 右边距 + + + topMargin + 顶边距 + + + bottomMargin + 底边距 + + + objectName + 对象名称 + + + borders + 边框 + + + geometry + 形状 + + + itemAlign + 对齐方式 + + + pageOrientation + 页面布局 + + + pageSize + 页面规格 + + + TopLine + 顶边框 + + + BottomLine + 底边框 + + + LeftLine + 左边框 + + + RightLine + 右边框 + + + reprintOnEachPage + 重新打印每页 + + + borderLineSize + 边框线宽 + + + autoHeight + 自动高度 + + + backgroundColor + 背景颜色 + + + columnCount + 列数 + + + columnsFillDirection + 列填充方向 + + + datasource + 数据源 + + + keepBottomSpace + 保持底部空间 + + + keepFooterTogether + 保持页脚 + + + keepSubdetailTogether + 保持子细节脚 + + + printIfEmpty + 为空时打印 + + + sliceLastRow + 分割末行 + + + splittable + 可拆分 + + + alignment + 对齐 + + + angle + 角度 + + + autoWidth + 自动宽度 + + + backgroundMode + 背景模式 + + + backgroundOpacity + 背景不透明度 + + + content + 内容 + + + font + 字体 + + + fontColor + 字体颜色 + + + foregroundOpacity + 背景不透明度 + + + itemLocation + 组件位置 + + + margin + 边距 + + + stretchToMaxHeight + 拉伸到最大高度 + + + trimValue + 裁剪值 + + + lineWidth + 线宽 + + + opacity + 不透明度 + + + penStyle + 画笔样式 + + + shape + 形状 + + + shapeBrush + 画刷 + + + shapeBrushColor + 画刷颜色 + + + gridStep + 栅格长 + + + fullPage + 全页 + + + oldPrintMode + 旧打印模式 + + + borderColor + 边框颜色 + + + resetPageNumber + 重置页号 + + + alternateBackgroundColor + 变更背景色 + + + backgroundBrushStyle + 背景画刷样式 + + + startFromNewPage + 从新页开始 + + + startNewPage + 开始新页 + + + adaptFontToSize + 字体适应字号 + + + allowHTML + 允许HTML + + + allowHTMLInFields + 允许字段HTML + + + followTo + 跟随 + + + format + 格式 + + + lineSpacing + 线距 + + + textIndent + 文本缩进 + + + textLayoutDirection + 文本布局方向 + + + underlineLineSize + 下划线宽 + + + underlines + 下划线 + + + valueType + 值类型 + + + securityLevel + 安全级别 + + + testValue + 测试值 + + + whitespace + 空格 + + + resourcePath + 资源路径 + + + scale + 比例 + + + cornerRadius + 圆角半径 + + + shapeColor + 颜色 + + + layoutType + 布局类型 + + + barcodeType + 条码类型 + + + barcodeWidth + 条码宽度 + + + foregroundColor + 颜色 + + + inputMode + 输入法 + + + pdf417CodeWords + PDF417码 + + + autoSize + 自动大小 + + + center + 居中 + + + field + 字段 + + + image + 图像 + + + keepAspectRatio + 保持比例 + + + columnsCount + 列数 + + + useAlternateBackgroundColor + 使用变更背景色 + + + printBeforePageHeader + 页眉前打印 + + + maxScalePercent + 最大百分比 + + + printOnFirstPage + 打印到首页 + + + printOnLastPage + 打印到尾页 + + + printAlways + 始终打印 + + + repeatOnEachRow + 每行重复 + + + condition + 条件 + + + groupFieldName + 组字段名 + + + keepGroupTogether + 保持组脚 + + + Property Name + 属性名 + + + Property value + 属性值 + + + Warning + 警告 + + + + LimeReport::RectMMPropItem + + width + + + + height + + + + + LimeReport::RectPropItem + + width + + + + height + + + + + LimeReport::ReportDesignWidget + + Script + 脚本 + + + Report file name + 报表文件名 + + + Error + 错误 + + + Wrong file format + 文件格式错误 + + + + LimeReport::ReportDesignWindow + + New Report + 新建报表 + + + New Report Page + 新建页 + + + Delete Report Page + 删除也 + + + Edit Mode + 编辑模式 + + + Undo + 撤销 + + + Redo + 重做 + + + Copy + 复制 + + + Paste + 粘贴 + + + Cut + 剪切 + + + Settings + 设置 + + + Use grid + 使用栅格 + + + Use magnet + 使用磁力 + + + Text Item + 文本组件 + + + Save Report + 保存报表 + + + Save Report As + 另存为 + + + Load Report + 读取报表 + + + Delete item + 删除组建 + + + Zoom In + 放大 + + + Zoom Out + 缩小 + + + Render Report + 生成报表 + + + Edit layouts mode + 编辑布局模式 + + + Horizontal layout + 水平布局 + + + About + 关于 + + + Hide left panel | Alt+L + 隐藏左面板 | Alt+L + + + Hide right panel | Alt+R + 隐藏右面板 | Alt+R + + + Report Tools + 报表工具 + + + Main Tools + 工具 + + + Font + 字体 + + + Text alignment + 文本对齐 + + + Items alignment + 组件对齐 + + + Borders + 边框 + + + Report bands + 报表带 + + + Report Header + 表头 + + + Report Footer + 表脚 + + + Page Header + 页眉 + + + Page Footer + 页脚 + + + Data + 数据带 + + + Data Header + 数据带头 + + + Data Footer + 数据带脚 + + + SubDetail + 子细节带 + + + SubDetailHeader + 子细节带头 + + + SubDetailFooter + 子细节带脚 + + + GroupHeader + 组带头 + + + GroupFooter + 组带脚 + + + Tear-off Band + 分离带 + + + File + 文件 + + + Edit + 编辑 + + + Info + 信息 + + + Recent Files + 最近打开文件 + + + Object Inspector + 对象观察器 + + + Report structure + 报表结构 + + + Data Browser + 数据浏览器 + + + Script Browser + 脚本浏览器 + + + Report has been modified! Do you want save the report? + 报表已修改! 是否保存? + + + Report file name + 报表文件名 + + + Rendering report + 生成报表 + + + Abort + 关于 + + + page rendered + 报表生成 + + + Warning + 警告 + + + File "%1" not found! + 未找到文件 "%1"! + + + + LimeReport::ReportEnginePrivate + + Preview + 预览 + + + Error + 错误 + + + Report File Change + 报表文件改变 + + + The report file "%1" has changed names or been deleted. + +This preview is no longer valid. + 报表文件 "%1" 重命名或删除。 + +预览已无效。 + + + + LimeReport::ReportFooter + + Report Footer + 表脚 + + + + LimeReport::ReportHeader + + Report Header + 表头 + + + + LimeReport::ReportRender + + Error + 错误 + + + page index out of range + 页索引越界 + + + Databand "%1" not found + 未找到数据带 "%1" + + + Wrong using function %1 + 函数 %1 使用错误 + + + + LimeReport::SQLEditDialog + + Datasource + 数据源 + + + Connection + 数据连接 + + + Datasource Name + 数据源名 + + + Subdetail + 子细节 + + + Master datasource + 主数据源 + + + Subquery mode + 子查询模式 + + + Filter mode + 筛选模式 + + + SQL + + + + Preview + 预览 + + + Hide Preview + 隐藏预览 + + + Child datasource + 子数据源 + + + Fields map + 字段映射 + + + ... + + + + Data preview + 数据预览 + + + Cancel + 取消 + + + Ok + 确定 + + + Error + 错误 + + + Datasource Name is empty! + 数据源名为空! + + + SQL is empty! + SQL语句为空! + + + Datasource with name: "%1" already exists! + 数据源 "%1" 已存在! + + + defaultConnection + 默认连接 + + + Datasource with name %1 already exist + 数据源 "%1" 已存在 + + + Attention + 注意 + + + Connection is not specified + 未指定连接 + + + Refresh + 刷新 + + + + LimeReport::ScriptBrowser + + Form + 表单 + + + Functions + 函数 + + + ... + + + + Dialogs + 对话框 + + + Type + 类型 + + + Name + 名称 + + + NO CATEGORY + 无类别 + + + Error + 错误 + + + Dialog with name: %1 already exists + 对话框 %1 已存在 + + + ui file must cointain QDialog instead QWidget or QMainWindow + ui 文件必须包含 QDialog 而不是 QWidget 或 QMainWindow + + + wrong file format + 文件格式错误 + + + + LimeReport::ScriptEngineContext + + Dialog with name: %1 can`t be created + 无法创建对话框 %1 + + + + LimeReport::ScriptEngineManager + + GROUP FUNCTIONS + 组函数 + + + Value + + + + BandName + 带名称 + + + Variable %1 not found + 未找到变量 %1 + + + SYSTEM + 系统 + + + NUMBER + 数字 + + + Format + 格式 + + + Precision + 精度 + + + Locale + 区域 + + + DATE&TIME + 日期时间 + + + Seconds + + + + CurrencySymbol + 货币符号 + + + GENERAL + 通用 + + + Name + 名称 + + + + LimeReport::SettingDialog + + Designer setting + 设计器设置 + + + Designer Setting + 设计器设置 + + + Default font + 默认字体 + + + Grid + 栅格 + + + Vertical grid step + 竖栅格 + + + Horizontal grid step + 横栅格 + + + Report Setting + 报表设置 + + + Suppress absent fields and variables warning + 抑制缺失字段及变量警告 + + + + LimeReport::SubDetailBand + + SubDetail + 子细节 + + + + LimeReport::SubDetailHeaderBand + + SubDetailHeader + 子细节头 + + + + LimeReport::TearOffBand + + Tear-off Band + 分离带 + + + + LimeReport::TextAlignmentEditorWidget + + Text align left + 文本左对齐 + + + Text align center + 文本居中对齐 + + + Text align right + 文本右对齐 + + + Text align justify + 文本行对齐 + + + Text align top + 文本顶部对齐 + + + Text align bottom + 文本底部对齐 + + + + LimeReport::TextItem + + Edit + 编辑 + + + Auto height + 自动高度 + + + Allow HTML + 允许HTML + + + Allow HTML in fields + 允许字段HTML + + + Stretch to max height + 拉伸至最大高度 + + + Error + 错误 + + + TextItem " %1 " already has folower " %2 " + 文本框 "%1 " 已有 "%2 " + + + TextItem " %1 " not found! + 未找到文本框 "%1"! + + + + LimeReport::TextItemEditor + + Text Item Editor + 文本编辑器 + + + Content + 内容 + + + Data + 数据 + + + Functions + 函数 + + + Editor settings + 编辑器设置 + + + Editor font + 编辑器字体 + + + ... + + + + Ok + 确定 + + + Ctrl+Return + + + + Cancel + 取消 + + + Esc + + + + + LimeReport::VariablesHolder + + variable with name + 变量 + + + already exists! + 已存在! + + + does not exists! + 不存在! + + + + QObject + + Data + 数据带 + + + DataHeader + 数据带头 + + + DataFooter + 数据带脚 + + + GroupHeader + 组带头 + + + GroupFooter + 组带脚 + + + Page Footer + 页脚 + + + Page Header + 页眉 + + + Report Footer + 表脚 + + + Report Header + 表头 + + + SubDetail + 子细节 + + + SubDetailHeader + 子细节头 + + + SubDetailFooter + 子细节带脚 + + + Tear-off Band + 分离带 + + + alignment + 对齐 + + + Barcode Item + 条码组件 + + + HLayout + 水平布局 + + + Image Item + 图像组件 + + + Shape Item + 图形组件 + + + itemLocation + 组件位置 + + + Text Item + 文本组件 + + + Invalid connection! %1 + 无效连接 %1 + + + Master datasource "%1" not found! + 未找到主数据源 "%1"! + + + Master datasouce "%1" not found! + 未找到主数据源 "%1"! + + + Child + + + + and child + 子数据源 + + + datasouce "%1" not found! + 未找到子数据源"%1"! + + + bool + + + + QColor + + + + content + 内容 + + + datasource + 数据源 + + + field + 字段映射 + + + enum + + + + flags + + + + QFont + + + + QImage + + + + int + + + + qreal + + + + QRect + + + + QRectF + + + + geometry + 形状 + + + QString + + + + Attention! + 注意! + + + Selected elements have different parent containers + 选中元素有不同的容器 + + + Object with name %1 already exists! + 对象 %1 已存在! + + + Function %1 not found or have wrong arguments + 未找到函数 %1 或参数错误 + + + mm + 毫米 + + + Wrong file format + 文件格式错误 + + + File %1 not opened + 无法打开文件 %1 + + + Content string is empty + 字符串为空 + + + Content is empty + 字符串为空 + + +