mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-02 11:10:31 +03:00
core: extend debug allocation tracing and reduce render-time allocations
- intercept global new/delete operations in debug memory-manager builds - distinguish anonymous global allocations from source-tracked leaks - modernize eeNew to support constructor-expression syntax - improve memory reports with tabulated leaks and separate largest allocations - cache autocomplete shortcuts instead of hashing string keys on key events - reuse UIStyle property sets across state changes - reuse Text shadow colors and avoid temporary outline-color copies - use inline storage for temporary linear-gradient rendering data - make unit tests resolve assets from the executable directory - add memory-manager and PropertyIdSet regression coverage
This commit is contained in:
@@ -6,20 +6,23 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <eepp/config.hpp>
|
#include <eepp/config.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace EE {
|
namespace EE {
|
||||||
|
|
||||||
class EE_API AllocatedPointer {
|
class EE_API AllocatedPointer {
|
||||||
public:
|
public:
|
||||||
AllocatedPointer( void* data, const std::string& File, int Line, size_t memory,
|
AllocatedPointer( void* data, const std::string& File, int Line, size_t memory,
|
||||||
bool track = false );
|
bool track = false, bool globalAllocation = false );
|
||||||
|
|
||||||
std::string mFile;
|
std::string mFile;
|
||||||
int mLine;
|
int mLine;
|
||||||
size_t mMemory;
|
size_t mMemory;
|
||||||
void* mData;
|
void* mData;
|
||||||
bool mTrack;
|
bool mTrack;
|
||||||
|
bool mGlobalAllocation;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unordered_map<void*, AllocatedPointer> AllocatedPointerMap;
|
typedef std::unordered_map<void*, AllocatedPointer> AllocatedPointerMap;
|
||||||
@@ -39,14 +42,19 @@ class EE_API MemoryManager {
|
|||||||
|
|
||||||
static bool removePointer( void* data, const char* file, const size_t& line );
|
static bool removePointer( void* data, const char* file, const size_t& line );
|
||||||
|
|
||||||
|
/** Removes a pointer when it is tracked, without diagnosing foreign allocator bookkeeping. */
|
||||||
|
static bool removePointerIfTracked( void* data );
|
||||||
|
|
||||||
static void showResults();
|
static void showResults();
|
||||||
|
|
||||||
template <class T> static T* deletePtr( T* data ) {
|
template <class T> static T* deletePtr( T* data, const char* file, size_t line ) {
|
||||||
|
removePointer( data, file, line );
|
||||||
delete data;
|
delete data;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> static T* deleteArrayPtr( T* data ) {
|
template <class T> static T* deleteArrayPtr( T* data, const char* file, size_t line ) {
|
||||||
|
removePointer( data, file, line );
|
||||||
delete[] data;
|
delete[] data;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -60,25 +68,46 @@ class EE_API MemoryManager {
|
|||||||
|
|
||||||
static void* reallocate( void* ptr, size_t size );
|
static void* reallocate( void* ptr, size_t size );
|
||||||
|
|
||||||
|
/** Allocation entry points used by the debug global new/delete overrides. */
|
||||||
|
static void* allocateGlobal( size_t size, size_t alignment );
|
||||||
|
|
||||||
|
static void freeGlobal( void* ptr ) noexcept;
|
||||||
|
|
||||||
|
template <typename Factory>
|
||||||
|
static auto create( Factory&& factory, const char* file, int line ) -> decltype( factory() ) {
|
||||||
|
auto* pointer = std::forward<Factory>( factory )();
|
||||||
|
using Type = typename std::remove_pointer<decltype( pointer )>::type;
|
||||||
|
return static_cast<Type*>(
|
||||||
|
addPointer( AllocatedPointer( pointer, file, line, sizeof( Type ) ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
static size_t getPeakMemoryUsage();
|
static size_t getPeakMemoryUsage();
|
||||||
|
|
||||||
static size_t getTotalMemoryUsage();
|
static size_t getTotalMemoryUsage();
|
||||||
|
|
||||||
static AllocatedPointer getBiggestAllocation();
|
static AllocatedPointer getBiggestAllocation();
|
||||||
|
|
||||||
|
static AllocatedPointer getBiggestNonAnonymousAllocation();
|
||||||
};
|
};
|
||||||
#if defined( __GNUC__ ) && __GNUC__ >= 12
|
#if defined( __GNUC__ ) && __GNUC__ >= 12
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef EE_MEMORY_MANAGER
|
#ifdef EE_MEMORY_MANAGER
|
||||||
|
#define eeNewExpression( constructor ) \
|
||||||
|
EE::MemoryManager::create( [&]() { return new constructor; }, __FILE__, __LINE__ )
|
||||||
|
|
||||||
|
#define eeNewLegacy( classType, constructor ) \
|
||||||
|
EE::MemoryManager::create( [&]() { return new classType constructor; }, __FILE__, __LINE__ )
|
||||||
|
|
||||||
|
#define eeNewSelect( _1, _2, NAME, ... ) NAME
|
||||||
|
|
||||||
|
#define eeNew( ... ) eeNewSelect( __VA_ARGS__, eeNewLegacy, eeNewExpression )( __VA_ARGS__ )
|
||||||
|
|
||||||
#define eeNewTracked( classType, constructor ) \
|
#define eeNewTracked( classType, constructor ) \
|
||||||
(classType*)EE::MemoryManager::addPointer( EE::AllocatedPointer( \
|
(classType*)EE::MemoryManager::addPointer( EE::AllocatedPointer( \
|
||||||
new classType constructor, __FILE__, __LINE__, sizeof( classType ), true ) )
|
new classType constructor, __FILE__, __LINE__, sizeof( classType ), true ) )
|
||||||
|
|
||||||
#define eeNew( classType, constructor ) \
|
|
||||||
(classType*)EE::MemoryManager::addPointer( EE::AllocatedPointer( \
|
|
||||||
new classType constructor, __FILE__, __LINE__, sizeof( classType ) ) )
|
|
||||||
|
|
||||||
#define eeNewInPlace( place, classType, constructor ) \
|
#define eeNewInPlace( place, classType, constructor ) \
|
||||||
(classType*)EE::MemoryManager::addPointerInPlace( \
|
(classType*)EE::MemoryManager::addPointerInPlace( \
|
||||||
place, EE::AllocatedPointer( new place classType constructor, __FILE__, __LINE__, \
|
place, EE::AllocatedPointer( new place classType constructor, __FILE__, __LINE__, \
|
||||||
@@ -104,12 +133,7 @@ class EE_API MemoryManager {
|
|||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||||
#endif
|
#endif
|
||||||
#define eeDelete( data ) \
|
#define eeDelete( data ) EE::MemoryManager::deletePtr( data, __FILE__, __LINE__ )
|
||||||
{ \
|
|
||||||
if ( EE::MemoryManager::removePointer( EE::MemoryManager::deletePtr( data ), __FILE__, \
|
|
||||||
__LINE__ ) == false ) \
|
|
||||||
printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \
|
|
||||||
}
|
|
||||||
#if defined( __GNUC__ ) && __GNUC__ >= 12
|
#if defined( __GNUC__ ) && __GNUC__ >= 12
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
@@ -117,12 +141,7 @@ class EE_API MemoryManager {
|
|||||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define eeDeleteArray( data ) \
|
#define eeDeleteArray( data ) EE::MemoryManager::deleteArrayPtr( data, __FILE__, __LINE__ )
|
||||||
{ \
|
|
||||||
if ( EE::MemoryManager::removePointer( EE::MemoryManager::deleteArrayPtr( data ), \
|
|
||||||
__FILE__, __LINE__ ) == false ) \
|
|
||||||
printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \
|
|
||||||
}
|
|
||||||
#if defined( __GNUC__ ) && __GNUC__ >= 12
|
#if defined( __GNUC__ ) && __GNUC__ >= 12
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
@@ -143,7 +162,13 @@ class EE_API MemoryManager {
|
|||||||
|
|
||||||
#define eeNewTracked( classType, constructor ) new classType constructor
|
#define eeNewTracked( classType, constructor ) new classType constructor
|
||||||
|
|
||||||
#define eeNew( classType, constructor ) new classType constructor
|
#define eeNewExpression( constructor ) new constructor
|
||||||
|
|
||||||
|
#define eeNewLegacy( classType, constructor ) new classType constructor
|
||||||
|
|
||||||
|
#define eeNewSelect( _1, _2, NAME, ... ) NAME
|
||||||
|
|
||||||
|
#define eeNew( ... ) eeNewSelect( __VA_ARGS__, eeNewLegacy, eeNewExpression )( __VA_ARGS__ )
|
||||||
|
|
||||||
#define eeNewInPlace( place, classType, constructor ) new place classType constructor
|
#define eeNewInPlace( place, classType, constructor ) new place classType constructor
|
||||||
|
|
||||||
|
|||||||
@@ -422,6 +422,7 @@ class EE_API Text {
|
|||||||
|
|
||||||
std::vector<VertexCoords> mVertices;
|
std::vector<VertexCoords> mVertices;
|
||||||
std::vector<Color> mColors;
|
std::vector<Color> mColors;
|
||||||
|
std::vector<Color> mShadowColors;
|
||||||
std::vector<GlyphRenderMode> mRenderModes;
|
std::vector<GlyphRenderMode> mRenderModes;
|
||||||
std::vector<VertexCoords> mOutlineVertices;
|
std::vector<VertexCoords> mOutlineVertices;
|
||||||
std::vector<Color> mOutlineColors;
|
std::vector<Color> mOutlineColors;
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ class EE_API PropertyIdSet {
|
|||||||
|
|
||||||
size_t size() const { return mIds.size(); }
|
size_t size() const { return mIds.size(); }
|
||||||
|
|
||||||
bool operator==( const PropertyIdSet& other ) { return mIds == other.mIds; }
|
bool operator==( const PropertyIdSet& other ) const { return mIds == other.mIds; }
|
||||||
|
|
||||||
bool operator!=( const PropertyIdSet& other ) { return mIds != other.mIds; }
|
bool operator!=( const PropertyIdSet& other ) const { return mIds != other.mIds; }
|
||||||
|
|
||||||
// Union with another set
|
// Union with another set
|
||||||
PropertyIdSet& operator|=( const PropertyIdSet& other ) {
|
PropertyIdSet& operator|=( const PropertyIdSet& other ) {
|
||||||
@@ -43,7 +43,7 @@ class EE_API PropertyIdSet {
|
|||||||
|
|
||||||
// Intersection with another set
|
// Intersection with another set
|
||||||
PropertyIdSet& operator&=( const PropertyIdSet& other ) {
|
PropertyIdSet& operator&=( const PropertyIdSet& other ) {
|
||||||
if ( mIds.size() > 0 && other.mIds.size() > 0 ) {
|
if ( !mIds.empty() && !other.mIds.empty() ) {
|
||||||
for ( auto it = mIds.begin(); it != mIds.end(); )
|
for ( auto it = mIds.begin(); it != mIds.end(); )
|
||||||
if ( other.mIds.count( *it ) == 0 )
|
if ( other.mIds.count( *it ) == 0 )
|
||||||
it = mIds.erase( it );
|
it = mIds.erase( it );
|
||||||
@@ -57,7 +57,7 @@ class EE_API PropertyIdSet {
|
|||||||
|
|
||||||
PropertyIdSet operator&( const PropertyIdSet& other ) const {
|
PropertyIdSet operator&( const PropertyIdSet& other ) const {
|
||||||
PropertyIdSet result;
|
PropertyIdSet result;
|
||||||
if ( mIds.size() > 0 && other.mIds.size() > 0 ) {
|
if ( !mIds.empty() && !other.mIds.empty() ) {
|
||||||
for ( Uint32 id : mIds )
|
for ( Uint32 id : mIds )
|
||||||
if ( other.mIds.count( id ) == 1 )
|
if ( other.mIds.count( id ) == 1 )
|
||||||
result.mIds.insert( id );
|
result.mIds.insert( id );
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <eepp/math/ease.hpp>
|
#include <eepp/math/ease.hpp>
|
||||||
#include <eepp/ui/css/animationdefinition.hpp>
|
#include <eepp/ui/css/animationdefinition.hpp>
|
||||||
#include <eepp/ui/css/elementdefinition.hpp>
|
#include <eepp/ui/css/elementdefinition.hpp>
|
||||||
|
#include <eepp/ui/css/propertyidset.hpp>
|
||||||
#include <eepp/ui/css/stylesheetproperty.hpp>
|
#include <eepp/ui/css/stylesheetproperty.hpp>
|
||||||
#include <eepp/ui/css/stylesheetstyle.hpp>
|
#include <eepp/ui/css/stylesheetstyle.hpp>
|
||||||
#include <eepp/ui/css/transitiondefinition.hpp>
|
#include <eepp/ui/css/transitiondefinition.hpp>
|
||||||
@@ -103,6 +104,7 @@ class EE_API UIStyle : public UIState {
|
|||||||
UnorderedSet<UIWidget*> mRelatedWidgets;
|
UnorderedSet<UIWidget*> mRelatedWidgets;
|
||||||
UnorderedSet<UIWidget*> mSubscribedWidgets;
|
UnorderedSet<UIWidget*> mSubscribedWidgets;
|
||||||
UnorderedSet<UIWidget*> mStructurallyVolatileChildren;
|
UnorderedSet<UIWidget*> mStructurallyVolatileChildren;
|
||||||
|
std::optional<CSS::PropertyIdSet> mChangedProperties;
|
||||||
Uint32 mStateDepthCounter{ 0 };
|
Uint32 mStateDepthCounter{ 0 };
|
||||||
Uint64 mLoadedVersion{ 0 };
|
Uint64 mLoadedVersion{ 0 };
|
||||||
const CSS::StyleSheet* mLoadedStyleSheet{ nullptr };
|
const CSS::StyleSheet* mLoadedStyleSheet{ nullptr };
|
||||||
|
|||||||
@@ -509,10 +509,15 @@ function popen( executable_path )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function build_link_configuration( package_name, use_ee_icon )
|
function build_link_configuration( package_name, use_ee_icon )
|
||||||
|
configuration {}
|
||||||
includedirs { "include" }
|
includedirs { "include" }
|
||||||
|
|
||||||
local extension = "";
|
local extension = "";
|
||||||
|
|
||||||
|
if package_name ~= "eepp" and package_name ~= "eepp-static" then
|
||||||
|
files { "src/eepp/core/memorymanagerglobal.cpp" }
|
||||||
|
end
|
||||||
|
|
||||||
if package_name == "eepp" then
|
if package_name == "eepp" then
|
||||||
defines { "EE_EXPORTS" }
|
defines { "EE_EXPORTS" }
|
||||||
elseif package_name == "eepp-static" then
|
elseif package_name == "eepp-static" then
|
||||||
|
|||||||
@@ -459,9 +459,14 @@ function get_architecture()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function build_link_configuration( package_name, use_ee_icon )
|
function build_link_configuration( package_name, use_ee_icon )
|
||||||
|
filter {}
|
||||||
incdirs { "include" }
|
incdirs { "include" }
|
||||||
local extension = "";
|
local extension = "";
|
||||||
|
|
||||||
|
if package_name ~= "eepp" and package_name ~= "eepp-static" then
|
||||||
|
files { "src/eepp/core/memorymanagerglobal.cpp" }
|
||||||
|
end
|
||||||
|
|
||||||
if os.istarget("emscripten") and package_name ~= "eepp" and package_name ~= "eepp-static" then
|
if os.istarget("emscripten") and package_name ~= "eepp" and package_name ~= "eepp-static" then
|
||||||
local without_assets = {
|
local without_assets = {
|
||||||
["eepp-empty-window"] = true,
|
["eepp-empty-window"] = true,
|
||||||
|
|||||||
+167
-68
@@ -1,3 +1,4 @@
|
|||||||
|
#include <cstddef>
|
||||||
#include <eepp/core/debug.hpp>
|
#include <eepp/core/debug.hpp>
|
||||||
#include <eepp/core/memorymanager.hpp>
|
#include <eepp/core/memorymanager.hpp>
|
||||||
#include <eepp/system/filesystem.hpp>
|
#include <eepp/system/filesystem.hpp>
|
||||||
@@ -5,42 +6,47 @@
|
|||||||
#include <eepp/system/log.hpp>
|
#include <eepp/system/log.hpp>
|
||||||
#include <eepp/system/mutex.hpp>
|
#include <eepp/system/mutex.hpp>
|
||||||
#include <eepp/window/engine.hpp>
|
#include <eepp/window/engine.hpp>
|
||||||
|
#include <new>
|
||||||
|
#include <tabulate/tabulate.hpp>
|
||||||
|
|
||||||
|
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace EE::System;
|
using namespace EE::System;
|
||||||
using namespace EE::Window;
|
using namespace EE::Window;
|
||||||
|
|
||||||
// #ifdef EE_DEBUG
|
|
||||||
// #define EE_OVERRIDE_NEW_DELETE
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
#ifdef EE_OVERRIDE_NEW_DELETE
|
|
||||||
void* operator new( std::size_t n ) {
|
|
||||||
return malloc( n );
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator delete( void* p ) throw() {
|
|
||||||
free( p );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace EE {
|
namespace EE {
|
||||||
|
|
||||||
AllocatedPointer::AllocatedPointer( void* data, const std::string& file, int line, size_t memory,
|
AllocatedPointer::AllocatedPointer( void* data, const std::string& file, int line, size_t memory,
|
||||||
bool track ) {
|
bool track, bool globalAllocation ) {
|
||||||
mData = data;
|
mData = data;
|
||||||
mFile = file;
|
mFile = file;
|
||||||
mLine = line;
|
mLine = line;
|
||||||
mMemory = memory;
|
mMemory = memory;
|
||||||
mTrack = track;
|
mTrack = track;
|
||||||
|
mGlobalAllocation = globalAllocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
thread_local bool insideMemoryManager = false;
|
||||||
|
|
||||||
|
class MemoryManagerScope {
|
||||||
|
public:
|
||||||
|
MemoryManagerScope() : mWasInside( insideMemoryManager ) { insideMemoryManager = true; }
|
||||||
|
~MemoryManagerScope() { insideMemoryManager = mWasInside; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mWasInside;
|
||||||
|
};
|
||||||
|
|
||||||
struct MemoryManagerState {
|
struct MemoryManagerState {
|
||||||
AllocatedPointerMap pointers;
|
AllocatedPointerMap pointers;
|
||||||
size_t totalMemoryUsage{ 0 };
|
size_t totalMemoryUsage{ 0 };
|
||||||
size_t peakMemoryUsage{ 0 };
|
size_t peakMemoryUsage{ 0 };
|
||||||
AllocatedPointer biggestAllocation{ NULL, "", 0, 0 };
|
AllocatedPointer biggestAllocation{ NULL, "", 0, 0 };
|
||||||
|
AllocatedPointer biggestNonAnonymousAllocation{ NULL, "", 0, 0 };
|
||||||
Mutex allocationMutex;
|
Mutex allocationMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,6 +69,7 @@ void* MemoryManager::reallocate( void* ptr, size_t size ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* MemoryManager::addPointerInPlace( void* place, const AllocatedPointer& aAllocatedPointer ) {
|
void* MemoryManager::addPointerInPlace( void* place, const AllocatedPointer& aAllocatedPointer ) {
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
AllocatedPointerMapIt it = state.pointers.find( place );
|
AllocatedPointerMapIt it = state.pointers.find( place );
|
||||||
@@ -75,11 +82,16 @@ void* MemoryManager::addPointerInPlace( void* place, const AllocatedPointer& aAl
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* MemoryManager::addPointer( const AllocatedPointer& aAllocatedPointer ) {
|
void* MemoryManager::addPointer( const AllocatedPointer& aAllocatedPointer ) {
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
|
|
||||||
state.pointers.insert(
|
auto result = state.pointers.insert(
|
||||||
AllocatedPointerMap::value_type( aAllocatedPointer.mData, aAllocatedPointer ) );
|
AllocatedPointerMap::value_type( aAllocatedPointer.mData, aAllocatedPointer ) );
|
||||||
|
if ( !result.second ) {
|
||||||
|
state.totalMemoryUsage -= result.first->second.mMemory;
|
||||||
|
result.first->second = aAllocatedPointer;
|
||||||
|
}
|
||||||
|
|
||||||
state.totalMemoryUsage += aAllocatedPointer.mMemory;
|
state.totalMemoryUsage += aAllocatedPointer.mMemory;
|
||||||
|
|
||||||
@@ -91,6 +103,11 @@ void* MemoryManager::addPointer( const AllocatedPointer& aAllocatedPointer ) {
|
|||||||
state.biggestAllocation = aAllocatedPointer;
|
state.biggestAllocation = aAllocatedPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !aAllocatedPointer.mGlobalAllocation &&
|
||||||
|
aAllocatedPointer.mMemory > state.biggestNonAnonymousAllocation.mMemory ) {
|
||||||
|
state.biggestNonAnonymousAllocation = aAllocatedPointer;
|
||||||
|
}
|
||||||
|
|
||||||
if ( aAllocatedPointer.mTrack )
|
if ( aAllocatedPointer.mTrack )
|
||||||
eePRINTL( "Allocating pointer %p at '%s' %d", aAllocatedPointer.mData,
|
eePRINTL( "Allocating pointer %p at '%s' %d", aAllocatedPointer.mData,
|
||||||
aAllocatedPointer.mFile.c_str(), aAllocatedPointer.mLine );
|
aAllocatedPointer.mFile.c_str(), aAllocatedPointer.mLine );
|
||||||
@@ -99,6 +116,7 @@ void* MemoryManager::addPointer( const AllocatedPointer& aAllocatedPointer ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* MemoryManager::reallocPointer( void* data, const AllocatedPointer& aAllocatedPointer ) {
|
void* MemoryManager::reallocPointer( void* data, const AllocatedPointer& aAllocatedPointer ) {
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
|
|
||||||
@@ -134,12 +152,18 @@ void* MemoryManager::reallocPointer( void* data, const AllocatedPointer& aAlloca
|
|||||||
if ( aAllocatedPointer.mMemory > state.biggestAllocation.mMemory ) {
|
if ( aAllocatedPointer.mMemory > state.biggestAllocation.mMemory ) {
|
||||||
state.biggestAllocation = aAllocatedPointer;
|
state.biggestAllocation = aAllocatedPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !aAllocatedPointer.mGlobalAllocation &&
|
||||||
|
aAllocatedPointer.mMemory > state.biggestNonAnonymousAllocation.mMemory ) {
|
||||||
|
state.biggestNonAnonymousAllocation = aAllocatedPointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return aAllocatedPointer.mData;
|
return aAllocatedPointer.mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemoryManager::removePointer( void* data, const char* file, const size_t& line ) {
|
bool MemoryManager::removePointer( void* data, const char* file, const size_t& line ) {
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
|
|
||||||
@@ -161,24 +185,88 @@ bool MemoryManager::removePointer( void* data, const char* file, const size_t& l
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MemoryManager::removePointerIfTracked( void* data ) {
|
||||||
|
MemoryManagerScope scope;
|
||||||
|
auto& state = getMemoryManagerState();
|
||||||
|
Lock lock( state.allocationMutex );
|
||||||
|
auto it = state.pointers.find( data );
|
||||||
|
if ( it == state.pointers.end() )
|
||||||
|
return false;
|
||||||
|
state.totalMemoryUsage -= it->second.mMemory;
|
||||||
|
state.pointers.erase( it );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* MemoryManager::allocateGlobal( size_t size, size_t alignment ) {
|
||||||
|
size = size == 0 ? 1 : size;
|
||||||
|
void* ptr = nullptr;
|
||||||
|
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||||
|
ptr = _aligned_malloc( size, alignment );
|
||||||
|
#else
|
||||||
|
if ( alignment <= alignof( std::max_align_t ) ) {
|
||||||
|
ptr = malloc( size );
|
||||||
|
} else if ( posix_memalign( &ptr, alignment, size ) != 0 ) {
|
||||||
|
ptr = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if ( ptr == nullptr )
|
||||||
|
throw std::bad_alloc();
|
||||||
|
if ( !insideMemoryManager ) {
|
||||||
|
MemoryManagerScope scope;
|
||||||
|
try {
|
||||||
|
addPointer( AllocatedPointer( ptr, "<global new>", 0, size, false, true ) );
|
||||||
|
} catch ( ... ) {
|
||||||
|
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||||
|
_aligned_free( ptr );
|
||||||
|
#else
|
||||||
|
free( ptr );
|
||||||
|
#endif
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryManager::freeGlobal( void* ptr ) noexcept {
|
||||||
|
if ( ptr == nullptr )
|
||||||
|
return;
|
||||||
|
if ( !insideMemoryManager )
|
||||||
|
removePointerIfTracked( ptr );
|
||||||
|
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||||
|
_aligned_free( ptr );
|
||||||
|
#else
|
||||||
|
free( ptr );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
size_t MemoryManager::getPeakMemoryUsage() {
|
size_t MemoryManager::getPeakMemoryUsage() {
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
return state.peakMemoryUsage;
|
return state.peakMemoryUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t MemoryManager::getTotalMemoryUsage() {
|
size_t MemoryManager::getTotalMemoryUsage() {
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
return state.totalMemoryUsage;
|
return state.totalMemoryUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocatedPointer MemoryManager::getBiggestAllocation() {
|
AllocatedPointer MemoryManager::getBiggestAllocation() {
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
return state.biggestAllocation;
|
return state.biggestAllocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AllocatedPointer MemoryManager::getBiggestNonAnonymousAllocation() {
|
||||||
|
MemoryManagerScope scope;
|
||||||
|
auto& state = getMemoryManagerState();
|
||||||
|
Lock lock( state.allocationMutex );
|
||||||
|
return state.biggestNonAnonymousAllocation;
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryManager::showResults() {
|
void MemoryManager::showResults() {
|
||||||
#ifdef EE_MEMORY_MANAGER
|
#ifdef EE_MEMORY_MANAGER
|
||||||
|
|
||||||
@@ -188,63 +276,74 @@ void MemoryManager::showResults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Engine::destroySingleton();
|
Engine::destroySingleton();
|
||||||
|
MemoryManagerScope scope;
|
||||||
auto& state = getMemoryManagerState();
|
auto& state = getMemoryManagerState();
|
||||||
Lock lock( state.allocationMutex );
|
Lock lock( state.allocationMutex );
|
||||||
|
size_t globalAllocationCount = 0;
|
||||||
eePRINTL( "\n|--Memory Manager Report-------------------------------------|" );
|
size_t globalAllocationMemory = 0;
|
||||||
eePRINTL( "|" );
|
size_t leakCount = 0;
|
||||||
|
for ( const auto& pointer : state.pointers ) {
|
||||||
if ( state.pointers.empty() ) {
|
if ( pointer.second.mGlobalAllocation ) {
|
||||||
eePRINTL( "| No memory leaks detected." );
|
++globalAllocationCount;
|
||||||
} else {
|
globalAllocationMemory += pointer.second.mMemory;
|
||||||
eePRINTL( "| Memory leaks detected: " );
|
} else {
|
||||||
eePRINTL( "|" );
|
++leakCount;
|
||||||
eePRINTL( "| address\t file" );
|
|
||||||
|
|
||||||
// Get max length of file name
|
|
||||||
int lMax = 0;
|
|
||||||
AllocatedPointerMapIt it = state.pointers.begin();
|
|
||||||
|
|
||||||
for ( ; it != state.pointers.end(); ++it ) {
|
|
||||||
AllocatedPointer& ap = it->second;
|
|
||||||
|
|
||||||
if ( (int)ap.mFile.length() > lMax )
|
|
||||||
lMax = (int)ap.mFile.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
lMax += 5;
|
|
||||||
|
|
||||||
for ( int i = 0; i < lMax - 4; ++i )
|
|
||||||
eePRINT( " " );
|
|
||||||
|
|
||||||
eePRINTL( "line\t\t memory usage\t " );
|
|
||||||
|
|
||||||
eePRINTL( "|-----------------------------------------------------------|" );
|
|
||||||
|
|
||||||
it = state.pointers.begin();
|
|
||||||
|
|
||||||
for ( ; it != state.pointers.end(); ++it ) {
|
|
||||||
AllocatedPointer& ap = it->second;
|
|
||||||
|
|
||||||
eePRINT( "| %p\t %s", ap.mData, ap.mFile.c_str() );
|
|
||||||
|
|
||||||
for ( int i = 0; i < lMax - (int)ap.mFile.length(); ++i )
|
|
||||||
eePRINT( " " );
|
|
||||||
|
|
||||||
eePRINTL( "%d\t\t %d\t", ap.mLine, ap.mMemory );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eePRINTL( "|" );
|
eePRINTL( "\nMemory Manager Report" );
|
||||||
eePRINTL( "| Memory left: %s",
|
eePRINTL( "=====================" );
|
||||||
FileSystem::sizeToString( static_cast<Int64>( state.totalMemoryUsage ) ).c_str() );
|
|
||||||
eePRINTL( "| Biggest allocation:" );
|
if ( leakCount == 0 ) {
|
||||||
eePRINTL( "| %s in file: %s at line: %d",
|
eePRINTL( "\nNo actionable memory leaks detected." );
|
||||||
FileSystem::sizeToString( state.biggestAllocation.mMemory ).c_str(),
|
} else {
|
||||||
state.biggestAllocation.mFile.c_str(), state.biggestAllocation.mLine );
|
eePRINTL( "\nActionable memory leaks: %zu\n", leakCount );
|
||||||
eePRINTL( "| Peak Memory Usage: %s",
|
tabulate::Table leaks;
|
||||||
FileSystem::sizeToString( state.peakMemoryUsage ).c_str() );
|
leaks.add_row( { "Address", "File", "Line", "Memory usage" } );
|
||||||
eePRINTL( "|------------------------------------------------------------|\n" );
|
for ( size_t column = 0; column < 4; ++column )
|
||||||
|
leaks[0][column].format().font_style( { tabulate::FontStyle::bold } );
|
||||||
|
|
||||||
|
for ( const auto& pointer : state.pointers ) {
|
||||||
|
const AllocatedPointer& ap = pointer.second;
|
||||||
|
if ( ap.mGlobalAllocation )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char address[2 + sizeof( void* ) * 2 + 1];
|
||||||
|
snprintf( address, sizeof( address ), "%p", ap.mData );
|
||||||
|
leaks.add_row( { address, ap.mFile, std::to_string( ap.mLine ),
|
||||||
|
FileSystem::sizeToString( static_cast<Int64>( ap.mMemory ) ) } );
|
||||||
|
}
|
||||||
|
|
||||||
|
eePRINTL( "%s", leaks.str().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto allocationLocation = []( const AllocatedPointer& allocation ) {
|
||||||
|
return allocation.mData == nullptr
|
||||||
|
? std::string( "-" )
|
||||||
|
: allocation.mFile + ":" + std::to_string( allocation.mLine );
|
||||||
|
};
|
||||||
|
|
||||||
|
tabulate::Table summary;
|
||||||
|
summary.add_row( { "Metric", "Value", "Details" } );
|
||||||
|
for ( size_t column = 0; column < 3; ++column )
|
||||||
|
summary[0][column].format().font_style( { tabulate::FontStyle::bold } );
|
||||||
|
summary.add_row( { "Anonymous allocations still live",
|
||||||
|
std::to_string( globalAllocationCount ) + " / " +
|
||||||
|
FileSystem::sizeToString( static_cast<Int64>( globalAllocationMemory ) ),
|
||||||
|
"Snapshot only; includes process-static and shared-library allocations" } );
|
||||||
|
summary.add_row( { "Total memory still live",
|
||||||
|
FileSystem::sizeToString( static_cast<Int64>( state.totalMemoryUsage ) ),
|
||||||
|
"All tracked allocations" } );
|
||||||
|
summary.add_row( { "Biggest non-anonymous allocation",
|
||||||
|
FileSystem::sizeToString( state.biggestNonAnonymousAllocation.mMemory ),
|
||||||
|
allocationLocation( state.biggestNonAnonymousAllocation ) } );
|
||||||
|
summary.add_row( { "Biggest allocation overall",
|
||||||
|
FileSystem::sizeToString( state.biggestAllocation.mMemory ),
|
||||||
|
allocationLocation( state.biggestAllocation ) } );
|
||||||
|
summary.add_row(
|
||||||
|
{ "Peak memory usage", FileSystem::sizeToString( state.peakMemoryUsage ), "-" } );
|
||||||
|
|
||||||
|
eePRINTL( "\nSummary\n-------\n%s\n", summary.str().c_str() );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#include <eepp/core/memorymanager.hpp>
|
||||||
|
|
||||||
|
#ifdef EE_MEMORY_MANAGER
|
||||||
|
|
||||||
|
void* operator new( std::size_t size ) {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, alignof( std::max_align_t ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new[]( std::size_t size ) {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, alignof( std::max_align_t ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new( std::size_t size, std::align_val_t alignment ) {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, static_cast<std::size_t>( alignment ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new[]( std::size_t size, std::align_val_t alignment ) {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, static_cast<std::size_t>( alignment ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new( std::size_t size, const std::nothrow_t& ) noexcept {
|
||||||
|
try {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, alignof( std::max_align_t ) );
|
||||||
|
} catch ( ... ) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new[]( std::size_t size, const std::nothrow_t& ) noexcept {
|
||||||
|
try {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, alignof( std::max_align_t ) );
|
||||||
|
} catch ( ... ) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new( std::size_t size, std::align_val_t alignment, const std::nothrow_t& ) noexcept {
|
||||||
|
try {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, static_cast<std::size_t>( alignment ) );
|
||||||
|
} catch ( ... ) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new[]( std::size_t size, std::align_val_t alignment,
|
||||||
|
const std::nothrow_t& ) noexcept {
|
||||||
|
try {
|
||||||
|
return EE::MemoryManager::allocateGlobal( size, static_cast<std::size_t>( alignment ) );
|
||||||
|
} catch ( ... ) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete( void* ptr ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete[]( void* ptr ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete( void* ptr, std::size_t ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete[]( void* ptr, std::size_t ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete( void* ptr, std::align_val_t ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete[]( void* ptr, std::align_val_t ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete( void* ptr, std::size_t, std::align_val_t ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete[]( void* ptr, std::size_t, std::align_val_t ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete( void* ptr, const std::nothrow_t& ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete[]( void* ptr, const std::nothrow_t& ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete( void* ptr, std::align_val_t, const std::nothrow_t& ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
void operator delete[]( void* ptr, std::align_val_t, const std::nothrow_t& ) noexcept {
|
||||||
|
EE::MemoryManager::freeGlobal( ptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1856,16 +1856,20 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl
|
|||||||
ensureColorUpdate();
|
ensureColorUpdate();
|
||||||
|
|
||||||
if ( mFontStyleConfig.Style & Shadow ) {
|
if ( mFontStyleConfig.Style & Shadow ) {
|
||||||
std::vector<Color> colors;
|
|
||||||
Color shadowColor( getShadowColor() );
|
Color shadowColor( getShadowColor() );
|
||||||
if ( getFillColor().a != 255 ) {
|
if ( getFillColor().a != 255 ) {
|
||||||
shadowColor.a =
|
shadowColor.a =
|
||||||
(Uint8)( (Float)shadowColor.a * ( (Float)getFillColor().a / (Float)255 ) );
|
(Uint8)( (Float)shadowColor.a * ( (Float)getFillColor().a / (Float)255 ) );
|
||||||
}
|
}
|
||||||
colors.assign( mColors.size(), shadowColor );
|
if ( mShadowColors.size() != mColors.size() ||
|
||||||
|
( !mShadowColors.empty() && mShadowColors.front() != shadowColor ) )
|
||||||
|
mShadowColors.assign( mColors.size(), shadowColor );
|
||||||
|
|
||||||
|
static const std::vector<Color> emptyColors;
|
||||||
|
const std::vector<Color>& shadowOutlineColors =
|
||||||
|
mFontStyleConfig.OutlineThickness > 0 ? mOutlineColors : emptyColors;
|
||||||
draw( X + mFontStyleConfig.ShadowOffset.x, Y + mFontStyleConfig.ShadowOffset.y, scale,
|
draw( X + mFontStyleConfig.ShadowOffset.x, Y + mFontStyleConfig.ShadowOffset.y, scale,
|
||||||
rotation, effect, rotationCenter, scaleCenter, colors,
|
rotation, effect, rotationCenter, scaleCenter, mShadowColors, shadowOutlineColors,
|
||||||
mFontStyleConfig.OutlineThickness > 0 ? mOutlineColors : std::vector<Color>{},
|
|
||||||
Color::Transparent );
|
Color::Transparent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,11 +80,12 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
|||||||
Float txLen = tMax - tMin;
|
Float txLen = tMax - tMin;
|
||||||
|
|
||||||
// Normalize all stops to [0,1] using the gradient-line pixel length
|
// Normalize all stops to [0,1] using the gradient-line pixel length
|
||||||
std::vector<ColorStop> stops;
|
SmallVector<ColorStop, 4> stops;
|
||||||
stops.reserve( mColorStops.size() );
|
stops.reserve( mColorStops.size() );
|
||||||
for ( const auto& s : mColorStops )
|
for ( const auto& s : mColorStops ) {
|
||||||
stops.push_back(
|
stops.push_back(
|
||||||
ColorStop( s.getNormalized( txLen ), s.color, CSS::StyleSheetLength::Percentage ) );
|
ColorStop( s.getNormalized( txLen ), s.color, CSS::StyleSheetLength::Percentage ) );
|
||||||
|
}
|
||||||
|
|
||||||
std::sort( stops.begin(), stops.end(),
|
std::sort( stops.begin(), stops.end(),
|
||||||
[]( const ColorStop& a, const ColorStop& b ) { return a.value < b.value; } );
|
[]( const ColorStop& a, const ColorStop& b ) { return a.value < b.value; } );
|
||||||
@@ -196,7 +197,7 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
|||||||
Vector2f a;
|
Vector2f a;
|
||||||
Vector2f b;
|
Vector2f b;
|
||||||
};
|
};
|
||||||
std::vector<PerpSeg> segments;
|
SmallVector<PerpSeg, 4> segments;
|
||||||
segments.reserve( stops.size() );
|
segments.reserve( stops.size() );
|
||||||
|
|
||||||
for ( size_t i = 0; i < stops.size(); i++ ) {
|
for ( size_t i = 0; i < stops.size(); i++ ) {
|
||||||
|
|||||||
@@ -392,7 +392,10 @@ void UIStyle::onStateChange() {
|
|||||||
mWidget->getUISceneNode()->getStyleSheet().getElementStyles( mWidget, true );
|
mWidget->getUISceneNode()->getStyleSheet().getElementStyles( mWidget, true );
|
||||||
|
|
||||||
if ( newDefinition != mDefinition || mForceReapplyProperties ) {
|
if ( newDefinition != mDefinition || mForceReapplyProperties ) {
|
||||||
PropertyIdSet changedProperties;
|
if ( !mChangedProperties )
|
||||||
|
mChangedProperties.emplace();
|
||||||
|
PropertyIdSet& changedProperties = *mChangedProperties;
|
||||||
|
changedProperties.clear();
|
||||||
|
|
||||||
if ( mDefinition )
|
if ( mDefinition )
|
||||||
changedProperties = mDefinition->getPropertyIds();
|
changedProperties = mDefinition->getPropertyIds();
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
#include "utest.h"
|
#include "utest.h"
|
||||||
|
|
||||||
UTEST_MAIN()
|
#include <eepp/system/filesystem.hpp>
|
||||||
|
#include <eepp/system/sys.hpp>
|
||||||
|
|
||||||
|
UTEST_STATE();
|
||||||
|
|
||||||
|
int main( int argc, const char* const argv[] ) {
|
||||||
|
EE::System::FileSystem::changeWorkingDirectory( EE::System::Sys::getProcessPath() );
|
||||||
|
return utest_main( argc, argv );
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include "utest.h"
|
||||||
|
#include <eepp/core/memorymanager.hpp>
|
||||||
|
|
||||||
|
using namespace EE;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct AllocationProbe {
|
||||||
|
AllocationProbe( int first, int second ) : value( first + second ) {}
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
UTEST( MemoryManager, tracesPlainNew ) {
|
||||||
|
const size_t before = MemoryManager::getTotalMemoryUsage();
|
||||||
|
auto* value = new Uint64( 42 );
|
||||||
|
const size_t allocated = MemoryManager::getTotalMemoryUsage();
|
||||||
|
delete value;
|
||||||
|
const size_t after = MemoryManager::getTotalMemoryUsage();
|
||||||
|
|
||||||
|
EXPECT_GE( allocated, before + sizeof( Uint64 ) );
|
||||||
|
EXPECT_EQ( after, before );
|
||||||
|
}
|
||||||
|
|
||||||
|
UTEST( MemoryManager, supportsExpressionAndLegacyNewSyntax ) {
|
||||||
|
const size_t before = MemoryManager::getTotalMemoryUsage();
|
||||||
|
auto* expression = eeNew( AllocationProbe( 20, 22 ) );
|
||||||
|
auto* legacy = eeNew( AllocationProbe, ( 19, 23 ) );
|
||||||
|
const bool valuesAreValid = expression->value == 42 && legacy->value == 42;
|
||||||
|
eeDelete( expression );
|
||||||
|
eeDelete( legacy );
|
||||||
|
const size_t after = MemoryManager::getTotalMemoryUsage();
|
||||||
|
|
||||||
|
EXPECT_TRUE( valuesAreValid );
|
||||||
|
EXPECT_EQ( after, before );
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#include "utest.h"
|
||||||
|
#include <eepp/core/memorymanager.hpp>
|
||||||
|
#include <eepp/ui/css/propertyidset.hpp>
|
||||||
|
|
||||||
|
using namespace EE;
|
||||||
|
using namespace EE::UI::CSS;
|
||||||
|
|
||||||
|
UTEST( PropertyIdSet, setOperations ) {
|
||||||
|
PropertyIdSet first;
|
||||||
|
first.insert( 30 );
|
||||||
|
first.insert( 10 );
|
||||||
|
first.insert( 20 );
|
||||||
|
first.insert( 20 );
|
||||||
|
|
||||||
|
PropertyIdSet second;
|
||||||
|
second.insert( 20 );
|
||||||
|
second.insert( 40 );
|
||||||
|
|
||||||
|
const PropertyIdSet setUnion = first | second;
|
||||||
|
const PropertyIdSet intersection = first & second;
|
||||||
|
|
||||||
|
EXPECT_EQ( first.size(), 3u );
|
||||||
|
EXPECT_TRUE( setUnion.contains( 10 ) );
|
||||||
|
EXPECT_TRUE( setUnion.contains( 20 ) );
|
||||||
|
EXPECT_TRUE( setUnion.contains( 30 ) );
|
||||||
|
EXPECT_TRUE( setUnion.contains( 40 ) );
|
||||||
|
EXPECT_EQ( intersection.size(), 1u );
|
||||||
|
EXPECT_TRUE( intersection.contains( 20 ) );
|
||||||
|
|
||||||
|
PropertyIdSet sameAsFirst;
|
||||||
|
sameAsFirst.insert( 20 );
|
||||||
|
sameAsFirst.insert( 30 );
|
||||||
|
sameAsFirst.insert( 10 );
|
||||||
|
EXPECT_TRUE( first == sameAsFirst );
|
||||||
|
|
||||||
|
first &= second;
|
||||||
|
EXPECT_TRUE( first == intersection );
|
||||||
|
first.erase( 20 );
|
||||||
|
EXPECT_TRUE( first.empty() );
|
||||||
|
}
|
||||||
|
|
||||||
|
UTEST( PropertyIdSet, iteratorErase ) {
|
||||||
|
PropertyIdSet ids;
|
||||||
|
ids.insert( 10 );
|
||||||
|
ids.insert( 20 );
|
||||||
|
ids.insert( 30 );
|
||||||
|
|
||||||
|
for ( auto it = ids.begin(); it != ids.end(); ) {
|
||||||
|
if ( *it == 20 )
|
||||||
|
it = ids.erase( it );
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ( ids.size(), 2u );
|
||||||
|
EXPECT_FALSE( ids.contains( 20 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
UTEST( PropertyIdSet, clearedSetReusesStorage ) {
|
||||||
|
PropertyIdSet ids;
|
||||||
|
for ( Uint32 id = 0; id < 16; ++id )
|
||||||
|
ids.insert( id );
|
||||||
|
ids.clear();
|
||||||
|
|
||||||
|
const size_t memoryBefore = MemoryManager::getTotalMemoryUsage();
|
||||||
|
for ( Uint32 id = 0; id < 16; ++id )
|
||||||
|
ids.insert( id );
|
||||||
|
const size_t memoryAfter = MemoryManager::getTotalMemoryUsage();
|
||||||
|
|
||||||
|
EXPECT_EQ( ids.size(), 16u );
|
||||||
|
EXPECT_EQ( memoryAfter, memoryBefore );
|
||||||
|
}
|
||||||
@@ -827,22 +827,21 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
|||||||
}
|
}
|
||||||
const auto command = editor->getKeyBindings().getCommandFromKeyBind( eventShortcut );
|
const auto command = editor->getKeyBindings().getCommandFromKeyBind( eventShortcut );
|
||||||
const bool choiceNavigationShortcut =
|
const bool choiceNavigationShortcut =
|
||||||
mSnippetChoiceSuggestions &&
|
mSnippetChoiceSuggestions && ( mShortcuts.prevSuggestion == eventShortcut ||
|
||||||
( mShortcuts["autocomplete-prev-suggestion"] == eventShortcut ||
|
mShortcuts.nextSuggestion == eventShortcut ||
|
||||||
mShortcuts["autocomplete-next-suggestion"] == eventShortcut ||
|
mShortcuts.firstSuggestion == eventShortcut ||
|
||||||
mShortcuts["autocomplete-first-suggestion"] == eventShortcut ||
|
mShortcuts.lastSuggestion == eventShortcut ||
|
||||||
mShortcuts["autocomplete-last-suggestion"] == eventShortcut ||
|
mShortcuts.prevSuggestionPage == eventShortcut ||
|
||||||
mShortcuts["autocomplete-prev-suggestion-page"] == eventShortcut ||
|
mShortcuts.nextSuggestionPage == eventShortcut );
|
||||||
mShortcuts["autocomplete-next-suggestion-page"] == eventShortcut );
|
|
||||||
if ( !choiceNavigationShortcut && isSnippetNavigationCommand( command ) )
|
if ( !choiceNavigationShortcut && isSnippetNavigationCommand( command ) )
|
||||||
cancelSnippetSession( &editor->getDocument(), true );
|
cancelSnippetSession( &editor->getDocument(), true );
|
||||||
}
|
}
|
||||||
if ( mSignatureHelpVisible ) {
|
if ( mSignatureHelpVisible ) {
|
||||||
if ( mShortcuts["autocomplete-close-signature-help"] == eventShortcut ) {
|
if ( mShortcuts.closeSignatureHelp == eventShortcut ) {
|
||||||
resetSignatureHelp();
|
resetSignatureHelp();
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-prev-signature-help"] == eventShortcut ) {
|
} else if ( mShortcuts.prevSignatureHelp == eventShortcut ) {
|
||||||
if ( mSignatureHelp.signatures.size() > 1 ) {
|
if ( mSignatureHelp.signatures.size() > 1 ) {
|
||||||
mSignatureHelpSelected = mSignatureHelpSelected == -1 ? 0 : mSignatureHelpSelected;
|
mSignatureHelpSelected = mSignatureHelpSelected == -1 ? 0 : mSignatureHelpSelected;
|
||||||
++mSignatureHelpSelected;
|
++mSignatureHelpSelected;
|
||||||
@@ -853,7 +852,7 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
|||||||
} else if ( mSuggestions.empty() ) {
|
} else if ( mSuggestions.empty() ) {
|
||||||
resetSignatureHelp();
|
resetSignatureHelp();
|
||||||
}
|
}
|
||||||
} else if ( mShortcuts["autocomplete-next-signature-help"] == eventShortcut ) {
|
} else if ( mShortcuts.nextSignatureHelp == eventShortcut ) {
|
||||||
if ( mSignatureHelp.signatures.size() > 1 ) {
|
if ( mSignatureHelp.signatures.size() > 1 ) {
|
||||||
mSignatureHelpSelected = mSignatureHelpSelected <= 0
|
mSignatureHelpSelected = mSignatureHelpSelected <= 0
|
||||||
? mSignatureHelp.signatures.size()
|
? mSignatureHelp.signatures.size()
|
||||||
@@ -881,15 +880,14 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !mSnippetChoiceSuggestions &&
|
if ( !mSnippetChoiceSuggestions && mShortcuts.updateSuggestions == eventShortcut ) {
|
||||||
mShortcuts["autocomplete-update-suggestions"] == eventShortcut ) {
|
|
||||||
std::string partialSymbol( getPartialSymbol( &editor->getDocument() ) );
|
std::string partialSymbol( getPartialSymbol( &editor->getDocument() ) );
|
||||||
updateSuggestions( partialSymbol, editor );
|
updateSuggestions( partialSymbol, editor );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !mSuggestions.empty() ) {
|
if ( !mSuggestions.empty() ) {
|
||||||
if ( mShortcuts["autocomplete-next-suggestion"] == eventShortcut ) {
|
if ( mShortcuts.nextSuggestion == eventShortcut ) {
|
||||||
if ( mSuggestionIndex + 1 < (int)mSuggestions.size() ) {
|
if ( mSuggestionIndex + 1 < (int)mSuggestions.size() ) {
|
||||||
mSuggestionIndex++;
|
mSuggestionIndex++;
|
||||||
if ( mSuggestionIndex < mSuggestionsStartIndex )
|
if ( mSuggestionIndex < mSuggestionsStartIndex )
|
||||||
@@ -904,7 +902,7 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
|||||||
}
|
}
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-prev-suggestion"] == eventShortcut ) {
|
} else if ( mShortcuts.prevSuggestion == eventShortcut ) {
|
||||||
if ( mSuggestionIndex - 1 < 0 ) {
|
if ( mSuggestionIndex - 1 < 0 ) {
|
||||||
mSuggestionIndex = mSuggestions.size() - 1;
|
mSuggestionIndex = mSuggestions.size() - 1;
|
||||||
mSuggestionsStartIndex =
|
mSuggestionsStartIndex =
|
||||||
@@ -916,22 +914,22 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
|||||||
mSuggestionsStartIndex = mSuggestionIndex;
|
mSuggestionsStartIndex = mSuggestionIndex;
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-close-suggestion"] == eventShortcut ) {
|
} else if ( mShortcuts.closeSuggestion == eventShortcut ) {
|
||||||
resetSuggestions( editor );
|
resetSuggestions( editor );
|
||||||
resetSignatureHelp();
|
resetSignatureHelp();
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-first-suggestion"] == eventShortcut ) {
|
} else if ( mShortcuts.firstSuggestion == eventShortcut ) {
|
||||||
mSuggestionIndex = 0;
|
mSuggestionIndex = 0;
|
||||||
mSuggestionsStartIndex = 0;
|
mSuggestionsStartIndex = 0;
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-last-suggestion"] == eventShortcut ) {
|
} else if ( mShortcuts.lastSuggestion == eventShortcut ) {
|
||||||
mSuggestionIndex = mSuggestions.size() - 1;
|
mSuggestionIndex = mSuggestions.size() - 1;
|
||||||
mSuggestionsStartIndex = eemax( 0, (int)mSuggestions.size() - mSuggestionsMaxVisible );
|
mSuggestionsStartIndex = eemax( 0, (int)mSuggestions.size() - mSuggestionsMaxVisible );
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-prev-suggestion-page"] == eventShortcut ) {
|
} else if ( mShortcuts.prevSuggestionPage == eventShortcut ) {
|
||||||
if ( mSuggestionIndex - (int)( mSuggestionsMaxVisible - 1 ) >= 0 ) {
|
if ( mSuggestionIndex - (int)( mSuggestionsMaxVisible - 1 ) >= 0 ) {
|
||||||
mSuggestionIndex -= ( mSuggestionsMaxVisible - 1 );
|
mSuggestionIndex -= ( mSuggestionsMaxVisible - 1 );
|
||||||
if ( mSuggestionIndex < mSuggestionsStartIndex )
|
if ( mSuggestionIndex < mSuggestionsStartIndex )
|
||||||
@@ -942,7 +940,7 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
|||||||
}
|
}
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-next-suggestion-page"] == eventShortcut ) {
|
} else if ( mShortcuts.nextSuggestionPage == eventShortcut ) {
|
||||||
if ( mSuggestionIndex + mSuggestionsMaxVisible < (int)mSuggestions.size() ) {
|
if ( mSuggestionIndex + mSuggestionsMaxVisible < (int)mSuggestions.size() ) {
|
||||||
mSuggestionIndex += mSuggestionsMaxVisible - 1;
|
mSuggestionIndex += mSuggestionsMaxVisible - 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -952,9 +950,9 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event
|
|||||||
eemax<int>( 0, mSuggestionIndex - ( mSuggestionsMaxVisible - 1 ) );
|
eemax<int>( 0, mSuggestionIndex - ( mSuggestionsMaxVisible - 1 ) );
|
||||||
editor->invalidateDraw();
|
editor->invalidateDraw();
|
||||||
return true;
|
return true;
|
||||||
} else if ( mShortcuts["autocomplete-pick-suggestion"] == eventShortcut ||
|
} else if ( mShortcuts.pickSuggestion == eventShortcut ||
|
||||||
mShortcuts["autocomplete-pick-suggestion-alt"] == eventShortcut ||
|
mShortcuts.pickSuggestionAlt == eventShortcut ||
|
||||||
mShortcuts["autocomplete-pick-suggestion-alt-2"] == eventShortcut ) {
|
mShortcuts.pickSuggestionAlt2 == eventShortcut ) {
|
||||||
pickSuggestion( editor );
|
pickSuggestion( editor );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1166,10 +1164,8 @@ SnippetParser::VariableMap AutoCompletePlugin::snippetVariables( TextDocument& d
|
|||||||
{ "CURRENT_SECOND", formatSnippetTime( localTime, "%S" ) },
|
{ "CURRENT_SECOND", formatSnippetTime( localTime, "%S" ) },
|
||||||
{ "CURRENT_MILLISECOND",
|
{ "CURRENT_MILLISECOND",
|
||||||
String::format( "%03d", static_cast<int>( milliseconds % 1000 ) ) },
|
String::format( "%03d", static_cast<int>( milliseconds % 1000 ) ) },
|
||||||
{ "CURRENT_SECONDS_UNIX",
|
{ "CURRENT_SECONDS_UNIX", String::toString( static_cast<Int64>( milliseconds / 1000 ) ) },
|
||||||
String::toString( static_cast<Int64>( milliseconds / 1000 ) ) },
|
{ "CURRENT_MILLISECONDS_UNIX", String::toString( static_cast<Int64>( milliseconds ) ) },
|
||||||
{ "CURRENT_MILLISECONDS_UNIX",
|
|
||||||
String::toString( static_cast<Int64>( milliseconds ) ) },
|
|
||||||
{ "CURRENT_TIMEZONE_OFFSET", timezoneOffset },
|
{ "CURRENT_TIMEZONE_OFFSET", timezoneOffset },
|
||||||
{ "CURRENT_TIMEZONE_NAME", formatSnippetTime( localTime, "%Z" ) },
|
{ "CURRENT_TIMEZONE_NAME", formatSnippetTime( localTime, "%Z" ) },
|
||||||
{ "RANDOM", String::format( "%06d", Math::randi( 0, 999999 ) ) },
|
{ "RANDOM", String::format( "%06d", Math::randi( 0, 999999 ) ) },
|
||||||
@@ -1864,8 +1860,21 @@ void AutoCompletePlugin::updateShortcuts() {
|
|||||||
getManager()->getUISceneNode()->getEventDispatcher()->getInput(), keys );
|
getManager()->getUISceneNode()->getEventDispatcher()->getInput(), keys );
|
||||||
};
|
};
|
||||||
|
|
||||||
for ( const auto& kb : mKeyBindings )
|
mShortcuts.closeSuggestion = toShortcut( mKeyBindings["autocomplete-close-suggestion"] );
|
||||||
mShortcuts[kb.first] = toShortcut( kb.second );
|
mShortcuts.prevSuggestion = toShortcut( mKeyBindings["autocomplete-prev-suggestion"] );
|
||||||
|
mShortcuts.nextSuggestion = toShortcut( mKeyBindings["autocomplete-next-suggestion"] );
|
||||||
|
mShortcuts.firstSuggestion = toShortcut( mKeyBindings["autocomplete-first-suggestion"] );
|
||||||
|
mShortcuts.lastSuggestion = toShortcut( mKeyBindings["autocomplete-last-suggestion"] );
|
||||||
|
mShortcuts.prevSuggestionPage = toShortcut( mKeyBindings["autocomplete-prev-suggestion-page"] );
|
||||||
|
mShortcuts.nextSuggestionPage = toShortcut( mKeyBindings["autocomplete-next-suggestion-page"] );
|
||||||
|
mShortcuts.pickSuggestion = toShortcut( mKeyBindings["autocomplete-pick-suggestion"] );
|
||||||
|
mShortcuts.pickSuggestionAlt = toShortcut( mKeyBindings["autocomplete-pick-suggestion-alt"] );
|
||||||
|
mShortcuts.pickSuggestionAlt2 =
|
||||||
|
toShortcut( mKeyBindings["autocomplete-pick-suggestion-alt-2"] );
|
||||||
|
mShortcuts.updateSuggestions = toShortcut( mKeyBindings["autocomplete-update-suggestions"] );
|
||||||
|
mShortcuts.closeSignatureHelp = toShortcut( mKeyBindings["autocomplete-close-signature-help"] );
|
||||||
|
mShortcuts.prevSignatureHelp = toShortcut( mKeyBindings["autocomplete-prev-signature-help"] );
|
||||||
|
mShortcuts.nextSignatureHelp = toShortcut( mKeyBindings["autocomplete-next-signature-help"] );
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginRequestHandle AutoCompletePlugin::processResponse( const PluginMessage& msg ) {
|
PluginRequestHandle AutoCompletePlugin::processResponse( const PluginMessage& msg ) {
|
||||||
|
|||||||
@@ -193,7 +193,22 @@ class AutoCompletePlugin : public Plugin {
|
|||||||
size_t mMaxLabelCharacters{ 100 };
|
size_t mMaxLabelCharacters{ 100 };
|
||||||
String::HashType mConfigHash{ 0 };
|
String::HashType mConfigHash{ 0 };
|
||||||
std::unordered_map<std::string, std::string> mKeyBindings;
|
std::unordered_map<std::string, std::string> mKeyBindings;
|
||||||
UnorderedMap<std::string, KeyBindings::Shortcut> mShortcuts;
|
struct Shortcuts {
|
||||||
|
KeyBindings::Shortcut closeSuggestion;
|
||||||
|
KeyBindings::Shortcut prevSuggestion;
|
||||||
|
KeyBindings::Shortcut nextSuggestion;
|
||||||
|
KeyBindings::Shortcut firstSuggestion;
|
||||||
|
KeyBindings::Shortcut lastSuggestion;
|
||||||
|
KeyBindings::Shortcut prevSuggestionPage;
|
||||||
|
KeyBindings::Shortcut nextSuggestionPage;
|
||||||
|
KeyBindings::Shortcut pickSuggestion;
|
||||||
|
KeyBindings::Shortcut pickSuggestionAlt;
|
||||||
|
KeyBindings::Shortcut pickSuggestionAlt2;
|
||||||
|
KeyBindings::Shortcut updateSuggestions;
|
||||||
|
KeyBindings::Shortcut closeSignatureHelp;
|
||||||
|
KeyBindings::Shortcut prevSignatureHelp;
|
||||||
|
KeyBindings::Shortcut nextSignatureHelp;
|
||||||
|
} mShortcuts;
|
||||||
std::string mMaxSuggestionDocumentationWidth{ "100%" };
|
std::string mMaxSuggestionDocumentationWidth{ "100%" };
|
||||||
std::string mMaxSignatureHelperWidth{ "90%" };
|
std::string mMaxSignatureHelperWidth{ "90%" };
|
||||||
bool mSignatureHelpMultiLine{ true };
|
bool mSignatureHelpMultiLine{ true };
|
||||||
|
|||||||
Reference in New Issue
Block a user