diff --git a/CMakeLists.txt b/CMakeLists.txt index b992d62..1432d6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ if (MSVC) endif () list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/dep/superluminal/API") include(${PROJECT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake) @@ -67,8 +68,6 @@ set(SIMPLEGRAPHIC_SOURCES "ui_api.cpp" "ui_console.cpp" "ui_console.h" - "ui_debug.cpp" - "ui_debug.h" "ui_local.h" "ui_main.cpp" "ui_main.h" @@ -108,6 +107,7 @@ target_compile_definitions(SimpleGraphic "GLFW_INCLUDE_NONE" "GL_SILENCE_DEPRECATION" "SIMPLEGRAPHIC_EXPORTS" + "PERFORMANCEAPI_ENABLED=$" ) target_include_directories(SimpleGraphic @@ -126,6 +126,7 @@ find_package(Microsoft.GSL CONFIG REQUIRED) find_package(PkgConfig REQUIRED) find_package(re2 CONFIG REQUIRED) find_package(sol2 CONFIG REQUIRED) +find_package(SuperluminalAPI) find_package(Threads REQUIRED) find_package(zstd REQUIRED) find_package(ZLIB REQUIRED) @@ -187,6 +188,7 @@ target_include_directories(SimpleGraphic PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/dep/glad/include ${CMAKE_CURRENT_SOURCE_DIR}/dep/stb + ${CMAKE_CURRENT_SOURCE_DIR}/dep/superluminal/API/include ) if (CMAKE_SYSTEM_NAME MATCHES "Linux") @@ -212,6 +214,13 @@ if (WIN32) PRIVATE "winmm.lib" ) + + if (SuperluminalAPI_FOUND) + target_link_libraries(SimpleGraphic + PRIVATE + SuperluminalAPI + ) + endif () endif () if (APPLE) diff --git a/dep/superluminal/API/FindSuperluminalAPI.cmake b/dep/superluminal/API/FindSuperluminalAPI.cmake new file mode 100644 index 0000000..8d52551 --- /dev/null +++ b/dep/superluminal/API/FindSuperluminalAPI.cmake @@ -0,0 +1,83 @@ +# This module can be used to find the Superluminal API libs & headers via find_package. +# +# For example: find_package(SuperluminalAPI REQUIRED) +# +# You can use it by adding the API directory of your Superluminal install to your CMAKE_PREFIX_PATH, or alternatively +# by copying the entire API directory to a place of your own choosing and adding that location to CMAKE_PREFIX_PATH. +# +# The following (optional) variables can be set prior to issueing the find_package command: +# - SuperluminalAPI_ROOT : The root directory where the libs & headers should be found. For example \API. +# If this is not set, the libs & headers are assumed to be next to the location of FindSuperluminalAPI.cmake +# - SuperluminalAPI_USE_STATIC_RUNTIME : If this is set, the libraries linked to the static C runtime (i.e. /MT and /MTd) will be returned +# If not set, the libraries linked to the dynamic C runtime (i.e. /MD and /MDd) will be returned +# +# On completion of find_package, the following variables will be set: +# +# SuperluminalAPI_FOUND : Whether the package was found +# SuperluminalAPI_LIBS_RELEASE : The Release libraries to link against +# SuperluminalAPI_LIBS_DEBUG : The Debug libraries to link against +# SuperluminalAPI_INCLUDE_DIRS : The include directories to use +# +# In addition, if find_package completed successfully, the target "SuperluminalAPI" will be defined. +# You should prefer to consume this target via target_link_libraries(YOUR_TARGET PRIVATE SuperluminalAPI), rather than by using the above variables directly +SET(SuperluminalAPI_SEARCH_PATHS + ${CMAKE_CURRENT_LIST_DIR} + ${SuperluminalAPI_ROOT} + ) + +find_path(SuperluminalAPI_INCLUDE_DIRS Superluminal/PerformanceAPI.h + PATHS ${SuperluminalAPI_SEARCH_PATHS} + PATH_SUFFIXES include + ) + +if (CMAKE_SIZEOF_VOID_P MATCHES 4) + set(SELECTED_ARCH "x86") +else(CMAKE_SIZEOF_VOID_P MATCHES 8) + set(SELECTED_ARCH "x64") +endif() + +if(WIN32) + if (NOT (${MSVC_VERSION} LESS 1900)) # Test for VS2015 and higher (older CMake versions don't have a >= operator) + if (${SuperluminalAPI_USE_STATIC_RUNTIME}) + find_library(SuperluminalAPI_LIBS_RELEASE + NAMES PerformanceAPI_MT + PATH_SUFFIXES lib/${SELECTED_ARCH} + PATHS ${SuperluminalAPI_SEARCH_PATHS} + ) + + find_library(SuperluminalAPI_LIBS_DEBUG + NAMES PerformanceAPI_MTd + PATH_SUFFIXES lib/${SELECTED_ARCH} + PATHS ${SuperluminalAPI_SEARCH_PATHS} + ) + else() + find_library(SuperluminalAPI_LIBS_RELEASE + NAMES PerformanceAPI_MD + PATH_SUFFIXES lib/${SELECTED_ARCH} + PATHS ${SuperluminalAPI_SEARCH_PATHS} + ) + + find_library(SuperluminalAPI_LIBS_DEBUG + NAMES PerformanceAPI_MDd + PATH_SUFFIXES lib/${SELECTED_ARCH} + PATHS ${SuperluminalAPI_SEARCH_PATHS} + ) + endif() + else() + message(SEND_ERROR "Your Visual Studio version is not currently supported. Please contact Superluminal support.") + endif() +endif() + +mark_as_advanced(SuperluminalAPI_FOUND) +mark_as_advanced(SuperluminalAPI_LIBS_RELEASE) +mark_as_advanced(SuperluminalAPI_LIBS_DEBUG) +mark_as_advanced(SuperluminalAPI_INCLUDE_DIRS) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SuperluminalAPI REQUIRED_VARS SuperluminalAPI_INCLUDE_DIRS SuperluminalAPI_LIBS_RELEASE SuperluminalAPI_LIBS_DEBUG) + +if(SuperluminalAPI_FOUND AND NOT TARGET SuperluminalAPI) + add_library(SuperluminalAPI INTERFACE IMPORTED) + target_include_directories(SuperluminalAPI INTERFACE "${SuperluminalAPI_INCLUDE_DIRS}") + target_link_libraries(SuperluminalAPI INTERFACE debug "${SuperluminalAPI_LIBS_DEBUG}" optimized "${SuperluminalAPI_LIBS_RELEASE}" ) +endif() \ No newline at end of file diff --git a/dep/superluminal/API/dll/x64/PerformanceAPI.dll b/dep/superluminal/API/dll/x64/PerformanceAPI.dll new file mode 100644 index 0000000..9321687 Binary files /dev/null and b/dep/superluminal/API/dll/x64/PerformanceAPI.dll differ diff --git a/dep/superluminal/API/include/Superluminal/PerformanceAPI.h b/dep/superluminal/API/include/Superluminal/PerformanceAPI.h new file mode 100644 index 0000000..62849aa --- /dev/null +++ b/dep/superluminal/API/include/Superluminal/PerformanceAPI.h @@ -0,0 +1,284 @@ +/* +BSD LICENSE + +Copyright (c) 2019-2020 Superluminal. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + +#include "PerformanceAPI_capi.h" + +// When PERFORMANCEAPI_ENABLED is defined to 0, all calls to the PerformanceAPI (either through macro or direct function calls) will be compiled out. +#ifndef PERFORMANCEAPI_ENABLED + #ifdef _WIN32 + #define PERFORMANCEAPI_ENABLED 1 + #else + #define PERFORMANCEAPI_ENABLED 0 + #endif +#endif + +/* ------------------------------------------------------------------------ +* Documentation +* ------------------------------------------------------------------------ +* +* NOTE: The C++ free functions in this header are deprecated. They remain here only for backwards compatibility. The C functions, prefixed with PerformanceAPI_ and found in PerformanceAPI_capi.h, +* should be used instead. The C++ functions are no longer maintained and new features will be added to the C interface only. +* The InstrumentationScope helper is *not* deprecated. +* +* The Performance API can be used to augment the sampling data that is naturally collected by Superluminal Performance +* with instrumentation data. Instrumentation data is seamlessly blended in all views in the UI. +* +* To send instrumentation data to Superluminal, two mechanisms are provided: +* - The InstrumentationScope class. This class will signal the start of a scope in its constructor and signal the end of the scope in its destructor. +* InstrumentationScopes can be freely nested. +* +* - The PerformanceAPI_BeginScope/PerformanceAPI_EndScope free functions. These functions are designed for integration with existing profiling systems that, for example, +* already define their own scope-based profiling classes. +* +* Note: calls to the Begin/EndScope functions must be within the same function. For example, it is not allowed to call BeginScope in function Foo +* and EndScope in function Bar. +* +* When sending an instrumentation event through either of these mechanisms, two pieces of data can be provided: +* - The event ID [required] : This must be a static string (e.g. a regular C string literal). It is used to distinguish events +* in the UI and is displayed in all views (Instrumentation Chart, Timeline, CallGraph). +* It is important that the ID of a particular scope remains the same over the lifetime of the program: +* it's not allowed to use a string that changes for every invocation of the function/scope. +* Some examples of IDs: the name of a function ("Game::Update"), the operation being performed ("ReadFile"), etc +* +* - The event Data [optional] : This must be a string that is either dynamically allocated or a regular string literal. You are free to put +* whatever data you want in the string; there are no restrictions. The data is also free to change over the lifetime of the program. +* The intent of the data string is to include data in the event that can differ per instance. +* This data is displayed in the Instrumentation Chart and Timeline. +* Some examples of Data strings: the current frame number (for "Game::Update"), the path of the file being read (for "ReadFile"), etc +* This parameter is optional; use nullptr as argument if you don't have any contextual data. +* +* - The event Color [optional] : This is a color that will be used to display the event in the timeline. The color for a specific scope is coupled to the ID and must +* be the same over the lifetime of the program. It's an RGB value encoded as an uint32_t: RRGGBB00. +* You can use PERFORMANCEAPI_MAKE_COLOR to create the uint32_t from 3 RGB values in the range of [0, 255]. +* This parameter is optional; use PERFORMANCEAPI_DEFAULT_COLOR as argument to use the default coloring. +* +* All const char* arguments in the API are assumed to be UTF8 encoded strings (i.e. non-ASCII chars are fully supported). +*/ +namespace PerformanceAPI +{ +#if PERFORMANCEAPI_ENABLED + // An InstrumentationScope measures the time of the scope it is contained in; time starts when the constructor is called and ends when the + // destructor is called. + // An ID for the scope must be provided, with optional data and an optional color (see documentation at the top of this file for more info) + // While you can manually use this, it's usually more convenient to use the PERFORMANCEAPI_* macros + struct InstrumentationScope final + { + /** + * @param inID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + */ + InstrumentationScope(const char* inID); + + /** + * @param inID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + */ + InstrumentationScope(const char* inID, const char* inData); + + /** + * @param inID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + * @param inColor The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + InstrumentationScope(const char* inID, const char* inData, uint32_t inColor); + + /** + * @param inID The ID of this scope as an UTF16 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + */ + InstrumentationScope(const wchar_t* inID); + + /** + * @param inID The ID of this scope as an UTF16 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF16 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + */ + InstrumentationScope(const wchar_t* inID, const wchar_t* inData); + + /** + * @param inID The ID of this scope as an UTF16 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF16 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + * @param inColor The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + InstrumentationScope(const wchar_t* inID, const wchar_t* inData, uint32_t inColor); + + ~InstrumentationScope(); + }; + + // Private helper macros + #define PERFORMANCEAPI_CAT_IMPL(a, b) a##b + #define PERFORMANCEAPI_CAT(a, b) PERFORMANCEAPI_CAT_IMPL(a, b) + #define PERFORMANCEAPI_UNIQUE_IDENTIFIER(a) PERFORMANCEAPI_CAT(a, __LINE__) + + /** + * Creates an InstrumentationScope with the specified ID. + * + * @param InstrumentationID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + */ + #define PERFORMANCEAPI_INSTRUMENT(InstrumentationID) PerformanceAPI::InstrumentationScope PERFORMANCEAPI_UNIQUE_IDENTIFIER(__instrumentation_scope__)((InstrumentationID)); + + /** + * Creates an InstrumentationScope with the specified ID and runtime data + * + * @param InstrumentationID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program. See docs at the top of this file. + * @param InstrumentationData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + */ + #define PERFORMANCEAPI_INSTRUMENT_DATA(InstrumentationID, InstrumentationData) PerformanceAPI::InstrumentationScope PERFORMANCEAPI_UNIQUE_IDENTIFIER(__instrumentation_scope__)((InstrumentationID), (InstrumentationData)); + + /** + * Creates an InstrumentationScope with the specified ID and color + * + * @param InstrumentationID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program. See docs at the top of this file. + * @param InstrumentationColor The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + #define PERFORMANCEAPI_INSTRUMENT_COLOR(InstrumentationID, InstrumentationColor) PerformanceAPI::InstrumentationScope PERFORMANCEAPI_UNIQUE_IDENTIFIER(__instrumentation_scope__)((InstrumentationID), "", (InstrumentationColor)); + + /** + * Creates an InstrumentationScope with the specified ID, runtime data and color + * + * @param InstrumentationID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program. See docs at the top of this file. + * @param InstrumentationData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + * @param InstrumentationColor The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + #define PERFORMANCEAPI_INSTRUMENT_DATA_COLOR(InstrumentationID, InstrumentationData, InstrumentationColor) PerformanceAPI::InstrumentationScope PERFORMANCEAPI_UNIQUE_IDENTIFIER(__instrumentation_scope__)((InstrumentationID), (InstrumentationData), (InstrumentationColor)); + + /** + * Convenience wrapper around PERFORMANCEAPI_INSTRUMENT to create an InstrumentationScope with the name of the function as ID + */ + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION() PERFORMANCEAPI_INSTRUMENT(__FUNCTION__) + + /** + * Convenience wrapper around PERFORMANCEAPI_INSTRUMENT_DATA to create an InstrumentationScope with the name of the function as ID + * + * @param InstrumentationData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + */ + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION_DATA(InstrumentationData) PERFORMANCEAPI_INSTRUMENT_DATA(__FUNCTION__, (InstrumentationData)) + + /** + * Convenience wrapper around PERFORMANCEAPI_INSTRUMENT_COLOR to create an InstrumentationScope with the name of the function as ID + * + * @param InstrumentationColor The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION_COLOR(InstrumentationColor) PERFORMANCEAPI_INSTRUMENT_COLOR(__FUNCTION__, (InstrumentationColor)) + + /** + * Convenience wrapper around PERFORMANCEAPI_INSTRUMENT_DATA_COLOR to create an InstrumentationScope with the name of the function as ID + * + * @param InstrumentationData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + * @param InstrumentationColor The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION_DATA_COLOR(InstrumentationData, InstrumentationColor) PERFORMANCEAPI_INSTRUMENT_DATA_COLOR(__FUNCTION__, (InstrumentationData), (InstrumentationColor)) + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Deprecated functions. + // + // These functions remain here only for backwards compatibility. They're no longer maintained and new features will be added to the C interface only. + // The C functions, prefixed with PerformanceAPI_ and found in PerformanceAPI_capi.h, should be used instead. + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set the name of the current thread to the specified thread name. + * + * @param inThreadName The thread name as an UTF8 encoded string. + */ + inline void SetCurrentThreadName(const char* inThreadName) { PerformanceAPI_SetCurrentThreadName(inThreadName); } + + /** + * Begin an instrumentation event with the specified ID + * + * @param inID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + */ + inline void BeginEvent(const char* inID) { PerformanceAPI_BeginEvent(inID, nullptr, PERFORMANCEAPI_DEFAULT_COLOR); } + + /** + * Begin an instrumentation event with the specified ID + * + * @param inID The ID of this scope as an UTF16 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + */ + inline void BeginEvent(const wchar_t* inID) { PerformanceAPI_BeginEvent_Wide(inID, nullptr, PERFORMANCEAPI_DEFAULT_COLOR); } + + /** + * Begin an instrumentation event with the specified ID and runtime data + * + * @param inID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + */ + inline void BeginEvent(const char* inID, const char* inData) { PerformanceAPI_BeginEvent(inID, inData, PERFORMANCEAPI_DEFAULT_COLOR); } + + /** + * Begin an instrumentation event with the specified ID and runtime data + * + * @param inID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + * @param inColor The color for this event. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + inline void BeginEvent(const char* inID, const char* inData, uint32_t inColor) { PerformanceAPI_BeginEvent(inID, inData, inColor); } + + /** + * Begin an instrumentation event with the specified ID and runtime data + * + * @param inID The ID of this scope as an UTF16 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF16 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + */ + inline void BeginEvent(const wchar_t* inID, const wchar_t* inData) { PerformanceAPI_BeginEvent_Wide(inID, inData, PERFORMANCEAPI_DEFAULT_COLOR); } + + /** + * Begin an instrumentation event with the specified ID and runtime data + * + * @param inID The ID of this scope as an UTF16 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData The data for this scope as an UTF16 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + * @param inColor The color for this event. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + */ + inline void BeginEvent(const wchar_t* inID, const wchar_t* inData, uint32_t inColor) { PerformanceAPI_BeginEvent_Wide(inID, inData, inColor); } + + /** + * End an instrumentation event. Must be matched with a call to BeginEvent within the same function + * Note: the return value can be ignored. It is only there to prevent calls to the function from being optimized to jmp instructions as part of tail call optimization. + */ + inline PerformanceAPI_SuppressTailCallOptimization EndEvent() { return PerformanceAPI_EndEvent(); } + +#else + #define PERFORMANCEAPI_INSTRUMENT(InstrumentationID) + #define PERFORMANCEAPI_INSTRUMENT_DATA(InstrumentationID, InstrumentationData) + #define PERFORMANCEAPI_INSTRUMENT_COLOR(InstrumentationID, InstrumentationColor) + #define PERFORMANCEAPI_INSTRUMENT_DATA_COLOR(InstrumentationID, InstrumentationData, InstrumentationColor) + + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION() + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION_DATA(InstrumentationData) + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION_COLOR(InstrumentationColor) + #define PERFORMANCEAPI_INSTRUMENT_FUNCTION_DATA_COLOR(InstrumentationData, InstrumentationColor) + + inline void SetCurrentThreadName(const char* inThreadName) {} + inline void BeginEvent(const char* inID) {} + inline void BeginEvent(const char* inID, const char* inData) {} + inline void BeginEvent(const char* inID, const char* inData, uint32_t inColor) {} + inline void BeginEvent(const wchar_t* inID) {} + inline void BeginEvent(const wchar_t* inID, const wchar_t* inData) {} + inline void BeginEvent(const wchar_t* inID, const wchar_t* inData, uint32_t inColor) {} + inline void EndEvent() {} +#endif +} \ No newline at end of file diff --git a/dep/superluminal/API/include/Superluminal/PerformanceAPI_capi.h b/dep/superluminal/API/include/Superluminal/PerformanceAPI_capi.h new file mode 100644 index 0000000..74bad84 --- /dev/null +++ b/dep/superluminal/API/include/Superluminal/PerformanceAPI_capi.h @@ -0,0 +1,255 @@ +/* +BSD LICENSE + +Copyright (c) 2019-2020 Superluminal. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + +#include +#include + +// When PERFORMANCEAPI_ENABLED is defined to 0, all calls to the PerformanceAPI (either through macro or direct function calls) will be compiled out. +#ifndef PERFORMANCEAPI_ENABLED + #ifdef _WIN32 + #define PERFORMANCEAPI_ENABLED 1 + #else + #define PERFORMANCEAPI_ENABLED 0 + #endif +#endif + +#define PERFORMANCEAPI_MAJOR_VERSION 3 +#define PERFORMANCEAPI_MINOR_VERSION 0 +#define PERFORMANCEAPI_VERSION ((PERFORMANCEAPI_MAJOR_VERSION << 16) | PERFORMANCEAPI_MINOR_VERSION) + +/** + * This header has been designed to be fully self-contained, which makes it easy to copy this header into your own source tree as needed. + * + * See PerformanceAPI.h for the high level documentation on how to use the API. + * + * Note that this header is split into two sections: + * - The first section defines the static library interface. If you use these functions directly, you need to link against the PerformanceAPI static library. + * - The second section defines the DLL interface. The DLL interface allows you to use the API without linking to a library. Instead, you can load the DLL yourself + * through LoadLibrary, then find the "PerformanceAPI_GetAPI" export through GetProcAddress. PerformanceAPI_GetAPI can be called to get a table of function pointers + * to the API. A convenience function to perform the DLL load & retrieve the API functions is provided for you in a separate header, PerformanceAPI_loader.h. + */ +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Static library interface - if you use these functions, you need to link against the PerformanceAPI library +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Helper struct that is used to prevent calls to EndEvent from being optimized to jmp instructions as part of tail call optimization. + * You don't ever need to do anything with this as user of the API. + */ +typedef struct +{ + int64_t SuppressTailCall[3]; +} PerformanceAPI_SuppressTailCallOptimization; + +#if PERFORMANCEAPI_ENABLED + /** + * Helper function to create an uint32_t color from 3 RGB values. The R, G and B values must be in range [0, 255]. + * The resulting color can be passed to the BeginEvent function. + */ + #define PERFORMANCEAPI_MAKE_COLOR(R, G, B) ((((uint32_t)(R)) << 24) | (((uint32_t)(G)) << 16) | (((uint32_t)(B)) << 8) | (uint32_t)0xFF) + + /** + * Use this define if you don't care about the color of an event and just want to use the default + */ + #define PERFORMANCEAPI_DEFAULT_COLOR 0xFFFFFFFF + + /** + * Set the name of the current thread to the specified thread name. + * + * @param inThreadName The thread name as an UTF8 encoded string. + */ + void PerformanceAPI_SetCurrentThreadName(const char* inThreadName); + + /** + * Set the name of the current thread to the specified thread name. + * + * @param inThreadName The thread name as an UTF8 encoded string. + * @param inThreadNameLength The length of the thread name, in characters, excluding the null terminator. + */ + void PerformanceAPI_SetCurrentThreadName_N(const char* inThreadName, uint16_t inThreadNameLength); + + /** + * Begin an instrumentation event with the specified ID and runtime data + * + * @param inID The ID of this scope as an UTF8 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData [optional] The data for this scope as an UTF8 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + * Set to null if not available. + * @param inColor [optional] The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + * Set to PERFORMANCEAPI_DEFAULT_COLOR to use default coloring. + * + */ + void PerformanceAPI_BeginEvent(const char* inID, const char* inData, uint32_t inColor); + + /** + * Begin an instrumentation event with the specified ID and runtime data, both with an explicit length. + + * It works the same as the regular BeginEvent function (see docs above). The difference is that it allows you to specify the length of both the ID and the data, + * which is useful for languages that do not have null-terminated strings. + * + * Note: both lengths should be specified in the number of characters, not bytes, excluding the null terminator. + */ + void PerformanceAPI_BeginEvent_N(const char* inID, uint16_t inIDLength, const char* inData, uint16_t inDataLength, uint32_t inColor); + + /** + * Begin an instrumentation event with the specified ID and runtime data + * + * @param inID The ID of this scope as an UTF16 encoded string. The ID for a specific scope must be the same over the lifetime of the program (see docs at the top of this file) + * @param inData [optional] The data for this scope as an UTF16 encoded string. The data can vary for each invocation of this scope and is intended to hold information that is only available at runtime. See docs at the top of this file. + Set to null if not available. + * @param inColor [optional] The color for this scope. The color for a specific scope is coupled to the ID and must be the same over the lifetime of the program + * Set to PERFORMANCEAPI_DEFAULT_COLOR to use default coloring. + */ + void PerformanceAPI_BeginEvent_Wide(const wchar_t* inID, const wchar_t* inData, uint32_t inColor); + + /** + * Begin an instrumentation event with the specified ID and runtime data, both with an explicit length. + + * It works the same as the regular BeginEvent_Wide function (see docs above). The difference is that it allows you to specify the length of both the ID and the data, + * which is useful for languages that do not have null-terminated strings. + * + * Note: both lengths should be specified in the number of characters, not bytes, excluding the null terminator. + */ + void PerformanceAPI_BeginEvent_Wide_N(const wchar_t* inID, uint16_t inIDLength, const wchar_t* inData, uint16_t inDataLength, uint32_t inColor); + + /** + * End an instrumentation event. Must be matched with a call to BeginEvent within the same function + * Note: the return value can be ignored. It is only there to prevent calls to the function from being optimized to jmp instructions as part of tail call optimization. + */ + PerformanceAPI_SuppressTailCallOptimization PerformanceAPI_EndEvent(); + + /** + * Call this function when a fiber starts running + * + * @param inFiberID The currently running fiber + */ + void PerformanceAPI_RegisterFiber(uint64_t inFiberID); + + /** + * Call this function before a fiber ends + * + * @param inFiberID The currently running fiber + */ + void PerformanceAPI_UnregisterFiber(uint64_t inFiberID); + + /** + * The call to the Windows SwitchFiber function should be surrounded by BeginFiberSwitch and EndFiberSwitch calls. For example: + * + * PerformanceAPI_BeginFiberSwitch(currentFiber, otherFiber); + * SwitchToFiber(otherFiber); + * PerformanceAPI_EndFiberSwitch(currentFiber); + * + * @param inCurrentFiberID The currently running fiber + * @param inNewFiberID The fiber we're switching to + */ + void PerformanceAPI_BeginFiberSwitch(uint64_t inCurrentFiberID, uint64_t inNewFiberID); + + /** + * The call to the Windows SwitchFiber function should be surrounded by BeginFiberSwitch and EndFiberSwitch calls + * + * PerformanceAPI_BeginFiberSwitch(currentFiber, otherFiber); + * SwitchToFiber(otherFiber); + * PerformanceAPI_EndFiberSwitch(currentFiber); + * + * @param inFiberID The fiber that was running before the call to SwitchFiber (so, the same as inCurrentFiberID in the BeginFiberSwitch call) + */ + void PerformanceAPI_EndFiberSwitch(uint64_t inFiberID); +#else + #define PERFORMANCEAPI_MAKE_COLOR(R, G, B) 0xFFFFFFFF + #define PERFORMANCEAPI_DEFAULT_COLOR 0xFFFFFFFF + + inline void PerformanceAPI_SetCurrentThreadName(const char* inThreadName) {} + inline void PerformanceAPI_SetCurrentThreadName_N(const char* inThreadName, uint16_t inThreadNameLength) {} + inline void PerformanceAPI_BeginEvent(const char* inID, const char* inData, uint32_t inColor) {} + inline void PerformanceAPI_BeginEvent_N(const char* inID, uint16_t inIDLength, const char* inData, uint16_t inDataLength, uint32_t inColor) {} + inline void PerformanceAPI_BeginEvent_Wide(const wchar_t* inID, const wchar_t* inData, uint32_t inColor) {} + inline void PerformanceAPI_BeginEvent_Wide_N(const wchar_t* inID, uint16_t inIDLength, const wchar_t* inData, uint16_t inDataLength, uint32_t inColor) {} + inline void PerformanceAPI_EndEvent() {} + + inline void PerformanceAPI_RegisterFiber(uint64_t inFiberID) {} + inline void PerformanceAPI_UnregisterFiber(uint64_t inFiberID) {} + inline void PerformanceAPI_BeginFiberSwitch(uint64_t inCurrentFiberID, uint64_t inNewFiberID) {} + inline void PerformanceAPI_EndFiberSwitch(uint64_t inFiberID) {} +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// DLL interface - These functions can be used without linking by loading PerformanceAPI.dll and using GetProcAddress to find the PerformanceAPI_GetAPI function. +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef void (PerformanceAPI_SetCurrentThreadName_Func)(const char* inThreadName); +typedef void (PerformanceAPI_SetCurrentThreadName_N_Func)(const char* inThreadName, uint16_t inThreadNameLength); +typedef void (PerformanceAPI_BeginEvent_Func)(const char* inID, const char* inData, uint32_t inColor); +typedef void (PerformanceAPI_BeginEvent_N_Func)(const char* inID, uint16_t inIDLength, const char* inData, uint16_t inDataLength, uint32_t inColor); +typedef void (PerformanceAPI_BeginEvent_Wide_Func)(const wchar_t* inID, const wchar_t* inData, uint32_t inColor); +typedef void (PerformanceAPI_BeginEvent_Wide_N_Func)(const wchar_t* inID, uint16_t inIDLength, const wchar_t* inData, uint16_t inDataLength, uint32_t inColor); +typedef PerformanceAPI_SuppressTailCallOptimization (PerformanceAPI_EndEvent_Func)(); + +typedef void (PerformanceAPI_RegisterFiber_Func)(uint64_t inFiberID); +typedef void (PerformanceAPI_UnregisterFiber_Func)(uint64_t inFiberID); +typedef void (PerformanceAPI_BeginFiberSwitch_Func)(uint64_t inCurrentFiberID, uint64_t inNewFiberID); +typedef void (PerformanceAPI_EndFiberSwitch_Func)(uint64_t inFiberID); + +typedef struct +{ + PerformanceAPI_SetCurrentThreadName_Func* SetCurrentThreadName; + PerformanceAPI_SetCurrentThreadName_N_Func* SetCurrentThreadNameN; + PerformanceAPI_BeginEvent_Func* BeginEvent; + PerformanceAPI_BeginEvent_N_Func* BeginEventN; + PerformanceAPI_BeginEvent_Wide_Func* BeginEventWide; + PerformanceAPI_BeginEvent_Wide_N_Func* BeginEventWideN; + PerformanceAPI_EndEvent_Func* EndEvent; + + PerformanceAPI_RegisterFiber_Func* RegisterFiber; + PerformanceAPI_UnregisterFiber_Func* UnregisterFiber; + PerformanceAPI_BeginFiberSwitch_Func* BeginFiberSwitch; + PerformanceAPI_EndFiberSwitch_Func* EndFiberSwitch; + +} PerformanceAPI_Functions; + +/** + * Entry point for the PerformanceAPI when used through a DLL. You can get the actual function from the DLL through + * GetProcAddress and then cast it to this function pointer. The name of the function exported from the DLL is "PerformanceAPI_GetAPI". + * + * A convenience function to find & call this function from the PerformanceAPI dll is provided in a separate header, PerformanceAPI_loader.h (PerformanceAPI_LoadFrom) + * + * @param inVersion The version of the header that's used to request the function table. Always specify PERFORMANCEAPI_VERSION for this argument (defined at the top of this file). + * Note: the version of the header and DLL must match exactly; if it doesn't an error will be returned. + * @param outFunctions Pointer to a PerformanceAPI_Functions struct that will be filled with the correct function pointers to use the API + * + * @return 0 if there was an error (version mismatch), 1 on success + */ +typedef int (*PerformanceAPI_GetAPI_Func)(int inVersion, PerformanceAPI_Functions* outFunctions); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/dep/superluminal/API/include/Superluminal/PerformanceAPI_loader.h b/dep/superluminal/API/include/Superluminal/PerformanceAPI_loader.h new file mode 100644 index 0000000..cc476cb --- /dev/null +++ b/dep/superluminal/API/include/Superluminal/PerformanceAPI_loader.h @@ -0,0 +1,106 @@ +/* +BSD LICENSE + +Copyright (c) 2019-2020 Superluminal. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + +#include "PerformanceAPI_capi.h" + +#ifdef __cplusplus + #define PERFORMANCEAPI_API inline +#else + #define PERFORMANCEAPI_API static inline +#endif + +#if PERFORMANCEAPI_ENABLED + typedef HMODULE PerformanceAPI_ModuleHandle; +#else + typedef void* PerformanceAPI_ModuleHandle; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Load the PerformanceAPI functions from the specified DLL path. If any part of this fails, the output + * outFunctions will be zero-initialized. + * + * @param inPathToDLL The path to the PerformanceAPI DLL. Note: The DLL at the specified path must match the architecture (i.e. x86 or x64) of the program this API is used in. + * @param outFunctions Pointer to a PerformanceAPI_Functions struct that will be filled with the correct function pointers to use the API. Filled with null pointers if the load failed for whatever reason. + * + * @return A handle to the module if the module was successfully loaded and the API retrieved; NULL otherwise. This can be used to free the module through PerformanceAPI_Free if needed. + */ +PERFORMANCEAPI_API PerformanceAPI_ModuleHandle PerformanceAPI_LoadFrom(const wchar_t* inPathToDLL, PerformanceAPI_Functions* outFunctions) +{ + // Zero-initialize functions and copy to the output. This ensures we can return from this function at any point, + // without leaving the user in a state where the output is only partially initialized. + PerformanceAPI_Functions functions = { 0 }; + *outFunctions = functions; + + // If the API is not enabled (i.e. non-Windows or explicitly disabled by the user), we don't try to initialize any of the functions. + // In this case the user will be left with a default (zero) initialized functions struct. +#if PERFORMANCEAPI_ENABLED + HMODULE module = LoadLibraryW(inPathToDLL); + if (module == NULL) + return NULL; + + PerformanceAPI_GetAPI_Func getAPI = (PerformanceAPI_GetAPI_Func)((void*)GetProcAddress(module, "PerformanceAPI_GetAPI")); + if (getAPI == NULL) + { + FreeLibrary(module); + return NULL; + } + + if (getAPI(PERFORMANCEAPI_VERSION, outFunctions) == 0) + { + FreeLibrary(module); + return NULL; + } + + return module; +#else + return NULL; +#endif +} + +/** + * Free the PerformanceAPI module that was previously loaded through PerformanceAPI_LoadFrom. After this function is called, you can no longer use the function pointers + * in the PerformanceAPI_Functions struct that you previously retrieved through PerformanceAPI_LoadFrom. + * + * @param inModule The module to free + */ +PERFORMANCEAPI_API void PerformanceAPI_Free(PerformanceAPI_ModuleHandle* inModule) +{ +#if PERFORMANCEAPI_ENABLED + FreeLibrary(*inModule); +#endif +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/dep/superluminal/API/lib/x64/PerformanceAPI_MD.lib b/dep/superluminal/API/lib/x64/PerformanceAPI_MD.lib new file mode 100644 index 0000000..bf1b301 Binary files /dev/null and b/dep/superluminal/API/lib/x64/PerformanceAPI_MD.lib differ diff --git a/dep/superluminal/API/lib/x64/PerformanceAPI_MDd.lib b/dep/superluminal/API/lib/x64/PerformanceAPI_MDd.lib new file mode 100644 index 0000000..46e6349 Binary files /dev/null and b/dep/superluminal/API/lib/x64/PerformanceAPI_MDd.lib differ diff --git a/dep/superluminal/API/lib/x64/PerformanceAPI_MDd_NoIteratorDebug.lib b/dep/superluminal/API/lib/x64/PerformanceAPI_MDd_NoIteratorDebug.lib new file mode 100644 index 0000000..d545a04 Binary files /dev/null and b/dep/superluminal/API/lib/x64/PerformanceAPI_MDd_NoIteratorDebug.lib differ diff --git a/dep/superluminal/API/lib/x64/PerformanceAPI_MT.lib b/dep/superluminal/API/lib/x64/PerformanceAPI_MT.lib new file mode 100644 index 0000000..bd15f23 Binary files /dev/null and b/dep/superluminal/API/lib/x64/PerformanceAPI_MT.lib differ diff --git a/dep/superluminal/API/lib/x64/PerformanceAPI_MTd.lib b/dep/superluminal/API/lib/x64/PerformanceAPI_MTd.lib new file mode 100644 index 0000000..fca8791 Binary files /dev/null and b/dep/superluminal/API/lib/x64/PerformanceAPI_MTd.lib differ diff --git a/dep/superluminal/API/lib/x64/PerformanceAPI_MTd_NoIteratorDebug.lib b/dep/superluminal/API/lib/x64/PerformanceAPI_MTd_NoIteratorDebug.lib new file mode 100644 index 0000000..7eeada6 Binary files /dev/null and b/dep/superluminal/API/lib/x64/PerformanceAPI_MTd_NoIteratorDebug.lib differ diff --git a/engine/common.h b/engine/common.h index a5ebcff..0006b6f 100644 --- a/engine/common.h +++ b/engine/common.h @@ -22,6 +22,8 @@ #include #include +#include + #ifdef _DEBUG #include "common/memtrak3.h" #endif diff --git a/engine/common/base64.c b/engine/common/base64.c index c02a2b6..4518961 100644 --- a/engine/common/base64.c +++ b/engine/common/base64.c @@ -47,7 +47,7 @@ static const unsigned char decodetable[] = 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; /* - * Base64Decode() née Curl_base64_decode() + * Base64Decode() [previously Curl_base64_decode()] * * Given a base64 NUL-terminated string at src, decode it and return a * pointer in *outptr to a newly allocated memory area holding decoded @@ -235,7 +235,7 @@ static bool base64_encode(const char* table64, } /* - * Base64Encode() née Curl_base64_encode() + * Base64Encode() [previously Curl_base64_encode()] * * Given a pointer to an input buffer and an input size, encode it and * return a pointer in *outptr to a newly allocated memory area holding @@ -256,7 +256,7 @@ bool Base64Encode(const char* inputbuff, size_t insize, } /* - * Base64UrlEncode() née Curl_base64url_encode() + * Base64UrlEncode() [previously Curl_base64url_encode()] * * Given a pointer to an input buffer and an input size, encode it and * return a pointer in *outptr to a newly allocated memory area holding diff --git a/engine/render/r_main.cpp b/engine/render/r_main.cpp index 1d46715..c2031bc 100644 --- a/engine/render/r_main.cpp +++ b/engine/render/r_main.cpp @@ -25,6 +25,8 @@ #include #include +#include + static uint64_t MurmurHash64A(void const* data, int len, uint64_t seed); // ======= @@ -125,8 +127,9 @@ Mat4 OrthoMatrix(double left, double right, double bottom, double top, double ne // Layer queue class // ================= +#pragma pack(push, r_layerCmd, 1) struct r_layerCmd_s { - enum Command { + enum Command : uint8_t { VIEWPORT, BLEND, BIND, @@ -165,6 +168,7 @@ struct r_layerCmdQuad_s { int stackLayer, maskLayer; } quad; }; +#pragma pack(pop, r_layerCmd) r_layer_c::r_layer_c(r_renderer_c* renderer, int layer, int subLayer) : renderer(renderer), layer(layer), subLayer(subLayer) @@ -418,6 +422,7 @@ struct RenderStrategy { virtual void ProcessCommand(r_layerCmd_s* cmd) = 0; virtual void Flush() = 0; virtual void SetShowStats(bool showStats) { showStats_ = showStats; } + virtual bool UsedIncompleteTextures() const { return false; } protected: bool showStats_{}; @@ -572,6 +577,8 @@ struct AdjacentMergeStrategy : RenderStrategy { } } + bool UsedIncompleteTextures() const override { return usedIncompleteTextures; }; + private: void Dispatch() { glBindBuffer(GL_ARRAY_BUFFER, vbo_); @@ -623,7 +630,10 @@ struct AdjacentMergeStrategy : RenderStrategy { auto tex = textures[i]; tex->Bind(); if (showStats_) { - ImGui::Text("New tex %d (%s)", tex->texId, tex->fileName.c_str()); + ImGui::Text("New tex %d (%s) %d", tex->texId, tex->fileName.c_str(), tex->status.load()); + } + if (!usedIncompleteTextures && tex->status != r_tex_c::Status::DONE) { + usedIncompleteTextures = true; } } else { @@ -655,7 +665,7 @@ struct AdjacentMergeStrategy : RenderStrategy { struct TexturedBatch { explicit TexturedBatch(GLuint prog) : batch(prog) { - textures.reserve(1ull << 20); + textures.reserve(128); } BatchKey key{}; @@ -673,9 +683,11 @@ struct AdjacentMergeStrategy : RenderStrategy { size_t totalVertexCount_ = 0; size_t batchIndex = 0; + + bool usedIncompleteTextures = false; }; -void r_layer_c::Render() +bool r_layer_c::Render() { int const optLevel = renderer->r_layerOptimize->intVal; bool const shuffle = renderer->r_layerShuffle->intVal == 1; @@ -713,6 +725,8 @@ void r_layer_c::Render() if (renderer->glPopGroupMarkerEXT) { renderer->glPopGroupMarkerEXT(); } + + return strat->UsedIncompleteTextures(); } void r_layer_c::Discard() @@ -1150,12 +1164,6 @@ void r_renderer_c::Shutdown() void r_renderer_c::PumpShaders() { texMan->ProcessPendingTextureUploads(); - for (size_t idx = 0; idx < numShader; ++idx) - if (auto* sh = shaderList[idx]) - if (auto tex = sh->tex; tex && tex->status != r_tex_c::DONE) { - inhibitElision = true; - break; - } } void r_renderer_c::BeginFrame() @@ -1344,13 +1352,15 @@ void r_renderer_c::EndFrame() ImGui::Text("Total dense footprint: %sB", BinaryUnitPrefix(totalDenseFootprint).c_str()); size_t totalCmd{}; - if (ImGui::BeginTable("Layer stats", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { + if (ImGui::BeginTable("Layer stats", 8, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { ImGui::TableSetupColumn("Index"); ImGui::TableSetupColumn("Layer"); ImGui::TableSetupColumn("Sublayer"); ImGui::TableSetupColumn("Command count"); ImGui::TableSetupColumn("Dense"); ImGui::TableSetupColumn("Debug"); + ImGui::TableSetupColumn("XXH3-64"); + ImGui::TableSetupColumn("MH64A"); ImGui::TableHeadersRow(); for (int l = 0; l < numLayer; ++l) { auto layer = layerSort[l]; @@ -1372,6 +1382,22 @@ void r_renderer_c::EndFrame() if (ImGui::Button("Debug")) { layerBreak = { layer->layer, layer->subLayer }; } + + std::chrono::high_resolution_clock::time_point tic; + std::chrono::microseconds dt; + + ImGui::TableNextColumn(); + tic = std::chrono::high_resolution_clock::now(); + volatile auto xxh_hash = XXH3_64bits(layer->cmdStorage.data(), layer->cmdCursor); + dt = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - tic); + ImGui::Text("%d µs", dt.count()); + + ImGui::TableNextColumn(); + tic = std::chrono::high_resolution_clock::now(); + volatile auto mh_hash = MurmurHash64A(layer->cmdStorage.data(), (int)layer->cmdCursor, 0ull); + dt = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - tic); + ImGui::Text("%d µs", dt.count()); + ImGui::PopID(); ImGui::PopID(); } @@ -1383,83 +1409,46 @@ void r_renderer_c::EndFrame() if (inhibitElision || elideFrames != !!r_elideFrames->intVal) { elideFrames = !!r_elideFrames->intVal; - lastFrameHash.clear(); + lastFrameHash = 0; } - std::future>> elidedFrameHashFut; + auto tic = std::chrono::high_resolution_clock::now(); + + uint64_t commandDigest = 0; if (elideFrames) { - elidedFrameHashFut = std::async([&]() -> std::optional> { - std::vector commandDigest; - - for (auto lIdx = 0; lIdx < numLayer; ++lIdx) { - auto layer = layerSort[lIdx]; - uint64_t subHash = MurmurHash64A(layer->cmdStorage.data(), (int)layer->cmdCursor, 0ull); - uint8_t const* p = (uint8_t const*)&subHash; - commandDigest.insert(commandDigest.end(), p, p + sizeof(subHash)); - } + std::shared_ptr hashState(XXH3_createState(), XXH3_freeState); + XXH3_64bits_reset(hashState.get()); - return commandDigest; - }); - } - else { - std::promise>> p; - elidedFrameHashFut = p.get_future(); - p.set_value({}); - } + for (auto lIdx = 0; lIdx < numLayer; ++lIdx) { + auto layer = layerSort[lIdx]; + uint64_t subHash = XXH3_64bits(layer->cmdStorage.data(), (int)layer->cmdCursor); + XXH3_64bits_update(hashState.get(), &subHash, sizeof(subHash)); + } - elidedFrameHashFut.wait(); + commandDigest = XXH3_64bits_digest(hashState.get()); + } ++totalFrames; - bool decideDraw = false; - bool elideDraw = false; + const bool elideDraw = lastFrameHash != 0 && lastFrameHash == commandDigest; + if (!elideDraw) { glBindFramebuffer(GL_FRAMEBUFFER, GetDrawRenderTarget().framebuffer); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - int l{}; - for (l = 0; l < numLayer; l++) { - if (!decideDraw && elidedFrameHashFut.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) { - decideDraw = true; - auto commandDigest = elidedFrameHashFut.get(); - if (commandDigest) { - if (*commandDigest == lastFrameHash) { - elideDraw = true; - break; - } - else { - lastFrameHash = *commandDigest; - } - } - else { - lastFrameHash.clear(); - } - } + for (int l = 0; l < numLayer; l++) { auto& layer = layerSort[l]; if (layerBreak && layerBreak->first == layer->layer && layerBreak->second == layer->subLayer) { #ifdef _WIN32 DebugBreak(); #endif } - layer->Render(); - } - if (!elideDraw) { - presentRtt = 1 - presentRtt; - ++drawnFrames; - } - } - - if (!decideDraw) { - if (auto commandDigest = elidedFrameHashFut.get()) { - lastFrameHash = *commandDigest; - } - else { - lastFrameHash.clear(); + inhibitElision = layer->Render() || inhibitElision; } + presentRtt = 1 - presentRtt; + ++drawnFrames; } - if (inhibitElision) { - // If we explicitly inhibited elision due to things like incomplete textures, make sure that the next frame is drawn. - lastFrameHash.clear(); - } + // If we explicitly inhibited elision due to things like incomplete textures, make sure that the next frame is drawn. + lastFrameHash = inhibitElision ? 0 : commandDigest; for (int l = 0; l < numLayer; ++l) { layerSort[l]->Discard(); @@ -1500,7 +1489,7 @@ void r_renderer_c::EndFrame() if (ImGui::Begin("Hash")) { char* b64{}; size_t b64Len{}; - Base64UrlEncode((char const*)lastFrameHash.data(), lastFrameHash.size(), &b64, &b64Len); + Base64UrlEncode((char const*)&lastFrameHash, sizeof(lastFrameHash), &b64, &b64Len); ImGui::Text("%s", b64); free(b64); } @@ -1589,6 +1578,7 @@ r_shaderHnd_c* r_renderer_c::RegisterShader(std::string_view shname, int flags) } std::string name(shname); + PERFORMANCEAPI_INSTRUMENT_FUNCTION_DATA(name.c_str()); dword nameHash = StringHash(name, 0xFFFF); int newId = -1; for (int s = 0; s < numShader; s++) { @@ -1637,11 +1627,12 @@ r_shaderHnd_c* r_renderer_c::RegisterShaderFromImage(std::unique_ptr im void r_renderer_c::GetShaderImageSize(r_shaderHnd_c* hnd, int& width, int& height) { + PERFORMANCEAPI_INSTRUMENT_FUNCTION_DATA(hnd->sh->name.c_str()); if (hnd) { - while (hnd->sh->tex->status < r_tex_c::SIZE_KNOWN) { - Sleep(1); - } + auto* tex = hnd->sh->tex; + std::unique_lock lock(tex->statusMutex); + tex->statusCV.wait(lock, [tex] { return tex->status.load(std::memory_order_relaxed) >= r_tex_c::SIZE_KNOWN; }); width = hnd->sh->tex->fileWidth; height = hnd->sh->tex->fileHeight; } diff --git a/engine/render/r_main.h b/engine/render/r_main.h index f7072fb..808eeff 100644 --- a/engine/render/r_main.h +++ b/engine/render/r_main.h @@ -46,7 +46,7 @@ class r_layer_c { void Bind(r_tex_c* tex); void Color(col4_t col); void Quad(float s0, float t0, float x0, float y0, float s1, float t1, float x1, float y1, float s2, float t2, float x2, float y2, float s3, float t3, float x3, float y3, int stackLayer = 0, int maskLayer = -1); - void Render(); + bool Render(); void Discard(); struct CmdHandle { @@ -180,7 +180,7 @@ class r_renderer_c: public r_IRenderer, public conCmdHandler_c { RenderTarget rttMain[2]; int presentRtt = 0; - std::vector lastFrameHash{}; + uint64_t lastFrameHash{}; uint64_t totalFrames{}; uint64_t drawnFrames{}; diff --git a/engine/render/r_texture.cpp b/engine/render/r_texture.cpp index 07fdda8..69bd802 100644 --- a/engine/render/r_texture.cpp +++ b/engine/render/r_texture.cpp @@ -11,6 +11,7 @@ #include "cmp_core.h" #include "stb_image_resize.h" +#include #include #include @@ -74,6 +75,7 @@ class t_manager_c: public r_ITexManager, public thread_c { std::vector workers; std::vector textureQueue; std::mutex mutex; + std::condition_variable workCV; std::vector uploadQueue; std::mutex uploadMutex; @@ -103,7 +105,8 @@ t_manager_c::t_manager_c(r_renderer_c* renderer) for (int i = 0; i < runnersWanted; ++i) { - workers.emplace_back([this] { + workers.emplace_back([this, i] { + PerformanceAPI_SetCurrentThreadName(fmt::format("Tex{}", i).c_str()); ThreadProc(); }); } @@ -115,9 +118,14 @@ t_manager_c::t_manager_c(r_renderer_c* renderer) t_manager_c::~t_manager_c() { - doRun = false; + { + std::unique_lock lock(mutex); + doRun = false; + } + workCV.notify_all(); for (auto& worker : workers) - worker.join(); + if( worker.joinable()) + worker.join(); for (auto tex : textureQueue) delete tex; @@ -147,12 +155,15 @@ void t_manager_c::ProcessPendingTextureUploads() bool t_manager_c::AsyncAdd(r_tex_c* tex) { - std::lock_guard lock( mutex ); - if ( runnersRunning == 0 ) { - return true; + { + std::lock_guard lock(mutex); + if (runnersRunning == 0) { + return true; + } + textureQueue.push_back(tex); + tex->status = r_tex_c::IN_QUEUE; } - textureQueue.push_back( tex ); - tex->status = r_tex_c::IN_QUEUE; + workCV.notify_one(); return false; } @@ -170,11 +181,15 @@ bool t_manager_c::AsyncRemove(r_tex_c* tex) } } } - while (tex->status == r_tex_c::PROCESSING || tex->status == r_tex_c::SIZE_KNOWN) { - renderer->sys->Sleep( 1 ); + { + std::unique_lock lock(tex->statusMutex); + tex->statusCV.wait(lock, [tex] { + const auto status = tex->status.load(std::memory_order_relaxed); + return status != r_tex_c::PROCESSING && status != r_tex_c::SIZE_KNOWN; + }); } - if (tex->status == r_tex_c::PENDING_UPLOAD) { + if (tex->status.load(std::memory_order_relaxed) == r_tex_c::PENDING_UPLOAD) { RemovePendingTextureUpload(tex); } @@ -197,10 +212,14 @@ void t_manager_c::RemovePendingTextureUpload(r_tex_c* tex) void t_manager_c::ThreadProc() { ++runnersRunning; - while (doRun) { + while (true) { r_tex_c *doTex = nullptr; { - std::lock_guard lock( mutex ); + std::unique_lock lock( mutex ); + workCV.wait(lock, [this] { return !doRun || !textureQueue.empty(); }); + + if (!doRun) + break; // Find a texture with the highest loading priority int maxPri = 0; @@ -216,7 +235,7 @@ void t_manager_c::ThreadProc() if (doTexItr != textureQueue.end()) { doTex = *doTexItr; textureQueue.erase(doTexItr); - doTex->status = r_tex_c::PROCESSING; + doTex->SetStatus(r_tex_c::PROCESSING); } } @@ -224,9 +243,6 @@ void t_manager_c::ThreadProc() // Load this texture doTex->LoadFile(); doTex = nullptr; - } else { - // Idle - renderer->sys->Sleep(1); } } --runnersRunning; @@ -557,7 +573,7 @@ void r_tex_c::LoadFile() auto raw = std::make_unique(); raw->CopyRaw(IMGTYPE_GRAY, 8, 8, t_whiteImage); Upload(*raw, TF_NOMIPMAP); - status = DONE; + SetStatus(DONE); return; } else if (_stricmp(fileName.c_str(), "@black") == 0) { @@ -565,7 +581,7 @@ void r_tex_c::LoadFile() auto raw = std::make_unique(); raw->CopyRaw(IMGTYPE_RGBA, 8, 8, t_blackImage); Upload(*raw, TF_NOMIPMAP); - status = DONE; + SetStatus(DONE); return; } @@ -576,7 +592,7 @@ void r_tex_c::LoadFile() auto sizeCallback = [this](int width, int height) { this->fileWidth = width; this->fileHeight = height; - this->status = SIZE_KNOWN; + SetStatus(SIZE_KNOWN); }; error = img->Load(path, sizeCallback); if ( !error ) { @@ -604,14 +620,23 @@ void r_tex_c::LoadFile() auto raw = std::make_unique(); raw->CopyRaw(IMGTYPE_GRAY, 8, 8, t_defaultTexture); Upload(*raw, TF_NOMIPMAP); - status = DONE; + SetStatus(DONE); +} + +void r_tex_c::SetStatus(Status newStatus) +{ + { + std::unique_lock statusLock(statusMutex); + status.store(newStatus, std::memory_order_release); + } + statusCV.notify_all(); } void r_tex_c::PerformUpload(r_tex_c* tex) { tex->Upload(*tex->img, tex->flags); tex->img = {}; - tex->status = DONE; + tex->SetStatus(DONE); } static std::atomic inputBytes = 0; diff --git a/engine/render/r_texture.h b/engine/render/r_texture.h index c20c8bf..d100191 100644 --- a/engine/render/r_texture.h +++ b/engine/render/r_texture.h @@ -10,6 +10,7 @@ #include #include +#include #include class image_c; @@ -28,6 +29,8 @@ class r_tex_c { PENDING_UPLOAD, DONE, }; + std::mutex statusMutex; + std::condition_variable statusCV; std::atomic status; std::atomic loadPri; dword texId; @@ -52,6 +55,8 @@ class r_tex_c { void ForceLoad(); void LoadFile(); + void SetStatus(Status newStatus); + static void PerformUpload(r_tex_c*); private: diff --git a/engine/system/win/sys_console.cpp b/engine/system/win/sys_console.cpp index 88b6de8..8e0570d 100644 --- a/engine/system/win/sys_console.cpp +++ b/engine/system/win/sys_console.cpp @@ -42,8 +42,9 @@ class sys_console_c: public sys_IConsole, public conPrintHook_c, public thread_c static LRESULT __stdcall WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - volatile bool doRun; - volatile bool isRunning; + HANDLE threadStartedEvent{}; + HANDLE threadShouldStopEvent{}; + HANDLE threadExitedEvent{}; void RunMessages(HWND hwnd = nullptr); void ThreadProc(); @@ -68,11 +69,12 @@ void sys_IConsole::FreeHandle(sys_IConsole* hnd) sys_console_c::sys_console_c(sys_IMain* sysHnd) : conPrintHook_c(sysHnd->con), sys((sys_main_c*)sysHnd), thread_c(sysHnd) { - isRunning = false; - doRun = true; + threadStartedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + threadShouldStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + threadExitedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); ThreadStart(true); - while ( !isRunning ); + WaitForSingleObject(threadStartedEvent, INFINITE); } void sys_console_c::RunMessages(HWND hwnd) @@ -90,6 +92,7 @@ void sys_console_c::RunMessages(HWND hwnd) void sys_console_c::ThreadProc() { + PerformanceAPI_SetCurrentThreadName("SysConsole"); // Get info of the monitor containing the mouse cursor POINT curPos; GetCursorPos(&curPos); @@ -154,10 +157,9 @@ void sys_console_c::ThreadProc() InstallPrintHook(); - isRunning = true; - while (doRun) { + SetEvent(threadStartedEvent); + while (WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1, &threadShouldStopEvent, FALSE, INFINITE, QS_ALLINPUT)) { RunMessages(hwMain); - sys->Sleep(1); } RemovePrintHook(); @@ -170,16 +172,19 @@ void sys_console_c::ThreadProc() DestroyWindow(hwMain); UnregisterClass(CFG_SCON_TITLE " Class", sys->hinst); - isRunning = false; - // Flush windowless messages (Like WM_QUIT) RunMessages(); + + SetEvent(threadExitedEvent); } sys_console_c::~sys_console_c() { - doRun = false; - while (isRunning); + SetEvent(threadShouldStopEvent); + WaitForSingleObject(threadExitedEvent, INFINITE); + DeleteObject(threadStartedEvent); + DeleteObject(threadShouldStopEvent); + DeleteObject(threadExitedEvent); } // ======================== @@ -204,7 +209,7 @@ LRESULT __stdcall sys_console_c::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPA } case WM_CLOSE: // Quit - conWin->doRun = false; + SetEvent(conWin->threadShouldStopEvent); PostQuitMessage(0); return FALSE; } diff --git a/ui_api.cpp b/ui_api.cpp index 6008350..8fc0105 100644 --- a/ui_api.cpp +++ b/ui_api.cpp @@ -1909,6 +1909,7 @@ SG_LUA_CPP_FUN_BEGIN(LoadModule) if (!fileName.has_extension()) { fileName.replace_extension(".lua"); } + PERFORMANCEAPI_INSTRUMENT_DATA("[API]LoadModule", fileName.generic_string().c_str()); ui->sys->SetWorkDir(ui->scriptPath); auto fileStr = fileName.generic_u8string(); @@ -1932,6 +1933,7 @@ SG_LUA_CPP_FUN_BEGIN(PLoadModule) if (!fileName.has_extension()) { fileName.replace_extension(".lua"); } + PERFORMANCEAPI_INSTRUMENT_DATA("[API]PLoadModule", fileName.generic_string().c_str()); ui->sys->SetWorkDir(ui->scriptPath); int err = luaL_loadfile(L, fileName.generic_u8string().c_str()); @@ -2116,10 +2118,6 @@ static int l_OpenURL(lua_State* L) static int l_SetProfiling(lua_State* L) { - ui_main_c* ui = GetUIPtr(L); - int n = lua_gettop(L); - ui->LAssert(L, n >= 1, "Usage: SetProfiling(isEnabled)"); - ui->debug->SetProfiling(lua_toboolean(L, 1) == 1); return 0; } diff --git a/ui_debug.cpp b/ui_debug.cpp deleted file mode 100644 index 73eba47..0000000 --- a/ui_debug.cpp +++ /dev/null @@ -1,299 +0,0 @@ -// DyLua: SimpleGraphic -// (c) David Gowor, 2014 -// -// Module: UI Debug -// - -#include "ui_local.h" - -// ======= -// Classes -// ======= - -struct d_lineHit_s { - char* source; - char* name; - int line; - int count; -}; - -struct d_callHit_s { - char* source; - char* name; - int count; - int lineHitNum; - int lineHitSz; - d_lineHit_s* lineHits; -}; - -// =================== -// ui_IDebug Interface -// =================== - -class ui_debug_c : public ui_IDebug, public thread_c { -public: - // Interface - void SetProfiling(bool enable) override; - void ToggleProfiling() override; - - // Encapsulated - ui_debug_c(ui_main_c* ui); - ~ui_debug_c(); - - ui_main_c* ui = nullptr; - - volatile bool doRun = false; - volatile bool isRunning = false; - - volatile bool profiling = false; - - volatile bool hookHold = false; - volatile bool hookHolding = false; - - volatile int lineHitNum = 0; - int lineHitSz = 0; - d_lineHit_s* lineHits = nullptr; - - volatile int callHitNum = 0; - int callHitSz = 0; - int callHitInitCount = 0; - d_callHit_s* callHits = nullptr; - - void ThreadProc(); -}; - -ui_IDebug* ui_IDebug::GetHandle(ui_main_c* ui) -{ - return new ui_debug_c(ui); -} - -void ui_IDebug::FreeHandle(ui_IDebug* hnd) -{ - delete (ui_debug_c*)hnd; -} - -ui_debug_c::ui_debug_c(ui_main_c* ui) - : thread_c(ui->sys), ui(ui) -{ - profiling = false; - - hookHold = false; - hookHolding = false; - - lineHitNum = 0; - lineHitSz = 16; - lineHits = new d_lineHit_s[lineHitSz]; - - callHitNum = 0; - callHitSz = 16; - callHitInitCount = 0; - callHits = new d_callHit_s[callHitSz]; - - doRun = true; - ThreadStart(); -} - -ui_debug_c::~ui_debug_c() -{ - profiling = false; - while (lineHitNum || callHitNum); - doRun = false; - while (isRunning); - delete lineHits; - for (int i = 0; i < callHitInitCount; i++) { - delete callHits[i].lineHits; - } - delete callHits; -} - -// ============== -// UI Debug Class -// ============== - -// Grab UI main pointer from the registry -static ui_debug_c* GetDebugPtr(lua_State* L) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, 0); - ui_main_c* ui = (ui_main_c*)lua_touserdata(L, -1); - lua_pop(L, 1); - return (ui_debug_c*)ui->debug; -} - -static void debugHook(lua_State* L, lua_Debug* ar) -{ - ui_debug_c* d = GetDebugPtr(L); - d->hookHolding = true; - while (d->hookHold); - d->hookHolding = false; -} - -static int lineComp(const void* aVoid, const void* bVoid) -{ - d_lineHit_s* a = (d_lineHit_s*)aVoid; - d_lineHit_s* b = (d_lineHit_s*)bVoid; - if (a->count == b->count) { - return 0; - } - else { - return a->count > b->count ? -1 : 1; - } -} - -static int callComp(const void* aVoid, const void* bVoid) -{ - d_callHit_s* a = (d_callHit_s*)aVoid; - d_callHit_s* b = (d_callHit_s*)bVoid; - if (a->count == b->count) { - return 0; - } - else { - return a->count > b->count ? -1 : 1; - } -} - -void ui_debug_c::ThreadProc() -{ - isRunning = true; - while (doRun) { - ui->sys->Sleep(1); - - if (profiling) { - if (!ui->inLua) { - continue; - } - hookHold = true; - lua_sethook(ui->L, &debugHook, LUA_MASKLINE, 0); - while (profiling && !hookHolding); - lua_sethook(ui->L, &debugHook, 0, 0); - if (!profiling) { - hookHold = false; - continue; - } - lua_Debug dbg; - memset(&dbg, 0, sizeof(dbg)); - if (lua_getstack(ui->L, 0, &dbg) && lua_getinfo(ui->L, "Sln", &dbg) && dbg.source) { - int l; - for (l = 0; l < lineHitNum; l++) { - if (dbg.currentline == lineHits[l].line && !strcmp(dbg.source, lineHits[l].source)) { - if (dbg.name && !lineHits[l].name) { - lineHits[l].name = AllocString(dbg.name); - } - lineHits[l].count++; - break; - } - } - if (l == lineHitNum) { - if (lineHitNum == lineHitSz) { - lineHitSz <<= 1; - trealloc(lineHits, lineHitSz); - } - lineHits[l].source = AllocString(dbg.source); - lineHits[l].name = AllocString(dbg.name); - lineHits[l].line = dbg.currentline; - lineHits[l].count = 1; - lineHitNum++; - } - const char* funcSource = dbg.source; - const char* funcName = dbg.name; - if (funcName && lua_getstack(ui->L, 1, &dbg) && lua_getinfo(ui->L, "Sln", &dbg) && dbg.source) { - int c; - for (c = 0; c < callHitNum; c++) { - if (!strcmp(funcSource, callHits[c].source) && !strcmp(funcName, callHits[c].name)) { - callHits[c].count++; - break; - } - } - if (c == callHitNum) { - if (callHitNum == callHitSz) { - callHitSz <<= 1; - trealloc(callHits, callHitSz); - } - if (callHitNum == callHitInitCount) { - callHits[c].lineHitSz = 16; - callHits[c].lineHits = new d_lineHit_s[16]; - callHitInitCount++; - } - callHits[c].source = AllocString(funcSource); - callHits[c].name = AllocString(funcName); - callHits[c].count = 1; - callHits[c].lineHitNum = 0; - callHitNum++; - } - d_callHit_s* call = callHits + c; - int l; - for (l = 0; l < call->lineHitNum; l++) { - if (dbg.currentline == call->lineHits[l].line && !strcmp(dbg.source, call->lineHits[l].source)) { - if (dbg.name && !call->lineHits[l].name) { - call->lineHits[l].name = AllocString(dbg.name); - } - call->lineHits[l].count++; - break; - } - } - if (l == call->lineHitNum) { - if (call->lineHitNum == call->lineHitSz) { - call->lineHitSz <<= 1; - trealloc(call->lineHits, call->lineHitSz); - } - call->lineHits[l].source = AllocString(dbg.source); - call->lineHits[l].name = AllocString(dbg.name); - call->lineHits[l].line = dbg.currentline; - call->lineHits[l].count = 1; - call->lineHitNum++; - } - } - } - hookHold = false; - while (hookHolding); - } - else if (lineHitNum) { - ui->sys->con->Printf("Hot lines:\n"); - qsort(lineHits, lineHitNum, sizeof(d_lineHit_s), lineComp); - for (int l = 0; l < lineHitNum; l++) { - if (l < 20) { - ui->sys->con->Printf("%s(%d) in '%s': %d\n", lineHits[l].source, lineHits[l].line, lineHits[l].name ? lineHits[l].name : "?", lineHits[l].count); - } - delete lineHits[l].source; - delete lineHits[l].name; - } - lineHitNum = 0; - ui->sys->con->Printf("Hot calls:\n"); - qsort(callHits, callHitNum, sizeof(d_callHit_s), callComp); - for (int c = 0; c < callHitNum; c++) { - qsort(callHits[c].lineHits, callHits[c].lineHitNum, sizeof(d_lineHit_s), lineComp); - if (c < 10) { - ui->sys->con->Printf("%s in '%s': %d\n", callHits[c].source, callHits[c].name, callHits[c].count); - } - for (int l = 0; l < callHits[c].lineHitNum; l++) { - if (c < 10 && l < 5) { - ui->sys->con->Printf("\t%s(%d) in '%s': %d\n", callHits[c].lineHits[l].source, callHits[c].lineHits[l].line, callHits[c].lineHits[l].name ? callHits[c].lineHits[l].name : "?", callHits[c].lineHits[l].count); - } - delete callHits[c].lineHits[l].source; - delete callHits[c].lineHits[l].name; - } - delete callHits[c].source; - delete callHits[c].name; - } - callHitNum = 0; - } - } - isRunning = false; -} - -void ui_debug_c::SetProfiling(bool enable) -{ - if (enable) { - ui->sys->con->Printf("Profiling enabled.\n"); - profiling = true; - } - else { - ui->sys->con->Printf("Profiling finished:\n"); - profiling = false; - while (lineHitNum || callHitNum); - } -} - -void ui_debug_c::ToggleProfiling() -{ - SetProfiling(!profiling); -} diff --git a/ui_debug.h b/ui_debug.h deleted file mode 100644 index 6c1cfca..0000000 --- a/ui_debug.h +++ /dev/null @@ -1,19 +0,0 @@ -// DyLua: SimpleGraphic -// (c) David Gowor, 2014 -// -// UI Debug Header -// - -// ========== -// Interfaces -// ========== - -// UI Debug Handler -class ui_IDebug { -public: - static ui_IDebug* GetHandle(class ui_main_c*); - static void FreeHandle(ui_IDebug*); - - virtual void SetProfiling(bool enable) = 0; - virtual void ToggleProfiling() = 0; -}; \ No newline at end of file diff --git a/ui_local.h b/ui_local.h index 5925ed7..780dbe7 100644 --- a/ui_local.h +++ b/ui_local.h @@ -16,7 +16,6 @@ #include #include "ui_console.h" -#include "ui_debug.h" #include "ui_subscript.h" #include "ui_main.h" \ No newline at end of file diff --git a/ui_main.cpp b/ui_main.cpp index 09e59be..7adfe19 100644 --- a/ui_main.cpp +++ b/ui_main.cpp @@ -317,9 +317,6 @@ void ui_main_c::ScriptInit() if (err) sys->Error("Error initialising Lua environment: \n%s\n", lua_tostring(L, -1)); lua_gc(L, LUA_GCRESTART, -1); - // Setup debug system - debug = ui_IDebug::GetHandle(this); - // Setup subscript system subScriptSize = 16; subScriptList = new ui_ISubScript*[subScriptSize]; @@ -453,14 +450,13 @@ void ui_main_c::ScriptShutdown() PCall(extraArgs, 0); } - // Shutdown subscript and debug systems + // Shutdown subscript system for (dword i = 0; i < subScriptSize; i++) { if (subScriptList[i]) { ui_ISubScript::FreeHandle(subScriptList[i]); } } delete subScriptList; - ui_IDebug::FreeHandle(debug); // Shutdown Lua L = NULL; @@ -532,11 +528,6 @@ void ui_main_c::KeyEvent(int key, int type) case KEY_F10: renderer->ToggleDebugImGui(); break; - case KEY_PAUSE: - if (sys->IsKeyDown(KEY_SHIFT)) { - debug->ToggleProfiling(); - break; - } default: CallKeyHandler("OnKeyUp", key, false); break; diff --git a/ui_main.h b/ui_main.h index 02ec907..32d8331 100644 --- a/ui_main.h +++ b/ui_main.h @@ -29,7 +29,6 @@ class ui_main_c: public ui_IMain { r_IRenderer* renderer = nullptr; ui_IConsole* conUI = nullptr; - ui_IDebug* debug = nullptr; dword subScriptSize = 0; ui_ISubScript** subScriptList = nullptr; diff --git a/ui_subscript.cpp b/ui_subscript.cpp index befba5c..0d0bcdb 100644 --- a/ui_subscript.cpp +++ b/ui_subscript.cpp @@ -372,6 +372,7 @@ void ui_subscript_c::Stop() void ui_subscript_c::ThreadProc() { + PerformanceAPI_SetCurrentThreadName("Subscript"); int numarg = (int)lua_tointeger(L, -1); lua_pop(L, 1); if (lua_pcall(L, numarg, LUA_MULTRET, 1)) { diff --git a/vcpkg.json b/vcpkg.json index 2e4efdd..5bdc5d3 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -13,6 +13,7 @@ "pkgconf", "re2", "sol2", + "xxhash", "zstd", "zlib" ] diff --git a/win/entry.cpp b/win/entry.cpp index af02f1f..582d9b5 100644 --- a/win/entry.cpp +++ b/win/entry.cpp @@ -77,6 +77,7 @@ extern "C" SIMPLEGRAPHIC_DLL_PUBLIC int RunLuaFileAsWin(int argc, char** argv) { + PerformanceAPI_SetCurrentThreadName("Main"); #ifdef _MEMTRAK_H strcpy_s(_memTrak_reportName, 512, "SimpleGraphic/memtrak.log"); #endif