mirror of
https://github.com/wolfpld/tracy
synced 2025-01-15 11:57:59 +00:00
Compare commits
188 Commits
d90758e92d
...
32b1e8dcb0
Author | SHA1 | Date | |
---|---|---|---|
|
32b1e8dcb0 | ||
|
759dd39f77 | ||
|
0e0692b7f7 | ||
|
e9aca02880 | ||
|
846bde1a11 | ||
|
15b42039bd | ||
|
d4b92e0fbe | ||
|
a10d71b766 | ||
|
a9a16b4d94 | ||
|
22352dd29b | ||
|
5d86002f82 | ||
|
5efc03cdf9 | ||
|
c445c3805c | ||
|
13c103065a | ||
|
7e17c8643c | ||
|
bc4016e97c | ||
|
2f3ee594f0 | ||
|
3305ea2775 | ||
|
07fe0a5447 | ||
|
3cc69b221e | ||
|
80e1b0c2cf | ||
|
52708268fe | ||
|
4e6a1bae22 | ||
|
326a2a70d8 | ||
|
ad2b6e932b | ||
|
a5991036dc | ||
|
c7b3d42852 | ||
|
90149641df | ||
|
8d52d2acfd | ||
|
2e3241ba54 | ||
|
6cc844abee | ||
|
cb63b35cb1 | ||
|
748c22df8e | ||
|
53111f9dd9 | ||
|
ee13d3fa44 | ||
|
8045ceee5d | ||
|
ec3ee09f09 | ||
|
654331a8c8 | ||
|
fe7db6db4e | ||
|
3ce2f6209a | ||
|
cc0ca341bb | ||
|
7ad9cafefb | ||
|
6e8d57bc98 | ||
|
c17a913cf5 | ||
|
b91cd9148d | ||
|
188320db48 | ||
|
f6c2255e88 | ||
|
7e1622083e | ||
|
2c4b2ce6f0 | ||
|
5e582ef057 | ||
|
91f1845d92 | ||
|
c69cf5bd3f | ||
|
8e5a53a191 | ||
|
e1b68232dc | ||
|
a96410547b | ||
|
d49da69972 | ||
|
7086f2db65 | ||
|
d2ebe341f2 | ||
|
f145ca5897 | ||
|
538cf3bcf1 | ||
|
2f7d79adba | ||
|
50e0d4781b | ||
|
48fe6550a6 | ||
|
f1bf663160 | ||
|
2d5f6a411c | ||
|
8fa9860764 | ||
|
bdb11f6e48 | ||
|
5b642cad01 | ||
|
db1dc311b0 | ||
|
0fdd5ab01b | ||
|
6d03b18d1c | ||
|
891e1ac280 | ||
|
697ec4372e | ||
|
06dcdd342f | ||
|
004681b02b | ||
|
698e5f0210 | ||
|
2d2a5c92fd | ||
|
e17316fded | ||
|
1f07a4ce35 | ||
|
f773e18375 | ||
|
0c13889589 | ||
|
be07ccc1d3 | ||
|
b35dfe71d1 | ||
|
3968945e6a | ||
|
4dc05ab858 | ||
|
0168ab6535 | ||
|
0a954fda87 | ||
|
ed50447f7a | ||
|
f4c95eb021 | ||
|
ca2130d56c | ||
|
5b7cd06840 | ||
|
7889d33044 | ||
|
c20721ca4f | ||
|
ff54c29913 | ||
|
9e28f441c8 | ||
|
27e61b8204 | ||
|
7aa575fd2e | ||
|
de2ca9b22a | ||
|
b7b9912b10 | ||
|
90b51568e9 | ||
|
c61a7ef57b | ||
|
bb0f5025b2 | ||
|
9c2ea8a71f | ||
|
e1c3babf43 | ||
|
854613f210 | ||
|
85da04bcc4 | ||
|
def9570eda | ||
|
18cace42ca | ||
|
82bfee5127 | ||
|
2eb50bcf42 | ||
|
66318bdb3b | ||
|
c79bfa349f | ||
|
ecf0e3d587 | ||
|
b68c23fc64 | ||
|
4316d48bea | ||
|
7c378cd0a9 | ||
|
cc831e8192 | ||
|
0b05587b05 | ||
|
cb849ee14c | ||
|
3c076ddb6f | ||
|
39cf98f98c | ||
|
86510c48e0 | ||
|
b281ce9c0e | ||
|
859aa3b2b0 | ||
|
3c31fb56a8 | ||
|
a1acea0c50 | ||
|
2765be92fb | ||
|
1616411257 | ||
|
348582d6e4 | ||
|
f8bb24ed36 | ||
|
483d31c1f4 | ||
|
eb38640114 | ||
|
b79014f3ee | ||
|
b7c5939bb8 | ||
|
688a9722c7 | ||
|
4fc02f5680 | ||
|
f8ccd8c40d | ||
|
a6011cd1ad | ||
|
c13611cf0c | ||
|
cfb6d0d2ae | ||
|
233a0bb6d6 | ||
|
b2d5fe8e1f | ||
|
b1e4d16537 | ||
|
bbd1c4505c | ||
|
4ad6f682c8 | ||
|
fece23a32b | ||
|
7d3119cbac | ||
|
d869b9a8bc | ||
|
19c41b94c0 | ||
|
a6f85bd061 | ||
|
48ab346835 | ||
|
2544b891de | ||
|
1b589855e8 | ||
|
815cf7d00a | ||
|
77c33327f4 | ||
|
34ca6d865e | ||
|
c7026cbc1f | ||
|
089eda0be9 | ||
|
fef507dfa2 | ||
|
8ec08465ee | ||
|
afcebb6e6a | ||
|
4d668741eb | ||
|
ea5a9ee83b | ||
|
87cff004a4 | ||
|
3ba82ba4d6 | ||
|
c6f071a66a | ||
|
df50eb890f | ||
|
bcb7b94272 | ||
|
5a7e9cdbdd | ||
|
f0f3babacf | ||
|
9b4716c49e | ||
|
227a8d1aee | ||
|
5f3d1c0faf | ||
|
9ef5430c01 | ||
|
faf87809d7 | ||
|
24b5e16718 | ||
|
741de5c8fb | ||
|
3a0e12043d | ||
|
2e38e70049 | ||
|
28cc72a69c | ||
|
3228e20c2e | ||
|
373a2b66c6 | ||
|
101cdd9b4b | ||
|
64ed3d057d | ||
|
7794443453 | ||
|
16101571e0 | ||
|
9cd1b26bc7 | ||
|
b7d52d2eab |
24
NEWS
24
NEWS
@ -6,6 +6,7 @@ here.
|
||||
v0.x.x (xxxx-xx-xx)
|
||||
-------------------
|
||||
|
||||
- Cygwin and Mingw are deprecated due to lack of interest.
|
||||
- Added TRACY_NO_CALLSTACK_INLINES macro to disable inline functions
|
||||
resolution in call stacks on Windows.
|
||||
- Limited client query response rate.
|
||||
@ -13,6 +14,29 @@ v0.x.x (xxxx-xx-xx)
|
||||
- Added minimal CMake integration layer.
|
||||
- Reworked rpmalloc initialization.
|
||||
- Fixed display of messages with newlines on messages list.
|
||||
- Excluded some uninteresting wrapper functions from call stacks (for
|
||||
example SIMD pass-through intrinsics to the compiler built-ins).
|
||||
- Adjusted coloring of instruction hotness in symbol view.
|
||||
- Properly handle rare cases when sampling on Linux is momentary not able to
|
||||
resolve time stamps.
|
||||
- Added Rocket Lake microarchitectural data.
|
||||
- Updated CPU identifier lists.
|
||||
- Implemented GPU timer overflow handling heuristics.
|
||||
- Assembly instructions are now assigned to inline symbols.
|
||||
- You can not only see the assembly source file and line, but also the
|
||||
originating function.
|
||||
- If symbol view is restricted to a single inline function, all assembly
|
||||
instructions not in this context will be dimmed out.
|
||||
- Kernel call stacks will be now properly captured and displayed in the
|
||||
profiler. Kernel functions are marked with the red color.
|
||||
- The CPU hardware performance counters can be now sampled on Linux.
|
||||
- Three inferred statistics are displayed for lines in both source and
|
||||
assembly code in the symbol view window:
|
||||
- Instructions executed per cycle.
|
||||
- Branch miss rate.
|
||||
- Cache miss rate.
|
||||
- Instruction cost estimation method is no longer tied to software call
|
||||
stack sampling.
|
||||
|
||||
|
||||
v0.7.8 (2021-05-19)
|
||||
|
@ -42,6 +42,7 @@
|
||||
# else
|
||||
# include "libbacktrace/elf.cpp"
|
||||
# endif
|
||||
# include "common/TracyStackFrames.cpp"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -86,6 +86,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\common\TracySocket.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracySystem.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4hc.cpp" />
|
||||
@ -93,7 +94,6 @@
|
||||
<ClCompile Include="..\..\..\server\TracyMemory.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyMmap.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyPrint.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyStackFrames.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyTaskDispatch.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyTextureCompression.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyThreadCompress.cpp" />
|
||||
@ -137,6 +137,7 @@
|
||||
<ClInclude Include="..\..\..\common\TracyProtocol.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyQueue.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySocket.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4hc.hpp" />
|
||||
@ -150,7 +151,6 @@
|
||||
<ClInclude Include="..\..\..\server\TracyPopcnt.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyPrint.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracySlab.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyStackFrames.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyTaskDispatch.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyTextureCompression.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyThreadCompress.hpp" />
|
||||
|
@ -69,9 +69,6 @@
|
||||
<ClCompile Include="..\..\..\getopt\getopt.c">
|
||||
<Filter>getopt</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\server\TracyStackFrames.cpp">
|
||||
<Filter>server</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\zstd\common\debug.c">
|
||||
<Filter>zstd\common</Filter>
|
||||
</ClCompile>
|
||||
@ -159,6 +156,9 @@
|
||||
<ClCompile Include="..\..\..\zstd\dictBuilder\zdict.c">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp">
|
||||
<Filter>common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp">
|
||||
@ -236,9 +236,6 @@
|
||||
<ClInclude Include="..\..\..\getopt\getopt.h">
|
||||
<Filter>getopt</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\TracyStackFrames.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\zstd\zstd.h">
|
||||
<Filter>zstd</Filter>
|
||||
</ClInclude>
|
||||
@ -344,5 +341,8 @@
|
||||
<ClInclude Include="..\..\..\zstd\zdict.h">
|
||||
<Filter>zstd</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -14,10 +14,10 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../../common/TracyProtocol.hpp"
|
||||
#include "../../common/TracyStackFrames.hpp"
|
||||
#include "../../server/TracyFileWrite.hpp"
|
||||
#include "../../server/TracyMemory.hpp"
|
||||
#include "../../server/TracyPrint.hpp"
|
||||
#include "../../server/TracyStackFrames.hpp"
|
||||
#include "../../server/TracyWorker.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -61,8 +61,8 @@ static const char* DecodeArmPart( uint32_t impl, uint32_t part )
|
||||
case 0xc09: return " Cortex-A9";
|
||||
case 0xc0c: return " Cortex-A12";
|
||||
case 0xc0d: return " Rockchip RK3288";
|
||||
case 0xc0f: return " Cortex-A15";
|
||||
case 0xc0e: return " Cortex-A17";
|
||||
case 0xc0f: return " Cortex-A15";
|
||||
case 0xc14: return " Cortex-R4";
|
||||
case 0xc15: return " Cortex-R5";
|
||||
case 0xc17: return " Cortex-R7";
|
||||
@ -92,11 +92,16 @@ static const char* DecodeArmPart( uint32_t impl, uint32_t part )
|
||||
case 0xd13: return " Cortex-R52";
|
||||
case 0xd20: return " Cortex-M23";
|
||||
case 0xd21: return " Cortex-M33";
|
||||
case 0xd40: return " Zeus";
|
||||
case 0xd40: return " Neoverse V1";
|
||||
case 0xd41: return " Cortex-A78";
|
||||
case 0xd42: return " Cortex-A78AE";
|
||||
case 0xd43: return " Cortex-A65AE";
|
||||
case 0xd44: return " Cortex-X1";
|
||||
case 0xd47: return " Cortex-A710";
|
||||
case 0xd48: return " Cortex-X2";
|
||||
case 0xd49: return " Neoverse N2";
|
||||
case 0xd4a: return " Neoverse E1";
|
||||
case 0xd4b: return " Cortex-A78C";
|
||||
default: break;
|
||||
}
|
||||
case 0x42:
|
||||
@ -267,6 +272,10 @@ static const char* DecodeIosDevice( const char* id )
|
||||
"iPhone12,3", "iPhone 11 Pro",
|
||||
"iPhone12,5", "iPhone 11 Pro Max",
|
||||
"iPhone12,8", "iPhone SE 2nd Gen",
|
||||
"iPhone13,1", "iPhone 12 Mini",
|
||||
"iPhone13,2", "iPhone 12",
|
||||
"iPhone13,3", "iPhone 12 Pro",
|
||||
"iPhone13,4", "iPhone 12 Pro Max",
|
||||
"iPad1,1", "iPad (A1219/A1337)",
|
||||
"iPad2,1", "iPad 2 (A1395)",
|
||||
"iPad2,2", "iPad 2 (A1396)",
|
||||
@ -325,6 +334,18 @@ static const char* DecodeIosDevice( const char* id )
|
||||
"iPad11,2", "iPad Mini 5th gen (A2124/A2125/A2126)",
|
||||
"iPad11,3", "iPad Air 3rd gen (A2152)",
|
||||
"iPad11,4", "iPad Air 3rd gen (A2123/A2153/A2154)",
|
||||
"iPad11,6", "iPad 8th gen (WiFi)",
|
||||
"iPad11,7", "iPad 8th gen (WiFi+Cellular)",
|
||||
"iPad13,1", "iPad Air 4th gen (WiFi)",
|
||||
"iPad13,2", "iPad Air 4th gen (WiFi+Cellular)",
|
||||
"iPad13,4", "iPad Pro 11\" 3rd gen",
|
||||
"iPad13,5", "iPad Pro 11\" 3rd gen",
|
||||
"iPad13,6", "iPad Pro 11\" 3rd gen",
|
||||
"iPad13,7", "iPad Pro 11\" 3rd gen",
|
||||
"iPad13,8", "iPad Pro 12.9\" 5th gen",
|
||||
"iPad13,9", "iPad Pro 12.9\" 5th gen",
|
||||
"iPad13,10", "iPad Pro 12.9\" 5th gen",
|
||||
"iPad13,11", "iPad Pro 12.9\" 5th gen",
|
||||
"iPod1,1", "iPod Touch",
|
||||
"iPod2,1", "iPod Touch 2nd gen",
|
||||
"iPod3,1", "iPod Touch 3rd gen",
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "TracyCallstack.hpp"
|
||||
#include "TracyFastVector.hpp"
|
||||
#include "../common/TracyAlloc.hpp"
|
||||
#include "../common/TracyStackFrames.hpp"
|
||||
#include "TracyDebug.hpp"
|
||||
|
||||
#ifdef TRACY_HAS_CALLSTACK
|
||||
|
||||
@ -13,6 +15,7 @@
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <psapi.h>
|
||||
# include <algorithm>
|
||||
# ifdef _MSC_VER
|
||||
# pragma warning( push )
|
||||
# pragma warning( disable : 4091 )
|
||||
@ -23,8 +26,11 @@
|
||||
# endif
|
||||
#elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
|
||||
# include "../libbacktrace/backtrace.hpp"
|
||||
# include <algorithm>
|
||||
# include <dlfcn.h>
|
||||
# include <cxxabi.h>
|
||||
# include <stdlib.h>
|
||||
# include "TracyFastVector.hpp"
|
||||
#elif TRACY_HAS_CALLSTACK == 5
|
||||
# include <dlfcn.h>
|
||||
# include <cxxabi.h>
|
||||
@ -66,6 +72,25 @@ static inline char* CopyString( const char* src )
|
||||
return dst;
|
||||
}
|
||||
|
||||
static inline char* CopyStringFast( const char* src, size_t sz )
|
||||
{
|
||||
assert( strlen( src ) == sz );
|
||||
auto dst = (char*)tracy_malloc_fast( sz + 1 );
|
||||
memcpy( dst, src, sz );
|
||||
dst[sz] = '\0';
|
||||
return dst;
|
||||
}
|
||||
|
||||
static inline char* CopyStringFast( const char* src )
|
||||
{
|
||||
const auto sz = strlen( src );
|
||||
auto dst = (char*)tracy_malloc_fast( sz + 1 );
|
||||
memcpy( dst, src, sz );
|
||||
dst[sz] = '\0';
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if TRACY_HAS_CALLSTACK == 1
|
||||
|
||||
@ -99,6 +124,16 @@ struct ModuleCache
|
||||
};
|
||||
|
||||
static FastVector<ModuleCache>* s_modCache;
|
||||
|
||||
|
||||
struct KernelDriver
|
||||
{
|
||||
uint64_t addr;
|
||||
const char* mod;
|
||||
};
|
||||
|
||||
KernelDriver* s_krnlCache = nullptr;
|
||||
size_t s_krnlCacheCnt;
|
||||
#endif
|
||||
|
||||
void InitCallstack()
|
||||
@ -118,13 +153,55 @@ void InitCallstack()
|
||||
SymSetOptions( SYMOPT_LOAD_LINES );
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
HMODULE mod[1024];
|
||||
DWORD needed;
|
||||
HANDLE proc = GetCurrentProcess();
|
||||
LPVOID dev[4096];
|
||||
if( EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 )
|
||||
{
|
||||
char windir[MAX_PATH];
|
||||
if( !GetWindowsDirectoryA( windir, sizeof( windir ) ) ) memcpy( windir, "c:\\windows", 11 );
|
||||
const auto windirlen = strlen( windir );
|
||||
|
||||
const auto sz = needed / sizeof( LPVOID );
|
||||
s_krnlCache = (KernelDriver*)tracy_malloc( sizeof(KernelDriver) * sz );
|
||||
int cnt = 0;
|
||||
for( size_t i=0; i<sz; i++ )
|
||||
{
|
||||
char fn[MAX_PATH];
|
||||
const auto len = GetDeviceDriverBaseNameA( dev[i], fn, sizeof( fn ) );
|
||||
if( len != 0 )
|
||||
{
|
||||
auto buf = (char*)tracy_malloc_fast( len+3 );
|
||||
buf[0] = '<';
|
||||
memcpy( buf+1, fn, len );
|
||||
memcpy( buf+len+1, ">", 2 );
|
||||
s_krnlCache[cnt++] = KernelDriver { (uint64_t)dev[i], buf };
|
||||
|
||||
const auto len = GetDeviceDriverFileNameA( dev[i], fn, sizeof( fn ) );
|
||||
if( len != 0 )
|
||||
{
|
||||
char full[MAX_PATH];
|
||||
char* path = fn;
|
||||
|
||||
if( memcmp( fn, "\\SystemRoot\\", 12 ) == 0 )
|
||||
{
|
||||
memcpy( full, windir, windirlen );
|
||||
strcpy( full + windirlen, fn + 11 );
|
||||
path = full;
|
||||
}
|
||||
|
||||
SymLoadModuleEx( GetCurrentProcess(), nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
s_krnlCacheCnt = cnt;
|
||||
std::sort( s_krnlCache, s_krnlCache + s_krnlCacheCnt, []( const KernelDriver& lhs, const KernelDriver& rhs ) { return lhs.addr > rhs.addr; } );
|
||||
}
|
||||
|
||||
s_modCache = (FastVector<ModuleCache>*)tracy_malloc( sizeof( FastVector<ModuleCache> ) );
|
||||
new(s_modCache) FastVector<ModuleCache>( 512 );
|
||||
|
||||
HANDLE proc = GetCurrentProcess();
|
||||
HMODULE mod[1024];
|
||||
if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 )
|
||||
{
|
||||
const auto sz = needed / sizeof( HMODULE );
|
||||
@ -145,7 +222,7 @@ void InitCallstack()
|
||||
auto cache = s_modCache->push_next();
|
||||
cache->start = base;
|
||||
cache->end = base + info.SizeOfImage;
|
||||
cache->name = (char*)tracy_malloc( namelen+3 );
|
||||
cache->name = (char*)tracy_malloc_fast( namelen+3 );
|
||||
cache->name[0] = '[';
|
||||
memcpy( cache->name+1, ptr, namelen );
|
||||
cache->name[namelen+1] = ']';
|
||||
@ -199,7 +276,20 @@ const char* DecodeCallstackPtrFast( uint64_t ptr )
|
||||
|
||||
static const char* GetModuleName( uint64_t addr )
|
||||
{
|
||||
if( ( addr & 0x8000000000000000 ) != 0 ) return "[kernel]";
|
||||
if( ( addr >> 63 ) != 0 )
|
||||
{
|
||||
#ifndef __CYGWIN__
|
||||
if( s_krnlCache )
|
||||
{
|
||||
auto it = std::lower_bound( s_krnlCache, s_krnlCache + s_krnlCacheCnt, addr, []( const KernelDriver& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } );
|
||||
if( it != s_krnlCache + s_krnlCacheCnt )
|
||||
{
|
||||
return it->mod;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return "<kernel>";
|
||||
}
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
for( auto& v : *s_modCache )
|
||||
@ -214,6 +304,7 @@ static const char* GetModuleName( uint64_t addr )
|
||||
DWORD needed;
|
||||
HANDLE proc = GetCurrentProcess();
|
||||
|
||||
InitRpmalloc();
|
||||
if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 )
|
||||
{
|
||||
const auto sz = needed / sizeof( HMODULE );
|
||||
@ -236,7 +327,7 @@ static const char* GetModuleName( uint64_t addr )
|
||||
auto cache = s_modCache->push_next();
|
||||
cache->start = base;
|
||||
cache->end = base + info.SizeOfImage;
|
||||
cache->name = (char*)tracy_malloc( namelen+3 );
|
||||
cache->name = (char*)tracy_malloc_fast( namelen+3 );
|
||||
cache->name[0] = '[';
|
||||
memcpy( cache->name+1, ptr, namelen );
|
||||
cache->name[namelen+1] = ']';
|
||||
@ -285,6 +376,11 @@ CallstackSymbolData DecodeCodeAddress( uint64_t ptr )
|
||||
const auto proc = GetCurrentProcess();
|
||||
bool done = false;
|
||||
|
||||
char buf[sizeof( SYMBOL_INFO ) + MaxNameSize];
|
||||
auto si = (SYMBOL_INFO*)buf;
|
||||
si->SizeOfStruct = sizeof( SYMBOL_INFO );
|
||||
si->MaxNameLen = MaxNameSize;
|
||||
|
||||
IMAGEHLP_LINE64 line;
|
||||
DWORD displacement = 0;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
@ -307,6 +403,15 @@ CallstackSymbolData DecodeCodeAddress( uint64_t ptr )
|
||||
sym.file = line.FileName;
|
||||
sym.line = line.LineNumber;
|
||||
done = true;
|
||||
|
||||
if( _SymFromInlineContext( proc, ptr, ctx, nullptr, si ) != 0 )
|
||||
{
|
||||
sym.symAddr = si->Address;
|
||||
}
|
||||
else
|
||||
{
|
||||
sym.symAddr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -317,11 +422,21 @@ CallstackSymbolData DecodeCodeAddress( uint64_t ptr )
|
||||
{
|
||||
sym.file = "[unknown]";
|
||||
sym.line = 0;
|
||||
sym.symAddr = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sym.file = line.FileName;
|
||||
sym.line = line.LineNumber;
|
||||
|
||||
if( SymFromAddr( proc, ptr, nullptr, si ) != 0 )
|
||||
{
|
||||
sym.symAddr = si->Address;
|
||||
}
|
||||
else
|
||||
{
|
||||
sym.symAddr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef TRACY_DBGHELP_LOCK
|
||||
@ -335,6 +450,8 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
|
||||
{
|
||||
int write;
|
||||
const auto proc = GetCurrentProcess();
|
||||
InitRpmalloc();
|
||||
|
||||
#ifdef TRACY_DBGHELP_LOCK
|
||||
DBGHELP_LOCK;
|
||||
#endif
|
||||
@ -386,8 +503,8 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
|
||||
cb_data[write].line = line.LineNumber;
|
||||
}
|
||||
|
||||
cb_data[write].name = symValid ? CopyString( si->Name, si->NameLen ) : CopyString( moduleName );
|
||||
cb_data[write].file = CopyString( filename );
|
||||
cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName );
|
||||
cb_data[write].file = CopyStringFast( filename );
|
||||
if( symValid )
|
||||
{
|
||||
cb_data[write].symLen = si->Size;
|
||||
@ -419,8 +536,8 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
|
||||
cb.line = line.LineNumber;
|
||||
}
|
||||
|
||||
cb.name = symInlineValid ? CopyString( si->Name, si->NameLen ) : CopyString( moduleName );
|
||||
cb.file = CopyString( filename );
|
||||
cb.name = symInlineValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName );
|
||||
cb.file = CopyStringFast( filename );
|
||||
if( symInlineValid )
|
||||
{
|
||||
cb.symLen = si->Size;
|
||||
@ -452,9 +569,108 @@ int cb_num;
|
||||
CallstackEntry cb_data[MaxCbTrace];
|
||||
int cb_fixup;
|
||||
|
||||
#ifdef __linux
|
||||
struct KernelSymbol
|
||||
{
|
||||
uint64_t addr;
|
||||
const char* name;
|
||||
const char* mod;
|
||||
};
|
||||
|
||||
KernelSymbol* s_kernelSym = nullptr;
|
||||
size_t s_kernelSymCnt;
|
||||
|
||||
static void InitKernelSymbols()
|
||||
{
|
||||
FILE* f = fopen( "/proc/kallsyms", "rb" );
|
||||
if( !f ) return;
|
||||
tracy::FastVector<KernelSymbol> tmpSym( 1024 );
|
||||
size_t linelen = 16 * 1024; // linelen must be big enough to prevent reallocs in getline()
|
||||
auto linebuf = (char*)tracy_malloc( linelen );
|
||||
ssize_t sz;
|
||||
while( ( sz = getline( &linebuf, &linelen, f ) ) != -1 )
|
||||
{
|
||||
auto ptr = linebuf;
|
||||
uint64_t addr = 0;
|
||||
while( *ptr != ' ' )
|
||||
{
|
||||
auto v = *ptr;
|
||||
if( v >= '0' && v <= '9' )
|
||||
{
|
||||
v -= '0';
|
||||
}
|
||||
else if( v >= 'a' && v <= 'f' )
|
||||
{
|
||||
v -= 'a';
|
||||
v += 10;
|
||||
}
|
||||
else if( v >= 'A' && v <= 'F' )
|
||||
{
|
||||
v -= 'A';
|
||||
v += 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
assert( ( v & ~0xF ) == 0 );
|
||||
addr <<= 4;
|
||||
addr |= v;
|
||||
ptr++;
|
||||
}
|
||||
if( addr == 0 ) continue;
|
||||
ptr++;
|
||||
if( *ptr != 'T' && *ptr != 't' ) continue;
|
||||
ptr += 2;
|
||||
const auto namestart = ptr;
|
||||
while( *ptr != '\t' && *ptr != '\n' ) ptr++;
|
||||
const auto nameend = ptr;
|
||||
const char* modstart = nullptr;
|
||||
const char* modend;
|
||||
if( *ptr == '\t' )
|
||||
{
|
||||
ptr += 2;
|
||||
modstart = ptr;
|
||||
while( *ptr != ']' ) ptr++;
|
||||
modend = ptr;
|
||||
}
|
||||
|
||||
auto strname = (char*)tracy_malloc_fast( nameend - namestart + 1 );
|
||||
memcpy( strname, namestart, nameend - namestart );
|
||||
strname[nameend-namestart] = '\0';
|
||||
|
||||
char* strmod = nullptr;
|
||||
if( modstart )
|
||||
{
|
||||
strmod = (char*)tracy_malloc_fast( modend - modstart + 1 );
|
||||
memcpy( strmod, modstart, modend - modstart );
|
||||
strmod[modend-modstart] = '\0';
|
||||
}
|
||||
|
||||
auto sym = tmpSym.push_next();
|
||||
sym->addr = addr;
|
||||
sym->name = strname;
|
||||
sym->mod = strmod;
|
||||
}
|
||||
tracy_free_fast( linebuf );
|
||||
fclose( f );
|
||||
if( tmpSym.empty() ) return;
|
||||
|
||||
std::sort( tmpSym.begin(), tmpSym.end(), []( const KernelSymbol& lhs, const KernelSymbol& rhs ) { return lhs.addr > rhs.addr; } );
|
||||
s_kernelSymCnt = tmpSym.size();
|
||||
s_kernelSym = (KernelSymbol*)tracy_malloc_fast( sizeof( KernelSymbol ) * s_kernelSymCnt );
|
||||
memcpy( s_kernelSym, tmpSym.data(), sizeof( KernelSymbol ) * s_kernelSymCnt );
|
||||
TracyDebug( "Loaded %zu kernel symbols\n", s_kernelSymCnt );
|
||||
}
|
||||
#endif
|
||||
|
||||
void InitCallstack()
|
||||
{
|
||||
cb_bts = backtrace_create_state( nullptr, 0, nullptr, nullptr );
|
||||
|
||||
#ifdef __linux
|
||||
InitKernelSymbols();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int FastCallstackDataCb( void* data, uintptr_t pc, uintptr_t lowaddr, const char* fn, int lineno, const char* function )
|
||||
@ -530,9 +746,39 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
|
||||
return sym;
|
||||
}
|
||||
|
||||
static int CodeDataCb( void* data, uintptr_t pc, uintptr_t lowaddr, const char* fn, int lineno, const char* function )
|
||||
{
|
||||
if( !fn ) return 1;
|
||||
|
||||
const auto fnsz = strlen( fn );
|
||||
if( fnsz >= s_tracySkipSubframesMinLen )
|
||||
{
|
||||
auto ptr = s_tracySkipSubframes;
|
||||
do
|
||||
{
|
||||
if( fnsz >= ptr->len && memcmp( fn + fnsz - ptr->len, ptr->str, ptr->len ) == 0 ) return 0;
|
||||
ptr++;
|
||||
}
|
||||
while( ptr->str );
|
||||
}
|
||||
|
||||
auto& sym = *(CallstackSymbolData*)data;
|
||||
sym.file = CopyString( fn );
|
||||
sym.line = lineno;
|
||||
sym.needFree = true;
|
||||
sym.symAddr = lowaddr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void CodeErrorCb( void* /*data*/, const char* /*msg*/, int /*errnum*/ )
|
||||
{
|
||||
}
|
||||
|
||||
CallstackSymbolData DecodeCodeAddress( uint64_t ptr )
|
||||
{
|
||||
return DecodeSymbolAddress( ptr );
|
||||
CallstackSymbolData sym = { "[unknown]", 0, false, 0 };
|
||||
backtrace_pcinfo( cb_bts, ptr, CodeDataCb, CodeErrorCb, &sym );
|
||||
return sym;
|
||||
}
|
||||
|
||||
static int CallstackDataCb( void* /*data*/, uintptr_t pc, uintptr_t lowaddr, const char* fn, int lineno, const char* function )
|
||||
@ -571,21 +817,21 @@ static int CallstackDataCb( void* /*data*/, uintptr_t pc, uintptr_t lowaddr, con
|
||||
|
||||
if( symoff == 0 )
|
||||
{
|
||||
cb_data[cb_num].name = CopyString( symname );
|
||||
cb_data[cb_num].name = CopyStringFast( symname );
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[32];
|
||||
const auto offlen = sprintf( buf, " + %td", symoff );
|
||||
const auto namelen = strlen( symname );
|
||||
auto name = (char*)tracy_malloc( namelen + offlen + 1 );
|
||||
auto name = (char*)tracy_malloc_fast( namelen + offlen + 1 );
|
||||
memcpy( name, symname, namelen );
|
||||
memcpy( name + namelen, buf, offlen );
|
||||
name[namelen + offlen] = '\0';
|
||||
cb_data[cb_num].name = name;
|
||||
}
|
||||
|
||||
cb_data[cb_num].file = CopyString( "[unknown]" );
|
||||
cb_data[cb_num].file = CopyStringFast( "[unknown]" );
|
||||
cb_data[cb_num].line = 0;
|
||||
}
|
||||
else
|
||||
@ -609,8 +855,8 @@ static int CallstackDataCb( void* /*data*/, uintptr_t pc, uintptr_t lowaddr, con
|
||||
}
|
||||
}
|
||||
|
||||
cb_data[cb_num].name = CopyString( function );
|
||||
cb_data[cb_num].file = CopyString( fn );
|
||||
cb_data[cb_num].name = CopyStringFast( function );
|
||||
cb_data[cb_num].file = CopyStringFast( fn );
|
||||
cb_data[cb_num].line = lineno;
|
||||
}
|
||||
|
||||
@ -628,12 +874,12 @@ static void CallstackErrorCb( void* /*data*/, const char* /*msg*/, int /*errnum*
|
||||
{
|
||||
for( int i=0; i<cb_num; i++ )
|
||||
{
|
||||
tracy_free( (void*)cb_data[i].name );
|
||||
tracy_free( (void*)cb_data[i].file );
|
||||
tracy_free_fast( (void*)cb_data[i].name );
|
||||
tracy_free_fast( (void*)cb_data[i].file );
|
||||
}
|
||||
|
||||
cb_data[0].name = CopyString( "[error]" );
|
||||
cb_data[0].file = CopyString( "[error]" );
|
||||
cb_data[0].name = CopyStringFast( "[error]" );
|
||||
cb_data[0].file = CopyStringFast( "[error]" );
|
||||
cb_data[0].line = 0;
|
||||
|
||||
cb_num = 1;
|
||||
@ -653,17 +899,43 @@ void SymInfoError( void* /*data*/, const char* /*msg*/, int /*errnum*/ )
|
||||
|
||||
CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
|
||||
{
|
||||
cb_num = 0;
|
||||
backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr );
|
||||
assert( cb_num > 0 );
|
||||
InitRpmalloc();
|
||||
if( ptr >> 63 == 0 )
|
||||
{
|
||||
cb_num = 0;
|
||||
backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr );
|
||||
assert( cb_num > 0 );
|
||||
|
||||
backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr );
|
||||
backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr );
|
||||
|
||||
const char* symloc = nullptr;
|
||||
Dl_info dlinfo;
|
||||
if( dladdr( (void*)ptr, &dlinfo ) ) symloc = dlinfo.dli_fname;
|
||||
const char* symloc = nullptr;
|
||||
Dl_info dlinfo;
|
||||
if( dladdr( (void*)ptr, &dlinfo ) ) symloc = dlinfo.dli_fname;
|
||||
|
||||
return { cb_data, uint8_t( cb_num ), symloc ? symloc : "[unknown]" };
|
||||
return { cb_data, uint8_t( cb_num ), symloc ? symloc : "[unknown]" };
|
||||
}
|
||||
#ifdef __linux
|
||||
else if( s_kernelSym )
|
||||
{
|
||||
auto it = std::lower_bound( s_kernelSym, s_kernelSym + s_kernelSymCnt, ptr, []( const KernelSymbol& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } );
|
||||
if( it != s_kernelSym + s_kernelSymCnt )
|
||||
{
|
||||
cb_data[0].name = CopyStringFast( it->name );
|
||||
cb_data[0].file = CopyStringFast( "<kernel>" );
|
||||
cb_data[0].line = 0;
|
||||
cb_data[0].symLen = 0;
|
||||
cb_data[0].symAddr = it->addr;
|
||||
return { cb_data, 1, it->mod ? it->mod : "<kernel>" };
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
cb_data[0].name = CopyStringFast( "[unknown]" );
|
||||
cb_data[0].file = CopyStringFast( "<kernel>" );
|
||||
cb_data[0].line = 0;
|
||||
cb_data[0].symLen = 0;
|
||||
cb_data[0].symAddr = 0;
|
||||
return { cb_data, 1, "<kernel>" };
|
||||
}
|
||||
|
||||
#elif TRACY_HAS_CALLSTACK == 5
|
||||
@ -699,7 +971,7 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
|
||||
Dl_info dlinfo;
|
||||
if( dladdr( (void*)ptr, &dlinfo ) ) symloc = dlinfo.dli_fname;
|
||||
if( !symloc ) symloc = "[unknown]";
|
||||
return CallstackSymbolData { symloc, 0, false };
|
||||
return CallstackSymbolData { symloc, 0, false, 0 };
|
||||
}
|
||||
|
||||
CallstackSymbolData DecodeCodeAddress( uint64_t ptr )
|
||||
|
@ -27,6 +27,7 @@ struct CallstackSymbolData
|
||||
const char* file;
|
||||
uint32_t line;
|
||||
bool needFree;
|
||||
uint64_t symAddr;
|
||||
};
|
||||
|
||||
struct CallstackEntry
|
||||
|
11
client/TracyDebug.hpp
Normal file
11
client/TracyDebug.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __TRACYPRINT_HPP__
|
||||
#define __TRACYPRINT_HPP__
|
||||
|
||||
#ifdef TRACY_VERBOSE
|
||||
# include <stdio.h>
|
||||
# define TracyDebug(...) fprintf( stderr, __VA_ARGS__ );
|
||||
#else
|
||||
# define TracyDebug(...)
|
||||
#endif
|
||||
|
||||
#endif
|
@ -102,7 +102,7 @@ private:
|
||||
const auto size = size_t( m_write - m_ptr );
|
||||
T* ptr = (T*)tracy_malloc( sizeof( T ) * cap );
|
||||
memcpy( ptr, m_ptr, size * sizeof( T ) );
|
||||
tracy_free( m_ptr );
|
||||
tracy_free_fast( m_ptr );
|
||||
m_ptr = ptr;
|
||||
m_write = m_ptr + size;
|
||||
m_end = m_ptr + cap;
|
||||
|
@ -1207,14 +1207,6 @@ Profiler::Profiler()
|
||||
|
||||
void Profiler::SpawnWorkerThreads()
|
||||
{
|
||||
s_thread = (Thread*)tracy_malloc( sizeof( Thread ) );
|
||||
new(s_thread) Thread( LaunchWorker, this );
|
||||
|
||||
#ifndef TRACY_NO_FRAME_IMAGE
|
||||
s_compressThread = (Thread*)tracy_malloc( sizeof( Thread ) );
|
||||
new(s_compressThread) Thread( LaunchCompressWorker, this );
|
||||
#endif
|
||||
|
||||
#ifdef TRACY_HAS_SYSTEM_TRACING
|
||||
if( SysTraceStart( m_samplingPeriod ) )
|
||||
{
|
||||
@ -1224,6 +1216,14 @@ void Profiler::SpawnWorkerThreads()
|
||||
}
|
||||
#endif
|
||||
|
||||
s_thread = (Thread*)tracy_malloc( sizeof( Thread ) );
|
||||
new(s_thread) Thread( LaunchWorker, this );
|
||||
|
||||
#ifndef TRACY_NO_FRAME_IMAGE
|
||||
s_compressThread = (Thread*)tracy_malloc( sizeof( Thread ) );
|
||||
new(s_compressThread) Thread( LaunchCompressWorker, this );
|
||||
#endif
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
s_profilerThreadId = GetThreadId( s_thread->Handle() );
|
||||
AddVectoredExceptionHandler( 1, CrashFilter );
|
||||
@ -1344,16 +1344,19 @@ void Profiler::Worker()
|
||||
|
||||
const uint64_t pid = GetPid();
|
||||
|
||||
#ifdef TRACY_ON_DEMAND
|
||||
uint8_t onDemand = 1;
|
||||
#else
|
||||
uint8_t onDemand = 0;
|
||||
#endif
|
||||
uint8_t flags = 0;
|
||||
|
||||
#ifdef TRACY_ON_DEMAND
|
||||
flags |= WelcomeFlag::OnDemand;
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
uint8_t isApple = 1;
|
||||
#else
|
||||
uint8_t isApple = 0;
|
||||
flags |= WelcomeFlag::IsApple;
|
||||
#endif
|
||||
#ifndef TRACY_NO_CODE_TRANSFER
|
||||
flags |= WelcomeFlag::CodeTransfer;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
flags |= WelcomeFlag::CombineSamples;
|
||||
#endif
|
||||
|
||||
#if defined __i386 || defined _M_IX86
|
||||
@ -1368,12 +1371,6 @@ void Profiler::Worker()
|
||||
uint8_t cpuArch = CpuArchUnknown;
|
||||
#endif
|
||||
|
||||
#ifdef TRACY_NO_CODE_TRANSFER
|
||||
uint8_t codeTransfer = 0;
|
||||
#else
|
||||
uint8_t codeTransfer = 1;
|
||||
#endif
|
||||
|
||||
#if defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64
|
||||
uint32_t regs[4];
|
||||
char manufacturer[12];
|
||||
@ -1399,10 +1396,8 @@ void Profiler::Worker()
|
||||
MemWrite( &welcome.exectime, m_exectime );
|
||||
MemWrite( &welcome.pid, pid );
|
||||
MemWrite( &welcome.samplingPeriod, m_samplingPeriod );
|
||||
MemWrite( &welcome.onDemand, onDemand );
|
||||
MemWrite( &welcome.isApple, isApple );
|
||||
MemWrite( &welcome.flags, flags );
|
||||
MemWrite( &welcome.cpuArch, cpuArch );
|
||||
MemWrite( &welcome.codeTransfer, codeTransfer );
|
||||
memcpy( welcome.cpuManufacturer, manufacturer, 12 );
|
||||
MemWrite( &welcome.cpuId, cpuId );
|
||||
memcpy( welcome.programName, procname, pnsz );
|
||||
@ -1969,6 +1964,7 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
|
||||
[this, &connectionLost] ( QueueItem* item, size_t sz )
|
||||
{
|
||||
if( connectionLost ) return;
|
||||
InitRpmalloc();
|
||||
assert( sz > 0 );
|
||||
int64_t refThread = m_refTimeThread;
|
||||
int64_t refCtx = m_refTimeCtx;
|
||||
@ -1987,28 +1983,28 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
|
||||
ptr = MemRead<uint64_t>( &item->zoneTextFat.text );
|
||||
size = MemRead<uint16_t>( &item->zoneTextFat.size );
|
||||
SendSingleString( (const char*)ptr, size );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
case QueueType::Message:
|
||||
case QueueType::MessageCallstack:
|
||||
ptr = MemRead<uint64_t>( &item->messageFat.text );
|
||||
size = MemRead<uint16_t>( &item->messageFat.size );
|
||||
SendSingleString( (const char*)ptr, size );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
case QueueType::MessageColor:
|
||||
case QueueType::MessageColorCallstack:
|
||||
ptr = MemRead<uint64_t>( &item->messageColorFat.text );
|
||||
size = MemRead<uint16_t>( &item->messageColorFat.size );
|
||||
SendSingleString( (const char*)ptr, size );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
case QueueType::MessageAppInfo:
|
||||
ptr = MemRead<uint64_t>( &item->messageFat.text );
|
||||
size = MemRead<uint16_t>( &item->messageFat.size );
|
||||
SendSingleString( (const char*)ptr, size );
|
||||
#ifndef TRACY_ON_DEMAND
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
#endif
|
||||
break;
|
||||
case QueueType::ZoneBeginAllocSrcLoc:
|
||||
@ -2020,13 +2016,13 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
|
||||
MemWrite( &item->zoneBegin.time, dt );
|
||||
ptr = MemRead<uint64_t>( &item->zoneBegin.srcloc );
|
||||
SendSourceLocationPayload( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
}
|
||||
case QueueType::Callstack:
|
||||
ptr = MemRead<uint64_t>( &item->callstackFat.ptr );
|
||||
SendCallstackPayload( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
case QueueType::CallstackAlloc:
|
||||
ptr = MemRead<uint64_t>( &item->callstackAllocFat.nativePtr );
|
||||
@ -2034,17 +2030,17 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
|
||||
{
|
||||
CutCallstack( (void*)ptr, "lua_pcall" );
|
||||
SendCallstackPayload( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
}
|
||||
ptr = MemRead<uint64_t>( &item->callstackAllocFat.ptr );
|
||||
SendCallstackAlloc( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
case QueueType::CallstackSample:
|
||||
{
|
||||
ptr = MemRead<uint64_t>( &item->callstackSampleFat.ptr );
|
||||
SendCallstackPayload64( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
int64_t t = MemRead<int64_t>( &item->callstackSampleFat.time );
|
||||
int64_t dt = t - refCtx;
|
||||
refCtx = t;
|
||||
@ -2058,7 +2054,7 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
|
||||
const auto h = MemRead<uint16_t>( &item->frameImageFat.h );
|
||||
const auto csz = size_t( w * h / 2 );
|
||||
SendLongString( ptr, (const char*)ptr, csz, QueueType::FrameImageData );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
}
|
||||
case QueueType::ZoneBegin:
|
||||
@ -2096,7 +2092,7 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
|
||||
MemWrite( &item->gpuZoneBegin.cpuTime, dt );
|
||||
ptr = MemRead<uint64_t>( &item->gpuZoneBegin.srcloc );
|
||||
SendSourceLocationPayload( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
}
|
||||
case QueueType::GpuZoneEnd:
|
||||
@ -2112,7 +2108,7 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token )
|
||||
size = MemRead<uint16_t>( &item->gpuContextNameFat.size );
|
||||
SendSingleString( (const char*)ptr, size );
|
||||
#ifndef TRACY_ON_DEMAND
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
#endif
|
||||
break;
|
||||
case QueueType::PlotData:
|
||||
@ -2252,6 +2248,7 @@ Profiler::DequeueStatus Profiler::DequeueSerial()
|
||||
const auto sz = m_serialDequeue.size();
|
||||
if( sz > 0 )
|
||||
{
|
||||
InitRpmalloc();
|
||||
int64_t refSerial = m_refTimeSerial;
|
||||
int64_t refGpu = m_refTimeGpu;
|
||||
auto item = m_serialDequeue.data();
|
||||
@ -2267,7 +2264,7 @@ Profiler::DequeueStatus Profiler::DequeueSerial()
|
||||
case QueueType::CallstackSerial:
|
||||
ptr = MemRead<uint64_t>( &item->callstackFat.ptr );
|
||||
SendCallstackPayload( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
case QueueType::LockWait:
|
||||
case QueueType::LockSharedWait:
|
||||
@ -2302,7 +2299,7 @@ Profiler::DequeueStatus Profiler::DequeueSerial()
|
||||
uint16_t size = MemRead<uint16_t>( &item->lockNameFat.size );
|
||||
SendSingleString( (const char*)ptr, size );
|
||||
#ifndef TRACY_ON_DEMAND
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -2346,7 +2343,7 @@ Profiler::DequeueStatus Profiler::DequeueSerial()
|
||||
MemWrite( &item->gpuZoneBegin.cpuTime, dt );
|
||||
ptr = MemRead<uint64_t>( &item->gpuZoneBegin.srcloc );
|
||||
SendSourceLocationPayload( ptr );
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
break;
|
||||
}
|
||||
case QueueType::GpuZoneEndSerial:
|
||||
@ -2371,7 +2368,7 @@ Profiler::DequeueStatus Profiler::DequeueSerial()
|
||||
uint16_t size = MemRead<uint16_t>( &item->gpuContextNameFat.size );
|
||||
SendSingleString( (const char*)ptr, size );
|
||||
#ifndef TRACY_ON_DEMAND
|
||||
tracy_free( (void*)ptr );
|
||||
tracy_free_fast( (void*)ptr );
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -2604,6 +2601,7 @@ void Profiler::SendCallstackFrame( uint64_t ptr )
|
||||
AppendData( &item, QueueDataSize[(int)QueueType::CallstackFrameSize] );
|
||||
}
|
||||
|
||||
InitRpmalloc();
|
||||
for( uint8_t i=0; i<frameData.size; i++ )
|
||||
{
|
||||
const auto& frame = frameData.data[i];
|
||||
@ -2619,8 +2617,8 @@ void Profiler::SendCallstackFrame( uint64_t ptr )
|
||||
|
||||
AppendData( &item, QueueDataSize[(int)QueueType::CallstackFrame] );
|
||||
|
||||
tracy_free( (void*)frame.name );
|
||||
tracy_free( (void*)frame.file );
|
||||
tracy_free_fast( (void*)frame.name );
|
||||
tracy_free_fast( (void*)frame.file );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -3215,14 +3213,26 @@ static bool EnsureReadable( uintptr_t address )
|
||||
void Profiler::HandleSymbolQuery( uint64_t symbol )
|
||||
{
|
||||
#ifdef TRACY_HAS_CALLSTACK
|
||||
#ifdef __ANDROID__
|
||||
// Special handling for kernel frames
|
||||
if( symbol >> 63 != 0 )
|
||||
{
|
||||
SendSingleString( "<kernel>" );
|
||||
QueueItem item;
|
||||
MemWrite( &item.hdr.type, QueueType::SymbolInformation );
|
||||
MemWrite( &item.symbolInformation.line, 0 );
|
||||
MemWrite( &item.symbolInformation.symAddr, symbol );
|
||||
AppendData( &item, QueueDataSize[(int)QueueType::SymbolInformation] );
|
||||
return;
|
||||
}
|
||||
# ifdef __ANDROID__
|
||||
// On Android it's common for code to be in mappings that are only executable
|
||||
// but not readable.
|
||||
if( !EnsureReadable( symbol ) )
|
||||
{
|
||||
AckServerQuery();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
const auto sym = DecodeSymbolAddress( symbol );
|
||||
|
||||
SendSingleString( sym.file );
|
||||
@ -3245,6 +3255,7 @@ void Profiler::HandleSymbolCodeQuery( uint64_t symbol, uint32_t size )
|
||||
// but not readable.
|
||||
if( !EnsureReadable( symbol ) )
|
||||
{
|
||||
AckServerQuery();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@ -3256,14 +3267,15 @@ void Profiler::HandleSourceCodeQuery()
|
||||
assert( m_exectime != 0 );
|
||||
assert( m_queryData );
|
||||
|
||||
InitRpmalloc();
|
||||
struct stat st;
|
||||
if( stat( m_queryData, &st ) == 0 && (uint64_t)st.st_mtime < m_exectime && st.st_size < ( TargetFrameSize - 16 ) )
|
||||
{
|
||||
FILE* f = fopen( m_queryData, "rb" );
|
||||
tracy_free( m_queryData );
|
||||
tracy_free_fast( m_queryData );
|
||||
if( f )
|
||||
{
|
||||
auto ptr = (char*)tracy_malloc( st.st_size );
|
||||
auto ptr = (char*)tracy_malloc_fast( st.st_size );
|
||||
auto rd = fread( ptr, 1, st.st_size, f );
|
||||
fclose( f );
|
||||
if( rd == (size_t)st.st_size )
|
||||
@ -3274,7 +3286,7 @@ void Profiler::HandleSourceCodeQuery()
|
||||
{
|
||||
AckSourceCodeNotAvailable();
|
||||
}
|
||||
tracy_free( ptr );
|
||||
tracy_free_fast( ptr );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3283,7 +3295,7 @@ void Profiler::HandleSourceCodeQuery()
|
||||
}
|
||||
else
|
||||
{
|
||||
tracy_free( m_queryData );
|
||||
tracy_free_fast( m_queryData );
|
||||
AckSourceCodeNotAvailable();
|
||||
}
|
||||
m_queryData = nullptr;
|
||||
@ -3300,6 +3312,7 @@ void Profiler::SendCodeLocation( uint64_t ptr )
|
||||
MemWrite( &item.hdr.type, QueueType::CodeInformation );
|
||||
MemWrite( &item.codeInformation.ptr, ptr );
|
||||
MemWrite( &item.codeInformation.line, sym.line );
|
||||
MemWrite( &item.codeInformation.symAddr, sym.symAddr );
|
||||
|
||||
AppendData( &item, QueueDataSize[(int)QueueType::CodeInformation] );
|
||||
|
||||
|
@ -5,8 +5,9 @@ template<size_t Size>
|
||||
class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer( int fd )
|
||||
: m_fd( fd )
|
||||
RingBuffer( int fd, int id )
|
||||
: m_id( id )
|
||||
, m_fd( fd )
|
||||
{
|
||||
const auto pageSize = uint32_t( getpagesize() );
|
||||
assert( Size >= pageSize );
|
||||
@ -16,12 +17,14 @@ public:
|
||||
if( !mapAddr )
|
||||
{
|
||||
m_fd = 0;
|
||||
m_metadata = nullptr;
|
||||
close( fd );
|
||||
return;
|
||||
}
|
||||
m_metadata = (perf_event_mmap_page*)mapAddr;
|
||||
assert( m_metadata->data_offset == pageSize );
|
||||
m_buffer = ((char*)mapAddr) + pageSize;
|
||||
m_tail = m_metadata->data_tail;
|
||||
}
|
||||
|
||||
~RingBuffer()
|
||||
@ -49,21 +52,16 @@ public:
|
||||
}
|
||||
|
||||
bool IsValid() const { return m_metadata != nullptr; }
|
||||
int GetId() const { return m_id; }
|
||||
|
||||
void Enable()
|
||||
{
|
||||
ioctl( m_fd, PERF_EVENT_IOC_ENABLE, 0 );
|
||||
}
|
||||
|
||||
bool HasData() const
|
||||
{
|
||||
const auto head = LoadHead();
|
||||
return head > m_metadata->data_tail;
|
||||
}
|
||||
|
||||
void Read( void* dst, uint64_t offset, uint64_t cnt )
|
||||
{
|
||||
auto src = ( m_metadata->data_tail + offset ) % Size;
|
||||
auto src = ( m_tail + offset ) % Size;
|
||||
if( src + cnt <= Size )
|
||||
{
|
||||
memcpy( dst, m_buffer + src, cnt );
|
||||
@ -78,7 +76,8 @@ public:
|
||||
|
||||
void Advance( uint64_t cnt )
|
||||
{
|
||||
StoreTail( m_metadata->data_tail + cnt );
|
||||
m_tail += cnt;
|
||||
StoreTail();
|
||||
}
|
||||
|
||||
bool CheckTscCaps() const
|
||||
@ -88,26 +87,33 @@ public:
|
||||
|
||||
int64_t ConvertTimeToTsc( int64_t timestamp ) const
|
||||
{
|
||||
assert( m_metadata->cap_user_time_zero );
|
||||
if( !m_metadata->cap_user_time_zero ) return 0;
|
||||
const auto time = timestamp - m_metadata->time_zero;
|
||||
const auto quot = time / m_metadata->time_mult;
|
||||
const auto rem = time % m_metadata->time_mult;
|
||||
return ( quot << m_metadata->time_shift ) + ( rem << m_metadata->time_shift ) / m_metadata->time_mult;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t LoadHead() const
|
||||
{
|
||||
return std::atomic_load_explicit( (const volatile std::atomic<uint64_t>*)&m_metadata->data_head, std::memory_order_acquire );
|
||||
}
|
||||
|
||||
void StoreTail( uint64_t tail )
|
||||
uint64_t GetTail() const
|
||||
{
|
||||
std::atomic_store_explicit( (volatile std::atomic<uint64_t>*)&m_metadata->data_tail, tail, std::memory_order_release );
|
||||
return m_tail;
|
||||
}
|
||||
|
||||
perf_event_mmap_page* m_metadata;
|
||||
private:
|
||||
void StoreTail()
|
||||
{
|
||||
std::atomic_store_explicit( (volatile std::atomic<uint64_t>*)&m_metadata->data_tail, m_tail, std::memory_order_release );
|
||||
}
|
||||
|
||||
uint64_t m_tail;
|
||||
char* m_buffer;
|
||||
int m_id;
|
||||
perf_event_mmap_page* m_metadata;
|
||||
|
||||
size_t m_mapSize;
|
||||
int m_fd;
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "TracyDebug.hpp"
|
||||
#include "TracySysTrace.hpp"
|
||||
#include "../common/TracySystem.hpp"
|
||||
|
||||
#ifdef TRACY_HAS_SYSTEM_TRACING
|
||||
|
||||
@ -201,7 +203,7 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
|
||||
if( hdr.EventDescriptor.Opcode == 32 )
|
||||
{
|
||||
const auto sw = (const StackWalkEvent*)record->UserData;
|
||||
if( sw->stackProcess == s_pid && ( sw->stack[0] & 0x8000000000000000 ) == 0 )
|
||||
if( sw->stackProcess == s_pid )
|
||||
{
|
||||
const uint64_t sz = ( record->UserDataLength - 16 ) / 8;
|
||||
if( sz > 0 )
|
||||
@ -650,6 +652,10 @@ void SysTraceSendExternalName( uint64_t thread )
|
||||
# include "TracySysTracePayload.hpp"
|
||||
# endif
|
||||
|
||||
# ifdef __AVX2__
|
||||
# include <immintrin.h>
|
||||
# endif
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
@ -666,6 +672,7 @@ static const char TracePipe[] = "trace_pipe";
|
||||
static std::atomic<bool> traceActive { false };
|
||||
static Thread* s_threadSampling = nullptr;
|
||||
static int s_numCpus = 0;
|
||||
static int s_numBuffers = 0;
|
||||
|
||||
static constexpr size_t RingBufSize = 64*1024;
|
||||
static RingBuffer<RingBufSize>* s_ring = nullptr;
|
||||
@ -675,156 +682,417 @@ static int perf_event_open( struct perf_event_attr* hw_event, pid_t pid, int cpu
|
||||
return syscall( __NR_perf_event_open, hw_event, pid, cpu, group_fd, flags );
|
||||
}
|
||||
|
||||
enum TraceEventId
|
||||
{
|
||||
EventCallstack,
|
||||
EventCpuCycles,
|
||||
EventInstructionsRetired,
|
||||
EventCacheReference,
|
||||
EventCacheMiss,
|
||||
EventBranchRetired,
|
||||
EventBranchMiss
|
||||
};
|
||||
|
||||
static void ProbePreciseIp( perf_event_attr& pe, unsigned long long config0, unsigned long long config1, pid_t pid )
|
||||
{
|
||||
pe.config = config1;
|
||||
pe.precise_ip = 3;
|
||||
while( pe.precise_ip != 0 )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, pid, 0, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
close( fd );
|
||||
break;
|
||||
}
|
||||
pe.precise_ip--;
|
||||
}
|
||||
pe.config = config0;
|
||||
while( pe.precise_ip != 0 )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, pid, 0, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
close( fd );
|
||||
break;
|
||||
}
|
||||
pe.precise_ip--;
|
||||
}
|
||||
TracyDebug( " Probed precise_ip: %i\n", pe.precise_ip );
|
||||
}
|
||||
|
||||
static void ProbePreciseIp( perf_event_attr& pe, pid_t pid )
|
||||
{
|
||||
pe.precise_ip = 3;
|
||||
while( pe.precise_ip != 0 )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, pid, 0, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
close( fd );
|
||||
break;
|
||||
}
|
||||
pe.precise_ip--;
|
||||
}
|
||||
TracyDebug( " Probed precise_ip: %i\n", pe.precise_ip );
|
||||
}
|
||||
|
||||
static bool IsGenuineIntel()
|
||||
{
|
||||
#if defined __i386 || defined __x86_64__
|
||||
uint32_t regs[4];
|
||||
__get_cpuid( 0, regs, regs+1, regs+2, regs+3 );
|
||||
char manufacturer[12];
|
||||
memcpy( manufacturer, regs+1, 4 );
|
||||
memcpy( manufacturer+4, regs+3, 4 );
|
||||
memcpy( manufacturer+8, regs+2, 4 );
|
||||
return memcmp( manufacturer, "GenuineIntel", 12 ) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetupSampling( int64_t& samplingPeriod )
|
||||
{
|
||||
#ifndef CLOCK_MONOTONIC_RAW
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifdef TRACY_NO_SAMPLE_RETIREMENT
|
||||
const bool noRetirement = true;
|
||||
#else
|
||||
const char* noRetirementEnv = GetEnvVar( "TRACY_NO_SAMPLE_RETIREMENT" );
|
||||
const bool noRetirement = noRetirementEnv && noRetirementEnv[0] == '1';
|
||||
#endif
|
||||
|
||||
#ifdef TRACY_NO_SAMPLE_CACHE
|
||||
const bool noCache = true;
|
||||
#else
|
||||
const char* noCacheEnv = GetEnvVar( "TRACY_NO_SAMPLE_CACHE" );
|
||||
const bool noCache = noCacheEnv && noCacheEnv[0] == '1';
|
||||
#endif
|
||||
|
||||
#ifdef TRACY_NO_SAMPLE_BRANCH
|
||||
const bool noBranch = true;
|
||||
#else
|
||||
const char* noBranchEnv = GetEnvVar( "TRACY_NO_SAMPLE_BRANCH" );
|
||||
const bool noBranch = noBranchEnv && noBranchEnv[0] == '1';
|
||||
#endif
|
||||
|
||||
samplingPeriod = GetSamplingPeriod();
|
||||
uint32_t currentPid = (uint32_t)getpid();
|
||||
|
||||
s_numCpus = (int)std::thread::hardware_concurrency();
|
||||
s_ring = (RingBuffer<RingBufSize>*)tracy_malloc( sizeof( RingBuffer<RingBufSize> ) * s_numCpus );
|
||||
s_ring = (RingBuffer<RingBufSize>*)tracy_malloc( sizeof( RingBuffer<RingBufSize> ) * s_numCpus * 7 );
|
||||
s_numBuffers = 0;
|
||||
|
||||
// Stack traces
|
||||
perf_event_attr pe = {};
|
||||
|
||||
pe.type = PERF_TYPE_SOFTWARE;
|
||||
pe.size = sizeof( perf_event_attr );
|
||||
pe.config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
|
||||
pe.sample_freq = GetSamplingFrequency();
|
||||
pe.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_CALLCHAIN;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 8, 0 )
|
||||
pe.sample_max_stack = 127;
|
||||
#endif
|
||||
pe.exclude_callchain_kernel = 1;
|
||||
|
||||
pe.disabled = 1;
|
||||
pe.freq = 1;
|
||||
pe.inherit = 1;
|
||||
#if !defined TRACY_HW_TIMER || !( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
|
||||
pe.use_clockid = 1;
|
||||
pe.clockid = CLOCK_MONOTONIC_RAW;
|
||||
#endif
|
||||
|
||||
TracyDebug( "Setup software sampling\n" );
|
||||
ProbePreciseIp( pe, currentPid );
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, -1, i, -1, 0 );
|
||||
const int fd = perf_event_open( &pe, currentPid, i, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd == -1 )
|
||||
{
|
||||
for( int j=0; j<i; j++ ) s_ring[j].~RingBuffer<RingBufSize>();
|
||||
for( int j=0; j<s_numBuffers; j++ ) s_ring[j].~RingBuffer<RingBufSize>();
|
||||
tracy_free( s_ring );
|
||||
return;
|
||||
}
|
||||
new( s_ring+i ) RingBuffer<RingBufSize>( fd );
|
||||
new( s_ring+s_numBuffers ) RingBuffer<RingBufSize>( fd, EventCallstack );
|
||||
s_numBuffers++;
|
||||
}
|
||||
|
||||
// CPU cycles + instructions retired
|
||||
pe = {};
|
||||
pe.type = PERF_TYPE_HARDWARE;
|
||||
pe.size = sizeof( perf_event_attr );
|
||||
pe.sample_freq = 5000;
|
||||
pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TIME;
|
||||
pe.disabled = 1;
|
||||
pe.exclude_kernel = 1;
|
||||
pe.exclude_guest = 1;
|
||||
pe.exclude_hv = 1;
|
||||
pe.freq = 1;
|
||||
pe.inherit = 1;
|
||||
#if !defined TRACY_HW_TIMER || !( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
|
||||
pe.use_clockid = 1;
|
||||
pe.clockid = CLOCK_MONOTONIC_RAW;
|
||||
#endif
|
||||
|
||||
if( !noRetirement )
|
||||
{
|
||||
TracyDebug( "Setup sampling cycles + retirement\n" );
|
||||
ProbePreciseIp( pe, PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS, currentPid );
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, currentPid, i, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
new( s_ring+s_numBuffers ) RingBuffer<RingBufSize>( fd, EventCpuCycles );
|
||||
s_numBuffers++;
|
||||
TracyDebug( " Core %i ok\n", i );
|
||||
}
|
||||
}
|
||||
|
||||
pe.config = PERF_COUNT_HW_INSTRUCTIONS;
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, currentPid, i, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
new( s_ring+s_numBuffers ) RingBuffer<RingBufSize>( fd, EventInstructionsRetired );
|
||||
s_numBuffers++;
|
||||
TracyDebug( " Core %i ok\n", i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cache reference + miss
|
||||
if( !noCache )
|
||||
{
|
||||
TracyDebug( "Setup sampling CPU cache references + misses\n" );
|
||||
ProbePreciseIp( pe, PERF_COUNT_HW_CACHE_REFERENCES, PERF_COUNT_HW_CACHE_MISSES, currentPid );
|
||||
if( IsGenuineIntel() )
|
||||
{
|
||||
pe.precise_ip = 0;
|
||||
TracyDebug( " CPU is GenuineIntel, forcing precise_ip down to 0\n" );
|
||||
}
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, currentPid, i, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
new( s_ring+s_numBuffers ) RingBuffer<RingBufSize>( fd, EventCacheReference );
|
||||
s_numBuffers++;
|
||||
TracyDebug( " Core %i ok\n", i );
|
||||
}
|
||||
}
|
||||
|
||||
pe.config = PERF_COUNT_HW_CACHE_MISSES;
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, currentPid, i, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
new( s_ring+s_numBuffers ) RingBuffer<RingBufSize>( fd, EventCacheMiss );
|
||||
s_numBuffers++;
|
||||
TracyDebug( " Core %i ok\n", i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// branch retired + miss
|
||||
if( !noBranch )
|
||||
{
|
||||
TracyDebug( "Setup sampling CPU branch retirements + misses\n" );
|
||||
ProbePreciseIp( pe, PERF_COUNT_HW_BRANCH_INSTRUCTIONS, PERF_COUNT_HW_BRANCH_MISSES, currentPid );
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, currentPid, i, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
new( s_ring+s_numBuffers ) RingBuffer<RingBufSize>( fd, EventBranchRetired );
|
||||
s_numBuffers++;
|
||||
TracyDebug( " Core %i ok\n", i );
|
||||
}
|
||||
}
|
||||
|
||||
pe.config = PERF_COUNT_HW_BRANCH_MISSES;
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
{
|
||||
const int fd = perf_event_open( &pe, currentPid, i, -1, PERF_FLAG_FD_CLOEXEC );
|
||||
if( fd != -1 )
|
||||
{
|
||||
new( s_ring+s_numBuffers ) RingBuffer<RingBufSize>( fd, EventBranchMiss );
|
||||
s_numBuffers++;
|
||||
TracyDebug( " Core %i ok\n", i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
s_threadSampling = (Thread*)tracy_malloc( sizeof( Thread ) );
|
||||
new(s_threadSampling) Thread( [] (void*) {
|
||||
ThreadExitHandler threadExitHandler;
|
||||
SetThreadName( "Tracy Sampling" );
|
||||
InitRpmalloc();
|
||||
sched_param sp = { 5 };
|
||||
pthread_setschedparam( pthread_self(), SCHED_FIFO, &sp );
|
||||
uint32_t currentPid = (uint32_t)getpid();
|
||||
#if defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
for( int i=0; i<s_numBuffers; i++ )
|
||||
{
|
||||
if( !s_ring[i].CheckTscCaps() )
|
||||
if( s_ring[i].GetId() == EventCallstack && !s_ring[i].CheckTscCaps() )
|
||||
{
|
||||
for( int j=0; j<s_numCpus; j++ ) s_ring[j].~RingBuffer<RingBufSize>();
|
||||
tracy_free( s_ring );
|
||||
for( int j=0; j<s_numBuffers; j++ ) s_ring[j].~RingBuffer<RingBufSize>();
|
||||
tracy_free_fast( s_ring );
|
||||
const char* err = "Tracy Profiler: sampling is disabled due to non-native scheduler clock. Are you running under a VM?";
|
||||
Profiler::MessageAppInfo( err, strlen( err ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for( int i=0; i<s_numCpus; i++ ) s_ring[i].Enable();
|
||||
for( int i=0; i<s_numBuffers; i++ ) s_ring[i].Enable();
|
||||
for(;;)
|
||||
{
|
||||
bool hadData = false;
|
||||
for( int i=0; i<s_numCpus; i++ )
|
||||
for( int i=0; i<s_numBuffers; i++ )
|
||||
{
|
||||
if( !traceActive.load( std::memory_order_relaxed ) ) break;
|
||||
if( !s_ring[i].HasData() ) continue;
|
||||
auto& ring = s_ring[i];
|
||||
const auto head = ring.LoadHead();
|
||||
const auto tail = ring.GetTail();
|
||||
if( head == tail ) continue;
|
||||
assert( head > tail );
|
||||
hadData = true;
|
||||
|
||||
perf_event_header hdr;
|
||||
s_ring[i].Read( &hdr, 0, sizeof( perf_event_header ) );
|
||||
if( hdr.type == PERF_RECORD_SAMPLE )
|
||||
const auto end = head - tail;
|
||||
uint64_t pos = 0;
|
||||
while( pos < end )
|
||||
{
|
||||
uint32_t pid, tid;
|
||||
uint64_t t0;
|
||||
uint64_t cnt;
|
||||
|
||||
auto offset = sizeof( perf_event_header );
|
||||
s_ring[i].Read( &pid, offset, sizeof( uint32_t ) );
|
||||
if( pid == currentPid )
|
||||
perf_event_header hdr;
|
||||
ring.Read( &hdr, pos, sizeof( perf_event_header ) );
|
||||
if( hdr.type == PERF_RECORD_SAMPLE )
|
||||
{
|
||||
offset += sizeof( uint32_t );
|
||||
s_ring[i].Read( &tid, offset, sizeof( uint32_t ) );
|
||||
offset += sizeof( uint32_t );
|
||||
s_ring[i].Read( &t0, offset, sizeof( uint64_t ) );
|
||||
offset += sizeof( uint64_t );
|
||||
s_ring[i].Read( &cnt, offset, sizeof( uint64_t ) );
|
||||
offset += sizeof( uint64_t );
|
||||
|
||||
if( cnt > 0 )
|
||||
auto offset = pos + sizeof( perf_event_header );
|
||||
const auto id = ring.GetId();
|
||||
if( id == EventCallstack )
|
||||
{
|
||||
auto trace = (uint64_t*)tracy_malloc( ( 1 + cnt ) * sizeof( uint64_t ) );
|
||||
s_ring[i].Read( trace+1, offset, sizeof( uint64_t ) * cnt );
|
||||
// Layout:
|
||||
// u32 pid, tid
|
||||
// u64 time
|
||||
// u64 cnt
|
||||
// u64 ip[cnt]
|
||||
|
||||
uint32_t tid;
|
||||
uint64_t t0;
|
||||
uint64_t cnt;
|
||||
|
||||
offset += sizeof( uint32_t );
|
||||
ring.Read( &tid, offset, sizeof( uint32_t ) );
|
||||
offset += sizeof( uint32_t );
|
||||
ring.Read( &t0, offset, sizeof( uint64_t ) );
|
||||
offset += sizeof( uint64_t );
|
||||
ring.Read( &cnt, offset, sizeof( uint64_t ) );
|
||||
offset += sizeof( uint64_t );
|
||||
|
||||
if( cnt > 0 )
|
||||
{
|
||||
#if defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
|
||||
t0 = ring.ConvertTimeToTsc( t0 );
|
||||
if( t0 != 0 )
|
||||
#endif
|
||||
{
|
||||
auto trace = (uint64_t*)tracy_malloc_fast( ( 1 + cnt ) * sizeof( uint64_t ) );
|
||||
ring.Read( trace+1, offset, sizeof( uint64_t ) * cnt );
|
||||
|
||||
#if defined __x86_64__ || defined _M_X64
|
||||
// remove non-canonical pointers
|
||||
do
|
||||
{
|
||||
const auto test = (int64_t)trace[cnt];
|
||||
const auto m1 = test >> 63;
|
||||
const auto m2 = test >> 47;
|
||||
if( m1 == m2 ) break;
|
||||
}
|
||||
while( --cnt > 0 );
|
||||
for( uint64_t j=1; j<cnt; j++ )
|
||||
{
|
||||
const auto test = (int64_t)trace[j];
|
||||
const auto m1 = test >> 63;
|
||||
const auto m2 = test >> 47;
|
||||
if( m1 != m2 ) trace[j] = 0;
|
||||
}
|
||||
// remove non-canonical pointers
|
||||
do
|
||||
{
|
||||
const auto test = (int64_t)trace[cnt];
|
||||
const auto m1 = test >> 63;
|
||||
const auto m2 = test >> 47;
|
||||
if( m1 == m2 ) break;
|
||||
}
|
||||
while( --cnt > 0 );
|
||||
for( uint64_t j=1; j<cnt; j++ )
|
||||
{
|
||||
const auto test = (int64_t)trace[j];
|
||||
const auto m1 = test >> 63;
|
||||
const auto m2 = test >> 47;
|
||||
if( m1 != m2 ) trace[j] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// skip kernel frames
|
||||
uint64_t j;
|
||||
for( j=0; j<cnt; j++ )
|
||||
{
|
||||
if( (int64_t)trace[j+1] >= 0 ) break;
|
||||
}
|
||||
if( j == cnt )
|
||||
{
|
||||
tracy_free( trace );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( j > 0 )
|
||||
{
|
||||
cnt -= j;
|
||||
memmove( trace+1, trace+1+j, sizeof( uint64_t ) * cnt );
|
||||
for( uint64_t j=1; j<=cnt; j++ )
|
||||
{
|
||||
if( trace[j] >= (uint64_t)-4095 ) // PERF_CONTEXT_MAX
|
||||
{
|
||||
memmove( trace+j, trace+j+1, sizeof( uint64_t ) * ( cnt - j ) );
|
||||
cnt--;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy( trace, &cnt, sizeof( uint64_t ) );
|
||||
|
||||
TracyLfqPrepare( QueueType::CallstackSample );
|
||||
MemWrite( &item->callstackSampleFat.time, t0 );
|
||||
MemWrite( &item->callstackSampleFat.thread, (uint64_t)tid );
|
||||
MemWrite( &item->callstackSampleFat.ptr, (uint64_t)trace );
|
||||
TracyLfqCommit;
|
||||
}
|
||||
memcpy( trace, &cnt, sizeof( uint64_t ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Layout:
|
||||
// u64 ip
|
||||
// u64 time
|
||||
|
||||
uint64_t ip, t0;
|
||||
ring.Read( &ip, offset, sizeof( uint64_t ) );
|
||||
offset += sizeof( uint64_t );
|
||||
ring.Read( &t0, offset, sizeof( uint64_t ) );
|
||||
|
||||
#if defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
|
||||
t0 = s_ring[i].ConvertTimeToTsc( t0 );
|
||||
t0 = ring.ConvertTimeToTsc( t0 );
|
||||
if( t0 != 0 )
|
||||
#endif
|
||||
{
|
||||
QueueType type;
|
||||
switch( id )
|
||||
{
|
||||
case EventCpuCycles:
|
||||
type = QueueType::HwSampleCpuCycle;
|
||||
break;
|
||||
case EventInstructionsRetired:
|
||||
type = QueueType::HwSampleInstructionRetired;
|
||||
break;
|
||||
case EventCacheReference:
|
||||
type = QueueType::HwSampleCacheReference;
|
||||
break;
|
||||
case EventCacheMiss:
|
||||
type = QueueType::HwSampleCacheMiss;
|
||||
break;
|
||||
case EventBranchRetired:
|
||||
type = QueueType::HwSampleBranchRetired;
|
||||
break;
|
||||
case EventBranchMiss:
|
||||
type = QueueType::HwSampleBranchMiss;
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
break;
|
||||
}
|
||||
|
||||
TracyLfqPrepare( QueueType::CallstackSample );
|
||||
MemWrite( &item->callstackSampleFat.time, t0 );
|
||||
MemWrite( &item->callstackSampleFat.thread, (uint64_t)tid );
|
||||
MemWrite( &item->callstackSampleFat.ptr, (uint64_t)trace );
|
||||
TracyLfqPrepare( type );
|
||||
MemWrite( &item->hwSample.ip, ip );
|
||||
MemWrite( &item->hwSample.time, t0 );
|
||||
TracyLfqCommit;
|
||||
}
|
||||
}
|
||||
}
|
||||
pos += hdr.size;
|
||||
}
|
||||
s_ring[i].Advance( hdr.size );
|
||||
assert( pos == end );
|
||||
ring.Advance( end );
|
||||
}
|
||||
if( !traceActive.load( std::memory_order_relaxed) ) break;
|
||||
if( !hadData )
|
||||
@ -833,8 +1101,8 @@ static void SetupSampling( int64_t& samplingPeriod )
|
||||
}
|
||||
}
|
||||
|
||||
for( int i=0; i<s_numCpus; i++ ) s_ring[i].~RingBuffer<RingBufSize>();
|
||||
tracy_free( s_ring );
|
||||
for( int i=0; i<s_numBuffers; i++ ) s_ring[i].~RingBuffer<RingBufSize>();
|
||||
tracy_free_fast( s_ring );
|
||||
}, nullptr );
|
||||
}
|
||||
|
||||
@ -971,16 +1239,24 @@ static uint64_t ReadNumber( const char*& data )
|
||||
{
|
||||
auto ptr = data;
|
||||
assert( *ptr >= '0' && *ptr <= '9' );
|
||||
uint64_t val = *ptr++ - '0';
|
||||
uint64_t val = 0;
|
||||
for(;;)
|
||||
{
|
||||
const uint8_t v = uint8_t( *ptr - '0' );
|
||||
if( v > 9 ) break;
|
||||
val = val * 10 + v;
|
||||
ptr++;
|
||||
uint64_t q;
|
||||
memcpy( &q, ptr, 8 );
|
||||
for( int i=0; i<8; i++ )
|
||||
{
|
||||
const uint64_t v = ( q & 0xFF ) - '0';
|
||||
if( v > 9 )
|
||||
{
|
||||
data = ptr + i;
|
||||
return val;
|
||||
}
|
||||
val = val * 10 + v;
|
||||
q >>= 8;
|
||||
}
|
||||
ptr += 8;
|
||||
}
|
||||
data = ptr;
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint8_t ReadState( char state )
|
||||
@ -1073,14 +1349,147 @@ ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __AVX2__
|
||||
static inline void AdvanceTo( const char*& line, char match )
|
||||
{
|
||||
__m256i m = _mm256_set1_epi8( match );
|
||||
auto ptr = line;
|
||||
for(;;)
|
||||
{
|
||||
__m256i l = _mm256_loadu_si256( (const __m256i*)ptr );
|
||||
__m256i c = _mm256_cmpeq_epi8( l, m );
|
||||
auto b = uint32_t( _mm256_movemask_epi8( c ) );
|
||||
if( b != 0 )
|
||||
{
|
||||
line = ptr + __builtin_ctz( b );
|
||||
return;
|
||||
}
|
||||
ptr += 32;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void AdvanceTo( const char*& line, char match )
|
||||
{
|
||||
auto ptr = line;
|
||||
for(;;)
|
||||
{
|
||||
uint64_t l;
|
||||
memcpy( &l, ptr, 8 );
|
||||
for( int i=0; i<8; i++ )
|
||||
{
|
||||
if( ( l & 0xFF ) == uint8_t( match ) )
|
||||
{
|
||||
line = ptr + i;
|
||||
return;
|
||||
}
|
||||
l >>= 8;
|
||||
}
|
||||
ptr += 8;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __AVX2__
|
||||
static inline void AdvanceToNot( const char*& line, char match )
|
||||
{
|
||||
__m256i m = _mm256_set1_epi8( match );
|
||||
auto ptr = line;
|
||||
for(;;)
|
||||
{
|
||||
__m256i l = _mm256_loadu_si256( (const __m256i*)ptr );
|
||||
__m256i c = _mm256_cmpeq_epi8( l, m );
|
||||
auto b = ~uint32_t( _mm256_movemask_epi8( c ) );
|
||||
if( b != 0 )
|
||||
{
|
||||
line = ptr + __builtin_ctz( b );
|
||||
return;
|
||||
}
|
||||
ptr += 32;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void AdvanceToNot( const char*& line, char match )
|
||||
{
|
||||
auto ptr = line;
|
||||
for(;;)
|
||||
{
|
||||
uint64_t l;
|
||||
memcpy( &l, ptr, 8 );
|
||||
for( int i=0; i<8; i++ )
|
||||
{
|
||||
if( ( l & 0xFF ) != uint8_t( match ) )
|
||||
{
|
||||
line = ptr + i;
|
||||
return;
|
||||
}
|
||||
l >>= 8;
|
||||
}
|
||||
ptr += 8;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __AVX2__
|
||||
template<int S>
|
||||
static inline void AdvanceTo( const char*& line, const char* match )
|
||||
{
|
||||
auto first = uint8_t( match[0] );
|
||||
__m256i m = _mm256_set1_epi8( first );
|
||||
auto ptr = line;
|
||||
for(;;)
|
||||
{
|
||||
__m256i l = _mm256_loadu_si256( (const __m256i*)ptr );
|
||||
__m256i c = _mm256_cmpeq_epi8( l, m );
|
||||
auto b = uint32_t( _mm256_movemask_epi8( c ) );
|
||||
while( b != 0 )
|
||||
{
|
||||
auto bit = __builtin_ctz( b );
|
||||
auto test = ptr + bit;
|
||||
if( memcmp( test, match, S ) == 0 )
|
||||
{
|
||||
line = test;
|
||||
return;
|
||||
}
|
||||
b ^= ( 1u << bit );
|
||||
}
|
||||
ptr += 32;
|
||||
}
|
||||
}
|
||||
#else
|
||||
template<int S>
|
||||
static inline void AdvanceTo( const char*& line, const char* match )
|
||||
{
|
||||
auto first = uint8_t( match[0] );
|
||||
auto ptr = line;
|
||||
for(;;)
|
||||
{
|
||||
uint64_t l;
|
||||
memcpy( &l, ptr, 8 );
|
||||
for( int i=0; i<8; i++ )
|
||||
{
|
||||
if( ( l & 0xFF ) == first )
|
||||
{
|
||||
if( memcmp( ptr + i, match, S ) == 0 )
|
||||
{
|
||||
line = ptr + i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
l >>= 8;
|
||||
}
|
||||
ptr += 8;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void HandleTraceLine( const char* line )
|
||||
{
|
||||
line += 23;
|
||||
while( *line != '[' ) line++;
|
||||
AdvanceTo( line, '[' );
|
||||
line++;
|
||||
const auto cpu = (uint8_t)ReadNumber( line );
|
||||
line++; // ']'
|
||||
while( *line == ' ' ) line++;
|
||||
AdvanceToNot( line, ' ' );
|
||||
|
||||
#if defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
|
||||
const auto time = ReadNumber( line );
|
||||
@ -1096,19 +1505,19 @@ static void HandleTraceLine( const char* line )
|
||||
{
|
||||
line += 14;
|
||||
|
||||
while( memcmp( line, "prev_pid", 8 ) != 0 ) line++;
|
||||
AdvanceTo<8>( line, "prev_pid" );
|
||||
line += 9;
|
||||
|
||||
const auto oldPid = ReadNumber( line );
|
||||
line++;
|
||||
|
||||
while( memcmp( line, "prev_state", 10 ) != 0 ) line++;
|
||||
AdvanceTo<10>( line, "prev_state" );
|
||||
line += 11;
|
||||
|
||||
const auto oldState = (uint8_t)ReadState( *line );
|
||||
line += 5;
|
||||
|
||||
while( memcmp( line, "next_pid", 8 ) != 0 ) line++;
|
||||
AdvanceTo<8>( line, "next_pid" );
|
||||
line += 9;
|
||||
|
||||
const auto newPid = ReadNumber( line );
|
||||
@ -1128,7 +1537,7 @@ static void HandleTraceLine( const char* line )
|
||||
{
|
||||
line += 14;
|
||||
|
||||
while( memcmp( line, "pid=", 4 ) != 0 ) line++;
|
||||
AdvanceTo<4>( line, "pid=" );
|
||||
line += 4;
|
||||
|
||||
const auto pid = ReadNumber( line );
|
||||
@ -1244,7 +1653,8 @@ void SysTraceWorker( void* ptr )
|
||||
#else
|
||||
static void ProcessTraceLines( int fd )
|
||||
{
|
||||
char* buf = (char*)tracy_malloc( 64*1024 );
|
||||
// 32 bytes buffer space for wide unbound reads
|
||||
char* buf = (char*)tracy_malloc( 64*1024 + 32 );
|
||||
|
||||
struct pollfd pfd;
|
||||
pfd.fd = fd;
|
||||
@ -1332,7 +1742,7 @@ void SysTraceSendExternalName( uint64_t thread )
|
||||
break;
|
||||
}
|
||||
}
|
||||
tracy_free( line );
|
||||
tracy_free_fast( line );
|
||||
fclose( f );
|
||||
if( pid >= 0 )
|
||||
{
|
||||
|
@ -26,6 +26,15 @@ static inline void* tracy_malloc( size_t size )
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void* tracy_malloc_fast( size_t size )
|
||||
{
|
||||
#ifdef TRACY_ENABLE
|
||||
return rpmalloc( size );
|
||||
#else
|
||||
return malloc( size );
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void tracy_free( void* ptr )
|
||||
{
|
||||
#ifdef TRACY_ENABLE
|
||||
@ -36,6 +45,15 @@ static inline void tracy_free( void* ptr )
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void tracy_free_fast( void* ptr )
|
||||
{
|
||||
#ifdef TRACY_ENABLE
|
||||
rpfree( ptr );
|
||||
#else
|
||||
free( ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void* tracy_realloc( void* ptr, size_t size )
|
||||
{
|
||||
#ifdef TRACY_ENABLE
|
||||
|
@ -9,7 +9,7 @@ namespace tracy
|
||||
|
||||
constexpr unsigned Lz4CompressBound( unsigned isize ) { return isize + ( isize / 255 ) + 16; }
|
||||
|
||||
enum : uint32_t { ProtocolVersion = 46 };
|
||||
enum : uint32_t { ProtocolVersion = 48 };
|
||||
enum : uint16_t { BroadcastVersion = 2 };
|
||||
|
||||
using lz4sz_t = uint32_t;
|
||||
@ -77,6 +77,17 @@ enum CpuArchitecture : uint8_t
|
||||
};
|
||||
|
||||
|
||||
struct WelcomeFlag
|
||||
{
|
||||
enum _t : uint8_t
|
||||
{
|
||||
OnDemand = 1 << 0,
|
||||
IsApple = 1 << 1,
|
||||
CodeTransfer = 1 << 2,
|
||||
CombineSamples = 1 << 3,
|
||||
};
|
||||
};
|
||||
|
||||
struct WelcomeMessage
|
||||
{
|
||||
double timerMul;
|
||||
@ -88,10 +99,8 @@ struct WelcomeMessage
|
||||
uint64_t exectime;
|
||||
uint64_t pid;
|
||||
int64_t samplingPeriod;
|
||||
uint8_t onDemand;
|
||||
uint8_t isApple;
|
||||
uint8_t flags;
|
||||
uint8_t cpuArch;
|
||||
uint8_t codeTransfer;
|
||||
char cpuManufacturer[12];
|
||||
uint32_t cpuId;
|
||||
char programName[WelcomeMessageProgramNameSize];
|
||||
|
@ -82,6 +82,12 @@ enum class QueueType : uint8_t
|
||||
CodeInformation,
|
||||
SysTimeReport,
|
||||
TidToPid,
|
||||
HwSampleCpuCycle,
|
||||
HwSampleInstructionRetired,
|
||||
HwSampleCacheReference,
|
||||
HwSampleCacheMiss,
|
||||
HwSampleBranchRetired,
|
||||
HwSampleBranchMiss,
|
||||
PlotConfig,
|
||||
ParamSetup,
|
||||
AckServerQueryNoop,
|
||||
@ -437,6 +443,7 @@ struct QueueCodeInformation
|
||||
{
|
||||
uint64_t ptr;
|
||||
uint32_t line;
|
||||
uint64_t symAddr;
|
||||
};
|
||||
|
||||
struct QueueCrashReport
|
||||
@ -473,6 +480,12 @@ struct QueueTidToPid
|
||||
uint64_t pid;
|
||||
};
|
||||
|
||||
struct QueueHwSample
|
||||
{
|
||||
uint64_t ip;
|
||||
int64_t time;
|
||||
};
|
||||
|
||||
enum class PlotFormatType : uint8_t
|
||||
{
|
||||
Number,
|
||||
@ -567,6 +580,7 @@ struct QueueItem
|
||||
QueueContextSwitch contextSwitch;
|
||||
QueueThreadWakeup threadWakeup;
|
||||
QueueTidToPid tidToPid;
|
||||
QueueHwSample hwSample;
|
||||
QueuePlotConfig plotConfig;
|
||||
QueueParamSetup paramSetup;
|
||||
QueueCpuTopology cpuTopology;
|
||||
@ -653,6 +667,12 @@ static constexpr size_t QueueDataSize[] = {
|
||||
sizeof( QueueHeader ) + sizeof( QueueCodeInformation ),
|
||||
sizeof( QueueHeader ) + sizeof( QueueSysTime ),
|
||||
sizeof( QueueHeader ) + sizeof( QueueTidToPid ),
|
||||
sizeof( QueueHeader ) + sizeof( QueueHwSample ), // cpu cycle
|
||||
sizeof( QueueHeader ) + sizeof( QueueHwSample ), // instruction retired
|
||||
sizeof( QueueHeader ) + sizeof( QueueHwSample ), // cache reference
|
||||
sizeof( QueueHeader ) + sizeof( QueueHwSample ), // cache miss
|
||||
sizeof( QueueHeader ) + sizeof( QueueHwSample ), // branch retired
|
||||
sizeof( QueueHeader ) + sizeof( QueueHwSample ), // branch miss
|
||||
sizeof( QueueHeader ) + sizeof( QueuePlotConfig ),
|
||||
sizeof( QueueHeader ) + sizeof( QueueParamSetup ),
|
||||
sizeof( QueueHeader ), // server query acknowledgement
|
||||
|
123
common/TracyStackFrames.cpp
Normal file
123
common/TracyStackFrames.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "TracyStackFrames.hpp"
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
const char* s_tracyStackFrames_[] = {
|
||||
"tracy::Callstack",
|
||||
"tracy::Callstack(int)",
|
||||
"tracy::GpuCtxScope::{ctor}",
|
||||
"tracy::Profiler::SendCallstack",
|
||||
"tracy::Profiler::SendCallstack(int)",
|
||||
"tracy::Profiler::SendCallstack(int, unsigned long)",
|
||||
"tracy::Profiler::MemAllocCallstack",
|
||||
"tracy::Profiler::MemAllocCallstack(void const*, unsigned long, int)",
|
||||
"tracy::Profiler::MemFreeCallstack",
|
||||
"tracy::Profiler::MemFreeCallstack(void const*, int)",
|
||||
"tracy::ScopedZone::{ctor}",
|
||||
"tracy::ScopedZone::ScopedZone(tracy::SourceLocationData const*, int, bool)",
|
||||
"tracy::CallTrace",
|
||||
"tracy::Profiler::Message",
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char** s_tracyStackFrames = s_tracyStackFrames_;
|
||||
|
||||
const StringMatch s_tracySkipSubframes_[] = {
|
||||
{ "/include/arm_neon.h", 19 },
|
||||
{ "/include/adxintrin.h", 20 },
|
||||
{ "/include/ammintrin.h", 20 },
|
||||
{ "/include/amxbf16intrin.h", 24 },
|
||||
{ "/include/amxint8intrin.h", 24 },
|
||||
{ "/include/amxtileintrin.h", 24 },
|
||||
{ "/include/avx2intrin.h", 21 },
|
||||
{ "/include/avx5124fmapsintrin.h", 29 },
|
||||
{ "/include/avx5124vnniwintrin.h", 29 },
|
||||
{ "/include/avx512bf16intrin.h", 27 },
|
||||
{ "/include/avx512bf16vlintrin.h", 29 },
|
||||
{ "/include/avx512bitalgintrin.h", 29 },
|
||||
{ "/include/avx512bwintrin.h", 25 },
|
||||
{ "/include/avx512cdintrin.h", 25 },
|
||||
{ "/include/avx512dqintrin.h", 25 },
|
||||
{ "/include/avx512erintrin.h", 25 },
|
||||
{ "/include/avx512fintrin.h", 24 },
|
||||
{ "/include/avx512ifmaintrin.h", 27 },
|
||||
{ "/include/avx512ifmavlintrin.h", 29 },
|
||||
{ "/include/avx512pfintrin.h", 25 },
|
||||
{ "/include/avx512vbmi2intrin.h", 28 },
|
||||
{ "/include/avx512vbmi2vlintrin.h", 30 },
|
||||
{ "/include/avx512vbmiintrin.h", 27 },
|
||||
{ "/include/avx512vbmivlintrin.h", 29 },
|
||||
{ "/include/avx512vlbwintrin.h", 27 },
|
||||
{ "/include/avx512vldqintrin.h", 27 },
|
||||
{ "/include/avx512vlintrin.h", 25 },
|
||||
{ "/include/avx512vnniintrin.h", 27 },
|
||||
{ "/include/avx512vnnivlintrin.h", 29 },
|
||||
{ "/include/avx512vp2intersectintrin.h", 35 },
|
||||
{ "/include/avx512vp2intersectvlintrin.h", 37 },
|
||||
{ "/include/avx512vpopcntdqintrin.h", 32 },
|
||||
{ "/include/avx512vpopcntdqvlintrin.h", 34 },
|
||||
{ "/include/avxintrin.h", 20 },
|
||||
{ "/include/avxvnniintrin.h", 24 },
|
||||
{ "/include/bmi2intrin.h", 21 },
|
||||
{ "/include/bmiintrin.h", 20 },
|
||||
{ "/include/bmmintrin.h", 20 },
|
||||
{ "/include/cetintrin.h", 20 },
|
||||
{ "/include/cldemoteintrin.h", 25 },
|
||||
{ "/include/clflushoptintrin.h", 27 },
|
||||
{ "/include/clwbintrin.h", 21 },
|
||||
{ "/include/clzerointrin.h", 23 },
|
||||
{ "/include/emmintrin.h", 20 },
|
||||
{ "/include/enqcmdintrin.h", 23 },
|
||||
{ "/include/f16cintrin.h", 21 },
|
||||
{ "/include/fma4intrin.h", 21 },
|
||||
{ "/include/fmaintrin.h", 20 },
|
||||
{ "/include/fxsrintrin.h", 21 },
|
||||
{ "/include/gfniintrin.h", 21 },
|
||||
{ "/include/hresetintrin.h", 23 },
|
||||
{ "/include/ia32intrin.h", 21 },
|
||||
{ "/include/immintrin.h", 20 },
|
||||
{ "/include/keylockerintrin.h", 26 },
|
||||
{ "/include/lwpintrin.h", 20 },
|
||||
{ "/include/lzcntintrin.h", 22 },
|
||||
{ "/include/mmintrin.h", 19 },
|
||||
{ "/include/movdirintrin.h", 23 },
|
||||
{ "/include/mwaitxintrin.h", 23 },
|
||||
{ "/include/nmmintrin.h", 20 },
|
||||
{ "/include/pconfigintrin.h", 24 },
|
||||
{ "/include/pkuintrin.h", 20 },
|
||||
{ "/include/pmmintrin.h", 20 },
|
||||
{ "/include/popcntintrin.h", 23 },
|
||||
{ "/include/prfchwintrin.h", 23 },
|
||||
{ "/include/rdseedintrin.h", 23 },
|
||||
{ "/include/rtmintrin.h", 20 },
|
||||
{ "/include/serializeintrin.h", 26 },
|
||||
{ "/include/sgxintrin.h", 20 },
|
||||
{ "/include/shaintrin.h", 20 },
|
||||
{ "/include/smmintrin.h", 20 },
|
||||
{ "/include/tbmintrin.h", 20 },
|
||||
{ "/include/tmmintrin.h", 20 },
|
||||
{ "/include/tsxldtrkintrin.h", 25 },
|
||||
{ "/include/uintrintrin.h", 22 },
|
||||
{ "/include/vaesintrin.h", 21 },
|
||||
{ "/include/vpclmulqdqintrin.h", 27 },
|
||||
{ "/include/waitpkgintrin.h", 24 },
|
||||
{ "/include/wbnoinvdintrin.h", 25 },
|
||||
{ "/include/wmmintrin.h", 20 },
|
||||
{ "/include/x86gprintrin.h", 23 },
|
||||
{ "/include/x86intrin.h", 20 },
|
||||
{ "/include/xmmintrin.h", 20 },
|
||||
{ "/include/xopintrin.h", 20 },
|
||||
{ "/include/xsavecintrin.h", 23 },
|
||||
{ "/include/xsaveintrin.h", 22 },
|
||||
{ "/include/xsaveoptintrin.h", 25 },
|
||||
{ "/include/xsavesintrin.h", 23 },
|
||||
{ "/include/xtestintrin.h", 22 },
|
||||
{ "/bits/atomic_base.h", 19 },
|
||||
{ "/atomic", 7 },
|
||||
{}
|
||||
};
|
||||
|
||||
const StringMatch* s_tracySkipSubframes = s_tracySkipSubframes_;
|
||||
|
||||
}
|
22
common/TracyStackFrames.hpp
Normal file
22
common/TracyStackFrames.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef __TRACYSTACKFRAMES_HPP__
|
||||
#define __TRACYSTACKFRAMES_HPP__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
struct StringMatch
|
||||
{
|
||||
const char* str;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
extern const char** s_tracyStackFrames;
|
||||
extern const StringMatch* s_tracySkipSubframes;
|
||||
|
||||
static constexpr int s_tracySkipSubframesMinLen = 7;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -164,7 +164,7 @@ TRACY_API void SetThreadName( const char* name )
|
||||
char* buf = (char*)tracy_malloc( sz+1 );
|
||||
memcpy( buf, name, sz );
|
||||
buf[sz] = '\0';
|
||||
auto data = (ThreadNameData*)tracy_malloc( sizeof( ThreadNameData ) );
|
||||
auto data = (ThreadNameData*)tracy_malloc_fast( sizeof( ThreadNameData ) );
|
||||
data->id = detail::GetThreadHandleImpl();
|
||||
data->name = buf;
|
||||
data->next = GetThreadNameData().load( std::memory_order_relaxed );
|
||||
@ -241,7 +241,7 @@ TRACY_API const char* GetThreadName( uint64_t id )
|
||||
|
||||
TRACY_API const char* GetEnvVar( const char* name )
|
||||
{
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#if defined _WIN32
|
||||
// unfortunately getenv() on Windows is just fundamentally broken. It caches the entire
|
||||
// environment block once on startup, then never refreshes it again. If any environment
|
||||
// strings are added or modified after startup of the CRT, those changes will not be
|
||||
|
@ -86,6 +86,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\common\TracySocket.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracySystem.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4hc.cpp" />
|
||||
@ -136,6 +137,7 @@
|
||||
<ClInclude Include="..\..\..\common\TracyProtocol.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyQueue.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySocket.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4hc.hpp" />
|
||||
|
@ -156,6 +156,9 @@
|
||||
<ClCompile Include="..\..\..\zstd\dictBuilder\zdict.c">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp">
|
||||
<Filter>common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp">
|
||||
@ -338,5 +341,8 @@
|
||||
<ClInclude Include="..\..\..\zstd\dictBuilder\divsufsort.h">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
64
extra/color-hot.cpp
Normal file
64
extra/color-hot.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
inline float linear2sRGB( float v )
|
||||
{
|
||||
float s1 = sqrt( v );
|
||||
float s2 = sqrt( s1 );
|
||||
float s3 = sqrt( s2 );
|
||||
return 0.585122381f * s1 + 0.783140355f * s2 - 0.368262736f * s3;
|
||||
}
|
||||
|
||||
float lerp( float v0, float v1, float t )
|
||||
{
|
||||
return ( 1-t ) * v0 + t * v1;
|
||||
}
|
||||
|
||||
inline float sRGB2linear( float v )
|
||||
{
|
||||
return v * ( v * ( v * 0.305306011f + 0.682171111f ) + 0.012522878f );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int c0 = 0x3333FF;
|
||||
int c1 = 0x33FF33;
|
||||
|
||||
uint32_t t[256] = {};
|
||||
|
||||
float r0 = ( c0 & 0xFF ) / 255.f;
|
||||
float r1 = ( c1 & 0xFF ) / 255.f;
|
||||
float g0 = ( ( c0 >> 8 ) & 0xFF ) / 255.f;
|
||||
float g1 = ( ( c1 >> 8 ) & 0xFF ) / 255.f;
|
||||
float b0 = ( ( c0 >> 16 ) & 0xFF ) / 255.f;
|
||||
float b1 = ( ( c1 >> 16 ) & 0xFF ) / 255.f;
|
||||
|
||||
for( int i=0; i<256; i++ )
|
||||
{
|
||||
float m = i / 255.f;
|
||||
float rf = linear2sRGB( lerp( sRGB2linear( r0 ), sRGB2linear( r1 ), m ) );
|
||||
float gf = linear2sRGB( lerp( sRGB2linear( g0 ), sRGB2linear( g1 ), m ) );
|
||||
float bf = linear2sRGB( lerp( sRGB2linear( b0 ), sRGB2linear( b1 ), m ) );
|
||||
|
||||
int r = (int)std::clamp( rf * 255.f, 0.f, 255.f );
|
||||
int g = (int)std::clamp( gf * 255.f, 0.f, 255.f );
|
||||
int b = (int)std::clamp( bf * 255.f, 0.f, 255.f );
|
||||
|
||||
t[i] = 0xFF000000 | ( b << 16 ) | ( g << 8 ) | r;
|
||||
}
|
||||
|
||||
printf( "uint32_t GoodnessColor[256] = {\n" );
|
||||
for( int i=0; i<256; i += 8 )
|
||||
{
|
||||
printf( " " );
|
||||
for( int j=i; j<i+8; j++ )
|
||||
{
|
||||
printf( " 0x%X,", t[j] );
|
||||
}
|
||||
printf( "\n" );
|
||||
}
|
||||
printf( "};\n" );
|
||||
}
|
@ -42,7 +42,8 @@
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
|
797
imgui/imgui.cpp
797
imgui/imgui.cpp
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.82
|
||||
// dear imgui, v1.83
|
||||
// (headers)
|
||||
|
||||
// Help:
|
||||
@ -11,9 +11,9 @@
|
||||
// - FAQ http://dearimgui.org/faq
|
||||
// - Homepage & latest https://github.com/ocornut/imgui
|
||||
// - Releases & changelog https://github.com/ocornut/imgui/releases
|
||||
// - Gallery https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!)
|
||||
// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!)
|
||||
// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
|
||||
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
|
||||
// - Wiki https://github.com/ocornut/imgui/wiki
|
||||
// - Issues & support https://github.com/ocornut/imgui/issues
|
||||
// - Discussions https://github.com/ocornut/imgui/discussions
|
||||
|
||||
@ -61,8 +61,8 @@ Index of this file:
|
||||
|
||||
// Version
|
||||
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
|
||||
#define IMGUI_VERSION "1.82"
|
||||
#define IMGUI_VERSION_NUM 18200
|
||||
#define IMGUI_VERSION "1.83"
|
||||
#define IMGUI_VERSION_NUM 18300
|
||||
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
|
||||
#define IMGUI_HAS_TABLE
|
||||
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch
|
||||
@ -103,7 +103,20 @@ Index of this file:
|
||||
#define IM_FMTLIST(FMT)
|
||||
#endif
|
||||
|
||||
// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions)
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && !defined(IMGUI_DEBUG_PARANOID)
|
||||
#define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off))
|
||||
#define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop))
|
||||
#else
|
||||
#define IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
#define IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
#endif
|
||||
|
||||
// Warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
|
||||
#endif
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
@ -234,6 +247,7 @@ typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11)
|
||||
#endif
|
||||
|
||||
// 2D vector (often used to store positions or sizes)
|
||||
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
struct ImVec2
|
||||
{
|
||||
float x, y;
|
||||
@ -256,6 +270,7 @@ struct ImVec4
|
||||
IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.
|
||||
#endif
|
||||
};
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Dear ImGui end-user API functions
|
||||
@ -386,7 +401,7 @@ namespace ImGui
|
||||
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame().
|
||||
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame().
|
||||
IMGUI_API void PopStyleVar(int count = 1);
|
||||
IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets
|
||||
IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets
|
||||
IMGUI_API void PopAllowKeyboardFocus();
|
||||
IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame.
|
||||
IMGUI_API void PopButtonRepeat();
|
||||
@ -517,8 +532,8 @@ namespace ImGui
|
||||
IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
|
||||
IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
|
||||
IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL, ImGuiSliderFlags flags = 0);
|
||||
IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
|
||||
IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
|
||||
IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
|
||||
IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
|
||||
|
||||
// Widgets: Regular Sliders
|
||||
// - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds.
|
||||
@ -624,13 +639,14 @@ namespace ImGui
|
||||
// - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar.
|
||||
// - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it.
|
||||
// - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it.
|
||||
// - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.
|
||||
IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window).
|
||||
IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true!
|
||||
IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar.
|
||||
IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true!
|
||||
IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true!
|
||||
IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true!
|
||||
IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment
|
||||
IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated.
|
||||
IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL
|
||||
|
||||
// Tooltips
|
||||
@ -660,18 +676,20 @@ namespace ImGui
|
||||
// - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually.
|
||||
// - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options).
|
||||
// - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup().
|
||||
// - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened.
|
||||
IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!).
|
||||
IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. return true when just opened. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors)
|
||||
IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks
|
||||
IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors)
|
||||
IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into.
|
||||
// Popups: open+begin combined functions helpers
|
||||
// - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking.
|
||||
// - They are convenient to easily create context menus, hence the name.
|
||||
// - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future.
|
||||
// - IMPORTANT: we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight.
|
||||
IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!
|
||||
IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!
|
||||
IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window.
|
||||
IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows).
|
||||
// Popups: test function
|
||||
// Popups: query functions
|
||||
// - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack.
|
||||
// - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack.
|
||||
// - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open.
|
||||
@ -733,6 +751,7 @@ namespace ImGui
|
||||
IMGUI_API int TableGetRowIndex(); // return current row index.
|
||||
IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.
|
||||
IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column.
|
||||
IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change enabled/disabled state of a column, set to false to hide the column. Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)
|
||||
IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details.
|
||||
|
||||
// Legacy Columns API (2020: prefer using Tables!)
|
||||
@ -758,12 +777,12 @@ namespace ImGui
|
||||
// Docking
|
||||
// [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable.
|
||||
// Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking!
|
||||
// - To dock windows: if io.ConfigDockingWithShift == false (default) drag window from their title bar.
|
||||
// - To dock windows: if io.ConfigDockingWithShift == true: hold SHIFT anywhere while moving windows.
|
||||
// - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.
|
||||
// - Drag from window menu button (upper-left button) to undock an entire node (all windows).
|
||||
// About DockSpace:
|
||||
// - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details.
|
||||
// - DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app.
|
||||
IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL);
|
||||
IMGUI_API ImGuiID DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL);
|
||||
IMGUI_API ImGuiID DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL);
|
||||
IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK)
|
||||
IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info)
|
||||
@ -803,7 +822,7 @@ namespace ImGui
|
||||
IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window.
|
||||
IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget.
|
||||
|
||||
// Item/Widgets Utilities
|
||||
// Item/Widgets Utilities and Query Functions
|
||||
// - Most of the functions are referring to the previous Item that has been submitted.
|
||||
// - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions.
|
||||
IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options.
|
||||
@ -828,7 +847,7 @@ namespace ImGui
|
||||
// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
|
||||
// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports.
|
||||
// - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode.
|
||||
IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport.
|
||||
IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL.
|
||||
|
||||
// Miscellaneous Utilities
|
||||
IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped.
|
||||
@ -900,6 +919,7 @@ namespace ImGui
|
||||
IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
|
||||
|
||||
// Debug Utilities
|
||||
// - This is used by the IMGUI_CHECKVERSION() macro.
|
||||
IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro.
|
||||
|
||||
// Memory Allocators
|
||||
@ -993,10 +1013,7 @@ enum ImGuiInputTextFlags_
|
||||
ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID().
|
||||
ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input)
|
||||
ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
|
||||
ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active)
|
||||
// [Internal]
|
||||
ImGuiInputTextFlags_Multiline = 1 << 20, // For internal use by InputTextMultiline()
|
||||
ImGuiInputTextFlags_NoMarkEdited = 1 << 21 // For internal use by functions using InputText() before reformatting data
|
||||
ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active)
|
||||
|
||||
// Obsolete names (will be removed soon)
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
@ -1703,6 +1720,7 @@ template<typename T> void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p
|
||||
// Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
template<typename T>
|
||||
struct ImVector
|
||||
{
|
||||
@ -1761,6 +1779,7 @@ struct ImVector
|
||||
inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; }
|
||||
inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; }
|
||||
};
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImGuiStyle
|
||||
@ -1853,7 +1872,6 @@ struct ImGuiIO
|
||||
|
||||
// Docking options (when ImGuiConfigFlags_DockingEnable is set)
|
||||
bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.
|
||||
bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space)
|
||||
bool ConfigDockingAlwaysTabBar; // = false // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node.
|
||||
bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge.
|
||||
|
||||
@ -1925,7 +1943,7 @@ struct ImGuiIO
|
||||
bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
|
||||
bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
|
||||
bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
|
||||
float Framerate; // Application framerate estimate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames.
|
||||
float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames.
|
||||
int MetricsRenderVertices; // Vertices output during last call to Render()
|
||||
int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3
|
||||
int MetricsRenderWindows; // Number of visible windows
|
||||
@ -2326,6 +2344,9 @@ struct ImDrawCmd
|
||||
void* UserCallbackData; // 4-8 // The draw callback code can access this.
|
||||
|
||||
ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed
|
||||
|
||||
// Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)
|
||||
inline ImTextureID GetTexID() const { return TextureId; }
|
||||
};
|
||||
|
||||
// Vertex index, default to 16-bit
|
||||
@ -3070,6 +3091,10 @@ enum ImDrawCornerFlags_
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h)
|
||||
#ifdef IMGUI_INCLUDE_IMGUI_USER_H
|
||||
#include "imgui_user.h"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.82
|
||||
// dear imgui, v1.83
|
||||
// (demo code)
|
||||
|
||||
// Help:
|
||||
@ -6,9 +6,9 @@
|
||||
// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
|
||||
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
|
||||
// Read imgui.cpp for more details, documentation and comments.
|
||||
// Get latest version at https://github.com/ocornut/imgui
|
||||
// Get the latest version at https://github.com/ocornut/imgui
|
||||
|
||||
// Message to the person tempted to delete this file when integrating Dear ImGui into their code base:
|
||||
// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:
|
||||
// Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other
|
||||
// coders will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available
|
||||
// debug menu of your game/app! Removing this file from your project is hindering access to documentation for everyone
|
||||
@ -16,19 +16,19 @@
|
||||
// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().
|
||||
// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be
|
||||
// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
|
||||
// In other situation, whenever you have Dear ImGui available you probably want this to be available for reference.
|
||||
// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.
|
||||
// Thank you,
|
||||
// -Your beloved friend, imgui_demo.cpp (which you won't delete)
|
||||
|
||||
// Message to beginner C/C++ programmers about the meaning of the 'static' keyword:
|
||||
// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls,
|
||||
// In this demo code, we frequently use 'static' variables inside functions. A static variable persists across calls,
|
||||
// so it is essentially like a global variable but declared inside the scope of the function. We do this as a way to
|
||||
// gather code and data in the same place, to make the demo source code faster to read, faster to write, and smaller
|
||||
// in size. It also happens to be a convenient way of storing simple UI related information as long as your function
|
||||
// doesn't need to be reentrant or used in multiple threads. This might be a pattern you will want to use in your code,
|
||||
// but most of the real data you would be editing is likely going to be stored outside your functions.
|
||||
|
||||
// The Demo code in this file is designed to be easy to copy-and-paste in into your application!
|
||||
// The Demo code in this file is designed to be easy to copy-and-paste into your application!
|
||||
// Because of this:
|
||||
// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.
|
||||
// - We try to declare static variables in the local scope, as close as possible to the code using them.
|
||||
@ -54,6 +54,7 @@ Index of this file:
|
||||
// - sub section: ShowDemoWindowTables()
|
||||
// - sub section: ShowDemoWindowMisc()
|
||||
// [SECTION] About Window / ShowAboutWindow()
|
||||
// [SECTION] Font Viewer / ShowFontAtlas()
|
||||
// [SECTION] Style Editor / ShowStyleEditor()
|
||||
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
|
||||
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
|
||||
@ -93,7 +94,8 @@ Index of this file:
|
||||
|
||||
// Visual Studio warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
@ -456,14 +458,12 @@ void ImGui::ShowDemoWindow(bool* p_open)
|
||||
ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.");
|
||||
|
||||
ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable);
|
||||
ImGui::SameLine(); HelpMarker(io.ConfigDockingWithShift ? "[beta] Use SHIFT to dock window into each others." : "[beta] Drag from title bar to dock windows into each others.");
|
||||
ImGui::SameLine(); HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows).");
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
||||
{
|
||||
ImGui::Indent();
|
||||
ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit);
|
||||
ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.");
|
||||
ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift);
|
||||
ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)");
|
||||
ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar);
|
||||
ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows.");
|
||||
ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload);
|
||||
@ -1607,7 +1607,7 @@ static void ShowDemoWindowWidgets()
|
||||
// Plot/Graph widgets are not very good.
|
||||
// Consider writing your own, or using a third-party one, see:
|
||||
// - ImPlot https://github.com/epezent/implot
|
||||
// - others https://github.com/ocornut/imgui/wiki/Useful-Widgets
|
||||
// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions
|
||||
if (ImGui::TreeNode("Plots Widgets"))
|
||||
{
|
||||
static bool animate = true;
|
||||
@ -1656,7 +1656,7 @@ static void ShowDemoWindowWidgets()
|
||||
};
|
||||
static int func_type = 0, display_count = 70;
|
||||
ImGui::Separator();
|
||||
ImGui::SetNextItemWidth(100);
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::Combo("func", &func_type, "Sin\0Saw\0");
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderInt("Sample count", &display_count, 1, 400);
|
||||
@ -2449,7 +2449,7 @@ static void ShowDemoWindowLayout()
|
||||
// the POV of the parent window). See 'Demo->Querying Status (Active/Focused/Hovered etc.)' for details.
|
||||
{
|
||||
static int offset_x = 0;
|
||||
ImGui::SetNextItemWidth(100);
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000);
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
|
||||
@ -2471,15 +2471,15 @@ static void ShowDemoWindowLayout()
|
||||
|
||||
if (ImGui::TreeNode("Widgets Width"))
|
||||
{
|
||||
static float f = 0.0f;
|
||||
static bool show_indented_items = true;
|
||||
ImGui::Checkbox("Show indented items", &show_indented_items);
|
||||
|
||||
// Use SetNextItemWidth() to set the width of a single upcoming item.
|
||||
// Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
|
||||
// In real code use you'll probably want to choose width values that are proportional to your font size
|
||||
// e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
|
||||
|
||||
static float f = 0.0f;
|
||||
static bool show_indented_items = true;
|
||||
ImGui::Checkbox("Show indented items", &show_indented_items);
|
||||
|
||||
ImGui::Text("SetNextItemWidth/PushItemWidth(100)");
|
||||
ImGui::SameLine(); HelpMarker("Fixed width.");
|
||||
ImGui::PushItemWidth(100);
|
||||
@ -2895,6 +2895,8 @@ static void ShowDemoWindowLayout()
|
||||
{
|
||||
for (int item = 0; item < 100; item++)
|
||||
{
|
||||
if (item > 0)
|
||||
ImGui::SameLine();
|
||||
if (enable_track && item == track_item)
|
||||
{
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
|
||||
@ -2904,7 +2906,6 @@ static void ShowDemoWindowLayout()
|
||||
{
|
||||
ImGui::Text("Item %d", item);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
float scroll_x = ImGui::GetScrollX();
|
||||
@ -3248,46 +3249,84 @@ static void ShowDemoWindowPopups()
|
||||
|
||||
if (ImGui::TreeNode("Context menus"))
|
||||
{
|
||||
HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.");
|
||||
|
||||
// BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
|
||||
// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
|
||||
// OpenPopup(id);
|
||||
// return BeginPopup(id);
|
||||
// For more advanced uses you may want to replicate and customize this code.
|
||||
// See details in BeginPopupContextItem().
|
||||
static float value = 0.5f;
|
||||
ImGui::Text("Value = %.3f (<-- right-click here)", value);
|
||||
if (ImGui::BeginPopupContextItem("item context menu"))
|
||||
// if (id == 0)
|
||||
// id = GetItemID(); // Use last item id
|
||||
// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
|
||||
// OpenPopup(id);
|
||||
// return BeginPopup(id);
|
||||
// For advanced advanced uses you may want to replicate and customize this code.
|
||||
// See more details in BeginPopupContextItem().
|
||||
|
||||
// Example 1
|
||||
// When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
|
||||
// and BeginPopupContextItem() will use the last item ID as the popup ID.
|
||||
{
|
||||
if (ImGui::Selectable("Set to zero")) value = 0.0f;
|
||||
if (ImGui::Selectable("Set to PI")) value = 3.1415f;
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f);
|
||||
ImGui::EndPopup();
|
||||
const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" };
|
||||
for (int n = 0; n < 5; n++)
|
||||
{
|
||||
ImGui::Selectable(names[n]);
|
||||
if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
|
||||
{
|
||||
ImGui::Text("This a popup for \"%s\"!", names[n]);
|
||||
if (ImGui::Button("Close"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Right-click to open popup");
|
||||
}
|
||||
}
|
||||
|
||||
// We can also use OpenPopupOnItemClick() which is the same as BeginPopupContextItem() but without the
|
||||
// Begin() call. So here we will make it that clicking on the text field with the right mouse button (1)
|
||||
// will toggle the visibility of the popup above.
|
||||
ImGui::Text("(You can also right-click me to open the same popup as above.)");
|
||||
ImGui::OpenPopupOnItemClick("item context menu", 1);
|
||||
|
||||
// When used after an item that has an ID (e.g.Button), we can skip providing an ID to BeginPopupContextItem().
|
||||
// BeginPopupContextItem() will use the last item ID as the popup ID.
|
||||
// In addition here, we want to include your editable label inside the button label.
|
||||
// We use the ### operator to override the ID (read FAQ about ID for details)
|
||||
static char name[32] = "Label1";
|
||||
char buf[64];
|
||||
sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
|
||||
ImGui::Button(buf);
|
||||
if (ImGui::BeginPopupContextItem())
|
||||
// Example 2
|
||||
// Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().
|
||||
// Using an explicit identifier is also convenient if you want to activate the popups from different locations.
|
||||
{
|
||||
ImGui::Text("Edit name:");
|
||||
ImGui::InputText("##edit", name, IM_ARRAYSIZE(name));
|
||||
if (ImGui::Button("Close"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
HelpMarker("Text() elements don't have stable identifiers so we need to provide one.");
|
||||
static float value = 0.5f;
|
||||
ImGui::Text("Value = %.3f <-- (1) right-click this value", value);
|
||||
if (ImGui::BeginPopupContextItem("my popup"))
|
||||
{
|
||||
if (ImGui::Selectable("Set to zero")) value = 0.0f;
|
||||
if (ImGui::Selectable("Set to PI")) value = 3.1415f;
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
|
||||
// Here we make it that right-clicking this other text element opens the same popup as above.
|
||||
// The popup itself will be submitted by the code above.
|
||||
ImGui::Text("(2) Or right-click this text");
|
||||
ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight);
|
||||
|
||||
// Back to square one: manually open the same popup.
|
||||
if (ImGui::Button("(3) Or click this button"))
|
||||
ImGui::OpenPopup("my popup");
|
||||
}
|
||||
|
||||
// Example 3
|
||||
// When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),
|
||||
// we need to make sure your item identifier is stable.
|
||||
// In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).
|
||||
{
|
||||
HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.");
|
||||
static char name[32] = "Label1";
|
||||
char buf[64];
|
||||
sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
|
||||
ImGui::Button(buf);
|
||||
if (ImGui::BeginPopupContextItem())
|
||||
{
|
||||
ImGui::Text("Edit name:");
|
||||
ImGui::InputText("##edit", name, IM_ARRAYSIZE(name));
|
||||
if (ImGui::Button("Close"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SameLine(); ImGui::Text("(<-- right-click here)");
|
||||
}
|
||||
ImGui::SameLine(); ImGui::Text("(<-- right-click here)");
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
@ -5518,7 +5557,7 @@ static void ShowDemoWindowMisc()
|
||||
ImGui::InputText("3", buf, IM_ARRAYSIZE(buf));
|
||||
ImGui::PushAllowKeyboardFocus(false);
|
||||
ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf));
|
||||
//ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool) to disable tabbing through certain widgets.");
|
||||
ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
|
||||
ImGui::PopAllowKeyboardFocus();
|
||||
ImGui::InputText("5", buf, IM_ARRAYSIZE(buf));
|
||||
ImGui::TreePop();
|
||||
@ -5544,6 +5583,7 @@ static void ShowDemoWindowMisc()
|
||||
if (focus_3) ImGui::SetKeyboardFocusHere();
|
||||
ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf));
|
||||
if (ImGui::IsItemActive()) has_focus = 3;
|
||||
ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
|
||||
ImGui::PopAllowKeyboardFocus();
|
||||
|
||||
if (has_focus)
|
||||
@ -5740,7 +5780,6 @@ void ImGui::ShowAboutWindow(bool* p_open)
|
||||
if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration");
|
||||
if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent");
|
||||
if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit");
|
||||
if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift");
|
||||
if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar");
|
||||
if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload");
|
||||
if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors");
|
||||
@ -5780,31 +5819,15 @@ void ImGui::ShowAboutWindow(bool* p_open)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Style Editor / ShowStyleEditor()
|
||||
// [SECTION] Font viewer / ShowFontAtlas()
|
||||
//-----------------------------------------------------------------------------
|
||||
// - ShowStyleSelector()
|
||||
// - ShowFontSelector()
|
||||
// - ShowStyleEditor()
|
||||
// - ShowFont()
|
||||
// - ShowFontAtlas()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
|
||||
// Here we use the simplified Combo() api that packs items into a single literal string.
|
||||
// Useful for quick combo boxes where the choices are known locally.
|
||||
bool ImGui::ShowStyleSelector(const char* label)
|
||||
{
|
||||
static int style_idx = -1;
|
||||
if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0"))
|
||||
{
|
||||
switch (style_idx)
|
||||
{
|
||||
case 0: ImGui::StyleColorsDark(); break;
|
||||
case 1: ImGui::StyleColorsLight(); break;
|
||||
case 2: ImGui::StyleColorsClassic(); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// This isn't worth putting in public API but we want Metrics to use it
|
||||
namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); }
|
||||
|
||||
// Demo helper function to select among loaded fonts.
|
||||
// Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one.
|
||||
@ -5833,7 +5856,7 @@ void ImGui::ShowFontSelector(const char* label)
|
||||
}
|
||||
|
||||
// [Internal] Display details for a single font, called by ShowStyleEditor().
|
||||
static void NodeFont(ImFont* font)
|
||||
static void ShowFont(ImFont* font)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
@ -5843,9 +5866,13 @@ static void NodeFont(ImFont* font)
|
||||
if (!font_details_opened)
|
||||
return;
|
||||
|
||||
// Display preview text
|
||||
ImGui::PushFont(font);
|
||||
ImGui::Text("The quick brown fox jumps over the lazy dog");
|
||||
ImGui::PopFont();
|
||||
|
||||
// Display details
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font
|
||||
ImGui::SameLine(); HelpMarker(
|
||||
"Note than the default embedded font is NOT meant to be scaled.\n\n"
|
||||
@ -5863,9 +5890,10 @@ static void NodeFont(ImFont* font)
|
||||
if (const ImFontConfig* cfg = &font->ConfigData[config_i])
|
||||
ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
|
||||
config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
|
||||
|
||||
// Display all glyphs of the fonts in separate pages of 256 characters
|
||||
if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
|
||||
{
|
||||
// Display all glyphs of the fonts in separate pages of 256 characters
|
||||
const ImU32 glyph_col = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
|
||||
{
|
||||
@ -5920,6 +5948,50 @@ static void NodeFont(ImFont* font)
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
|
||||
{
|
||||
for (int i = 0; i < atlas->Fonts.Size; i++)
|
||||
{
|
||||
ImFont* font = atlas->Fonts[i];
|
||||
ImGui::PushID(font);
|
||||
ShowFont(font);
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
|
||||
{
|
||||
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
|
||||
ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Style Editor / ShowStyleEditor()
|
||||
//-----------------------------------------------------------------------------
|
||||
// - ShowStyleSelector()
|
||||
// - ShowStyleEditor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
|
||||
// Here we use the simplified Combo() api that packs items into a single literal string.
|
||||
// Useful for quick combo boxes where the choices are known locally.
|
||||
bool ImGui::ShowStyleSelector(const char* label)
|
||||
{
|
||||
static int style_idx = -1;
|
||||
if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0"))
|
||||
{
|
||||
switch (style_idx)
|
||||
{
|
||||
case 0: ImGui::StyleColorsDark(); break;
|
||||
case 1: ImGui::StyleColorsLight(); break;
|
||||
case 2: ImGui::StyleColorsClassic(); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImGui::ShowStyleEditor(ImGuiStyle* ref)
|
||||
{
|
||||
// You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
|
||||
@ -6076,21 +6148,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImFontAtlas* atlas = io.Fonts;
|
||||
HelpMarker("Read FAQ and docs/FONTS.md for details on font loading.");
|
||||
ImGui::PushItemWidth(120);
|
||||
for (int i = 0; i < atlas->Fonts.Size; i++)
|
||||
{
|
||||
ImFont* font = atlas->Fonts[i];
|
||||
ImGui::PushID(font);
|
||||
NodeFont(font);
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
|
||||
{
|
||||
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
|
||||
ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::ShowFontAtlas(atlas);
|
||||
|
||||
// Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
|
||||
// (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
|
||||
@ -6102,6 +6160,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
|
||||
"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
|
||||
"Using those settings here will give you poor quality results.");
|
||||
static float window_scale = 1.0f;
|
||||
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
|
||||
if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
|
||||
ImGui::SetWindowFontScale(window_scale);
|
||||
ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
|
||||
@ -6121,7 +6180,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
|
||||
HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
|
||||
|
||||
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
|
||||
ImGui::PushItemWidth(100);
|
||||
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
|
||||
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
|
||||
if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
|
||||
|
||||
@ -7462,7 +7521,7 @@ static void ShowExampleAppCustomRendering(bool* p_open)
|
||||
|
||||
// Demonstrate using DockSpace() to create an explicit docking node within an existing window.
|
||||
// Note that you dock windows into each others _without_ a dockspace, by just clicking on
|
||||
// a window title bar and moving it (+ hold SHIFT if io.ConfigDockingWithShift is set).
|
||||
// a window title bar or tab and moving it.
|
||||
// DockSpace() and DockSpaceOverViewport() are only useful to construct a central docking
|
||||
// location for your application.
|
||||
void ShowExampleAppDockSpace(bool* p_open)
|
||||
@ -7555,11 +7614,10 @@ void ShowExampleAppDockSpace(bool* p_open)
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
HelpMarker(
|
||||
"When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n\n"
|
||||
" > if io.ConfigDockingWithShift==false (default):" "\n"
|
||||
" drag windows from title bar to dock" "\n"
|
||||
" > if io.ConfigDockingWithShift==true:" "\n"
|
||||
" drag windows from anywhere and hold Shift to dock" "\n\n"
|
||||
"When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n"
|
||||
"- Drag from window title bar or their tab to dock/undock." "\n"
|
||||
"- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n"
|
||||
"- Hold SHIFT to disable docking." "\n"
|
||||
"This demo app has nothing to do with it!" "\n\n"
|
||||
"This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n"
|
||||
"ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." "\n\n"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.82
|
||||
// dear imgui, v1.83
|
||||
// (drawing and font code)
|
||||
|
||||
/*
|
||||
@ -54,9 +54,12 @@ Index of this file:
|
||||
|
||||
// Visual Studio warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4127) // condition expression is constant
|
||||
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||
#pragma warning (disable: 4127) // condition expression is constant
|
||||
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||
#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead.
|
||||
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
|
||||
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
@ -105,6 +108,9 @@ namespace IMGUI_STB_NAMESPACE
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration
|
||||
#pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'.
|
||||
#pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read.
|
||||
#pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did.
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
@ -145,7 +151,7 @@ namespace IMGUI_STB_NAMESPACE
|
||||
#define STBTT_sqrt(x) ImSqrt(x)
|
||||
#define STBTT_pow(x,y) ImPow(x,y)
|
||||
#define STBTT_fabs(x) ImFabs(x)
|
||||
#define STBTT_ifloor(x) ((int)ImFloorStd(x))
|
||||
#define STBTT_ifloor(x) ((int)ImFloorSigned(x))
|
||||
#define STBTT_iceil(x) ((int)ImCeil(x))
|
||||
#define STBTT_STATIC
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
@ -695,7 +701,8 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
|
||||
// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds.
|
||||
// Those macros expects l-values.
|
||||
#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0)
|
||||
#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = ImRecip(d2); VX *= inv_lensq; VY *= inv_lensq; } while (0)
|
||||
#define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366)
|
||||
#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } while (0)
|
||||
|
||||
// TODO: Thickness anti-aliased lines cap are missing their AA fringe.
|
||||
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
|
||||
@ -1043,7 +1050,6 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
|
||||
_Path.push_back(center);
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(a_min_sample <= a_max_sample);
|
||||
|
||||
// Calculate arc auto segment step size
|
||||
if (a_step <= 0)
|
||||
@ -1052,17 +1058,7 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
|
||||
// Make sure we never do steps larger than one quarter of the circle
|
||||
a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4);
|
||||
|
||||
// Normalize a_min_sample to always start lie in [0..IM_DRAWLIST_ARCFAST_SAMPLE_MAX] range.
|
||||
if (a_min_sample < 0)
|
||||
{
|
||||
int normalized_sample = a_min_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
if (normalized_sample < 0)
|
||||
normalized_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
a_max_sample += (normalized_sample - a_min_sample);
|
||||
a_min_sample = normalized_sample;
|
||||
}
|
||||
|
||||
const int sample_range = a_max_sample - a_min_sample;
|
||||
const int sample_range = ImAbs(a_max_sample - a_min_sample);
|
||||
const int a_next_step = a_step;
|
||||
|
||||
int samples = sample_range + 1;
|
||||
@ -1088,16 +1084,40 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
|
||||
ImVec2* out_ptr = _Path.Data + (_Path.Size - samples);
|
||||
|
||||
int sample_index = a_min_sample;
|
||||
for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
|
||||
if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
|
||||
{
|
||||
// a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
|
||||
if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
|
||||
sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
if (sample_index < 0)
|
||||
sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
}
|
||||
|
||||
const ImVec2 s = _Data->ArcFastVtx[sample_index];
|
||||
out_ptr->x = center.x + s.x * radius;
|
||||
out_ptr->y = center.y + s.y * radius;
|
||||
out_ptr++;
|
||||
if (a_max_sample >= a_min_sample)
|
||||
{
|
||||
for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
|
||||
{
|
||||
// a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
|
||||
if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
|
||||
sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
|
||||
const ImVec2 s = _Data->ArcFastVtx[sample_index];
|
||||
out_ptr->x = center.x + s.x * radius;
|
||||
out_ptr->y = center.y + s.y * radius;
|
||||
out_ptr++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step)
|
||||
{
|
||||
// a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
|
||||
if (sample_index < 0)
|
||||
sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
|
||||
const ImVec2 s = _Data->ArcFastVtx[sample_index];
|
||||
out_ptr->x = center.x + s.x * radius;
|
||||
out_ptr->y = center.y + s.y * radius;
|
||||
out_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
if (extra_max_sample)
|
||||
@ -1122,7 +1142,6 @@ void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, fl
|
||||
_Path.push_back(center);
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(a_min <= a_max);
|
||||
|
||||
// Note that we are adding a point at both a_min and a_max.
|
||||
// If you are trying to draw a full closed circle you don't want the overlapping points!
|
||||
@ -1142,7 +1161,6 @@ void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_
|
||||
_Path.push_back(center);
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(a_min_of_12 <= a_max_of_12);
|
||||
_PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0);
|
||||
}
|
||||
|
||||
@ -1153,7 +1171,6 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
|
||||
_Path.push_back(center);
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(a_min <= a_max);
|
||||
|
||||
if (num_segments > 0)
|
||||
{
|
||||
@ -1164,28 +1181,33 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
|
||||
// Automatic segment count
|
||||
if (radius <= _Data->ArcFastRadiusCutoff)
|
||||
{
|
||||
const bool a_is_reverse = a_max < a_min;
|
||||
|
||||
// We are going to use precomputed values for mid samples.
|
||||
// Determine first and last sample in lookup table that belong to the arc.
|
||||
const int a_min_sample = (int)ImCeil(IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f));
|
||||
const int a_max_sample = (int)( IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f));
|
||||
const int a_mid_samples = ImMax(a_max_sample - a_min_sample, 0);
|
||||
const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
|
||||
const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
|
||||
|
||||
const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
|
||||
const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f);
|
||||
const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
|
||||
|
||||
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
||||
const bool a_emit_start = (a_min_segment_angle - a_min) > 0.0f;
|
||||
const bool a_emit_end = (a_max - a_max_segment_angle) > 0.0f;
|
||||
const bool a_emit_start = (a_min_segment_angle - a_min) != 0.0f;
|
||||
const bool a_emit_end = (a_max - a_max_segment_angle) != 0.0f;
|
||||
|
||||
_Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));
|
||||
if (a_emit_start)
|
||||
_Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius));
|
||||
if (a_max_sample >= a_min_sample)
|
||||
if (a_mid_samples > 0)
|
||||
_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);
|
||||
if (a_emit_end)
|
||||
_Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));
|
||||
}
|
||||
else
|
||||
{
|
||||
const float arc_length = a_max - a_min;
|
||||
const float arc_length = ImAbs(a_max - a_min);
|
||||
const int circle_segment_count = _CalcCircleAutoSegmentCount(radius);
|
||||
const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length));
|
||||
_PathArcToN(center, radius, a_min, a_max, arc_segment_count);
|
||||
@ -3475,6 +3497,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
|
||||
return text_size;
|
||||
}
|
||||
|
||||
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
|
||||
void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const
|
||||
{
|
||||
const ImFontGlyph* glyph = FindGlyph(c);
|
||||
@ -3489,6 +3512,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
|
||||
draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
|
||||
}
|
||||
|
||||
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
|
||||
void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
|
||||
{
|
||||
if (!text_end)
|
||||
@ -3760,7 +3784,7 @@ void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, Im
|
||||
if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
|
||||
{
|
||||
pos -= offset;
|
||||
const ImTextureID tex_id = font_atlas->TexID;
|
||||
ImTextureID tex_id = font_atlas->TexID;
|
||||
draw_list->PushTextureID(tex_id);
|
||||
draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
|
||||
draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
|
||||
@ -3787,8 +3811,8 @@ void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half
|
||||
// and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window.
|
||||
void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col)
|
||||
{
|
||||
draw_list->AddRectFilled(p_min + ImVec2(sz * 0.10f, sz * 0.15f), p_min + ImVec2(sz * 0.70f, sz * 0.30f), col);
|
||||
RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.40f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col);
|
||||
draw_list->AddRectFilled(p_min + ImVec2(sz * 0.20f, sz * 0.15f), p_min + ImVec2(sz * 0.80f, sz * 0.30f), col);
|
||||
RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col);
|
||||
}
|
||||
|
||||
static inline float ImAcos01(float x)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.82
|
||||
// dear imgui, v1.83
|
||||
// (internal structures/api)
|
||||
|
||||
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
|
||||
@ -51,14 +51,19 @@ Index of this file:
|
||||
#include <math.h> // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf
|
||||
#include <limits.h> // INT_MIN, INT_MAX
|
||||
|
||||
// Enable SSE intrinsics if available
|
||||
#if defined __SSE__ || defined __x86_64__ || defined _M_X64
|
||||
#define IMGUI_ENABLE_SSE
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
// Visual Studio warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
|
||||
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
|
||||
#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
|
||||
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
|
||||
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
@ -68,6 +73,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx'
|
||||
#endif
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloorSigned()
|
||||
#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
@ -114,7 +120,7 @@ struct ImGuiGroupData; // Stacked storage data for BeginGroup()/End
|
||||
struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box
|
||||
struct ImGuiLastItemDataBackup; // Backup and restore IsItemHovered() internal data
|
||||
struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only
|
||||
struct ImGuiNavMoveResult; // Result of a gamepad/keyboard directional navigation move query result
|
||||
struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result
|
||||
struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions
|
||||
struct ImGuiNextWindowData; // Storage for SetNextWindow** functions
|
||||
struct ImGuiNextItemData; // Storage for SetNextItem** functions
|
||||
@ -128,16 +134,18 @@ struct ImGuiTabBar; // Storage for a tab bar
|
||||
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
|
||||
struct ImGuiTable; // Storage for a table
|
||||
struct ImGuiTableColumn; // Storage for one column of a table
|
||||
struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables.
|
||||
struct ImGuiTableSettings; // Storage for a table .ini settings
|
||||
struct ImGuiTableColumnsSettings; // Storage for a column .ini settings
|
||||
struct ImGuiWindow; // Storage for one window
|
||||
struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame)
|
||||
struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window)
|
||||
struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session)
|
||||
|
||||
// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists.
|
||||
typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // Enum: for storing the source authority (dock node vs window) of a field
|
||||
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
|
||||
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag()
|
||||
typedef int ImGuiItemAddFlags; // -> enum ImGuiItemAddFlags_ // Flags: for ItemAdd()
|
||||
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags
|
||||
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
|
||||
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
|
||||
@ -243,6 +251,13 @@ namespace ImStb
|
||||
#define IMGUI_CDECL
|
||||
#endif
|
||||
|
||||
// Warnings
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX))
|
||||
#else
|
||||
#define IM_MSVC_WARNING_SUPPRESS(XXXX)
|
||||
#endif
|
||||
|
||||
// Debug Tools
|
||||
// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item.
|
||||
#ifndef IM_DEBUG_BREAK
|
||||
@ -330,6 +345,7 @@ IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, cons
|
||||
// We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.)
|
||||
// We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself.
|
||||
#ifdef IMGUI_DEFINE_MATH_OPERATORS
|
||||
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); }
|
||||
static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); }
|
||||
static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }
|
||||
@ -345,6 +361,7 @@ static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs)
|
||||
static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }
|
||||
static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }
|
||||
static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
#endif
|
||||
|
||||
// Helpers: File System
|
||||
@ -370,6 +387,7 @@ IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 coun
|
||||
IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0);
|
||||
|
||||
// Helpers: Maths
|
||||
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy)
|
||||
#ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
|
||||
#define ImFabs(X) fabsf(X)
|
||||
@ -380,28 +398,23 @@ IMGUI_API void* ImFileLoadToMemory(const char* filename, const char*
|
||||
#define ImAcos(X) acosf(X)
|
||||
#define ImAtan2(Y, X) atan2f((Y), (X))
|
||||
#define ImAtof(STR) atof(STR)
|
||||
#define ImFloorStd(X) floorf(X) // We already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by e.g. stb_truetype)
|
||||
//#define ImFloorStd(X) floorf(X) // We use our own, see ImFloor() and ImFloorSigned()
|
||||
#define ImCeil(X) ceilf(X)
|
||||
static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
|
||||
static inline double ImPow(double x, double y) { return pow(x, y); }
|
||||
static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
|
||||
static inline double ImLog(double x) { return log(x); }
|
||||
static inline int ImAbs(int x) { return x < 0 ? -x : x; }
|
||||
static inline float ImAbs(float x) { return fabsf(x); }
|
||||
static inline double ImAbs(double x) { return fabs(x); }
|
||||
static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument
|
||||
static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : ((x > 0.0) ? 1.0 : 0.0); }
|
||||
#if defined __SSE__ || defined __x86_64__ || defined _M_X64
|
||||
#ifdef IMGUI_ENABLE_SSE
|
||||
static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }
|
||||
#else
|
||||
static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); }
|
||||
#endif
|
||||
static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); }
|
||||
#if defined __SSE__ || defined __x86_64__ || defined _M_X64
|
||||
static inline float ImRecip(float x) { return _mm_cvtss_f32(_mm_rcp_ps(_mm_set_ss(x))); }
|
||||
#else
|
||||
static inline float ImRecip(float x) { return 1.0f / x; }
|
||||
#endif
|
||||
static inline double ImRecip(double x) { return 1.0 / x; }
|
||||
#endif
|
||||
// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
|
||||
// (Exceptionally using templates here but we could also redefine them for those types)
|
||||
@ -424,12 +437,14 @@ static inline float ImLengthSqr(const ImVec2& lhs)
|
||||
static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
|
||||
static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
|
||||
static inline float ImFloor(float f) { return (float)(int)(f); }
|
||||
static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
|
||||
static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
|
||||
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
|
||||
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
|
||||
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
|
||||
static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }
|
||||
static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
|
||||
// Helpers: Geometry
|
||||
IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t);
|
||||
@ -445,6 +460,7 @@ IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy);
|
||||
|
||||
// Helper: ImVec1 (1D vector)
|
||||
// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
|
||||
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
struct ImVec1
|
||||
{
|
||||
float x;
|
||||
@ -498,6 +514,7 @@ struct IMGUI_API ImRect
|
||||
bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
|
||||
ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); }
|
||||
};
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
|
||||
// Helper: ImBitArray
|
||||
inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }
|
||||
@ -517,12 +534,12 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran
|
||||
}
|
||||
|
||||
// Helper: ImBitArray class (wrapper over ImBitArray functions)
|
||||
// Store 1-bit per value. NOT CLEARED by constructor.
|
||||
// Store 1-bit per value.
|
||||
template<int BITCOUNT>
|
||||
struct IMGUI_API ImBitArray
|
||||
{
|
||||
ImU32 Storage[(BITCOUNT + 31) >> 5];
|
||||
ImBitArray() { }
|
||||
ImBitArray() { ClearAllBits(); }
|
||||
void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); }
|
||||
void SetAllBits() { memset(Storage, 255, sizeof(Storage)); }
|
||||
bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
|
||||
@ -722,32 +739,51 @@ enum ImGuiItemFlags_
|
||||
ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false
|
||||
ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window
|
||||
ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
|
||||
ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
|
||||
ImGuiItemFlags_Default_ = 0
|
||||
ImGuiItemFlags_ReadOnly = 1 << 7 // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
|
||||
};
|
||||
|
||||
// Flags for ItemAdd()
|
||||
// FIXME-NAV: _Focusable is _ALMOST_ what you would expect to be called '_TabStop' but because SetKeyboardFocusHere() works on items with no TabStop we distinguish Focusable from TabStop.
|
||||
enum ImGuiItemAddFlags_
|
||||
{
|
||||
ImGuiItemAddFlags_None = 0,
|
||||
ImGuiItemAddFlags_Focusable = 1 << 0 // FIXME-NAV: In current/legacy scheme, Focusable+TabStop support are opt-in by widgets. We will transition it toward being opt-out, so this flag is expected to eventually disappear.
|
||||
};
|
||||
|
||||
// Storage for LastItem data
|
||||
enum ImGuiItemStatusFlags_
|
||||
{
|
||||
ImGuiItemStatusFlags_None = 0,
|
||||
ImGuiItemStatusFlags_HoveredRect = 1 << 0,
|
||||
ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // LastItemDisplayRect is valid
|
||||
ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test)
|
||||
ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // window->DC.LastItemDisplayRect is valid
|
||||
ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
|
||||
ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues.
|
||||
ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues.
|
||||
ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state.
|
||||
ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.
|
||||
ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.
|
||||
ImGuiItemStatusFlags_HoveredWindow = 1 << 7 // Override the HoveredWindow test to allow cross-window hover testing.
|
||||
ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing.
|
||||
ImGuiItemStatusFlags_FocusedByCode = 1 << 8, // Set when the Focusable item just got focused from code.
|
||||
ImGuiItemStatusFlags_FocusedByTabbing = 1 << 9, // Set when the Focusable item just got focused by Tabbing.
|
||||
ImGuiItemStatusFlags_Focused = ImGuiItemStatusFlags_FocusedByCode | ImGuiItemStatusFlags_FocusedByTabbing
|
||||
|
||||
#ifdef IMGUI_ENABLE_TEST_ENGINE
|
||||
, // [imgui_tests only]
|
||||
ImGuiItemStatusFlags_Openable = 1 << 10, //
|
||||
ImGuiItemStatusFlags_Opened = 1 << 11, //
|
||||
ImGuiItemStatusFlags_Checkable = 1 << 12, //
|
||||
ImGuiItemStatusFlags_Checked = 1 << 13 //
|
||||
ImGuiItemStatusFlags_Openable = 1 << 20, //
|
||||
ImGuiItemStatusFlags_Opened = 1 << 21, //
|
||||
ImGuiItemStatusFlags_Checkable = 1 << 22, //
|
||||
ImGuiItemStatusFlags_Checked = 1 << 23 //
|
||||
#endif
|
||||
};
|
||||
|
||||
// Extend ImGuiInputTextFlags_
|
||||
enum ImGuiInputTextFlagsPrivate_
|
||||
{
|
||||
// [Internal]
|
||||
ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline()
|
||||
ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data
|
||||
ImGuiInputTextFlags_MergedItem = 1 << 28 // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
|
||||
};
|
||||
|
||||
// Extend ImGuiButtonFlags_
|
||||
enum ImGuiButtonFlagsPrivate_
|
||||
{
|
||||
@ -855,6 +891,7 @@ enum ImGuiInputSource
|
||||
ImGuiInputSource_Keyboard,
|
||||
ImGuiInputSource_Gamepad,
|
||||
ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only
|
||||
ImGuiInputSource_Clipboard, // Currently only used by InputText()
|
||||
ImGuiInputSource_COUNT
|
||||
};
|
||||
|
||||
@ -1004,7 +1041,7 @@ struct IMGUI_API ImGuiInputTextState
|
||||
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
|
||||
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
|
||||
bool Edited; // edited this frame
|
||||
ImGuiInputTextFlags UserFlags; // Temporarily set while we call user's callback
|
||||
ImGuiInputTextFlags Flags; // copy of InputText() flags
|
||||
ImGuiInputTextCallback UserCallback; // "
|
||||
void* UserCallbackData; // "
|
||||
|
||||
@ -1037,18 +1074,18 @@ struct ImGuiPopupData
|
||||
ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; }
|
||||
};
|
||||
|
||||
struct ImGuiNavMoveResult
|
||||
struct ImGuiNavItemData
|
||||
{
|
||||
ImGuiWindow* Window; // Best candidate window
|
||||
ImGuiID ID; // Best candidate ID
|
||||
ImGuiID FocusScopeId; // Best candidate focus scope ID
|
||||
float DistBox; // Best candidate box distance to current NavId
|
||||
float DistCenter; // Best candidate center distance to current NavId
|
||||
float DistAxial;
|
||||
ImRect RectRel; // Best candidate bounding box in window relative space
|
||||
ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)
|
||||
ImGuiID ID; // Init,Move // Best candidate item ID
|
||||
ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID
|
||||
ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space
|
||||
float DistBox; // Move // Best candidate box distance to current NavId
|
||||
float DistCenter; // Move // Best candidate center distance to current NavId
|
||||
float DistAxial; // Move // Best candidate axial distance to current NavId
|
||||
|
||||
ImGuiNavMoveResult() { Clear(); }
|
||||
void Clear() { Window = NULL; ID = FocusScopeId = 0; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); }
|
||||
ImGuiNavItemData() { Clear(); }
|
||||
void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; }
|
||||
};
|
||||
|
||||
enum ImGuiNextWindowDataFlags_
|
||||
@ -1353,15 +1390,22 @@ struct ImGuiViewportP : public ImGuiViewport
|
||||
ImVec2 LastRendererSize;
|
||||
ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!)
|
||||
ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height).
|
||||
ImVec2 CurrWorkOffsetMin; // Work Area: Offset being built/increased during current frame
|
||||
ImVec2 CurrWorkOffsetMax; // Work Area: Offset being built/decreased during current frame
|
||||
ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f.
|
||||
ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f.
|
||||
|
||||
ImGuiViewportP() { Idx = -1; LastFrameActive = DrawListsLastFrame[0] = DrawListsLastFrame[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); }
|
||||
~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); }
|
||||
ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
|
||||
ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }
|
||||
void UpdateWorkRect() { WorkPos = ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); WorkSize = ImVec2(ImMax(0.0f, Size.x - WorkOffsetMin.x + WorkOffsetMax.x), ImMax(0.0f, Size.y - WorkOffsetMin.y + WorkOffsetMax.y)); }
|
||||
void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; }
|
||||
ImGuiViewportP() { Idx = -1; LastFrameActive = DrawListsLastFrame[0] = DrawListsLastFrame[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); }
|
||||
~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); }
|
||||
void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; }
|
||||
|
||||
// Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
|
||||
ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); }
|
||||
ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); }
|
||||
void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields
|
||||
|
||||
// Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)
|
||||
ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
|
||||
ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }
|
||||
ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1496,11 +1540,12 @@ struct ImGuiContext
|
||||
|
||||
// Windows state
|
||||
ImVector<ImGuiWindow*> Windows; // Windows, sorted in display order, back to front
|
||||
ImVector<ImGuiWindow*> WindowsFocusOrder; // Windows, sorted in focus order, back to front. (FIXME: We could only store root windows here!)
|
||||
ImVector<ImGuiWindow*> WindowsFocusOrder; // Root windows, sorted in focus order, back to front.
|
||||
ImVector<ImGuiWindow*> WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child
|
||||
ImVector<ImGuiWindow*> CurrentWindowStack;
|
||||
ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow*
|
||||
int WindowsActiveCount; // Number of unique windows submitted by frame
|
||||
ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING)
|
||||
ImGuiWindow* CurrentWindow; // Window being drawn into
|
||||
ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs.
|
||||
ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
|
||||
@ -1511,6 +1556,7 @@ struct ImGuiContext
|
||||
float WheelingWindowTimer;
|
||||
|
||||
// Item/widgets state and tracking information
|
||||
ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back()
|
||||
ImGuiID HoveredId; // Hovered widget, filled during the frame
|
||||
ImGuiID HoveredIdPreviousFrame;
|
||||
bool HoveredIdAllowOverlap;
|
||||
@ -1600,9 +1646,9 @@ struct ImGuiContext
|
||||
ImGuiKeyModFlags NavMoveRequestKeyMods;
|
||||
ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request
|
||||
ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename?
|
||||
ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow
|
||||
ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)
|
||||
ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)
|
||||
ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow
|
||||
ImGuiNavItemData NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)
|
||||
ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)
|
||||
ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around.
|
||||
ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags.
|
||||
|
||||
@ -1621,7 +1667,7 @@ struct ImGuiContext
|
||||
int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index
|
||||
int TabFocusRequestNextCounterRegular; // Stored for next frame
|
||||
int TabFocusRequestNextCounterTabStop; // "
|
||||
bool TabFocusPressed; //
|
||||
bool TabFocusPressed; // Set in NewFrame() when user pressed Tab
|
||||
|
||||
// Render
|
||||
float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
|
||||
@ -1648,8 +1694,9 @@ struct ImGuiContext
|
||||
|
||||
// Table
|
||||
ImGuiTable* CurrentTable;
|
||||
int CurrentTableStackIdx;
|
||||
ImPool<ImGuiTable> Tables;
|
||||
ImVector<ImGuiPtrOrIndex> CurrentTableStack;
|
||||
ImVector<ImGuiTableTempData> TablesTempDataStack;
|
||||
ImVector<float> TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC)
|
||||
ImVector<ImDrawChannel> DrawChannelsTempMergeBuffer;
|
||||
|
||||
@ -1721,6 +1768,7 @@ struct ImGuiContext
|
||||
// Misc
|
||||
float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds.
|
||||
int FramerateSecPerFrameIdx;
|
||||
int FramerateSecPerFrameCount;
|
||||
float FramerateSecPerFrameAccum;
|
||||
int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags
|
||||
int WantCaptureKeyboardNextFrame;
|
||||
@ -1753,6 +1801,7 @@ struct ImGuiContext
|
||||
WheelingWindow = NULL;
|
||||
WheelingWindowTimer = 0.0f;
|
||||
|
||||
CurrentItemFlags = ImGuiItemFlags_None;
|
||||
HoveredId = HoveredIdPreviousFrame = 0;
|
||||
HoveredIdAllowOverlap = false;
|
||||
HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false;
|
||||
@ -1838,6 +1887,7 @@ struct ImGuiContext
|
||||
memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
|
||||
|
||||
CurrentTable = NULL;
|
||||
CurrentTableStackIdx = -1;
|
||||
CurrentTabBar = NULL;
|
||||
|
||||
LastValidMousePos = ImVec2(0.0f, 0.0f);
|
||||
@ -1875,7 +1925,7 @@ struct ImGuiContext
|
||||
DebugItemPickerBreakId = 0;
|
||||
|
||||
memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
|
||||
FramerateSecPerFrameIdx = 0;
|
||||
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
|
||||
FramerateSecPerFrameAccum = 0.0f;
|
||||
WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
|
||||
memset(TempBuffer, 0, sizeof(TempBuffer));
|
||||
@ -1913,8 +1963,8 @@ struct IMGUI_API ImGuiWindowTempData
|
||||
|
||||
// Keyboard/Gamepad navigation
|
||||
ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1)
|
||||
int NavLayerActiveMask; // Which layers have been written to (result from previous frame)
|
||||
int NavLayerActiveMaskNext; // Which layers have been written to (accumulator for current frame)
|
||||
short NavLayersActiveMask; // Which layers have been written to (result from previous frame)
|
||||
short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame)
|
||||
ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending
|
||||
bool NavHideHighlightOneFrame;
|
||||
bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f)
|
||||
@ -1936,7 +1986,6 @@ struct IMGUI_API ImGuiWindowTempData
|
||||
|
||||
// Local parameters stacks
|
||||
// We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.
|
||||
ImGuiItemFlags ItemFlags; // == g.ItemFlagsStack.back()
|
||||
float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window).
|
||||
float TextWrapPos; // Current text wrap pos.
|
||||
ImVector<float> ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth)
|
||||
@ -1987,8 +2036,9 @@ struct IMGUI_API ImGuiWindow
|
||||
bool HasCloseButton; // Set when the window has a close button (p_open != NULL)
|
||||
signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3)
|
||||
short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
|
||||
short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0.
|
||||
short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues.
|
||||
short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0.
|
||||
short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.
|
||||
short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused.
|
||||
ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)
|
||||
ImS8 AutoFitFramesX, AutoFitFramesY;
|
||||
ImS8 AutoFitChildAxises;
|
||||
@ -2107,6 +2157,7 @@ enum ImGuiTabBarFlagsPrivate_
|
||||
// Extend ImGuiTabItemFlags_
|
||||
enum ImGuiTabItemFlagsPrivate_
|
||||
{
|
||||
ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing,
|
||||
ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)
|
||||
ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button
|
||||
ImGuiTabItemFlags_Unsorted = 1 << 22, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window.
|
||||
@ -2155,7 +2206,7 @@ struct IMGUI_API ImGuiTabBar
|
||||
float ScrollingRectMinX;
|
||||
float ScrollingRectMaxX;
|
||||
ImGuiID ReorderRequestTabId;
|
||||
ImS8 ReorderRequestDir;
|
||||
ImS16 ReorderRequestOffset;
|
||||
ImS8 BeginCount;
|
||||
bool WantLayout;
|
||||
bool VisibleTabWasSubmitted;
|
||||
@ -2182,8 +2233,6 @@ struct IMGUI_API ImGuiTabBar
|
||||
// [SECTION] Table support
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef IMGUI_HAS_TABLE
|
||||
|
||||
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
|
||||
#define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64.
|
||||
#define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableSetupDrawChannels()
|
||||
@ -2260,12 +2309,13 @@ struct ImGuiTableCellData
|
||||
ImGuiTableColumnIdx Column; // Column number
|
||||
};
|
||||
|
||||
// FIXME-TABLE: transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData
|
||||
// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData
|
||||
struct ImGuiTable
|
||||
{
|
||||
ImGuiID ID;
|
||||
ImGuiTableFlags Flags;
|
||||
void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[]
|
||||
ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[]
|
||||
ImSpan<ImGuiTableColumn> Columns; // Point within RawData[]
|
||||
ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
|
||||
ImSpan<ImGuiTableCellData> RowCellData; // Point within RawData[]. Store cells background requests for current row.
|
||||
@ -2317,22 +2367,11 @@ struct ImGuiTable
|
||||
ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped
|
||||
ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect.
|
||||
ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
|
||||
ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable()
|
||||
ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()
|
||||
ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground()
|
||||
ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable()
|
||||
ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable()
|
||||
ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
|
||||
ImVec2 UserOuterSize; // outer_size.x passed to BeginTable()
|
||||
ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable()
|
||||
float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable()
|
||||
int HostBackupItemWidthStackSize;// Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable()
|
||||
ImGuiWindow* OuterWindow; // Parent window for the table
|
||||
ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window)
|
||||
ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names
|
||||
ImDrawListSplitter DrawSplitter; // We carry our own ImDrawList splitter to allow recursion (FIXME: could be stored outside, worst case we need 1 splitter per recursing table)
|
||||
ImGuiTableColumnSortSpecs SortSpecsSingle;
|
||||
ImVector<ImGuiTableColumnSortSpecs> SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would work be good.
|
||||
ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly
|
||||
ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs()
|
||||
ImGuiTableColumnIdx SortSpecsCount;
|
||||
ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount)
|
||||
@ -2379,6 +2418,32 @@ struct ImGuiTable
|
||||
IMGUI_API ~ImGuiTable() { IM_FREE(RawData); }
|
||||
};
|
||||
|
||||
// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).
|
||||
// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.
|
||||
// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.
|
||||
// FIXME-TABLE: more transient data could be stored here: DrawSplitter, incoming RowData?
|
||||
struct ImGuiTableTempData
|
||||
{
|
||||
int TableIndex; // Index in g.Tables.Buf[] pool
|
||||
float LastTimeActive; // Last timestamp this structure was used
|
||||
|
||||
ImVec2 UserOuterSize; // outer_size.x passed to BeginTable()
|
||||
ImDrawListSplitter DrawSplitter;
|
||||
ImGuiTableColumnSortSpecs SortSpecsSingle;
|
||||
ImVector<ImGuiTableColumnSortSpecs> SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good.
|
||||
|
||||
ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable()
|
||||
ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()
|
||||
ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable()
|
||||
ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable()
|
||||
ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
|
||||
ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable()
|
||||
float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable()
|
||||
int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable()
|
||||
|
||||
IMGUI_API ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; }
|
||||
};
|
||||
|
||||
// sizeof() ~ 12
|
||||
struct ImGuiTableColumnSettings
|
||||
{
|
||||
@ -2417,8 +2482,6 @@ struct ImGuiTableSettings
|
||||
ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); }
|
||||
};
|
||||
|
||||
#endif // #ifdef IMGUI_HAS_TABLE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImGui internal API
|
||||
// No guarantee of forward compatibility here!
|
||||
@ -2478,6 +2541,7 @@ namespace ImGui
|
||||
IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos);
|
||||
IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale);
|
||||
IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport);
|
||||
IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport);
|
||||
const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport);
|
||||
|
||||
// Settings
|
||||
@ -2502,7 +2566,7 @@ namespace ImGui
|
||||
inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; }
|
||||
inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; }
|
||||
inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; }
|
||||
inline ImGuiItemFlags GetItemsFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.ItemFlags; }
|
||||
inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.CurrentItemFlags; }
|
||||
IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window);
|
||||
IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window);
|
||||
IMGUI_API void ClearActiveID();
|
||||
@ -2516,12 +2580,11 @@ namespace ImGui
|
||||
// Basic Helpers for widget code
|
||||
IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
|
||||
IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f);
|
||||
IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL);
|
||||
IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemAddFlags flags = 0);
|
||||
IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id);
|
||||
IMGUI_API void ItemFocusable(ImGuiWindow* window, ImGuiID id);
|
||||
IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged);
|
||||
IMGUI_API void SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);
|
||||
IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested
|
||||
IMGUI_API void FocusableItemUnregister(ImGuiWindow* window);
|
||||
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h);
|
||||
IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
|
||||
IMGUI_API void PushMultiItemsWidths(int components, float width_full);
|
||||
@ -2531,6 +2594,15 @@ namespace ImGui
|
||||
IMGUI_API ImVec2 GetContentRegionMaxAbs();
|
||||
IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
// If you have old/custom copy-and-pasted widgets that used FocusableItemRegister():
|
||||
// (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)'
|
||||
// (New) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0'
|
||||
// Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText()
|
||||
inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Focusable flag to ItemAdd()
|
||||
inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
|
||||
#endif
|
||||
|
||||
// Logging/Capture
|
||||
IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
|
||||
IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer
|
||||
@ -2548,6 +2620,7 @@ namespace ImGui
|
||||
IMGUI_API ImGuiWindow* GetTopMostPopupModal();
|
||||
IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window);
|
||||
IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy);
|
||||
IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags);
|
||||
|
||||
// Gamepad/Keyboard Navigation
|
||||
IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit);
|
||||
@ -2559,7 +2632,7 @@ namespace ImGui
|
||||
IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f);
|
||||
IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
|
||||
IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again.
|
||||
IMGUI_API void SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
|
||||
IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
|
||||
|
||||
// Focus Scope (WIP)
|
||||
// This is generally used to identify a selection set (multiple of which may be in the same window), as selection
|
||||
@ -2596,9 +2669,10 @@ namespace ImGui
|
||||
IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos);
|
||||
IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node);
|
||||
IMGUI_API void DockNodeEndAmendTabBar();
|
||||
inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; }
|
||||
inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; }
|
||||
inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; }
|
||||
inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; }
|
||||
inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; }
|
||||
inline ImGuiID DockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImHashStr("#COLLAPSE", 0, node->ID); }
|
||||
inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; }
|
||||
IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window);
|
||||
IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open);
|
||||
IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window);
|
||||
@ -2648,7 +2722,6 @@ namespace ImGui
|
||||
|
||||
// Tables: Candidates for public API
|
||||
IMGUI_API void TableOpenContextMenu(int column_n = -1);
|
||||
IMGUI_API void TableSetColumnEnabled(int column_n, bool enabled);
|
||||
IMGUI_API void TableSetColumnWidth(int column_n, float width);
|
||||
IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
|
||||
IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
|
||||
@ -2686,6 +2759,7 @@ namespace ImGui
|
||||
IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table);
|
||||
IMGUI_API void TableRemove(ImGuiTable* table);
|
||||
IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table);
|
||||
IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table);
|
||||
IMGUI_API void TableGcCompactSettings();
|
||||
|
||||
// Tables: Settings
|
||||
@ -2704,7 +2778,8 @@ namespace ImGui
|
||||
IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window);
|
||||
IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id);
|
||||
IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
|
||||
IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir);
|
||||
IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset);
|
||||
IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos);
|
||||
IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar);
|
||||
IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window);
|
||||
IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button);
|
||||
@ -2752,7 +2827,8 @@ namespace ImGui
|
||||
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col);
|
||||
IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);
|
||||
IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);
|
||||
IMGUI_API ImGuiID GetWindowResizeID(ImGuiWindow* window, int n); // 0..3: corners, 4..7: borders
|
||||
IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners
|
||||
IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir);
|
||||
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags);
|
||||
IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);
|
||||
IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value);
|
||||
@ -2864,8 +2940,8 @@ extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt,
|
||||
#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box
|
||||
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional)
|
||||
#define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
|
||||
#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA));
|
||||
#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2));
|
||||
#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA));
|
||||
#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2));
|
||||
#else
|
||||
#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) do { } while (0)
|
||||
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) do { } while (0)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.82
|
||||
// dear imgui, v1.83
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
@ -8,6 +8,7 @@ Index of this file:
|
||||
// [SECTION] Commentary
|
||||
// [SECTION] Header mess
|
||||
// [SECTION] Tables: Main code
|
||||
// [SECTION] Tables: Simple accessors
|
||||
// [SECTION] Tables: Row changes
|
||||
// [SECTION] Tables: Columns changes
|
||||
// [SECTION] Tables: Columns width management
|
||||
@ -73,7 +74,7 @@ Index of this file:
|
||||
// (Read carefully because this is subtle but it does make sense!)
|
||||
//-----------------------------------------------------------------------------
|
||||
// About 'outer_size':
|
||||
// Its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags.
|
||||
// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags.
|
||||
// Default value is ImVec2(0.0f, 0.0f).
|
||||
// X
|
||||
// - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge.
|
||||
@ -90,7 +91,7 @@ Index of this file:
|
||||
// Outer size is also affected by the NoHostExtendX/NoHostExtendY flags.
|
||||
// Important to that note how the two flags have slightly different behaviors!
|
||||
// - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
|
||||
// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.
|
||||
// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible.
|
||||
// In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height.
|
||||
// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not easily noticeable)
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -132,7 +133,7 @@ Index of this file:
|
||||
// - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
|
||||
// - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place!
|
||||
// that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in.
|
||||
// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the maximum contents width.
|
||||
// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents.
|
||||
// - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weight/widths.
|
||||
//-----------------------------------------------------------------------------
|
||||
// About using column width:
|
||||
@ -140,9 +141,9 @@ Index of this file:
|
||||
// - you may use GetContentRegionAvail().x to query the width available in a given column.
|
||||
// - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width.
|
||||
// If the column is not resizable and has no width specified with TableSetupColumn():
|
||||
// - its width will be automatic and be the set to the max of items submitted.
|
||||
// - its width will be automatic and be set to the max of items submitted.
|
||||
// - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN).
|
||||
// - but if the column has one or more item of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN).
|
||||
// - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN).
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
@ -161,7 +162,7 @@ Index of this file:
|
||||
// - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing
|
||||
// width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know
|
||||
// it is not going to contribute to row height.
|
||||
// In many situations, you may skip submitting contents for every columns but one (e.g. the first one).
|
||||
// In many situations, you may skip submitting contents for every column but one (e.g. the first one).
|
||||
// - Case A: column is not hidden by user, and at least partially in sight (most common case).
|
||||
// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output.
|
||||
// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.).
|
||||
@ -172,7 +173,7 @@ Index of this file:
|
||||
// ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way.
|
||||
// ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway).
|
||||
//
|
||||
// - We need distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.
|
||||
// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.
|
||||
// However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer.
|
||||
//-----------------------------------------------------------------------------
|
||||
// About clipping/culling of whole Tables:
|
||||
@ -209,6 +210,8 @@ Index of this file:
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
|
||||
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
|
||||
#endif
|
||||
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
|
||||
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
@ -235,6 +238,19 @@ Index of this file:
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Tables: Main code
|
||||
//-----------------------------------------------------------------------------
|
||||
// - TableFixFlags() [Internal]
|
||||
// - TableFindByID() [Internal]
|
||||
// - BeginTable()
|
||||
// - BeginTableEx() [Internal]
|
||||
// - TableBeginInitMemory() [Internal]
|
||||
// - TableBeginApplyRequests() [Internal]
|
||||
// - TableSetupColumnFlags() [Internal]
|
||||
// - TableUpdateLayout() [Internal]
|
||||
// - TableUpdateBorders() [Internal]
|
||||
// - EndTable()
|
||||
// - TableSetupColumn()
|
||||
// - TableSetupScrollFreeze()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Configuration
|
||||
static const int TABLE_DRAW_CHANNEL_BG0 = 0;
|
||||
@ -322,6 +338,16 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
if (instance_no > 0)
|
||||
IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID");
|
||||
|
||||
// Acquire temporary buffers
|
||||
const int table_idx = g.Tables.GetIndex(table);
|
||||
g.CurrentTableStackIdx++;
|
||||
if (g.CurrentTableStackIdx + 1 > g.TablesTempDataStack.Size)
|
||||
g.TablesTempDataStack.resize(g.CurrentTableStackIdx + 1, ImGuiTableTempData());
|
||||
ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempDataStack[g.CurrentTableStackIdx];
|
||||
temp_data->TableIndex = table_idx;
|
||||
table->DrawSplitter = &table->TempData->DrawSplitter;
|
||||
table->DrawSplitter->Clear();
|
||||
|
||||
// Fix flags
|
||||
table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0;
|
||||
flags = TableFixFlags(flags, outer_window);
|
||||
@ -335,7 +361,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->ColumnsCount = columns_count;
|
||||
table->IsLayoutLocked = false;
|
||||
table->InnerWidth = inner_width;
|
||||
table->UserOuterSize = outer_size;
|
||||
temp_data->UserOuterSize = outer_size;
|
||||
|
||||
// When not using a child window, WorkRect.Max will grow as we append contents.
|
||||
if (use_child_window)
|
||||
@ -384,14 +410,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->HostIndentX = inner_window->DC.Indent.x;
|
||||
table->HostClipRect = inner_window->ClipRect;
|
||||
table->HostSkipItems = inner_window->SkipItems;
|
||||
table->HostBackupWorkRect = inner_window->WorkRect;
|
||||
table->HostBackupParentWorkRect = inner_window->ParentWorkRect;
|
||||
table->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;
|
||||
table->HostBackupPrevLineSize = inner_window->DC.PrevLineSize;
|
||||
table->HostBackupCurrLineSize = inner_window->DC.CurrLineSize;
|
||||
table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos;
|
||||
table->HostBackupItemWidth = outer_window->DC.ItemWidth;
|
||||
table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
|
||||
temp_data->HostBackupWorkRect = inner_window->WorkRect;
|
||||
temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect;
|
||||
temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;
|
||||
temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize;
|
||||
temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize;
|
||||
temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos;
|
||||
temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth;
|
||||
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
|
||||
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
|
||||
|
||||
// Padding and Spacing
|
||||
@ -434,8 +460,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight);
|
||||
|
||||
// Make table current
|
||||
const int table_idx = g.Tables.GetIndex(table);
|
||||
g.CurrentTableStack.push_back(ImGuiPtrOrIndex(table_idx));
|
||||
g.CurrentTable = table;
|
||||
outer_window->DC.CurrentTableIdx = table_idx;
|
||||
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
|
||||
@ -448,13 +472,18 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
if (table_idx >= g.TablesLastTimeActive.Size)
|
||||
g.TablesLastTimeActive.resize(table_idx + 1, -1.0f);
|
||||
g.TablesLastTimeActive[table_idx] = (float)g.Time;
|
||||
temp_data->LastTimeActive = (float)g.Time;
|
||||
table->MemoryCompacted = false;
|
||||
|
||||
// Setup memory buffer (clear data if columns count changed)
|
||||
const int stored_size = table->Columns.size();
|
||||
if (stored_size != 0 && stored_size != columns_count)
|
||||
ImGuiTableColumn* old_columns_to_preserve = NULL;
|
||||
void* old_columns_raw_data = NULL;
|
||||
const int old_columns_count = table->Columns.size();
|
||||
if (old_columns_count != 0 && old_columns_count != columns_count)
|
||||
{
|
||||
IM_FREE(table->RawData);
|
||||
// Attempt to preserve width on column count change (#4046)
|
||||
old_columns_to_preserve = table->Columns.Data;
|
||||
old_columns_raw_data = table->RawData;
|
||||
table->RawData = NULL;
|
||||
}
|
||||
if (table->RawData == NULL)
|
||||
@ -477,14 +506,24 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
for (int n = 0; n < columns_count; n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[n];
|
||||
float width_auto = column->WidthAuto;
|
||||
*column = ImGuiTableColumn();
|
||||
column->WidthAuto = width_auto;
|
||||
column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker
|
||||
if (old_columns_to_preserve && n < old_columns_count)
|
||||
{
|
||||
// FIXME: We don't attempt to preserve column order in this path.
|
||||
*column = old_columns_to_preserve[n];
|
||||
}
|
||||
else
|
||||
{
|
||||
float width_auto = column->WidthAuto;
|
||||
*column = ImGuiTableColumn();
|
||||
column->WidthAuto = width_auto;
|
||||
column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker
|
||||
column->IsEnabled = column->IsEnabledNextFrame = true;
|
||||
}
|
||||
column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n;
|
||||
column->IsEnabled = column->IsEnabledNextFrame = true;
|
||||
}
|
||||
}
|
||||
if (old_columns_raw_data)
|
||||
IM_FREE(old_columns_raw_data);
|
||||
|
||||
// Load settings
|
||||
if (table->IsSettingsRequestLoad)
|
||||
@ -1082,7 +1121,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// Initial state
|
||||
ImGuiWindow* inner_window = table->InnerWindow;
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
else
|
||||
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
|
||||
}
|
||||
@ -1170,6 +1209,7 @@ void ImGui::EndTable()
|
||||
const ImGuiTableFlags flags = table->Flags;
|
||||
ImGuiWindow* inner_window = table->InnerWindow;
|
||||
ImGuiWindow* outer_window = table->OuterWindow;
|
||||
ImGuiTableTempData* temp_data = table->TempData;
|
||||
IM_ASSERT(inner_window == g.CurrentWindow);
|
||||
IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow);
|
||||
|
||||
@ -1182,9 +1222,9 @@ void ImGui::EndTable()
|
||||
TableOpenContextMenu((int)table->HoveredColumnBody);
|
||||
|
||||
// Finalize table height
|
||||
inner_window->DC.PrevLineSize = table->HostBackupPrevLineSize;
|
||||
inner_window->DC.CurrLineSize = table->HostBackupCurrLineSize;
|
||||
inner_window->DC.CursorMaxPos = table->HostBackupCursorMaxPos;
|
||||
inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize;
|
||||
inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize;
|
||||
inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos;
|
||||
const float inner_content_max_y = table->RowPosY2;
|
||||
IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y);
|
||||
if (inner_window != outer_window)
|
||||
@ -1231,10 +1271,11 @@ void ImGui::EndTable()
|
||||
#endif
|
||||
|
||||
// Flatten channels and merge draw calls
|
||||
table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0);
|
||||
ImDrawListSplitter* splitter = table->DrawSplitter;
|
||||
splitter->SetCurrentChannel(inner_window->DrawList, 0);
|
||||
if ((table->Flags & ImGuiTableFlags_NoClip) == 0)
|
||||
TableMergeDrawChannels(table);
|
||||
table->DrawSplitter.Merge(inner_window->DrawList);
|
||||
splitter->Merge(inner_window->DrawList);
|
||||
|
||||
// Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable()
|
||||
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
|
||||
@ -1276,18 +1317,18 @@ void ImGui::EndTable()
|
||||
|
||||
// Pop from id stack
|
||||
IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!");
|
||||
IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= table->HostBackupItemWidthStackSize, "Too many PopItemWidth!");
|
||||
IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!");
|
||||
PopID();
|
||||
|
||||
// Restore window data that we modified
|
||||
const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos;
|
||||
inner_window->WorkRect = table->HostBackupWorkRect;
|
||||
inner_window->ParentWorkRect = table->HostBackupParentWorkRect;
|
||||
inner_window->WorkRect = temp_data->HostBackupWorkRect;
|
||||
inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect;
|
||||
inner_window->SkipItems = table->HostSkipItems;
|
||||
outer_window->DC.CursorPos = table->OuterRect.Min;
|
||||
outer_window->DC.ItemWidth = table->HostBackupItemWidth;
|
||||
outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize;
|
||||
outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset;
|
||||
outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth;
|
||||
outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize;
|
||||
outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset;
|
||||
|
||||
// Layout in outer window
|
||||
// (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding
|
||||
@ -1310,20 +1351,20 @@ void ImGui::EndTable()
|
||||
IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth);
|
||||
}
|
||||
else if (table->UserOuterSize.x <= 0.0f)
|
||||
else if (temp_data->UserOuterSize.x <= 0.0f)
|
||||
{
|
||||
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f;
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - table->UserOuterSize.x);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth));
|
||||
}
|
||||
else
|
||||
{
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x);
|
||||
}
|
||||
if (table->UserOuterSize.y <= 0.0f)
|
||||
if (temp_data->UserOuterSize.y <= 0.0f)
|
||||
{
|
||||
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f;
|
||||
outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - table->UserOuterSize.y);
|
||||
outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);
|
||||
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y));
|
||||
}
|
||||
else
|
||||
@ -1339,8 +1380,15 @@ void ImGui::EndTable()
|
||||
|
||||
// Clear or restore current table, if any
|
||||
IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table);
|
||||
g.CurrentTableStack.pop_back();
|
||||
g.CurrentTable = g.CurrentTableStack.Size ? g.Tables.GetByIndex(g.CurrentTableStack.back().Index) : NULL;
|
||||
IM_ASSERT(g.CurrentTableStackIdx >= 0);
|
||||
g.CurrentTableStackIdx--;
|
||||
temp_data = g.CurrentTableStackIdx >= 0 ? &g.TablesTempDataStack[g.CurrentTableStackIdx] : NULL;
|
||||
g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
|
||||
if (g.CurrentTable)
|
||||
{
|
||||
g.CurrentTable->TempData = temp_data;
|
||||
g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter;
|
||||
}
|
||||
outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1;
|
||||
}
|
||||
|
||||
@ -1430,6 +1478,20 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
|
||||
table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Tables: Simple accessors
|
||||
//-----------------------------------------------------------------------------
|
||||
// - TableGetColumnCount()
|
||||
// - TableGetColumnName()
|
||||
// - TableGetColumnName() [Internal]
|
||||
// - TableSetColumnEnabled() [Internal]
|
||||
// - TableGetColumnFlags()
|
||||
// - TableGetCellBgRect() [Internal]
|
||||
// - TableGetColumnResizeID() [Internal]
|
||||
// - TableGetHoveredColumn() [Internal]
|
||||
// - TableSetBgColor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int ImGui::TableGetColumnCount()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@ -1458,6 +1520,9 @@ const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n)
|
||||
return &table->ColumnsNames.Buf[column->NameOffset];
|
||||
}
|
||||
|
||||
// Request enabling/disabling a column (often perceived as "showing/hiding" from users point of view)
|
||||
// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)
|
||||
// Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable()
|
||||
// For the getter you can use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled)
|
||||
void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
|
||||
{
|
||||
@ -1696,7 +1761,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
// always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
|
||||
if ((table->Flags & ImGuiTableFlags_NoClip) == 0)
|
||||
window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4();
|
||||
table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0);
|
||||
}
|
||||
|
||||
// Draw row background
|
||||
@ -1768,7 +1833,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
}
|
||||
|
||||
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
|
||||
@ -1883,14 +1948,14 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
{
|
||||
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
|
||||
table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
//IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME-TABLE: Could avoid this if draw channel is dummy channel?
|
||||
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
|
||||
table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
}
|
||||
|
||||
// Logging
|
||||
@ -2138,7 +2203,7 @@ void ImGui::TablePushBackgroundChannel()
|
||||
// Optimization: avoid SetCurrentChannel() + PushClipRect()
|
||||
table->HostBackupInnerClipRect = window->ClipRect;
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd);
|
||||
table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent);
|
||||
}
|
||||
|
||||
void ImGui::TablePopBackgroundChannel()
|
||||
@ -2150,7 +2215,7 @@ void ImGui::TablePopBackgroundChannel()
|
||||
|
||||
// Optimization: avoid PopClipRect() + SetCurrentChannel()
|
||||
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
|
||||
table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
}
|
||||
|
||||
// Allocate draw channels. Called by TableUpdateLayout()
|
||||
@ -2176,7 +2241,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table)
|
||||
const int channels_for_bg = 1 + 1 * freeze_row_multiplier;
|
||||
const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0;
|
||||
const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
|
||||
table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total);
|
||||
table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total);
|
||||
table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1);
|
||||
table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN;
|
||||
table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN);
|
||||
@ -2240,7 +2305,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table)
|
||||
void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImDrawListSplitter* splitter = &table->DrawSplitter;
|
||||
ImDrawListSplitter* splitter = table->DrawSplitter;
|
||||
const bool has_freeze_v = (table->FreezeRowsCount > 0);
|
||||
const bool has_freeze_h = (table->FreezeColumnsCount > 0);
|
||||
IM_ASSERT(splitter->_Current == 0);
|
||||
@ -2251,10 +2316,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
||||
ImRect ClipRect;
|
||||
int ChannelsCount;
|
||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> ChannelsMask;
|
||||
|
||||
MergeGroup() { ChannelsCount = 0; }
|
||||
};
|
||||
int merge_group_mask = 0x00;
|
||||
MergeGroup merge_groups[4];
|
||||
memset(merge_groups, 0, sizeof(merge_groups));
|
||||
|
||||
// 1. Scan channels and take note of those which can be merged
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
@ -2332,7 +2398,6 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
||||
g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized
|
||||
ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
|
||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> remaining_mask; // We need 132-bit of storage
|
||||
remaining_mask.ClearAllBits();
|
||||
remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count);
|
||||
remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen);
|
||||
IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN);
|
||||
@ -2411,7 +2476,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
return;
|
||||
|
||||
ImDrawList* inner_drawlist = inner_window->DrawList;
|
||||
table->DrawSplitter.SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0);
|
||||
table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0);
|
||||
inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false);
|
||||
|
||||
// Draw inner border and resizing feedback
|
||||
@ -2671,20 +2736,22 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
||||
TableSortSpecsSanitize(table);
|
||||
|
||||
// Write output
|
||||
table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount);
|
||||
ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if (column->SortOrder == -1)
|
||||
continue;
|
||||
IM_ASSERT(column->SortOrder < table->SortSpecsCount);
|
||||
ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder];
|
||||
sort_spec->ColumnUserID = column->UserID;
|
||||
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
|
||||
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
|
||||
sort_spec->SortDirection = column->SortDirection;
|
||||
}
|
||||
ImGuiTableTempData* temp_data = table->TempData;
|
||||
temp_data->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount);
|
||||
ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &temp_data->SortSpecsSingle : temp_data->SortSpecsMulti.Data;
|
||||
if (sort_specs != NULL)
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if (column->SortOrder == -1)
|
||||
continue;
|
||||
IM_ASSERT(column->SortOrder < table->SortSpecsCount);
|
||||
ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder];
|
||||
sort_spec->ColumnUserID = column->UserID;
|
||||
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
|
||||
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
|
||||
sort_spec->SortDirection = column->SortDirection;
|
||||
}
|
||||
table->SortSpecs.Specs = sort_specs;
|
||||
table->SortSpecs.SpecsCount = table->SortSpecsCount;
|
||||
table->SortSpecs.SpecsDirty = true; // Mark as dirty for user
|
||||
@ -3313,6 +3380,9 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle
|
||||
for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++)
|
||||
{
|
||||
// "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
|
||||
bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1);
|
||||
if (!save_column)
|
||||
continue;
|
||||
buf->appendf("Column %-2d", column_n);
|
||||
if (column->UserID != 0) buf->appendf(" UserID=%08X", column->UserID);
|
||||
if (save_size && column->IsStretch) buf->appendf(" Weight=%.4f", column->WidthOrWeight);
|
||||
@ -3366,8 +3436,6 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)
|
||||
//IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID);
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(table->MemoryCompacted == false);
|
||||
table->DrawSplitter.ClearFreeMemory();
|
||||
table->SortSpecsMulti.clear();
|
||||
table->SortSpecs.Specs = NULL;
|
||||
table->IsSortSpecsDirty = true;
|
||||
table->ColumnsNames.clear();
|
||||
@ -3377,6 +3445,13 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)
|
||||
g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f;
|
||||
}
|
||||
|
||||
void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data)
|
||||
{
|
||||
temp_data->DrawSplitter.ClearFreeMemory();
|
||||
temp_data->SortSpecsMulti.clear();
|
||||
temp_data->LastTimeActive = -1.0f;
|
||||
}
|
||||
|
||||
// Compact and remove unused settings data (currently only used by TestEngine)
|
||||
void ImGui::TableGcCompactSettings()
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.82
|
||||
// dear imgui, v1.83
|
||||
// (widgets code)
|
||||
|
||||
/*
|
||||
@ -59,6 +59,8 @@ Index of this file:
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
|
||||
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
|
||||
#endif
|
||||
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
|
||||
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
@ -122,7 +124,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// For InputTextEx()
|
||||
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data);
|
||||
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source);
|
||||
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
|
||||
static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
|
||||
|
||||
@ -350,17 +352,20 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const float w = CalcItemWidth();
|
||||
|
||||
const char* value_text_begin = &g.TempBuffer[0];
|
||||
const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
|
||||
const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2));
|
||||
const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y * 2) + label_size);
|
||||
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2));
|
||||
const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2));
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ItemAdd(total_bb, 0))
|
||||
return;
|
||||
|
||||
// Render
|
||||
const char* value_text_begin = &g.TempBuffer[0];
|
||||
const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
|
||||
RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f, 0.5f));
|
||||
RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f));
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
|
||||
}
|
||||
@ -683,7 +688,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
|
||||
if (!ItemAdd(bb, id))
|
||||
return false;
|
||||
|
||||
if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
|
||||
if (g.CurrentItemFlags & ImGuiItemFlags_ButtonRepeat)
|
||||
flags |= ImGuiButtonFlags_Repeat;
|
||||
bool hovered, held;
|
||||
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
|
||||
@ -759,7 +764,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu
|
||||
if (!ItemAdd(bb, id))
|
||||
return false;
|
||||
|
||||
if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
|
||||
if (g.CurrentItemFlags & ImGuiItemFlags_ButtonRepeat)
|
||||
flags |= ImGuiButtonFlags_Repeat;
|
||||
|
||||
bool hovered, held;
|
||||
@ -833,12 +838,11 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no
|
||||
|
||||
// Render
|
||||
//bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed);
|
||||
ImVec2 off = dock_node ? ImVec2(IM_FLOOR(-g.Style.ItemInnerSpacing.x * 0.5f) + 0.5f, 0.0f) : ImVec2(0.0f, 0.0f);
|
||||
ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
|
||||
ImU32 text_col = GetColorU32(ImGuiCol_Text);
|
||||
ImVec2 center = bb.GetCenter();
|
||||
if (hovered || held)
|
||||
window->DrawList->AddCircleFilled(center + off + ImVec2(0,-0.5f), g.FontSize * 0.5f + 1.0f, bg_col, 12);
|
||||
window->DrawList->AddCircleFilled(center + ImVec2(0,-0.5f), g.FontSize * 0.5f + 1.0f, bg_col, 12);
|
||||
|
||||
if (dock_node)
|
||||
RenderArrowDockMenu(window->DrawList, bb.Min + g.Style.FramePadding, g.FontSize, text_col);
|
||||
@ -1085,7 +1089,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ItemAdd(total_bb, id))
|
||||
{
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1101,7 +1105,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
||||
RenderNavHighlight(total_bb, id);
|
||||
RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
|
||||
ImU32 check_col = GetColorU32(ImGuiCol_CheckMark);
|
||||
bool mixed_value = (window->DC.ItemFlags & ImGuiItemFlags_MixedValue) != 0;
|
||||
bool mixed_value = (g.CurrentItemFlags & ImGuiItemFlags_MixedValue) != 0;
|
||||
if (mixed_value)
|
||||
{
|
||||
// Undocumented tristate/mixed/indeterminate checkbox (#2644)
|
||||
@ -1121,7 +1125,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(label_pos, label);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return pressed;
|
||||
}
|
||||
|
||||
@ -1133,11 +1137,11 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value)
|
||||
bool pressed;
|
||||
if (!all_on && any_on)
|
||||
{
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
ImGuiItemFlags backup_item_flags = window->DC.ItemFlags;
|
||||
window->DC.ItemFlags |= ImGuiItemFlags_MixedValue;
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
|
||||
g.CurrentItemFlags |= ImGuiItemFlags_MixedValue;
|
||||
pressed = Checkbox(label, &all_on);
|
||||
window->DC.ItemFlags = backup_item_flags;
|
||||
g.CurrentItemFlags = backup_item_flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1223,7 +1227,7 @@ bool ImGui::RadioButton(const char* label, bool active)
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(label_pos, label);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
|
||||
return pressed;
|
||||
}
|
||||
|
||||
@ -1435,10 +1439,10 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
|
||||
const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
|
||||
window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
|
||||
const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
|
||||
g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
|
||||
bool item_add = ItemAdd(bb, id);
|
||||
window->DC.ItemFlags = item_flags_backup;
|
||||
g.CurrentItemFlags = item_flags_backup;
|
||||
if (!item_add)
|
||||
return false;
|
||||
|
||||
@ -1580,7 +1584,9 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
|
||||
|
||||
bool hovered, held;
|
||||
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
|
||||
bool popup_open = IsPopupOpen(id, ImGuiPopupFlags_None);
|
||||
|
||||
const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id);
|
||||
bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None);
|
||||
|
||||
const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
|
||||
const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size);
|
||||
@ -1610,7 +1616,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
|
||||
{
|
||||
if (window->DC.NavLayerCurrent == 0)
|
||||
window->NavLastIds[0] = id;
|
||||
OpenPopupEx(id, ImGuiPopupFlags_None);
|
||||
OpenPopupEx(popup_id, ImGuiPopupFlags_None);
|
||||
popup_open = true;
|
||||
}
|
||||
|
||||
@ -1797,7 +1803,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] =
|
||||
{ sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64
|
||||
{ sizeof(ImU64), "U64", "%llu", "%llu" },
|
||||
#endif
|
||||
{ sizeof(float), "float", "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg)
|
||||
{ sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg)
|
||||
{ sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double
|
||||
};
|
||||
IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT);
|
||||
@ -1984,13 +1990,15 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
|
||||
{
|
||||
// All other types assign constant
|
||||
// We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future.
|
||||
sscanf(buf, format, p_data);
|
||||
if (sscanf(buf, format, p_data) < 1)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Small types need a 32-bit buffer to receive the result from scanf()
|
||||
int v32;
|
||||
sscanf(buf, format, &v32);
|
||||
if (sscanf(buf, format, &v32) < 1)
|
||||
return false;
|
||||
if (data_type == ImGuiDataType_S8)
|
||||
*(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX);
|
||||
else if (data_type == ImGuiDataType_U8)
|
||||
@ -2089,6 +2097,7 @@ static const char* ImAtoi(const char* src, TYPE* output)
|
||||
// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
|
||||
static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_size)
|
||||
{
|
||||
IM_UNUSED(fmt_out_size);
|
||||
const char* fmt_end = ImParseFormatFindEnd(fmt);
|
||||
IM_ASSERT((size_t)(fmt_end - fmt + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
|
||||
while (fmt < fmt_end)
|
||||
@ -2276,7 +2285,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v
|
||||
}
|
||||
if (g.ActiveId != id)
|
||||
return false;
|
||||
if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
|
||||
if ((g.CurrentItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
|
||||
return false;
|
||||
|
||||
switch (data_type)
|
||||
@ -2309,12 +2318,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const float w = CalcItemWidth();
|
||||
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
|
||||
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
|
||||
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ItemAdd(total_bb, id, &frame_bb))
|
||||
if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0))
|
||||
return false;
|
||||
|
||||
// Default format string when passing NULL
|
||||
@ -2325,11 +2336,10 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
|
||||
// Tabbing or CTRL-clicking on Drag turns it into an InputText
|
||||
const bool hovered = ItemHoverable(frame_bb, id);
|
||||
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
|
||||
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
|
||||
if (!temp_input_is_active)
|
||||
{
|
||||
const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id);
|
||||
const bool focus_requested = temp_input_allowed && (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Focused) != 0;
|
||||
const bool clicked = (hovered && g.IO.MouseClicked[0]);
|
||||
const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]);
|
||||
if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id)
|
||||
@ -2339,10 +2349,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
FocusWindow(window);
|
||||
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
|
||||
if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id))
|
||||
{
|
||||
temp_input_is_active = true;
|
||||
FocusableItemUnregister(window);
|
||||
}
|
||||
}
|
||||
// Experimental: simple click (without moving) turns Drag into an InputText
|
||||
// FIXME: Currently polling ImGuiConfigFlags_IsTouchScreen, may either poll an hypothetical ImGuiBackendFlags_HasKeyboard and/or an explicit drag settings.
|
||||
@ -2351,7 +2358,6 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
{
|
||||
g.NavInputId = id;
|
||||
temp_input_is_active = true;
|
||||
FocusableItemUnregister(window);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2382,7 +2388,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
@ -2882,7 +2888,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type
|
||||
IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
|
||||
if ((g.CurrentItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
|
||||
return false;
|
||||
|
||||
switch (data_type)
|
||||
@ -2932,8 +2938,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
|
||||
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
|
||||
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ItemAdd(total_bb, id, &frame_bb))
|
||||
if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0))
|
||||
return false;
|
||||
|
||||
// Default format string when passing NULL
|
||||
@ -2944,11 +2951,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
|
||||
// Tabbing or CTRL-clicking on Slider turns it into an input box
|
||||
const bool hovered = ItemHoverable(frame_bb, id);
|
||||
const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
|
||||
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
|
||||
if (!temp_input_is_active)
|
||||
{
|
||||
const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id);
|
||||
const bool focus_requested = temp_input_allowed && (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Focused) != 0;
|
||||
const bool clicked = (hovered && g.IO.MouseClicked[0]);
|
||||
if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id)
|
||||
{
|
||||
@ -2957,10 +2963,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
FocusWindow(window);
|
||||
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
|
||||
if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id))
|
||||
{
|
||||
temp_input_is_active = true;
|
||||
FocusableItemUnregister(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2996,7 +2999,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
@ -3287,7 +3290,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
|
||||
ClearActiveID();
|
||||
|
||||
g.CurrentWindow->DC.CursorPos = bb.Min;
|
||||
bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags);
|
||||
bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem);
|
||||
if (init)
|
||||
{
|
||||
// First frame we started displaying the InputText widget, we expect it to take the active id.
|
||||
@ -3584,12 +3587,12 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t
|
||||
namespace ImStb
|
||||
{
|
||||
|
||||
static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
|
||||
static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; }
|
||||
static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); }
|
||||
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; }
|
||||
static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; }
|
||||
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); }
|
||||
static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; }
|
||||
static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
|
||||
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
|
||||
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
|
||||
{
|
||||
const ImWchar* text = obj->TextW.Data;
|
||||
const ImWchar* text_remaining = NULL;
|
||||
@ -3602,19 +3605,20 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob
|
||||
r->num_chars = (int)(text_remaining - (text + line_start_idx));
|
||||
}
|
||||
|
||||
// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators.
|
||||
static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
|
||||
static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; }
|
||||
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
|
||||
static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; }
|
||||
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
|
||||
#ifdef __APPLE__ // FIXME: Move setting to IO structure
|
||||
static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; }
|
||||
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
|
||||
static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; }
|
||||
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
|
||||
#else
|
||||
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
|
||||
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
|
||||
#endif
|
||||
#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
|
||||
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
|
||||
|
||||
static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
|
||||
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
|
||||
{
|
||||
ImWchar* dst = obj->TextW.Data + pos;
|
||||
|
||||
@ -3630,9 +3634,9 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
|
||||
static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len)
|
||||
{
|
||||
const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0;
|
||||
const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
|
||||
const int text_len = obj->CurLenW;
|
||||
IM_ASSERT(pos <= text_len);
|
||||
|
||||
@ -3686,7 +3690,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im
|
||||
|
||||
// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling
|
||||
// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
|
||||
static void stb_textedit_replace(STB_TEXTEDIT_STRING* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len);
|
||||
ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW);
|
||||
@ -3769,8 +3773,9 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
|
||||
}
|
||||
|
||||
// Return false to discard a character.
|
||||
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source)
|
||||
{
|
||||
IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard);
|
||||
unsigned int c = *p_char;
|
||||
|
||||
// Filter non-printable (NB: isprint is unreliable! see #2467)
|
||||
@ -3783,15 +3788,18 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
|
||||
return false;
|
||||
}
|
||||
|
||||
// We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817)
|
||||
if (c == 127)
|
||||
return false;
|
||||
if (input_source != ImGuiInputSource_Clipboard)
|
||||
{
|
||||
// We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817)
|
||||
if (c == 127)
|
||||
return false;
|
||||
|
||||
// Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME)
|
||||
if (c >= 0xE000 && c <= 0xF8FF)
|
||||
return false;
|
||||
// Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME)
|
||||
if (c >= 0xE000 && c <= 0xF8FF)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter Unicode ranges we are not handling in this build.
|
||||
// Filter Unicode ranges we are not handling in this build
|
||||
if (c > IM_UNICODE_CODEPOINT_MAX)
|
||||
return false;
|
||||
|
||||
@ -3895,7 +3903,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
ImVec2 inner_size = frame_size;
|
||||
if (is_multiline)
|
||||
{
|
||||
if (!ItemAdd(total_bb, id, &frame_bb))
|
||||
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable))
|
||||
{
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
EndGroup();
|
||||
@ -3916,15 +3924,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
return false;
|
||||
}
|
||||
draw_window = g.CurrentWindow; // Child window
|
||||
draw_window->DC.NavLayerActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it.
|
||||
draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it.
|
||||
draw_window->DC.CursorPos += style.FramePadding;
|
||||
inner_size.x -= draw_window->ScrollbarSizes.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd)
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ItemAdd(total_bb, id, &frame_bb))
|
||||
return false;
|
||||
if (!(flags & ImGuiInputTextFlags_MergedItem))
|
||||
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable))
|
||||
return false;
|
||||
}
|
||||
const bool hovered = ItemHoverable(frame_bb, id);
|
||||
if (hovered)
|
||||
@ -3933,9 +3943,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
// We are only allowed to access the state if we are already the active widget.
|
||||
ImGuiInputTextState* state = GetInputTextState(id);
|
||||
|
||||
const bool focus_requested = FocusableItemRegister(window, id);
|
||||
const bool focus_requested_by_code = focus_requested && (g.TabFocusRequestCurrWindow == window && g.TabFocusRequestCurrCounterRegular == window->DC.FocusCounterRegular);
|
||||
const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
|
||||
const bool focus_requested_by_code = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_FocusedByCode) != 0;
|
||||
const bool focus_requested_by_tabbing = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
|
||||
|
||||
const bool user_clicked = hovered && io.MouseClicked[0];
|
||||
const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard));
|
||||
@ -3948,7 +3957,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
|
||||
|
||||
const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline);
|
||||
const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start);
|
||||
const bool init_make_active = (user_clicked || user_scroll_finish || user_nav_input_start || focus_requested_by_code || focus_requested_by_tabbing);
|
||||
const bool init_state = (init_make_active || user_scroll_active);
|
||||
if ((init_state && g.ActiveId != id) || init_changed_specs)
|
||||
{
|
||||
@ -3989,7 +3998,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
}
|
||||
if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
|
||||
state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863)
|
||||
if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl)))
|
||||
if (!is_multiline && (focus_requested_by_tabbing || (user_clicked && io.KeyCtrl)))
|
||||
select_all = true;
|
||||
}
|
||||
|
||||
@ -4067,7 +4076,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
backup_current_text_length = state->CurLenA;
|
||||
state->Edited = false;
|
||||
state->BufCapacityA = buf_size;
|
||||
state->UserFlags = flags;
|
||||
state->Flags = flags;
|
||||
state->UserCallback = callback;
|
||||
state->UserCallbackData = callback_user_data;
|
||||
|
||||
@ -4116,7 +4125,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (!io.InputQueueCharacters.contains('\t'))
|
||||
{
|
||||
unsigned int c = '\t'; // Insert TAB
|
||||
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
|
||||
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
|
||||
state->OnKeyPressed((int)c);
|
||||
}
|
||||
|
||||
@ -4131,7 +4140,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
unsigned int c = (unsigned int)io.InputQueueCharacters[n];
|
||||
if (c == '\t' && io.KeyShift)
|
||||
continue;
|
||||
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
|
||||
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
|
||||
state->OnKeyPressed((int)c);
|
||||
}
|
||||
|
||||
@ -4195,7 +4204,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
else if (!is_readonly)
|
||||
{
|
||||
unsigned int c = '\n'; // Insert new line
|
||||
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data))
|
||||
if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
|
||||
state->OnKeyPressed((int)c);
|
||||
}
|
||||
}
|
||||
@ -4248,7 +4257,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
s += ImTextCharFromUtf8(&c, s, NULL);
|
||||
if (c == 0)
|
||||
break;
|
||||
if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data))
|
||||
if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard))
|
||||
continue;
|
||||
clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
|
||||
}
|
||||
@ -4420,7 +4429,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
}
|
||||
|
||||
// Clear temporary user storage
|
||||
state->UserFlags = 0;
|
||||
state->Flags = ImGuiInputTextFlags_None;
|
||||
state->UserCallback = NULL;
|
||||
state->UserCallbackData = NULL;
|
||||
}
|
||||
@ -4604,7 +4613,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
{
|
||||
state->CursorAnim += io.DeltaTime;
|
||||
bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
|
||||
ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll;
|
||||
ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll);
|
||||
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
|
||||
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
|
||||
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
|
||||
@ -4657,7 +4666,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited))
|
||||
MarkItemEdited(id);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
|
||||
if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
|
||||
return enter_pressed;
|
||||
else
|
||||
@ -4813,11 +4822,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
||||
char* p = buf;
|
||||
while (*p == '#' || ImCharIsBlankA(*p))
|
||||
p++;
|
||||
i[0] = i[1] = i[2] = i[3] = 0;
|
||||
i[0] = i[1] = i[2] = 0;
|
||||
i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha)
|
||||
int r;
|
||||
if (alpha)
|
||||
sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
|
||||
r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
|
||||
else
|
||||
sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
|
||||
r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
|
||||
IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'.
|
||||
}
|
||||
if (!(flags & ImGuiColorEditFlags_NoOptions))
|
||||
OpenPopupOnItemClick("context");
|
||||
@ -5755,7 +5767,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
||||
{
|
||||
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
|
||||
TreePushOverrideID(id);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
return is_open;
|
||||
}
|
||||
|
||||
@ -5881,7 +5893,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
||||
|
||||
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
|
||||
TreePushOverrideID(id);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
return is_open;
|
||||
}
|
||||
|
||||
@ -6064,10 +6076,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
||||
bool item_add;
|
||||
if (flags & ImGuiSelectableFlags_Disabled)
|
||||
{
|
||||
ImGuiItemFlags backup_item_flags = window->DC.ItemFlags;
|
||||
window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus;
|
||||
ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
|
||||
g.CurrentItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus;
|
||||
item_add = ItemAdd(bb, id);
|
||||
window->DC.ItemFlags = backup_item_flags;
|
||||
g.CurrentItemFlags = backup_item_flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -6145,10 +6157,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
||||
if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
|
||||
|
||||
// Automatically close popups
|
||||
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
|
||||
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.CurrentItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
|
||||
CloseCurrentPopup();
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
|
||||
return pressed;
|
||||
}
|
||||
|
||||
@ -6297,7 +6309,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
|
||||
// Plot/Graph widgets are not very good.
|
||||
// Consider writing your own, or using a third-party one, see:
|
||||
// - ImPlot https://github.com/epezent/implot
|
||||
// - others https://github.com/ocornut/imgui/wiki/Useful-Widgets
|
||||
// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size)
|
||||
@ -6589,7 +6601,7 @@ void ImGui::EndMenuBar()
|
||||
// To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
|
||||
// This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost)
|
||||
const ImGuiNavLayer layer = ImGuiNavLayer_Menu;
|
||||
IM_ASSERT(window->DC.NavLayerActiveMaskNext & (1 << layer)); // Sanity check
|
||||
IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check
|
||||
FocusWindow(window);
|
||||
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
|
||||
g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
|
||||
@ -6599,11 +6611,12 @@ void ImGui::EndMenuBar()
|
||||
}
|
||||
}
|
||||
|
||||
IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
|
||||
IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
|
||||
IM_ASSERT(window->DC.MenuBarAppending);
|
||||
PopClipRect();
|
||||
PopID();
|
||||
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
|
||||
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
|
||||
g.GroupStack.back().EmitItem = false;
|
||||
EndGroup(); // Restore position on layer 0
|
||||
window->DC.LayoutType = ImGuiLayoutType_Vertical;
|
||||
@ -6611,47 +6624,67 @@ void ImGui::EndMenuBar()
|
||||
window->DC.MenuBarAppending = false;
|
||||
}
|
||||
|
||||
// Important: calling order matters!
|
||||
// FIXME: Somehow overlapping with docking tech.
|
||||
// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts)
|
||||
bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags)
|
||||
{
|
||||
IM_ASSERT(dir != ImGuiDir_None);
|
||||
|
||||
ImGuiWindow* bar_window = FindWindowByName(name);
|
||||
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());
|
||||
if (bar_window == NULL || bar_window->BeginCount == 0)
|
||||
{
|
||||
// Calculate and set window size/position
|
||||
ImRect avail_rect = viewport->GetBuildWorkRect();
|
||||
ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
|
||||
ImVec2 pos = avail_rect.Min;
|
||||
if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
|
||||
pos[axis] = avail_rect.Max[axis] - axis_size;
|
||||
ImVec2 size = avail_rect.GetSize();
|
||||
size[axis] = axis_size;
|
||||
SetNextWindowPos(pos);
|
||||
SetNextWindowSize(size);
|
||||
|
||||
// Report our size into work area (for next frame) using actual window size
|
||||
if (dir == ImGuiDir_Up || dir == ImGuiDir_Left)
|
||||
viewport->BuildWorkOffsetMin[axis] += axis_size;
|
||||
else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right)
|
||||
viewport->BuildWorkOffsetMax[axis] -= axis_size;
|
||||
}
|
||||
|
||||
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
|
||||
SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set.
|
||||
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint
|
||||
bool is_open = Begin(name, NULL, window_flags);
|
||||
PopStyleVar(2);
|
||||
|
||||
return is_open;
|
||||
}
|
||||
|
||||
bool ImGui::BeginMainMenuBar()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
|
||||
ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar");
|
||||
|
||||
// Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change
|
||||
SetCurrentViewport(NULL, viewport);
|
||||
|
||||
// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
|
||||
// FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?
|
||||
// FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.
|
||||
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
|
||||
|
||||
// Get our rectangle at the top of the work area
|
||||
if (menu_bar_window == NULL || menu_bar_window->BeginCount == 0)
|
||||
{
|
||||
// Set window position
|
||||
// We don't attempt to calculate our height ahead, as it depends on the per-viewport font size.
|
||||
// However menu-bar will affect the minimum window size so we'll get the right height.
|
||||
ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin;
|
||||
ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f);
|
||||
SetNextWindowPos(menu_bar_pos);
|
||||
SetNextWindowSize(menu_bar_size);
|
||||
}
|
||||
|
||||
// Create window
|
||||
SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set.
|
||||
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want.
|
||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
|
||||
bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
|
||||
PopStyleVar(2);
|
||||
|
||||
// Report our size into work area (for next frame) using actual window size
|
||||
menu_bar_window = GetCurrentWindow();
|
||||
if (menu_bar_window->BeginCount == 1)
|
||||
viewport->CurrWorkOffsetMin.y += menu_bar_window->Size.y;
|
||||
|
||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
|
||||
float height = GetFrameHeight();
|
||||
bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags);
|
||||
g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
|
||||
if (!is_open)
|
||||
{
|
||||
|
||||
if (is_open)
|
||||
BeginMenuBar();
|
||||
else
|
||||
End();
|
||||
return false;
|
||||
}
|
||||
return true; //-V1020
|
||||
return is_open;
|
||||
}
|
||||
|
||||
void ImGui::EndMainMenuBar()
|
||||
@ -6805,7 +6838,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
|
||||
if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None))
|
||||
ClosePopupToLevel(g.BeginPopupStack.Size, true);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
|
||||
if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
|
||||
{
|
||||
@ -6865,11 +6898,11 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
|
||||
{
|
||||
// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
|
||||
// Note that in this situation we render neither the shortcut neither the selected tick mark
|
||||
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
|
||||
float w = label_size.x;
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
|
||||
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
|
||||
pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
|
||||
pressed = Selectable(label, selected, flags, ImVec2(w, 0.0f));
|
||||
PopStyleVar();
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
@ -6892,7 +6925,7 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo
|
||||
RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
|
||||
}
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return pressed;
|
||||
}
|
||||
|
||||
@ -6954,12 +6987,17 @@ ImGuiTabBar::ImGuiTabBar()
|
||||
LastTabItemIdx = -1;
|
||||
}
|
||||
|
||||
static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab)
|
||||
{
|
||||
return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
||||
}
|
||||
|
||||
static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs)
|
||||
{
|
||||
const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
|
||||
const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;
|
||||
const int a_section = (a->Flags & ImGuiTabItemFlags_Leading) ? 0 : (a->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
||||
const int b_section = (b->Flags & ImGuiTabItemFlags_Leading) ? 0 : (b->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
||||
const int a_section = TabItemGetSectionIdx(a);
|
||||
const int b_section = TabItemGetSectionIdx(b);
|
||||
if (a_section != b_section)
|
||||
return a_section - b_section;
|
||||
return (int)(a->IndexDuringLayout - b->IndexDuringLayout);
|
||||
@ -7135,11 +7173,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
||||
tab->IndexDuringLayout = (ImS16)tab_dst_n;
|
||||
|
||||
// We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another)
|
||||
int curr_tab_section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
||||
int curr_tab_section_n = TabItemGetSectionIdx(tab);
|
||||
if (tab_dst_n > 0)
|
||||
{
|
||||
ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1];
|
||||
int prev_tab_section_n = (prev_tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (prev_tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
||||
int prev_tab_section_n = TabItemGetSectionIdx(prev_tab);
|
||||
if (curr_tab_section_n == 0 && prev_tab_section_n != 0)
|
||||
need_sort_by_section = true;
|
||||
if (prev_tab_section_n == 2 && curr_tab_section_n != 2)
|
||||
@ -7211,12 +7249,13 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
||||
const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0;
|
||||
tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x;
|
||||
|
||||
int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
||||
int section_n = TabItemGetSectionIdx(tab);
|
||||
ImGuiTabBarSection* section = §ions[section_n];
|
||||
section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f);
|
||||
curr_section_n = section_n;
|
||||
|
||||
// Store data so we can build an array sorted by width if we need to shrink tabs down
|
||||
IM_MSVC_WARNING_SUPPRESS(6385);
|
||||
int shrink_buffer_index = shrink_buffer_indexes[section_n]++;
|
||||
g.ShrinkWidthBuffer[shrink_buffer_index].Index = tab_n;
|
||||
g.ShrinkWidthBuffer[shrink_buffer_index].Width = tab->ContentWidth;
|
||||
@ -7266,7 +7305,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
||||
if (shrinked_width < 0.0f)
|
||||
continue;
|
||||
|
||||
int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
|
||||
int section_n = TabItemGetSectionIdx(tab);
|
||||
sections[section_n].Width -= (tab->Width - shrinked_width);
|
||||
tab->Width = shrinked_width;
|
||||
}
|
||||
@ -7394,6 +7433,9 @@ void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGu
|
||||
IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL);
|
||||
IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame)
|
||||
|
||||
if (!window->HasCloseButton)
|
||||
tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation.
|
||||
|
||||
ImGuiTabItem new_tab;
|
||||
new_tab.ID = window->ID;
|
||||
new_tab.Flags = tab_flags;
|
||||
@ -7449,7 +7491,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui
|
||||
ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id);
|
||||
if (tab == NULL)
|
||||
return;
|
||||
if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))
|
||||
if (tab->Flags & ImGuiTabItemFlags_SectionMask_)
|
||||
return;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
@ -7478,12 +7520,48 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir)
|
||||
void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset)
|
||||
{
|
||||
IM_ASSERT(dir == -1 || dir == +1);
|
||||
IM_ASSERT(offset != 0);
|
||||
IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
|
||||
tab_bar->ReorderRequestTabId = tab->ID;
|
||||
tab_bar->ReorderRequestDir = (ImS8)dir;
|
||||
tab_bar->ReorderRequestOffset = (ImS16)offset;
|
||||
}
|
||||
|
||||
void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* src_tab, ImVec2 mouse_pos)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
|
||||
if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0)
|
||||
return;
|
||||
|
||||
const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
|
||||
const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0);
|
||||
|
||||
// Count number of contiguous tabs we are crossing over
|
||||
const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1;
|
||||
const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab);
|
||||
int dst_idx = src_idx;
|
||||
for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir)
|
||||
{
|
||||
// Reordered tabs must share the same section
|
||||
const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i];
|
||||
if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder)
|
||||
break;
|
||||
if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_))
|
||||
break;
|
||||
dst_idx = i;
|
||||
|
||||
// Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered.
|
||||
const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x;
|
||||
const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x;
|
||||
//GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255));
|
||||
if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2))
|
||||
break;
|
||||
}
|
||||
|
||||
if (dst_idx != src_idx)
|
||||
TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx);
|
||||
}
|
||||
|
||||
bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)
|
||||
@ -7493,19 +7571,23 @@ bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)
|
||||
return false;
|
||||
|
||||
//IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools
|
||||
int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir;
|
||||
int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestOffset;
|
||||
if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size)
|
||||
return false;
|
||||
|
||||
// Reordered TabItem must share the same position flags than target
|
||||
// Reordered tabs must share the same section
|
||||
// (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too)
|
||||
ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
|
||||
if (tab2->Flags & ImGuiTabItemFlags_NoReorder)
|
||||
return false;
|
||||
if ((tab1->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (tab2->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)))
|
||||
if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_))
|
||||
return false;
|
||||
|
||||
ImGuiTabItem item_tmp = *tab1;
|
||||
*tab1 = *tab2;
|
||||
ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2;
|
||||
ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1;
|
||||
const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset;
|
||||
memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem));
|
||||
*tab2 = item_tmp;
|
||||
|
||||
if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
|
||||
@ -7798,7 +7880,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
|
||||
|
||||
// Layout
|
||||
const bool is_central_section = (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) == 0;
|
||||
const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
|
||||
size.x = tab->Width;
|
||||
if (is_central_section)
|
||||
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
|
||||
@ -7854,21 +7936,22 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
else if (held && !tab_appearing && IsMouseDragging(0))
|
||||
{
|
||||
// Drag and drop: re-order tabs
|
||||
int drag_dir = 0;
|
||||
float drag_distance_from_edge_x = 0.0f;
|
||||
if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL)))
|
||||
{
|
||||
// While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x
|
||||
if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)
|
||||
{
|
||||
drag_dir = -1;
|
||||
drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x;
|
||||
if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
|
||||
TabBarQueueReorder(tab_bar, tab, -1);
|
||||
TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);
|
||||
}
|
||||
else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)
|
||||
{
|
||||
drag_dir = +1;
|
||||
drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x;
|
||||
if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)
|
||||
TabBarQueueReorder(tab_bar, tab, +1);
|
||||
TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7877,11 +7960,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
{
|
||||
// We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar
|
||||
bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id);
|
||||
|
||||
if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift)
|
||||
{
|
||||
float threshold_base = g.FontSize;
|
||||
//float threshold_base = g.IO.ConfigDockingWithShift ? g.FontSize * 0.5f : g.FontSize;
|
||||
float threshold_x = (threshold_base * 2.2f);
|
||||
float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f);
|
||||
//GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG]
|
||||
@ -7889,8 +7970,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y);
|
||||
if (distance_from_edge_y >= threshold_y)
|
||||
undocking_tab = true;
|
||||
else if (drag_distance_from_edge_x > threshold_x)
|
||||
if ((tab_bar->ReorderRequestDir < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestDir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1))
|
||||
if (drag_distance_from_edge_x > threshold_x)
|
||||
if ((drag_dir < 0 && tab_bar->GetTabOrder(tab) == 0) || (drag_dir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1))
|
||||
undocking_tab = true;
|
||||
}
|
||||
|
||||
|
@ -28,3 +28,10 @@ See https://gist.github.com/ocornut/b3a9ecf13502fd818799a452969649ad
|
||||
|
||||
Small, thin anti-aliased fonts are typically benefiting a lots from Freetype's hinting:
|
||||
![comparing_font_rasterizers](https://user-images.githubusercontent.com/8225057/107550178-fef87f00-6bd0-11eb-8d09-e2edb2f0ccfc.gif)
|
||||
|
||||
### Colorful glyphs/emojis
|
||||
|
||||
You can use the `ImGuiFreeTypeBuilderFlags_LoadColor` flag to load certain colorful glyphs. See
|
||||
["Using Colorful Glyphs/Emojis"](https://github.com/ocornut/imgui/edit/master/docs/FONTS.md#using-colorful-glyphsemojis) section of FONTS.md.
|
||||
|
||||
![colored glyphs](https://user-images.githubusercontent.com/8225057/106171241-9dc4ba80-6191-11eb-8a69-ca1467b206d1.png)
|
||||
|
@ -1,7 +1,7 @@
|
||||
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||
// (code)
|
||||
|
||||
// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
|
||||
// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
|
||||
// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
|
||||
|
||||
// CHANGELOG
|
||||
|
@ -86,6 +86,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\common\TracySocket.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracySystem.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4hc.cpp" />
|
||||
@ -134,6 +135,7 @@
|
||||
<ClInclude Include="..\..\..\common\TracyProtocol.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyQueue.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySocket.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4hc.hpp" />
|
||||
|
@ -147,6 +147,9 @@
|
||||
<ClCompile Include="..\..\..\zstd\dictBuilder\zdict.c">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp">
|
||||
<Filter>common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp">
|
||||
@ -323,5 +326,8 @@
|
||||
<ClInclude Include="..\..\..\zstd\dictBuilder\divsufsort.h">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
116
manual/tracy.tex
116
manual/tracy.tex
@ -228,6 +228,8 @@ Tracy is aimed at understanding the inner workings of a tight loop of a game (or
|
||||
|
||||
Tracy is able to periodically sample what the profiled application is doing, which gives you detailed performance information at the source line/assembly instruction level. This can give you deep understanding of how the program is executed by the processor. Using this information you can get a coarse view at the call stacks, fine-tune your algorithms, or even 'steal' an optimization performed by one compiler and make it available for the others.
|
||||
|
||||
On some platforms it is possible to sample the hardware performance counters, which will give you information not only \emph{where} your program is running slowly, but also \emph{why}.
|
||||
|
||||
\subsection{Remote or embedded telemetry}
|
||||
|
||||
Tracy uses the client-server model to enable a wide range of use-cases (see figure~\ref{clientserver}). For example, a game on a mobile phone may be profiled over the wireless connection, with the profiler running on a desktop computer. Or you can run the client and server on the same machine, using a localhost connection. It is also possible to embed the visualization front-end in the profiled application, making the profiling self-contained\footnote{See section~\ref{embeddingserver} for guidelines.}.
|
||||
@ -368,8 +370,6 @@ Tracy Profiler supports MSVC, gcc and clang. A reasonably recent version of the
|
||||
\item Linux (x86, x64, ARM, ARM64)
|
||||
\item Android (ARM, ARM64, x86)
|
||||
\item FreeBSD (x64)
|
||||
\item Cygwin (x64)
|
||||
\item MinGW (x64)
|
||||
\item WSL (x64)
|
||||
\item OSX (x64)
|
||||
\item iOS (ARM, ARM64)
|
||||
@ -384,6 +384,8 @@ Moreover, the following platforms are not supported due to how secretive their o
|
||||
\item Google Stadia
|
||||
\end{itemize}
|
||||
|
||||
You may also try your luck with Cygwin or Mingw, but don't get your hopes too high. These platforms were usable some time ago, but nobody is actively working on resolving any issues you might encounter with them.
|
||||
|
||||
\subsection{Initial client setup}
|
||||
\label{initialsetup}
|
||||
|
||||
@ -522,9 +524,10 @@ To workaround this limitation, you will need to have a rooted device. Execute th
|
||||
setenforce 0
|
||||
mount -o remount,hidepid=0 /proc
|
||||
echo 0 > /proc/sys/kernel/perf_event_paranoid
|
||||
echo 0 > /proc/sys/kernel/kptr_restrict
|
||||
\end{lstlisting}
|
||||
|
||||
The first command will allow access to system CPU statistics. The second one will allow inspection of foreign processes (which is required for context switch capture). The last one will lower restrictions on access to performance counters. \emph{Be sure that you are fully aware of the consequences of making these changes.}
|
||||
The first command will allow access to system CPU statistics. The second one will allow inspection of foreign processes (which is required for context switch capture). The third one will lower restrictions on access to performance counters. The last one will allow retrieval of kernel symbol pointers. \emph{Be sure that you are fully aware of the consequences of making these changes.}
|
||||
|
||||
\paragraph{Virtual machines}
|
||||
|
||||
@ -789,6 +792,7 @@ CPU usage probing & \faCheck & \faCheck & \faCheck & \faCheck & \faCheck & \faCh
|
||||
Context switches & \faCheck & \faCheck & \faCheck & \faTimes & \faPoo & \faTimes \\
|
||||
CPU topology information & \faCheck & \faCheck & \faCheck & \faTimes & \faTimes & \faTimes \\
|
||||
Call stack sampling & \faCheck & \faCheck & \faCheck & \faTimes & \faPoo & \faTimes \\
|
||||
Hardware sampling & \faTimes & \faCheck & \faCheck & \faTimes & \faPoo & \faTimes \\
|
||||
VSync capture & \faCheck & \faTimes & \faTimes & \faTimes & \faTimes & \faTimes \\
|
||||
\end{tabular}
|
||||
|
||||
@ -1369,6 +1373,18 @@ If the graphic API you are using requires explicitly stating that you start and
|
||||
To fix such issues, add a nested scope, encompassing the command buffer recording section.
|
||||
\end{bclogo}
|
||||
|
||||
\begin{bclogo}[
|
||||
noborder=true,
|
||||
couleur=black!5,
|
||||
logo=\bcattention
|
||||
]{Caveat emptor}
|
||||
The profiling results you will get can be unreliable, or just plainly wrong. It all depends on the quality of graphics drivers and how the underlying hardware implements timers. While Tracy employs a number of heuristics in order to make things as reliable as possible, in the end it has to talk to the GPU through the commonly unreliable API calls.
|
||||
|
||||
For example, on Linux the Intel GPU driver will report 64-bit precision of time stamps. This is not true, as the driver will only provide time stamps with 36-bit precision, rolling over the exceeding values. Tracy is able to detect this and employ workarounds. This is, sadly, not enough to make the readings reliable, as this timer we can access through the API is not a real one. Deep down the driver has access to the true timer, which is used to provide the virtual values we can get. This hardware timer has a period which \emph{does not match} the period of the API timer. In result, the virtual timer will sometimes overflow \emph{in midst} of a cycle, making the reported time values jump forward. This is a problem that only the driver vendor can fix.
|
||||
|
||||
If you experience crippling problems while profiling the GPU, you might get better results with different driver, different operating system, or different hardware.
|
||||
\end{bclogo}
|
||||
|
||||
\subsubsection{OpenGL}
|
||||
|
||||
You will need to include the \texttt{tracy/TracyOpenGL.hpp} header file and declare each of your rendering contexts using the \texttt{TracyGpuContext} macro (typically you will only have one context). Tracy expects no more than one context per thread and no context migration. To set a custom name for the context, use the \texttt{TracyGpuContextName(name, size)} macro.
|
||||
@ -1383,8 +1399,7 @@ couleur=black!5,
|
||||
logo=\bcattention
|
||||
]{Caveats}
|
||||
\begin{itemize}
|
||||
\item GPU profiling is not supported on OSX, iOS\footnote{Because Apple is unable to implement standards properly.}.
|
||||
\item Android devices do work, if GPU drivers are not broken. Disjoint events are not currently handled, so some readings may be a bit spotty.
|
||||
\item OpenGL profiling is not supported on OSX, iOS\footnote{Because Apple is unable to implement standards properly.}.
|
||||
\item Nvidia drivers are unable to provide consistent timing results when two OpenGL contexts are used simultaneously.
|
||||
\item Calling the \texttt{TracyGpuCollect} macro is a fairly slow operation (couple \si{\micro\second}).
|
||||
\end{itemize}
|
||||
@ -1503,6 +1518,8 @@ You can force call stack capture in the non-\texttt{S} postfixed macros by addin
|
||||
|
||||
The maximum call stack depth that can be retrieved is 62 frames. This is a restriction at the level of operating system.
|
||||
|
||||
Tracy will automatically exclude certain uninteresting functions from the captured call stacks. For example, the pass-through intrinsic wrapper functions won't be reported.
|
||||
|
||||
\begin{bclogo}[
|
||||
noborder=true,
|
||||
couleur=black!5,
|
||||
@ -1874,14 +1891,53 @@ Manual markup of zones doesn't cover every function existing in a program and ca
|
||||
|
||||
This feature requires privilege elevation, as described in chapter~\ref{privilegeelevation}. Proper setup of the required program debugging data is described in chapter~\ref{collectingcallstacks}.
|
||||
|
||||
By default sampling is performed at 8 kHz frequency on Windows (which is the maximum possible value). On Linux and Android it is performed at 10 kHz. This value can be changed by providing the sampling frequency (in Hz) through the \texttt{TRACY\_SAMPLING\_HZ} macro.
|
||||
By default sampling is performed at 8 kHz frequency on Windows (which is the maximum possible value). On Linux and Android it is performed at 10 kHz\footnote{The maximum sampling frequency is limited by the \texttt{kernel.perf\_event\_max\_sample\_rate} sysctl parameter.}. This value can be changed by providing the sampling frequency (in Hz) through the \texttt{TRACY\_SAMPLING\_HZ} macro.
|
||||
|
||||
Call stack sampling may be disabled by using the \texttt{TRACY\_NO\_SAMPLING} define.
|
||||
|
||||
\begin{bclogo}[
|
||||
noborder=true,
|
||||
couleur=black!5,
|
||||
logo=\bcbombe
|
||||
]{Linux sampling rate limits}
|
||||
The operating system may decide that sampling is taking too much CPU time and reduce the allowed sampling rate. This can be seen in \texttt{dmesg} output as:
|
||||
|
||||
\texttt{perf: interrupt took too long, lowering kernel.perf\_event\_max\_sample\_rate to \emph{value}}.
|
||||
|
||||
If the \emph{value} goes below the sample rate Tracy wants to use, sampling will be silently disabled. To make it work again, you can set an appropriate value in the \texttt{kernel.perf\_event\_max\_sample\_rate} kernel parameter, using the \texttt{sysctl} utility.
|
||||
|
||||
Should you want to disable this mechanism, you can set the \texttt{kernel.perf\_cpu\_time\_max\_percent} parameter to zero. Be sure to read what this would do, as it may have serious consequences that you should be aware of.
|
||||
\end{bclogo}
|
||||
|
||||
\subsubsection{Hardware sampling}
|
||||
\label{hardwaresampling}
|
||||
|
||||
While the call stack sampling is a generic software-implemented functionality of the operating system, there's another way of sampling program execution patterns. Modern processors host a wide array of different hardware performance counters, which increase when some event in a CPU core happens. These could be as simple as counting each clock cycle, or as implementation specific as counting 'retired instructions that are delivered to the back-end after the front-end had at least 1 bubble-slot for a period of 2 cycles'.
|
||||
|
||||
Tracy is able to use these counters to present you the following three statistics, which may help guide you in discovery why your code is not as fast as possible:
|
||||
|
||||
\begin{enumerate}
|
||||
\item \emph{Instructions Per Cycle (IPC)} -- shows how many instructions were executing concurrently within a single core cycle. Higher values are better. The maximum achievable value depends on the design of CPU, including things such as the number of execution units and their individual capabilities. Calculated as $\frac{\text{\#instructions retired}}{\text{\#cycles}}$. Can be disabled with the \texttt{TRACY\_NO\_SAMPLE\_RETIREMENT} macro.
|
||||
\item \emph{Branch miss rate} -- shows how frequently the CPU branch predictor makes a wrong choice. Lower values are better. Calculated as $\frac{\text{\#branch misses}}{\text{\#branch instructions}}$. Can be disabled with the \texttt{TRACY\_NO\_SAMPLE\_BRANCH} macro.
|
||||
\item \emph{Cache miss rate} -- shows how frequently the CPU has to retrieve data from memory. Lower values are better. The specifics of which cache level is taken into account here vary from one implementation to another. Calculated as $\frac{\text{\#cache misses}}{\text{\#cache references}}$. Can be disabled with the \texttt{TRACY\_NO\_SAMPLE\_CACHE} macro.
|
||||
\end{enumerate}
|
||||
|
||||
Each performance counter has to be collected by a dedicated Performance Monitoring Unit (PMU). The availability of PMUs is very limited, so you may not be able to capture all the statistics mentioned above at the same time (as each requires capture of two different counters). In such case, you will need to manually select what needs to be sampled, with the macros specified above.
|
||||
|
||||
If the provided measurements are not specific enough for your needs, you will need to use a profiler better tailored to the hardware you are using, such as Intel VTune, or AMD \si{\micro}Prof.
|
||||
|
||||
Another problem to consider here is the measurement skid. It is quite hard to accurately pinpoint the exact assembly instruction which has caused the counter to trigger. Due to this the results you'll get may look a bit nonsense at times. For example, a branch miss may be attributed to the multiply instruction. Not much can be done with that, as this is exactly what the hardware is reporting. The amount of skid you will encounter depends on the specific implementation of a processor, and each vendor has their own solution to minimize it. Intel uses Precise Event Based Sampling (PEBS), which is rather good, but it still can, for example, blend the branch statistics across the comparison instruction and the following jump instruction. AMD employs their own Instruction Based Sampling (IBS), which tends to provide worse results in comparison.
|
||||
|
||||
Do note that the statistics presented by Tracy are a combination of two randomly sampled counters, so you should take them with a grain of salt. The random nature of sampling\footnote{The hardware counters in practice can be triggered only once per million-or-so events happening.} makes it fully possible to count more branch misses than branch instructions, or some other similar sillyness. You should always cross-check this data with the count of sampled events, in order to decide if the provided values can be reliably acted upon.
|
||||
|
||||
\subparagraph{Availability}
|
||||
|
||||
Currently the hardware performance counter readings are only available on Linux. Access to them is performed using the kernel-provided infrastructure, so what you get may depend on how your kernel was configured. This also means that the exact set of supported hardware is not known, as it depends on what has been implemented in the Linux itself. At this point the x86 hardware is fully supported (including features such as PEBS or IBS), and there's PMU support on a selection of ARM designs.
|
||||
|
||||
\subsubsection{Executable code retrieval}
|
||||
\label{executableretrieval}
|
||||
|
||||
To enable deep insight into program execution, Tracy will capture small chunks of the executable image during profiling. The retrieved code can be subsequently disassembled to be inspected in detail. This functionality will be performed only for functions that are no larger than 64 KB and only if symbol information is present.
|
||||
To enable deep insight into program execution, Tracy will capture small chunks of the executable image during profiling. The retrieved code can be subsequently disassembled to be inspected in detail. This functionality will be performed only for functions that are no larger than 128 KB and only if symbol information is present.
|
||||
|
||||
Discovery of previously unseen executable code may result in reduced performance of real-time capture. This is especially true when the profiling session had just started. Such behavior is expected and will go back to normal after a couple of moments.
|
||||
|
||||
@ -2561,7 +2617,7 @@ Ghost zones represent true function calls in the program, periodically reported
|
||||
|
||||
Another common pitfall to watch for is the order of presented functions. \emph{It is not what you expect it to be!} Read chapter~\ref{readingcallstacks} for a critical insight on how call stacks might seem nonsensical at first, and why they aren't.
|
||||
|
||||
The available information about ghost zones is quite limited, but it's enough to give you a rough outlook on the execution of your application. The timeline view alone is more than any other statistical profiler is able to present. In addition to that, Tracy properly handles inlined function calls, which are indicated by darker colored ghost zones.
|
||||
The available information about ghost zones is quite limited, but it's enough to give you a rough outlook on the execution of your application. The timeline view alone is more than any other statistical profiler is able to present. In addition to that, Tracy properly handles inlined function calls, which are indicated by darker background of ghost zones. Zones representing kernel-mode functions are displayed with red function names.
|
||||
|
||||
Clicking the \LMB{}~left mouse button on a ghost zone will open the corresponding source file location, if able (see chapter~\ref{sourceview} for conditions). There are three ways in which source locations can be assigned to a ghost zone:
|
||||
|
||||
@ -2779,7 +2835,7 @@ Data displayed in this mode is in essence very similar to the instrumentation on
|
||||
|
||||
First and foremost, the presented information is constructed from a number of call stack samples, which represent real addresses in the application's binary code, mapped to the line numbers in the source files. This reverse mapping may not be always possible, or may be erroneous. Furthermore, due to the nature of the sampling process, it is impossible to obtain exact time measurement. Instead, time values are guesstimated by multiplying number of sample counts by mean time between two distinct samples.
|
||||
|
||||
The \emph{Name} column contains name of the function in which the sampling was done. If the \emph{\faSitemap{}~Inlines} option is enabled, functions which were inlined will be preceded with a '\faCaretRight{}' symbol and additionally display their parent function name in parenthesis. Otherwise, only non-inlined functions are listed, with count of inlined functions in parenthesis. Any entry containing inlined function may be expanded to display the corresponding functions list (some functions may be hidden if the \emph{\faPuzzlePiece{}~Show all} option is disabled, due to lack of sampling data). Clicking on a function name will open the sample entry call stacks window (see chapter~\ref{sampleparents}). Note that if inclusive times are displayed, listed functions will be partially or completely coming from mid-stack frames, which will prevent, or limit the capability to display parent call stacks.
|
||||
The \emph{Name} column contains name of the function in which the sampling was done. Kernel-mode function samples are distinguished with the red color. If the \emph{\faSitemap{}~Inlines} option is enabled, functions which were inlined will be preceded with a '\faCaretRight{}' symbol and additionally display their parent function name in parenthesis. Otherwise, only non-inlined functions are listed, with count of inlined functions in parenthesis. Any entry containing inlined function may be expanded to display the corresponding functions list (some functions may be hidden if the \emph{\faPuzzlePiece{}~Show all} option is disabled, due to lack of sampling data). Clicking on a function name will open the sample entry call stacks window (see chapter~\ref{sampleparents}). Note that if inclusive times are displayed, listed functions will be partially or completely coming from mid-stack frames, which will prevent, or limit the capability to display parent call stacks.
|
||||
|
||||
The \emph{Location} column displays the corresponding source file name and line number. Depending on the \emph{Location} option selection it can either show function entry address, or the instruction at which the sampling was performed. The \emph{Entry} mode points at the beginning of a non-inlined function, or at the place where inlined function was inserted in its parent function. The \emph{Sample} mode is not useful for non-inlined functions, as it points to one randomly selected sampling point out of many that were captured. However, in case of inlined functions, this random sampling point is within the inlined function body. Using these options in tandem enable you to look at both the inlined function code and the place where it was inserted. If the \emph{Smart} location is selected, profiler will display entry point position for non-inlined functions and sample location for inlined functions. Selecting the \emph{\faAt{}~Address} option will instead print the symbol address.
|
||||
|
||||
@ -2791,7 +2847,7 @@ The \emph{Time} or \emph{Count} column (depending on the \emph{\faStopwatch{}~Sh
|
||||
|
||||
The last column, \emph{Code size}, displays the size of symbol in the executable image of the program. Since inlined routines are directly embedded into other functions, their symbol size will be based on the parent symbol, and displayed as 'less than'. In some cases this data won't be available. If the symbol code has been retrieved\footnote{Symbols larger than 64~KB are not captured.}, symbol size will be prepend with the \texttt{\faDatabase}~icon, and clicking the \RMB{}~right mouse button on the location column entry will open symbol view window (section~\ref{symbolview}).
|
||||
|
||||
Finally, the list can be filtered using the \emph{\faFilter{}~Filter symbols} entry field, just like in the instrumentation mode case. Additionally, you can also filter results by the originating image name of the symbol. The exclusive/inclusive time counting mode can be switched using the \emph{\faClock{}~Self time} switch. Limiting the time range is also available, but is restricted to self time. If the \emph{\faPuzzlePiece{}~Show all} option is selected, the list will include not only call stack samples, but also all other symbols collected during the profiling process (this is enabled by default, if no sampling was performed).
|
||||
Finally, the list can be filtered using the \emph{\faFilter{}~Filter symbols} entry field, just like in the instrumentation mode case. Additionally, you can also filter results by the originating image name of the symbol. Display of kernel symbols may be disabled with the \emph{\faHatWizard{}~Include kernel} switch. The exclusive/inclusive time counting mode can be switched using the \emph{\faClock{}~Self time} switch. Limiting the time range is also available, but is restricted to self time. If the \emph{\faPuzzlePiece{}~Show all} option is selected, the list will include not only call stack samples, but also all other symbols collected during the profiling process (this is enabled by default, if no sampling was performed).
|
||||
|
||||
\subsection{Find zone window}
|
||||
\label{findzone}
|
||||
@ -3130,7 +3186,7 @@ Clicking on the \emph{\faClipboard{}~Copy to clipboard} buttons will copy the ap
|
||||
\subsection{Call stack window}
|
||||
\label{callstackwindow}
|
||||
|
||||
This window shows the frames contained in the selected call stack. Each frame is described by a function name, source file location and originating image\footnote{Executable images are called \emph{modules} by Microsoft.} name. Clicking the \LMB{}~left mouse button on either the function name of source file location will copy the name to the clipboard. Clicking the \RMB{}~right mouse button on the source file location will open the source file view window (if applicable, see section~\ref{sourceview}).
|
||||
This window shows the frames contained in the selected call stack. Each frame is described by a function name, source file location and originating image\footnote{Executable images are called \emph{modules} by Microsoft.} name. Function frames originating from kernel are marked with a red color. Clicking the \LMB{}~left mouse button on either the function name of source file location will copy the name to the clipboard. Clicking the \RMB{}~right mouse button on the source file location will open the source file view window (if applicable, see section~\ref{sourceview}).
|
||||
|
||||
A single stack frame may have multiple function call places associated with it. This happens in case of inlined function calls. Such entries will be displayed in the call stack window, with \emph{inline} in place of frame number\footnote{Or '\faCaretRight{}'~icon in case of call stack tooltips.}.
|
||||
|
||||
@ -3234,9 +3290,9 @@ This is pretty much the original source file view window, but with the ability t
|
||||
|
||||
\paragraph{Assembly mode}
|
||||
|
||||
This mode shows the disassembly of the symbol machine code. Each assembly instruction is displayed listed with its location in the program memory during execution. If the \emph{\faSearchLocation{}~Relative locations} option is selected, an offset from the symbol beginning will be printed instead. Clicking the \LMB{}~left mouse button on the address/offset will switch to counting line numbers, using the selected one as origin (i.e. zero value). Line numbers are displayed inside \texttt{[]} brackets. This display mode can be useful to correlate lines with output of external tools, such as \texttt{llvm-mca}. To disable line numbering click the \RMB{}~right mouse button on a line number.
|
||||
This mode shows the disassembly of the symbol machine code. If only one inline function is selected through the \emph{\faSitemap{}~Function} selector, assembly instructions outside of this function will be dimmed out. Each assembly instruction is displayed listed with its location in the program memory during execution. If the \emph{\faSearchLocation{}~Relative locations} option is selected, an offset from the symbol beginning will be printed instead. Clicking the \LMB{}~left mouse button on the address/offset will switch to counting line numbers, using the selected one as origin (i.e. zero value). Line numbers are displayed inside \texttt{[]} brackets. This display mode can be useful to correlate lines with output of external tools, such as \texttt{llvm-mca}. To disable line numbering click the \RMB{}~right mouse button on a line number.
|
||||
|
||||
If the \emph{\faFileImport{}~Source locations} option is selected, each line of the assembly code will also contain information about the originating source file name and line number. For easier differentiation between different source files, each file is assigned its own color. Clicking the \LMB{}~left mouse button on a displayed source location will switch the source file, if necessary, and focus the source view on selected line.
|
||||
If the \emph{\faFileImport{}~Source locations} option is selected, each line of the assembly code will also contain information about the originating source file name and line number. For easier differentiation between different source files, each file is assigned its own color. Clicking the \LMB{}~left mouse button on a displayed source location will switch the source file, if necessary, and focus the source view on selected line. Additionally, hovering the \faMousePointer{}~mouse cursor over the presented location will show a tooltip containing the name of a function the instruction originates from, along with an appropriate source code fragment.
|
||||
|
||||
Selecting the \emph{\faCogs{}~Machine code} option will enable display of raw machine code bytes for each line.
|
||||
|
||||
@ -3246,7 +3302,7 @@ Enabling the \emph{\faShare{}~Jumps} option will show jumps within the symbol co
|
||||
|
||||
The \emph{AT\&T} switch can be used to select between \emph{Intel} and \emph{AT\&T} assembly syntax. Beware that microarchitecture data is only available if Intel syntax is selected.
|
||||
|
||||
Unlike the source file view, portions of the executable are stored within the captured profile and don't rely on the local disk files being available.
|
||||
Portions of the executable used to show the symbol view are stored within the captured profile and don't rely on the local disk files being available.
|
||||
|
||||
\subparagraph{Exploring microarchitecture}
|
||||
|
||||
@ -3296,16 +3352,26 @@ logo=\bcbombe
|
||||
An assembly instruction may be associated with only a single source line, but a source line might be associated with multiple assembly lines, sometimes intermixed with other assembly instructions.
|
||||
\end{bclogo}
|
||||
|
||||
\paragraph{Instruction pointer statistics}
|
||||
\paragraph{Instruction pointer cost statistics}
|
||||
|
||||
If automated call stack sampling (see chapter~\ref{sampling}) was performed, additional profiling information will be available. The first column of source and assembly views will contain percentage counts of collected instruction pointer samples for each displayed line, both in numerical and graphical bar form. This information can be used to determine which line of the function takes the most time. The displayed percentage values are heat map color coded, with the lowest values mapped to dark red, and the highest values mapped to bright yellow. The color code will appear next to the percentage value, and on the scroll bar, so that 'hot' places in code can be identified at a glance.
|
||||
|
||||
By default samples are displayed only from within the selected symbol, in isolation. In some cases you may however want to include samples from functions that were called. To do so, enable the \emph{\faSignOut*{}~Child calls} option, which may also be temporarily toggled by pressing the \keys{Z} key. Make sure to familiarize yourself with section~\ref{readingcallstacks} to be able to properly read the results.
|
||||
By default samples are displayed only from within the selected symbol, in isolation. In some cases you may however want to include samples from functions that were called. To do so, enable the \emph{\faSignOut*{}~Child calls} option, which may also be temporarily toggled by holding the \keys{Z} key. Make sure to familiarize yourself with section~\ref{readingcallstacks} to be able to properly read the results.
|
||||
|
||||
Instruction timings can be viewed as a group. To begin constructing such group, click the \LMB{}~left mouse button on the percentage value. Additional instructions can be added using the \keys{\ctrl}~key, while holding the \keys{\shift}~key will allow selection of a range. To cancel the selection, click the \RMB{}~right mouse button on a percentage value. Group statistics can be seen at the bottom of the pane.
|
||||
|
||||
Clicking the \MMB{}~middle mouse button on the percentage value of an assembly instruction will display entry call stacks of the selected sample (see chapter~\ref{sampleparents}). This functionality is only available for instructions that have collected sampling data, and only in the assembly view, as the source code may be inlined multiple times, which would result in ambiguous location data. Note that number of entry call stacks is displayed in a tooltip, for a quick reference.
|
||||
|
||||
\begin{bclogo}[
|
||||
noborder=true,
|
||||
couleur=black!5,
|
||||
logo=\bclampe
|
||||
]{How did I get here?}
|
||||
In some cases it may be difficult to understand what is being displayed in the disassembly. For example, calling the \texttt{std::lower\_bound} function may generate multiple level of inlined functions: first we enter the search algorithm, then the comparison functions, which in turn may be lambdas that call even more external code, and so on. In such event you will most likely see that some external code is taking a long time to execute and you will be none the wiser on how to improve things.
|
||||
|
||||
Using the entry call stacks view can be very helpful in such cases, as you will be able to see the call stack of inline functions, originating from a call site in the code you are familiar with. With this critical piece of information you will be able to make a connection between functions you call and the instructions that are executed.
|
||||
\end{bclogo}
|
||||
|
||||
Sample data source is controlled by the \emph{\faSitemap{}~Function} control, in the window header. If this option should be disabled, sample data will represent the whole symbol. If it is enabled, then the sample data will only include the selected function. The currently selected function can be changed by opening the drop-down box, which includes time statistics. The time percentage values of each contributing function are calculated relative to total number of samples collected within the symbol.
|
||||
|
||||
Selecting the \emph{Limit range} option will restrict counted samples to the time extent shared with the statistics view (displayed as a red striped region on the timeline). See section~\ref{timeranges} for more detail.
|
||||
@ -3318,6 +3384,24 @@ logo=\bcbombe
|
||||
Be aware that the data is not fully accurate, as it is the result of random sampling of program execution. Furthermore, undocumented implementation details of an out-of-order CPU architecture will highly impact the measurement. Read chapter~\ref{checkenvironmentcpu} to see the tip of an iceberg.
|
||||
\end{bclogo}
|
||||
|
||||
\paragraph{Inspecting hardware samples}
|
||||
|
||||
As described in chapter~\ref{hardwaresampling}, on some platforms Tracy is able to capture the internal statistics counted by the CPU hardware. If this data has been collected, a number of additional options become available.
|
||||
|
||||
If the \emph{\faHammer{}~Hardware samples} switch is enabled, the instruction pointer percentages column is supplemented with three additional columns, which show, in order: instructions per cycle, branch miss rate and cache miss rate. Refer to the description of hardware sampling to see how these statistics are calculated. The displayed values are color coded, with green color indicating good execution performance, and red color indicating that the CPU pipeline was stalled due to one reason or another.
|
||||
|
||||
Be aware that these percentage values do not take into account the relative count of events. For example, you may see 100\% cache miss rate when some instruction missed 10 out of 10 cache accesses. While not ideal, this is not as impactful as a seemingly better 50\% cache miss rate instruction, which actually has missed 1000 out of 2000 accesses. You should always cross-check the presented information with the respective event counts. To help a bit with this, Tracy will dim values that are statistically unimportant.
|
||||
|
||||
Another new feature available when hardware samples are present is the \emph{\faHighlighter{}~Cost} selection list, which allows changing what is displayed in the first column of statistics. The following options are available:
|
||||
|
||||
\begin{itemize}
|
||||
\item \emph{Sample count} -- this selects the default instruction pointer statistics, collected by call stack sampling performed by the operating system.
|
||||
\item \emph{Cycles} -- an option very similar to the \emph{sample count}, but the data is collected directly by the CPU hardware counters. This may make the results more reliable.
|
||||
\item \emph{Slow branches} -- indicates places where many branch instructions are issued, and at the same time, incorrectly predicted. Calculated as $\sqrt{\text{\#branch instructions}*\text{\#branch misses}}$. This is more useful than the raw branch miss rate, as it takes into account the number of events taking place.
|
||||
\item \emph{Slow cache} -- similar to \emph{slow branches}, but it shows cache miss data instead. These values are calculated as $\sqrt{\text{\#cache references}*\text{\#cache misses}}$, and will highlight places with lots of cache accesses that also miss.
|
||||
\item The rest of available selections just show raw values gathered from the hardware counters. These are: \emph{Retirements}, \emph{Branches taken}, \emph{Branch miss}, \emph{Cache access} and \emph{Cache miss}.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Lock information window}
|
||||
\label{lockwindow}
|
||||
|
||||
|
@ -104,6 +104,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\common\TracySocket.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracySystem.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4hc.cpp" />
|
||||
@ -126,7 +127,6 @@
|
||||
<ClCompile Include="..\..\..\server\TracySourceContents.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracySourceTokenizer.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracySourceView.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyStackFrames.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyStorage.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyTaskDispatch.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyTexture.cpp" />
|
||||
@ -134,6 +134,7 @@
|
||||
<ClCompile Include="..\..\..\server\TracyThreadCompress.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyUserData.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyView.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyWeb.cpp" />
|
||||
<ClCompile Include="..\..\..\server\TracyWorker.cpp" />
|
||||
<ClCompile Include="..\..\..\zstd\common\debug.c" />
|
||||
<ClCompile Include="..\..\..\zstd\common\entropy_common.c" />
|
||||
@ -184,6 +185,7 @@
|
||||
<ClInclude Include="..\..\..\common\TracyProtocol.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyQueue.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySocket.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyYield.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp" />
|
||||
@ -223,7 +225,6 @@
|
||||
<ClInclude Include="..\..\..\server\TracySourceContents.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracySourceTokenizer.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracySourceView.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyStackFrames.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyStorage.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyStringDiscovery.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyTaskDispatch.hpp" />
|
||||
@ -236,6 +237,7 @@
|
||||
<ClInclude Include="..\..\..\server\TracyVersion.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyView.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyViewData.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyWeb.hpp" />
|
||||
<ClInclude Include="..\..\..\server\TracyWorker.hpp" />
|
||||
<ClInclude Include="..\..\..\server\tracy_pdqsort.h" />
|
||||
<ClInclude Include="..\..\..\server\tracy_robin_hood.h" />
|
||||
|
@ -147,9 +147,6 @@
|
||||
<ClCompile Include="..\..\src\HttpRequest.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\server\TracyStackFrames.cpp">
|
||||
<Filter>server</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\imgui\imgui_tables.cpp">
|
||||
<Filter>imgui</Filter>
|
||||
</ClCompile>
|
||||
@ -249,6 +246,12 @@
|
||||
<ClCompile Include="..\..\..\zstd\dictBuilder\zdict.c">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp">
|
||||
<Filter>common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\server\TracyWeb.cpp">
|
||||
<Filter>server</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp">
|
||||
@ -455,9 +458,6 @@
|
||||
<ClInclude Include="..\..\src\HttpRequest.hpp">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\TracyStackFrames.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\TracySortedVector.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
@ -575,9 +575,15 @@
|
||||
<ClInclude Include="..\..\..\zstd\dictBuilder\divsufsort.h">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\TracyYield.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\server\TracyWeb.hpp">
|
||||
<Filter>server</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="DebugVis.natvis" />
|
||||
|
@ -84,6 +84,7 @@ static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown;
|
||||
static double g_Time = 0.0;
|
||||
static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {};
|
||||
static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
|
||||
static GLFWwindow* g_KeyOwnerWindows[512] = {};
|
||||
static bool g_InstalledCallbacks = false;
|
||||
static bool g_WantUpdateMonitors = true;
|
||||
|
||||
@ -134,10 +135,19 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int a
|
||||
g_PrevUserCallbackKey(window, key, scancode, action, mods);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (action == GLFW_PRESS)
|
||||
io.KeysDown[key] = true;
|
||||
if (action == GLFW_RELEASE)
|
||||
io.KeysDown[key] = false;
|
||||
if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown))
|
||||
{
|
||||
if (action == GLFW_PRESS)
|
||||
{
|
||||
io.KeysDown[key] = true;
|
||||
g_KeyOwnerWindows[key] = window;
|
||||
}
|
||||
if (action == GLFW_RELEASE)
|
||||
{
|
||||
io.KeysDown[key] = false;
|
||||
g_KeyOwnerWindows[key] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Modifiers are not reliable across systems
|
||||
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
|
||||
@ -607,6 +617,13 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport)
|
||||
HWND hwnd = (HWND)viewport->PlatformHandleRaw;
|
||||
::RemovePropA(hwnd, "IMGUI_VIEWPORT");
|
||||
#endif
|
||||
|
||||
// Release any keys that were pressed in the window being destroyed and are still held down,
|
||||
// because we will not receive any release events after window is destroyed.
|
||||
for (int i = 0; i < IM_ARRAYSIZE(g_KeyOwnerWindows); i++)
|
||||
if (g_KeyOwnerWindows[i] == data->Window)
|
||||
ImGui_ImplGlfw_KeyCallback(data->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called.
|
||||
|
||||
glfwDestroyWindow(data->Window);
|
||||
}
|
||||
data->Window = NULL;
|
||||
|
@ -15,6 +15,8 @@
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
|
||||
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
|
||||
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
|
||||
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
|
||||
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
|
||||
@ -273,11 +275,14 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||
#endif
|
||||
|
||||
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
|
||||
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
|
||||
#if defined(GL_CLIP_ORIGIN)
|
||||
bool clip_origin_lower_left = true;
|
||||
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin);
|
||||
if (current_clip_origin == GL_UPPER_LEFT)
|
||||
clip_origin_lower_left = false;
|
||||
if (g_GlVersion >= 450)
|
||||
{
|
||||
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin);
|
||||
if (current_clip_origin == GL_UPPER_LEFT)
|
||||
clip_origin_lower_left = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Setup viewport, orthographic projection matrix
|
||||
@ -287,7 +292,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
|
||||
#if defined(GL_CLIP_ORIGIN)
|
||||
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
|
||||
#endif
|
||||
const float ortho_projection[4][4] =
|
||||
@ -414,7 +419,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
|
||||
|
||||
// Bind texture, Draw
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
if (g_GlVersion >= 320)
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
|
||||
|
@ -22,11 +22,6 @@
|
||||
# include "../nfd/nfd.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ONLY_PNG
|
||||
#include "stb_image.h"
|
||||
@ -43,6 +38,7 @@
|
||||
#include "../../server/TracyStorage.hpp"
|
||||
#include "../../server/TracyVersion.hpp"
|
||||
#include "../../server/TracyView.hpp"
|
||||
#include "../../server/TracyWeb.hpp"
|
||||
#include "../../server/TracyWorker.hpp"
|
||||
#include "../../server/TracyVersion.hpp"
|
||||
#include "../../server/IconsFontAwesome5.h"
|
||||
@ -61,21 +57,6 @@ static void glfw_error_callback(int error, const char* description)
|
||||
fprintf(stderr, "Error %d: %s\n", error, description);
|
||||
}
|
||||
|
||||
static void OpenWebpage( const char* url )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ShellExecuteA( nullptr, nullptr, url, nullptr, nullptr, 0 );
|
||||
#elif defined __APPLE__
|
||||
char buf[1024];
|
||||
sprintf( buf, "open %s", url );
|
||||
system( buf );
|
||||
#else
|
||||
char buf[1024];
|
||||
sprintf( buf, "xdg-open %s", url );
|
||||
system( buf );
|
||||
#endif
|
||||
}
|
||||
|
||||
GLFWwindow* s_glfwWindow = nullptr;
|
||||
static bool s_customTitle = false;
|
||||
static void SetWindowTitleCallback( const char* title )
|
||||
@ -626,7 +607,7 @@ static void DrawContents()
|
||||
ImGui::Spacing();
|
||||
if( ImGui::Button( ICON_FA_BOOK " Manual" ) )
|
||||
{
|
||||
OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
|
||||
tracy::OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( ICON_FA_GLOBE_AMERICAS " Web" ) )
|
||||
@ -637,44 +618,44 @@ static void DrawContents()
|
||||
{
|
||||
if( ImGui::Selectable( ICON_FA_HOME " Tracy Profiler home page" ) )
|
||||
{
|
||||
OpenWebpage( "https://github.com/wolfpld/tracy" );
|
||||
tracy::OpenWebpage( "https://github.com/wolfpld/tracy" );
|
||||
}
|
||||
ImGui::Separator();
|
||||
if( ImGui::Selectable( ICON_FA_VIDEO " Overview of v0.2" ) )
|
||||
{
|
||||
OpenWebpage( "https://www.youtube.com/watch?v=fB5B46lbapc" );
|
||||
tracy::OpenWebpage( "https://www.youtube.com/watch?v=fB5B46lbapc" );
|
||||
}
|
||||
if( ImGui::Selectable( ICON_FA_VIDEO " New features in v0.3" ) )
|
||||
{
|
||||
OpenWebpage( "https://www.youtube.com/watch?v=3SXpDpDh2Uo" );
|
||||
tracy::OpenWebpage( "https://www.youtube.com/watch?v=3SXpDpDh2Uo" );
|
||||
}
|
||||
if( ImGui::Selectable( ICON_FA_VIDEO " New features in v0.4" ) )
|
||||
{
|
||||
OpenWebpage( "https://www.youtube.com/watch?v=eAkgkaO8B9o" );
|
||||
tracy::OpenWebpage( "https://www.youtube.com/watch?v=eAkgkaO8B9o" );
|
||||
}
|
||||
if( ImGui::Selectable( ICON_FA_VIDEO " New features in v0.5" ) )
|
||||
{
|
||||
OpenWebpage( "https://www.youtube.com/watch?v=P6E7qLMmzTQ" );
|
||||
tracy::OpenWebpage( "https://www.youtube.com/watch?v=P6E7qLMmzTQ" );
|
||||
}
|
||||
if( ImGui::Selectable( ICON_FA_VIDEO " New features in v0.6" ) )
|
||||
{
|
||||
OpenWebpage( "https://www.youtube.com/watch?v=uJkrFgriuOo" );
|
||||
tracy::OpenWebpage( "https://www.youtube.com/watch?v=uJkrFgriuOo" );
|
||||
}
|
||||
if( ImGui::Selectable( ICON_FA_VIDEO " New features in v0.7" ) )
|
||||
{
|
||||
OpenWebpage( "https://www.youtube.com/watch?v=_hU7vw00MZ4" );
|
||||
tracy::OpenWebpage( "https://www.youtube.com/watch?v=_hU7vw00MZ4" );
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( ICON_FA_COMMENT " Chat" ) )
|
||||
{
|
||||
OpenWebpage( "https://discord.gg/pk78auc" );
|
||||
tracy::OpenWebpage( "https://discord.gg/pk78auc" );
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( ICON_FA_HEART " Sponsor" ) )
|
||||
{
|
||||
OpenWebpage( "https://github.com/sponsors/wolfpld/" );
|
||||
tracy::OpenWebpage( "https://github.com/sponsors/wolfpld/" );
|
||||
}
|
||||
if( updateVersion != 0 && updateVersion > tracy::FileVersion( tracy::Version::Major, tracy::Version::Minor, tracy::Version::Patch ) )
|
||||
{
|
||||
@ -935,7 +916,7 @@ static void DrawContents()
|
||||
ImGui::Begin( "Update available!", &showReleaseNotes );
|
||||
if( ImGui::Button( ICON_FA_DOWNLOAD " Download" ) )
|
||||
{
|
||||
OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
|
||||
tracy::OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
|
||||
}
|
||||
ImGui::BeginChild( "###notes", ImVec2( 0, 0 ), true );
|
||||
if( releaseNotes.empty() )
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "TracyBadVersion.hpp"
|
||||
#include "TracyImGui.hpp"
|
||||
#include "TracyWeb.hpp"
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
@ -35,7 +36,7 @@ void BadVersionImpl( BadVersionState& badVer )
|
||||
if( ImGui::BeginPopupModal( "Bad file", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
|
||||
{
|
||||
TextCentered( ICON_FA_EXCLAMATION_TRIANGLE );
|
||||
ImGui::Text( "The file you are trying to open is not a tracy dump." );
|
||||
ImGui::Text( "The file you are trying to open is not a Tracy dump." );
|
||||
ImGui::Separator();
|
||||
if( ImGui::Button( "Oops" ) )
|
||||
{
|
||||
@ -59,9 +60,16 @@ void BadVersionImpl( BadVersionState& badVer )
|
||||
if( ImGui::BeginPopupModal( "Unsupported file version", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
|
||||
{
|
||||
TextCentered( ICON_FA_CLOUD_DOWNLOAD_ALT );
|
||||
ImGui::Text( "The file you are trying to open is unsupported.\nYou should update to tracy %i.%i.%i or newer and try again.", badVer.version >> 16, ( badVer.version >> 8 ) & 0xFF, badVer.version & 0xFF );
|
||||
ImGui::Text( "The file you are trying to open is unsupported.\nYou should update to Tracy %i.%i.%i or newer and try again.", badVer.version >> 16, ( badVer.version >> 8 ) & 0xFF, badVer.version & 0xFF );
|
||||
ImGui::Separator();
|
||||
if( ImGui::Button( "I understand" ) )
|
||||
if( ImGui::Button( ICON_FA_DOWNLOAD " Download update" ) )
|
||||
{
|
||||
tracy::OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
|
||||
ImGui::CloseCurrentPopup();
|
||||
badVer.state = BadVersionState::Ok;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( "Maybe later" ) )
|
||||
{
|
||||
ImGui::CloseCurrentPopup();
|
||||
badVer.state = BadVersionState::Ok;
|
||||
|
@ -133,6 +133,11 @@ public:
|
||||
SetVal( val );
|
||||
}
|
||||
|
||||
tracy_force_inline void Clear()
|
||||
{
|
||||
memset( m_val, 0, 6 );
|
||||
}
|
||||
|
||||
tracy_force_inline void SetVal( int64_t val )
|
||||
{
|
||||
memcpy( m_val, &val, 4 );
|
||||
@ -158,6 +163,8 @@ private:
|
||||
uint8_t m_val[6];
|
||||
};
|
||||
|
||||
struct Int48Sort { bool operator()( const Int48& lhs, const Int48& rhs ) { return lhs.Val() < rhs.Val(); }; };
|
||||
|
||||
|
||||
struct SourceLocationBase
|
||||
{
|
||||
@ -251,6 +258,40 @@ struct SampleDataRange
|
||||
enum { SampleDataRangeSize = sizeof( SampleDataRange ) };
|
||||
|
||||
|
||||
struct HwSampleData
|
||||
{
|
||||
SortedVector<Int48, Int48Sort> cycles;
|
||||
SortedVector<Int48, Int48Sort> retired;
|
||||
SortedVector<Int48, Int48Sort> cacheRef;
|
||||
SortedVector<Int48, Int48Sort> cacheMiss;
|
||||
SortedVector<Int48, Int48Sort> branchRetired;
|
||||
SortedVector<Int48, Int48Sort> branchMiss;
|
||||
|
||||
bool is_sorted() const
|
||||
{
|
||||
return
|
||||
cycles.is_sorted() &&
|
||||
retired.is_sorted() &&
|
||||
cacheRef.is_sorted() &&
|
||||
cacheMiss.is_sorted() &&
|
||||
branchRetired.is_sorted() &&
|
||||
branchMiss.is_sorted();
|
||||
}
|
||||
|
||||
void sort()
|
||||
{
|
||||
if( !cycles.is_sorted() ) cycles.sort();
|
||||
if( !retired.is_sorted() ) retired.sort();
|
||||
if( !cacheRef.is_sorted() ) cacheRef.sort();
|
||||
if( !cacheMiss.is_sorted() ) cacheMiss.sort();
|
||||
if( !branchRetired.is_sorted() ) branchRetired.sort();
|
||||
if( !branchMiss.is_sorted() ) branchMiss.sort();
|
||||
}
|
||||
};
|
||||
|
||||
enum { HwSampleDataSize = sizeof( HwSampleData ) };
|
||||
|
||||
|
||||
struct LockEvent
|
||||
{
|
||||
enum class Type : uint8_t
|
||||
@ -561,6 +602,8 @@ struct ThreadData
|
||||
uint64_t ghostIdx;
|
||||
#endif
|
||||
Vector<SampleData> samples;
|
||||
SampleData pendingSample;
|
||||
uint64_t kernelSampleCnt;
|
||||
};
|
||||
|
||||
struct GpuCtxThreadData
|
||||
@ -581,6 +624,9 @@ struct GpuCtxData
|
||||
int64_t calibratedGpuTime;
|
||||
int64_t calibratedCpuTime;
|
||||
double calibrationMod;
|
||||
int64_t lastGpuTime;
|
||||
uint64_t overflow;
|
||||
uint32_t overflowMul;
|
||||
StringIdx name;
|
||||
unordered_flat_map<uint64_t, GpuCtxThreadData> threadData;
|
||||
short_ptr<GpuEvent> query[64*1024];
|
||||
|
@ -90,7 +90,6 @@ public:
|
||||
ReadBig( &tmp, sizeof( T ) );
|
||||
memcpy( &v, &tmp, sizeof( T ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
|
@ -245,6 +245,12 @@ static const ImVec4 SyntaxColorsDimmed[] = {
|
||||
draw->AddPolyline( data, 2, col, 0, thickness );
|
||||
}
|
||||
|
||||
[[maybe_unused]] static tracy_force_inline void DrawLine( ImDrawList* draw, const ImVec2& v1, const ImVec2& v2, const ImVec2& v3, uint32_t col, float thickness = 1.0f )
|
||||
{
|
||||
const ImVec2 data[3] = { v1, v2, v3 };
|
||||
draw->AddPolyline( data, 3, col, 0, thickness );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because one or more lines are too long
@ -2,15 +2,21 @@
|
||||
#define __TRACYPOPCNT_HPP__
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined _WIN64
|
||||
# include <intrin.h>
|
||||
# define TracyCountBits __popcnt64
|
||||
# define TracyLzcnt __lzcnt64
|
||||
#elif defined __GNUC__ || defined __clang__
|
||||
static inline uint64_t TracyCountBits( uint64_t i )
|
||||
{
|
||||
return uint64_t( __builtin_popcountll( i ) );
|
||||
}
|
||||
static inline uint64_t TracyLzcnt( uint64_t i )
|
||||
{
|
||||
return uint64_t( __builtin_clzll( i ) );
|
||||
}
|
||||
#else
|
||||
static inline uint64_t TracyCountBits( uint64_t i )
|
||||
{
|
||||
@ -19,6 +25,16 @@ static inline uint64_t TracyCountBits( uint64_t i )
|
||||
i = ( (i + (i >> 4) ) & 0x0F0F0F0F0F0F0F0F );
|
||||
return ( i * (0x0101010101010101) ) >> 56;
|
||||
}
|
||||
static inline uint64_t TracyLzcnt( uint64_t i )
|
||||
{
|
||||
i |= i >> 1;
|
||||
i |= i >> 2;
|
||||
i |= i >> 4;
|
||||
i |= i >> 8;
|
||||
i |= i >> 16;
|
||||
i |= i >> 32;
|
||||
return 64 - TracyCountBits( i );
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning( disable: 4244 ) // conversion from don't care to whatever, possible loss of data
|
||||
# pragma warning( disable: 4244 ) // conversion from don't care to whatever, possible loss of data
|
||||
#endif
|
||||
#ifdef __MINGW32__
|
||||
# define __STDC_FORMAT_MACROS
|
||||
@ -411,6 +411,20 @@ const char* MemSizeToString( int64_t val )
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char* LocationToString( const char* fn, uint32_t line )
|
||||
{
|
||||
if( line == 0 ) return fn;
|
||||
|
||||
enum { Pool = 8 };
|
||||
static char bufpool[Pool][4096];
|
||||
static int bufsel = 0;
|
||||
char* buf = bufpool[bufsel];
|
||||
bufsel = ( bufsel + 1 ) % Pool;
|
||||
|
||||
sprintf( buf, "%s:%i", fn, line );
|
||||
return buf;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
|
@ -126,6 +126,7 @@ static inline const char* RealToString( double val )
|
||||
const char* TimeToString( int64_t ns );
|
||||
const char* TimeToStringExact( int64_t ns );
|
||||
const char* MemSizeToString( int64_t val );
|
||||
const char* LocationToString( const char* fn, uint32_t line );
|
||||
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,19 @@ public:
|
||||
NUMBER_OF_ENTRIES
|
||||
};
|
||||
|
||||
enum class CostType
|
||||
{
|
||||
SampleCount,
|
||||
Cycles,
|
||||
SlowBranches,
|
||||
SlowCache,
|
||||
Retirements,
|
||||
BranchesTaken,
|
||||
BranchMiss,
|
||||
CacheAccess,
|
||||
CacheMiss
|
||||
};
|
||||
|
||||
private:
|
||||
struct AsmOpParams
|
||||
{
|
||||
@ -102,8 +115,8 @@ private:
|
||||
|
||||
struct AddrStat
|
||||
{
|
||||
uint32_t local;
|
||||
uint32_t ext;
|
||||
uint64_t local;
|
||||
uint64_t ext;
|
||||
|
||||
AddrStat& operator+=( const AddrStat& other )
|
||||
{
|
||||
@ -122,7 +135,7 @@ public:
|
||||
|
||||
void OpenSource( const char* fileName, int line, const View& view, const Worker& worker );
|
||||
void OpenSymbol( const char* fileName, int line, uint64_t baseAddr, uint64_t symAddr, const Worker& worker, const View& view );
|
||||
void Render( const Worker& worker, View& view );
|
||||
void Render( Worker& worker, View& view );
|
||||
|
||||
void CalcInlineStats( bool val ) { m_calcInlineStats = val; }
|
||||
bool IsSymbolView() const { return !m_asm.empty(); }
|
||||
@ -134,18 +147,20 @@ private:
|
||||
void SelectViewMode();
|
||||
|
||||
void RenderSimpleSourceView();
|
||||
void RenderSymbolView( const Worker& worker, View& view );
|
||||
void RenderSymbolView( Worker& worker, View& view );
|
||||
|
||||
void RenderSymbolSourceView( const AddrStat& iptotal, const unordered_flat_map<uint64_t, AddrStat>& ipcount, const unordered_flat_map<uint64_t, AddrStat>& ipcountAsm, const AddrStat& ipmax, const Worker& worker, const View& view );
|
||||
uint64_t RenderSymbolAsmView( const AddrStat& iptotal, const unordered_flat_map<uint64_t, AddrStat>& ipcount, const AddrStat& ipmax, const Worker& worker, View& view );
|
||||
void RenderSymbolSourceView( const AddrStat& iptotal, const unordered_flat_map<uint64_t, AddrStat>& ipcount, const unordered_flat_map<uint64_t, AddrStat>& ipcountAsm, const AddrStat& ipmax, Worker& worker, const View& view );
|
||||
uint64_t RenderSymbolAsmView( const AddrStat& iptotal, const unordered_flat_map<uint64_t, AddrStat>& ipcount, const AddrStat& ipmax, Worker& worker, View& view );
|
||||
|
||||
void RenderLine( const Tokenizer::Line& line, int lineNum, const AddrStat& ipcnt, const AddrStat& iptotal, const AddrStat& ipmax, const Worker* worker );
|
||||
void RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const AddrStat& iptotal, const AddrStat& ipmax, const Worker& worker, uint64_t& jumpOut, int maxAddrLen, View& view );
|
||||
void RenderLine( const Tokenizer::Line& line, int lineNum, const AddrStat& ipcnt, const AddrStat& iptotal, const AddrStat& ipmax, Worker* worker, const View* view );
|
||||
void RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const AddrStat& iptotal, const AddrStat& ipmax, Worker& worker, uint64_t& jumpOut, int maxAddrLen, View& view );
|
||||
void RenderHwLinePart( size_t cycles, size_t retired, size_t branchRetired, size_t branchMiss, size_t cacheRef, size_t cacheMiss, const ImVec2& ts );
|
||||
|
||||
void SelectLine( uint32_t line, const Worker* worker, bool changeAsmLine = true, uint64_t targetAddr = 0 );
|
||||
void SelectAsmLines( uint32_t file, uint32_t line, const Worker& worker, bool changeAsmLine = true, uint64_t targetAddr = 0 );
|
||||
void SelectAsmLinesHover( uint32_t file, uint32_t line, const Worker& worker );
|
||||
|
||||
void GatherIpHwStats( AddrStat& iptotalSrc, AddrStat& iptotalAsm, unordered_flat_map<uint64_t, AddrStat>& ipcountSrc, unordered_flat_map<uint64_t, AddrStat>& ipcountAsm, AddrStat& ipmaxSrc, AddrStat& ipmaxAsm, Worker& worker, bool limitView, const View& view );
|
||||
void GatherIpStats( uint64_t baseAddr, AddrStat& iptotalSrc, AddrStat& iptotalAsm, unordered_flat_map<uint64_t, AddrStat>& ipcountSrc, unordered_flat_map<uint64_t, AddrStat>& ipcountAsm, AddrStat& ipmaxSrc, AddrStat& ipmaxAsm, const Worker& worker, bool limitView, const View& view );
|
||||
void GatherAdditionalIpStats( uint64_t baseAddr, AddrStat& iptotalSrc, AddrStat& iptotalAsm, unordered_flat_map<uint64_t, AddrStat>& ipcountSrc, unordered_flat_map<uint64_t, AddrStat>& ipcountAsm, AddrStat& ipmaxSrc, AddrStat& ipmaxAsm, const Worker& worker, bool limitView, const View& view );
|
||||
uint32_t CountAsmIpStats( uint64_t baseAddr, const Worker& worker, bool limitView, const View& view );
|
||||
@ -183,7 +198,9 @@ private:
|
||||
uint8_t m_maxAsmBytes;
|
||||
bool m_atnt;
|
||||
uint64_t m_jumpPopupAddr;
|
||||
bool m_hwSamples;
|
||||
bool m_childCalls;
|
||||
CostType m_cost;
|
||||
|
||||
SourceContents m_source;
|
||||
SourceContents m_sourceTooltip;
|
||||
|
@ -1,26 +0,0 @@
|
||||
#include "TracyStackFrames.hpp"
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
const char* s_tracyStackFrames_[] = {
|
||||
"tracy::Callstack",
|
||||
"tracy::Callstack(int)",
|
||||
"tracy::GpuCtxScope::{ctor}",
|
||||
"tracy::Profiler::SendCallstack",
|
||||
"tracy::Profiler::SendCallstack(int)",
|
||||
"tracy::Profiler::SendCallstack(int, unsigned long)",
|
||||
"tracy::Profiler::MemAllocCallstack",
|
||||
"tracy::Profiler::MemAllocCallstack(void const*, unsigned long, int)",
|
||||
"tracy::Profiler::MemFreeCallstack",
|
||||
"tracy::Profiler::MemFreeCallstack(void const*, int)",
|
||||
"tracy::ScopedZone::{ctor}",
|
||||
"tracy::ScopedZone::ScopedZone(tracy::SourceLocationData const*, int, bool)",
|
||||
"tracy::CallTrace",
|
||||
"tracy::Profiler::Message",
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char** s_tracyStackFrames = s_tracyStackFrames_;
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#ifndef __TRACYSTACKFRAMES_HPP__
|
||||
#define __TRACYSTACKFRAMES_HPP__
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
extern const char** s_tracyStackFrames;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -7,7 +7,7 @@ namespace Version
|
||||
{
|
||||
enum { Major = 0 };
|
||||
enum { Minor = 7 };
|
||||
enum { Patch = 8 };
|
||||
enum { Patch = 9 };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@
|
||||
#include "TracyPrint.hpp"
|
||||
#include "TracySort.hpp"
|
||||
#include "TracySourceView.hpp"
|
||||
#include "TracyStackFrames.hpp"
|
||||
#include "TracyView.hpp"
|
||||
#include "../common/TracyStackFrames.hpp"
|
||||
|
||||
#include "imgui_internal.h"
|
||||
|
||||
@ -472,7 +472,7 @@ bool View::Draw()
|
||||
TextFocused( "Function:", s_instance->m_worker.GetString( srcloc.function ) );
|
||||
TextDisabledUnformatted( "Location:" );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "%s:%i", s_instance->m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( s_instance->m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
}
|
||||
if( data.thread != 0 )
|
||||
{
|
||||
@ -581,14 +581,7 @@ bool View::Draw()
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushTextWrapPos( 0.0f );
|
||||
txt = s_instance->m_worker.GetString( frame.file );
|
||||
if( frame.line == 0 )
|
||||
{
|
||||
TextDisabledUnformatted( txt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", txt, frame.line );
|
||||
}
|
||||
TextDisabledUnformatted( LocationToString( txt, frame.line ) );
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
@ -646,7 +639,7 @@ bool View::Draw()
|
||||
{
|
||||
if( ImGui::RadioButton( CompressionName[idx], (int)comp == idx ) ) comp = (FileWrite::Compression)idx;
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled( CompressionDesc[idx] );
|
||||
TextDisabledUnformatted( CompressionDesc[idx] );
|
||||
idx++;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
@ -654,7 +647,10 @@ bool View::Draw()
|
||||
ImGui::SameLine();
|
||||
TextDisabledUnformatted( "Increasing level decreases file size, but increases save and load times" );
|
||||
ImGui::Indent();
|
||||
ImGui::SliderInt( "##zstd", &zlvl, 1, 22, "%d", ImGuiSliderFlags_AlwaysClamp );
|
||||
if( ImGui::SliderInt( "##zstd", &zlvl, 1, 22, "%d", ImGuiSliderFlags_AlwaysClamp ) )
|
||||
{
|
||||
comp = FileWrite::Compression::Zstd;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
static bool buildDict = false;
|
||||
@ -3143,10 +3139,18 @@ void View::DrawZones()
|
||||
}
|
||||
}
|
||||
TextFocused( "Zone count:", RealToString( v->count ) );
|
||||
if( isMultithreaded )
|
||||
if( v->period != 1.f )
|
||||
{
|
||||
TextFocused( "Timestamp accuracy:", TimeToString( v->period ) );
|
||||
}
|
||||
if( v->overflow != 0 )
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted( "GPU timer overflow has been detected." );
|
||||
TextFocused( "Timer resolution:", RealToString( 63 - TracyLzcnt( v->overflow ) ) );
|
||||
ImGui::SameLine();
|
||||
TextDisabledUnformatted( "bits" );
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
@ -3518,6 +3522,12 @@ void View::DrawZones()
|
||||
if( !v->samples.empty() )
|
||||
{
|
||||
TextFocused( "Call stack samples:", RealToString( v->samples.size() ) );
|
||||
if( v->kernelSampleCnt != 0 )
|
||||
{
|
||||
TextFocused( "Kernel samples:", RealToString( v->kernelSampleCnt ) );
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled( "(%.2f%%)", 100.f * v->kernelSampleCnt / v->samples.size() );
|
||||
}
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
|
||||
@ -4093,6 +4103,7 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
|
||||
const auto ostep = ty + 1;
|
||||
const auto offset = _offset + ostep * depth;
|
||||
auto draw = ImGui::GetWindowDrawList();
|
||||
const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
|
||||
|
||||
depth++;
|
||||
int maxdepth = depth;
|
||||
@ -4163,7 +4174,6 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
|
||||
{
|
||||
color = MixGhostColor( GetThreadColor( tid, depth ), 0x665555 );
|
||||
}
|
||||
const auto outline = HighlightColor( color );
|
||||
|
||||
const auto pr0 = ( ev.start.Val() - m_vd.zvStart ) * pxns;
|
||||
const auto pr1 = ( ev.end.Val() - m_vd.zvStart ) * pxns;
|
||||
@ -4175,9 +4185,12 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
|
||||
sprintf( symName, "0x%" PRIx64, m_worker.GetCanonicalPointer( ghostKey.frame ) );
|
||||
const auto tsz = ImGui::CalcTextSize( symName );
|
||||
|
||||
const auto accentColor = HighlightColor( color );
|
||||
const auto darkColor = DarkenColor( color );
|
||||
const auto txtColor = 0xFF888888;
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), DarkenColor( color ) );
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), outline, 0.f, -1 );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), accentColor, 1.f );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, 1.f );
|
||||
|
||||
if( tsz.x < zsz )
|
||||
{
|
||||
@ -4229,11 +4242,26 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
|
||||
const auto isInline = ghostKey.inlineFrame != frame->size-1;
|
||||
const auto col = isInline ? DarkenColor( color ) : color;
|
||||
auto symName = m_worker.GetString( sym.name );
|
||||
uint32_t txtColor = symName[0] == '[' ? 0xFF999999 : 0xFFFFFFFF;
|
||||
uint32_t txtColor;
|
||||
if( symName[0] == '[' )
|
||||
{
|
||||
txtColor = 0xFF999999;
|
||||
}
|
||||
else if( !isInline && ( m_worker.GetCanonicalPointer( ghostKey.frame ) >> 63 != 0 ) )
|
||||
{
|
||||
txtColor = 0xFF8888FF;
|
||||
}
|
||||
else
|
||||
{
|
||||
txtColor = 0xFFFFFFFF;
|
||||
}
|
||||
auto tsz = ImGui::CalcTextSize( symName );
|
||||
|
||||
const auto accentColor = HighlightColor( col );
|
||||
const auto darkColor = DarkenColor( col );
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), col );
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), outline, 0.f, -1 );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), accentColor, 1.f );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, 1.f );
|
||||
|
||||
auto origSymName = symName;
|
||||
if( tsz.x > zsz )
|
||||
@ -4272,6 +4300,11 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
|
||||
if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.start.Val(), ev.end.Val(), true };
|
||||
ImGui::BeginTooltip();
|
||||
TextDisabledUnformatted( ICON_FA_GHOST " Ghost zone" );
|
||||
if( sym.symAddr >> 63 != 0 )
|
||||
{
|
||||
ImGui::SameLine();
|
||||
TextDisabledUnformatted( ICON_FA_HAT_WIZARD " kernel" );
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted( origSymName );
|
||||
if( isInline )
|
||||
@ -4285,7 +4318,7 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
|
||||
ImGui::SameLine();
|
||||
const char* file = m_worker.GetString( sym.file );
|
||||
uint32_t line = sym.line;
|
||||
ImGui::Text( "%s:%i", file, line );
|
||||
ImGui::TextUnformatted( LocationToString( file, line ) );
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled( "(0x%" PRIx64 ")", sym.symAddr );
|
||||
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
|
||||
@ -4510,7 +4543,7 @@ int View::DrawZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, co
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto color = GetZoneColor( ev, tid, depth );
|
||||
const auto zoneColor = GetZoneColorData( ev, tid, depth );
|
||||
const char* zoneName = m_worker.GetZoneName( ev );
|
||||
|
||||
if( ev.HasChildren() )
|
||||
@ -4530,8 +4563,17 @@ int View::DrawZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, co
|
||||
const auto pr1 = ( end - m_vd.zvStart ) * pxns;
|
||||
const auto px0 = std::max( pr0, -10.0 );
|
||||
const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), color );
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), GetZoneHighlight( ev, tid, depth ), 0.f, -1, GetZoneThickness( ev ) );
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color );
|
||||
if( zoneColor.highlight )
|
||||
{
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto darkColor = DarkenColor( zoneColor.color );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness );
|
||||
}
|
||||
if( dsz > MinVisSize )
|
||||
{
|
||||
const auto diff = dsz - MinVisSize;
|
||||
@ -4733,6 +4775,7 @@ int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx,
|
||||
const auto ostep = ty + 1;
|
||||
const auto offset = _offset + ostep * depth;
|
||||
auto draw = ImGui::GetWindowDrawList();
|
||||
const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
|
||||
|
||||
depth++;
|
||||
int maxdepth = depth;
|
||||
@ -4741,7 +4784,6 @@ int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx,
|
||||
while( it < zitend )
|
||||
{
|
||||
auto& ev = a(*it);
|
||||
const auto color = GetZoneColor( ev );
|
||||
auto end = m_worker.GetZoneEnd( ev );
|
||||
if( end == std::numeric_limits<int64_t>::max() ) break;
|
||||
const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift );
|
||||
@ -4749,6 +4791,7 @@ int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx,
|
||||
const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 );
|
||||
if( zsz < MinVisSize )
|
||||
{
|
||||
const auto color = GetZoneColor( ev );
|
||||
const auto MinVisNs = MinVisSize * nspx;
|
||||
int num = 0;
|
||||
const auto px0 = ( start - m_vd.zvStart ) * pxns;
|
||||
@ -4829,8 +4872,18 @@ int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx,
|
||||
const auto pr1 = ( end - m_vd.zvStart ) * pxns;
|
||||
const auto px0 = std::max( pr0, -10.0 );
|
||||
const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), color );
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), GetZoneHighlight( ev ), 0.f, -1, GetZoneThickness( ev ) );
|
||||
const auto zoneColor = GetZoneColorData( ev );
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color );
|
||||
if( zoneColor.highlight )
|
||||
{
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto darkColor = DarkenColor( zoneColor.color );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness );
|
||||
}
|
||||
if( tsz.x < zsz )
|
||||
{
|
||||
const auto x = ( start - m_vd.zvStart ) * pxns + ( ( end - start ) * pxns - tsz.x ) / 2;
|
||||
@ -5208,7 +5261,7 @@ void View::DrawLockHeader( uint32_t id, const LockMap& lockmap, const SourceLoca
|
||||
assert( false );
|
||||
break;
|
||||
}
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
ImGui::Separator();
|
||||
TextFocused( ICON_FA_RANDOM " Appeared at", TimeToString( range.start ) );
|
||||
TextFocused( ICON_FA_RANDOM " Last event at", TimeToString( range.end ) );
|
||||
@ -5506,7 +5559,7 @@ int View::DrawLocks( uint64_t tid, bool hover, double pxns, const ImVec2& wpos,
|
||||
ImGui::Text( "Lock #%" PRIu32 ": %s", v.first, m_worker.GetString( srcloc.function ) );
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
TextFocused( "Time:", TimeToString( t1 - t0 ) );
|
||||
ImGui::Separator();
|
||||
|
||||
@ -5530,7 +5583,7 @@ int View::DrawLocks( uint64_t tid, bool hover, double pxns, const ImVec2& wpos,
|
||||
const auto& marklocdata = m_worker.GetSourceLocation( markloc );
|
||||
ImGui::TextUnformatted( "Lock event location:" );
|
||||
ImGui::TextUnformatted( m_worker.GetString( marklocdata.function ) );
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( marklocdata.file ), marklocdata.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( marklocdata.file ), marklocdata.line ) );
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
@ -5888,49 +5941,51 @@ int View::DrawCpuData( int offset, double pxns, const ImVec2& wpos, bool hover,
|
||||
const auto cpuUsageHeight = floor( 30.f * ImGui::GetTextLineHeight() / 15.f );
|
||||
if( wpos.y + offset + cpuUsageHeight + 3 >= yMin && wpos.y + offset <= yMax )
|
||||
{
|
||||
const auto iw = (size_t)w;
|
||||
m_worker.GetCpuUsage( m_vd.zvStart, nspxdbl, iw, m_cpuUsageBuf );
|
||||
|
||||
const float cpuCntRev = 1.f / cpuCnt;
|
||||
float pos = 0;
|
||||
int usageOwn, usageOther;
|
||||
auto usage = m_cpuUsageBuf.begin();
|
||||
while( pos < w )
|
||||
{
|
||||
m_worker.GetCpuUsageAtTime( m_vd.zvStart + pos * nspxdbl, usageOwn, usageOther );
|
||||
float base;
|
||||
if( usageOwn != 0 )
|
||||
if( usage->first != 0 )
|
||||
{
|
||||
base = dpos.y + offset + ( 1.f - usageOwn * cpuCntRev ) * cpuUsageHeight;
|
||||
base = dpos.y + offset + ( 1.f - usage->first * cpuCntRev ) * cpuUsageHeight;
|
||||
DrawLine( draw, ImVec2( dpos.x + pos, dpos.y + offset + cpuUsageHeight ), ImVec2( dpos.x + pos, base ), 0xFF55BB55 );
|
||||
}
|
||||
else
|
||||
{
|
||||
base = dpos.y + offset + cpuUsageHeight;
|
||||
}
|
||||
if( usageOther != 0 )
|
||||
if( usage->second != 0 )
|
||||
{
|
||||
int usageTotal = usageOwn + usageOther;
|
||||
int usageTotal = usage->first + usage->second;
|
||||
DrawLine( draw, ImVec2( dpos.x + pos, base ), ImVec2( dpos.x + pos, dpos.y + offset + ( 1.f - usageTotal * cpuCntRev ) * cpuUsageHeight ), 0xFF666666 );
|
||||
}
|
||||
pos++;
|
||||
usage++;
|
||||
}
|
||||
DrawLine( draw, dpos + ImVec2( 0, offset+cpuUsageHeight+2 ), dpos + ImVec2( w, offset+cpuUsageHeight+2 ), 0x22DD88DD );
|
||||
|
||||
if( hover && ImGui::IsMouseHoveringRect( ImVec2( wpos.x, wpos.y + offset ), ImVec2( wpos.x + w, wpos.y + offset + cpuUsageHeight ), true ) )
|
||||
{
|
||||
const auto mt = m_vd.zvStart + ( ImGui::GetIO().MousePos.x - wpos.x ) * nspxdbl;
|
||||
int usageOwn, usageOther;
|
||||
m_worker.GetCpuUsageAtTime( mt, usageOwn, usageOther );
|
||||
const auto& usage = m_cpuUsageBuf[ImGui::GetIO().MousePos.x - wpos.x];
|
||||
ImGui::BeginTooltip();
|
||||
TextFocused( "Cores used by profiled program:", RealToString( usageOwn ) );
|
||||
TextFocused( "Cores used by profiled program:", RealToString( usage.first ) );
|
||||
ImGui::SameLine();
|
||||
char buf[64];
|
||||
PrintStringPercent( buf, usageOwn * cpuCntRev * 100 );
|
||||
PrintStringPercent( buf, usage.first * cpuCntRev * 100 );
|
||||
TextDisabledUnformatted( buf );
|
||||
TextFocused( "Cores used by other programs:", RealToString( usageOther ) );
|
||||
TextFocused( "Cores used by other programs:", RealToString( usage.second ) );
|
||||
ImGui::SameLine();
|
||||
PrintStringPercent( buf, usageOther * cpuCntRev * 100 );
|
||||
PrintStringPercent( buf, usage.second * cpuCntRev * 100 );
|
||||
TextDisabledUnformatted( buf );
|
||||
TextFocused( "Number of cores:", RealToString( cpuCnt ) );
|
||||
if( usageOwn + usageOther != 0 )
|
||||
if( usage.first + usage.second != 0 )
|
||||
{
|
||||
const auto mt = m_vd.zvStart + ( ImGui::GetIO().MousePos.x - wpos.x ) * nspxdbl;
|
||||
ImGui::Separator();
|
||||
for( int i=0; i<cpuCnt; i++ )
|
||||
{
|
||||
@ -6072,7 +6127,7 @@ int View::DrawCpuData( int offset, double pxns, const ImVec2& wpos, bool hover,
|
||||
const auto px0 = std::max( pr0, -10.0 );
|
||||
const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
|
||||
|
||||
uint32_t color, highlight;
|
||||
uint32_t color;
|
||||
if( m_vd.dynamicColors != 0 )
|
||||
{
|
||||
color = local ? GetThreadColor( thread, 0 ) : ( untracked ? 0xFF663333 : 0xFF444444 );
|
||||
@ -6081,18 +6136,20 @@ int View::DrawCpuData( int offset, double pxns, const ImVec2& wpos, bool hover,
|
||||
{
|
||||
color = local ? 0xFF334488 : ( untracked ? 0xFF663333 : 0xFF444444 );
|
||||
}
|
||||
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + sty ), color );
|
||||
if( m_drawThreadHighlight == thread )
|
||||
{
|
||||
highlight = 0xFFFFFFFF;
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + sty ), 0xFFFFFFFF );
|
||||
}
|
||||
else
|
||||
{
|
||||
highlight = HighlightColor( color );
|
||||
const auto accentColor = HighlightColor( color );
|
||||
const auto darkColor = DarkenColor( color );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + sty ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), accentColor, 1.f );
|
||||
DrawLine( draw, dpos + ImVec2( px0, offset + sty ), dpos + ImVec2( px1-1, offset + sty ), dpos + ImVec2( px1-1, offset ), darkColor, 1.f );
|
||||
}
|
||||
|
||||
draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + sty ), color );
|
||||
draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + sty ), highlight );
|
||||
|
||||
auto tsz = ImGui::CalcTextSize( label );
|
||||
if( tsz.x < zsz )
|
||||
{
|
||||
@ -6852,14 +6909,7 @@ void DrawZoneTrace( T zone, const std::vector<T>& trace, const Worker& worker, B
|
||||
ImGui::SameLine();
|
||||
}
|
||||
const auto fileName = worker.GetString( frame->file );
|
||||
if( frame->line == 0 )
|
||||
{
|
||||
TextDisabledUnformatted( fileName );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", fileName, frame->line );
|
||||
}
|
||||
TextDisabledUnformatted( LocationToString( fileName, frame->line ) );
|
||||
if( ImGui::IsItemClicked( 1 ) )
|
||||
{
|
||||
if( !view.ViewDispatch( fileName, frame->line, frame->symAddr ) )
|
||||
@ -6910,14 +6960,7 @@ void DrawZoneTrace( T zone, const std::vector<T>& trace, const Worker& worker, B
|
||||
ImGui::SameLine();
|
||||
}
|
||||
const auto fileName = worker.GetString( frame->file );
|
||||
if( frame->line == 0 )
|
||||
{
|
||||
TextDisabledUnformatted( fileName );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", fileName, frame->line );
|
||||
}
|
||||
TextDisabledUnformatted( LocationToString( fileName, frame->line ) );
|
||||
if( ImGui::IsItemClicked( 1 ) )
|
||||
{
|
||||
if( !view.ViewDispatch( fileName, frame->line, frame->symAddr ) )
|
||||
@ -7168,13 +7211,11 @@ void View::DrawZoneInfoWindow()
|
||||
ImGui::SameLine();
|
||||
TextDisabledUnformatted( "Location:" );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
ImGui::SameLine();
|
||||
if( ClipboardButton( 3 ) )
|
||||
{
|
||||
char tmp[1024];
|
||||
sprintf( tmp, "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::SetClipboardText( tmp );
|
||||
ImGui::SetClipboardText( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
}
|
||||
SmallColorBox( GetThreadColor( tid, 0 ) );
|
||||
ImGui::SameLine();
|
||||
@ -7666,7 +7707,7 @@ void View::DrawZoneInfoWindow()
|
||||
{
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::TextDisabled( "(%s) %s:%i", TimeToString( m_worker.GetZoneEnd( *v ) - v->Start() ), fileName, srcloc.line );
|
||||
ImGui::TextDisabled( "(%s) %s", TimeToString( m_worker.GetZoneEnd( *v ) - v->Start() ), LocationToString( fileName, srcloc.line ) );
|
||||
ImGui::PopID();
|
||||
if( ImGui::IsItemClicked( 1 ) )
|
||||
{
|
||||
@ -7939,7 +7980,7 @@ void View::DrawZoneInfoChildren( const V& children, int64_t ztime )
|
||||
}
|
||||
ImGui::TextUnformatted( m_worker.GetString( srcloc.function ) );
|
||||
ImGui::Separator();
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
@ -8144,17 +8185,15 @@ void View::DrawGpuInfoWindow()
|
||||
TextFocused( "Function:", m_worker.GetString( srcloc.function ) );
|
||||
ImGui::SameLine();
|
||||
if( ClipboardButton( 2 ) ) ImGui::SetClipboardText( m_worker.GetString( srcloc.function ) );
|
||||
SmallColorBox( GetRawZoneColor( ev ) );
|
||||
SmallColorBox( GetZoneColor( ev ) );
|
||||
ImGui::SameLine();
|
||||
TextDisabledUnformatted( "Location:" );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
ImGui::SameLine();
|
||||
if( ClipboardButton( 3 ) )
|
||||
{
|
||||
char tmp[1024];
|
||||
sprintf( tmp, "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::SetClipboardText( tmp );
|
||||
ImGui::SetClipboardText( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
}
|
||||
SmallColorBox( GetThreadColor( tid, 0 ) );
|
||||
ImGui::SameLine();
|
||||
@ -8229,7 +8268,7 @@ void View::DrawGpuInfoWindow()
|
||||
{
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::TextDisabled( "(%s) %s:%i", TimeToString( m_worker.GetZoneEnd( *v ) - v->GpuStart() ), fileName, srcloc.line );
|
||||
ImGui::TextDisabled( "(%s) %s", TimeToString( m_worker.GetZoneEnd( *v ) - v->GpuStart() ), LocationToString( fileName, srcloc.line ) );
|
||||
ImGui::PopID();
|
||||
if( ImGui::IsItemClicked( 1 ) )
|
||||
{
|
||||
@ -8381,7 +8420,7 @@ void View::DrawGpuInfoChildren( const V& children, int64_t ztime )
|
||||
}
|
||||
ImGui::TextUnformatted( m_worker.GetString( srcloc.function ) );
|
||||
ImGui::Separator();
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
@ -8510,12 +8549,14 @@ void View::DrawOptions()
|
||||
m_vd.drawFrameTargets = val;
|
||||
ImGui::Indent();
|
||||
int tmp = m_vd.frameTarget;
|
||||
ImGui::SetNextItemWidth( 120 );
|
||||
ImGui::SetNextItemWidth( 90 );
|
||||
if( ImGui::InputInt( "Target FPS", &tmp ) )
|
||||
{
|
||||
if( tmp < 1 ) tmp = 1;
|
||||
m_vd.frameTarget = tmp;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
TextDisabledUnformatted( TimeToString( 1000*1000*1000 / tmp ) );
|
||||
ImGui::Unindent();
|
||||
if( m_worker.HasContextSwitches() )
|
||||
{
|
||||
@ -8822,7 +8863,7 @@ void View::DrawOptions()
|
||||
{
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::TextDisabled( "(%s) %s:%i", RealToString( l.second->timeline.size() ), fileName, sl.line );
|
||||
ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
DrawSourceTooltip( fileName, sl.line, 1, 1 );
|
||||
@ -8900,7 +8941,7 @@ void View::DrawOptions()
|
||||
{
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::TextDisabled( "(%s) %s:%i", RealToString( l.second->timeline.size() ), fileName, sl.line );
|
||||
ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
DrawSourceTooltip( fileName, sl.line, 1, 1 );
|
||||
@ -8978,7 +9019,7 @@ void View::DrawOptions()
|
||||
{
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::TextDisabled( "(%s) %s:%i", RealToString( l.second->timeline.size() ), fileName, sl.line );
|
||||
ImGui::TextDisabled( "(%s) %s", RealToString( l.second->timeline.size() ), LocationToString( fileName, sl.line ) );
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
DrawSourceTooltip( fileName, sl.line, 1, 1 );
|
||||
@ -9639,7 +9680,7 @@ void View::DrawFindZone()
|
||||
ImGui::SameLine();
|
||||
}
|
||||
const auto fileName = m_worker.GetString( srcloc.file );
|
||||
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s:%i", RealToString( zones.size() ), fileName, srcloc.line );
|
||||
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s", RealToString( zones.size() ), LocationToString( fileName, srcloc.line ) );
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
DrawSourceTooltip( fileName, srcloc.line );
|
||||
@ -11457,7 +11498,7 @@ void View::DrawCompare()
|
||||
ImGui::RadioButton( m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ), &m_compare.selMatch[0], idx++ );
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s:%i", RealToString( zones.size() ), m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s", RealToString( zones.size() ), LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
@ -11473,7 +11514,7 @@ void View::DrawCompare()
|
||||
ImGui::RadioButton( m_compare.second->GetString( srcloc.name.active ? srcloc.name : srcloc.function ), &m_compare.selMatch[1], idx++ );
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s:%i", RealToString( zones.size() ), m_compare.second->GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s", RealToString( zones.size() ), LocationToString( m_compare.second->GetString( srcloc.file ), srcloc.line ) );
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
@ -12486,6 +12527,10 @@ void View::DrawStatistics()
|
||||
ImGui::SameLine();
|
||||
ImGui::Spacing();
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox( ICON_FA_HAT_WIZARD " Include kernel", &m_statShowKernel );
|
||||
ImGui::SameLine();
|
||||
ImGui::Spacing();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if( m_statMode == 1 && !m_worker.AreSymbolSamplesReady() )
|
||||
{
|
||||
@ -12655,7 +12700,7 @@ void View::DrawStatistics()
|
||||
}
|
||||
const auto file = m_worker.GetString( srcloc.file );
|
||||
|
||||
ImGui::TextDisabled( "%s:%i", file, srcloc.line );
|
||||
TextDisabledUnformatted( LocationToString( file, srcloc.line ) );
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
DrawSourceTooltip( file, srcloc.line );
|
||||
@ -12709,13 +12754,13 @@ void View::DrawStatistics()
|
||||
if( m_showAllSymbols )
|
||||
{
|
||||
data.reserve( symMap.size() );
|
||||
if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() )
|
||||
if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
|
||||
{
|
||||
for( auto& v : symMap )
|
||||
{
|
||||
const auto name = m_worker.GetString( v.second.name );
|
||||
const auto image = m_worker.GetString( v.second.imageName );
|
||||
bool pass = m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
|
||||
bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
|
||||
if( !pass && v.second.size.Val() == 0 )
|
||||
{
|
||||
const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
|
||||
@ -12725,7 +12770,7 @@ void View::DrawStatistics()
|
||||
if( pit != symMap.end() )
|
||||
{
|
||||
const auto parentName = m_worker.GetString( pit->second.name );
|
||||
pass = m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
|
||||
pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12812,7 +12857,7 @@ void View::DrawStatistics()
|
||||
else
|
||||
{
|
||||
data.reserve( symStat.size() );
|
||||
if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() )
|
||||
if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
|
||||
{
|
||||
for( auto& v : symStat )
|
||||
{
|
||||
@ -12821,7 +12866,7 @@ void View::DrawStatistics()
|
||||
{
|
||||
const auto name = m_worker.GetString( sit->second.name );
|
||||
const auto image = m_worker.GetString( sit->second.imageName );
|
||||
bool pass = m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
|
||||
bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
|
||||
if( !pass && sit->second.size.Val() == 0 )
|
||||
{
|
||||
const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
|
||||
@ -12831,7 +12876,7 @@ void View::DrawStatistics()
|
||||
if( pit != symMap.end() )
|
||||
{
|
||||
const auto parentName = m_worker.GetString( pit->second.name );
|
||||
pass = m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
|
||||
pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13017,8 +13062,9 @@ void View::DrawStatistics()
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
const bool isKernel = v.symAddr >> 63 != 0;
|
||||
const char* parentName = nullptr;
|
||||
if( symlen == 0 )
|
||||
if( symlen == 0 && !isKernel )
|
||||
{
|
||||
const auto parentAddr = m_worker.GetSymbolForAddress( v.symAddr );
|
||||
if( parentAddr != 0 )
|
||||
@ -13061,15 +13107,22 @@ void View::DrawStatistics()
|
||||
}
|
||||
if( v.symAddr == 0 || excl == 0 )
|
||||
{
|
||||
ImGui::TextUnformatted( name );
|
||||
if( isKernel )
|
||||
{
|
||||
TextColoredUnformatted( 0xFF8888FF, name );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted( name );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::PushID( idx++ );
|
||||
if( ImGui::Selectable( name, m_sampleParents.symAddr == v.symAddr, ImGuiSelectableFlags_SpanAllColumns ) )
|
||||
{
|
||||
ShowSampleParents( v.symAddr );
|
||||
}
|
||||
if( isKernel ) ImGui::PushStyleColor( ImGuiCol_Text, 0xFF8888FF );
|
||||
const auto clicked = ImGui::Selectable( name, m_sampleParents.symAddr == v.symAddr, ImGuiSelectableFlags_SpanAllColumns );
|
||||
if( isKernel ) ImGui::PopStyleColor();
|
||||
if( clicked ) ShowSampleParents( v.symAddr );
|
||||
ImGui::PopID();
|
||||
}
|
||||
if( parentName )
|
||||
@ -13094,13 +13147,9 @@ void View::DrawStatistics()
|
||||
{
|
||||
ImGui::TextDisabled( "0x%" PRIx64, v.symAddr );
|
||||
}
|
||||
else if( line > 0 )
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", file, line );
|
||||
}
|
||||
else
|
||||
{
|
||||
TextDisabledUnformatted( file );
|
||||
TextDisabledUnformatted( LocationToString( file, line ) );
|
||||
}
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
@ -13271,13 +13320,9 @@ void View::DrawStatistics()
|
||||
{
|
||||
ImGui::TextDisabled( "0x%" PRIx64, iv.symAddr );
|
||||
}
|
||||
else if( line > 0 )
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", file, line );
|
||||
}
|
||||
else
|
||||
{
|
||||
TextDisabledUnformatted( file );
|
||||
TextDisabledUnformatted( LocationToString( file, line ) );
|
||||
}
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
@ -13531,6 +13576,10 @@ void View::DrawCallstackWindow()
|
||||
{
|
||||
TextDisabledUnformatted( txt );
|
||||
}
|
||||
else if( m_worker.GetCanonicalPointer( entry ) >> 63 != 0 )
|
||||
{
|
||||
TextColoredUnformatted( 0xFF8888FF, txt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted( txt );
|
||||
@ -13554,14 +13603,7 @@ void View::DrawCallstackWindow()
|
||||
switch( m_showCallstackFrameAddress )
|
||||
{
|
||||
case 0:
|
||||
if( frame.line == 0 )
|
||||
{
|
||||
TextDisabledUnformatted( txt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", txt, frame.line );
|
||||
}
|
||||
TextDisabledUnformatted( LocationToString( txt, frame.line ) );
|
||||
if( ImGui::IsItemClicked() )
|
||||
{
|
||||
ImGui::SetClipboardText( txt );
|
||||
@ -13606,14 +13648,7 @@ void View::DrawCallstackWindow()
|
||||
if( sym )
|
||||
{
|
||||
const auto symtxt = m_worker.GetString( sym->file );
|
||||
if( sym->line == 0 )
|
||||
{
|
||||
TextDisabledUnformatted( symtxt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", symtxt, sym->line );
|
||||
}
|
||||
TextDisabledUnformatted( LocationToString( symtxt, sym->line ) );
|
||||
if( ImGui::IsItemClicked() )
|
||||
{
|
||||
ImGui::SetClipboardText( symtxt );
|
||||
@ -13973,6 +14008,13 @@ void View::DrawInfo()
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
#endif
|
||||
TextFocused( "Hardware samples:", RealToString( m_worker.GetHwSampleCount() ) );
|
||||
if( ImGui::IsItemHovered() )
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
TextFocused( "Unique addresses:", RealToString( m_worker.GetHwSampleCountAddress() ) );
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
TextFocused( "Frame images:", RealToString( ficnt ) );
|
||||
if( ficnt != 0 && ImGui::IsItemHovered() )
|
||||
{
|
||||
@ -14836,7 +14878,7 @@ void View::DrawLockInfoWindow()
|
||||
{
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::Text( "%s:%i", fileName, srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( fileName, srcloc.line ) );
|
||||
if( ImGui::IsItemClicked( 1 ) )
|
||||
{
|
||||
if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
|
||||
@ -15526,14 +15568,7 @@ void View::DrawSampleParents()
|
||||
TextDisabledUnformatted( "Location:" );
|
||||
ImGui::SameLine();
|
||||
const auto callFile = m_worker.GetString( symbol->callFile );
|
||||
if( symbol->callLine > 0 )
|
||||
{
|
||||
ImGui::Text( "%s:%i", callFile, symbol->callLine );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted( callFile );
|
||||
}
|
||||
ImGui::TextUnformatted( LocationToString( callFile, symbol->callLine ) );
|
||||
if( ImGui::IsItemClicked( 1 ) )
|
||||
{
|
||||
ViewDispatch( callFile, symbol->callLine, m_sampleParents.symAddr );
|
||||
@ -15541,14 +15576,7 @@ void View::DrawSampleParents()
|
||||
TextDisabledUnformatted( "Entry point:" );
|
||||
ImGui::SameLine();
|
||||
const auto file = m_worker.GetString( symbol->file );
|
||||
if( symbol->line > 0 )
|
||||
{
|
||||
ImGui::Text( "%s:%i", file, symbol->line );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted( file );
|
||||
}
|
||||
ImGui::TextUnformatted( LocationToString( file, symbol->line ) );
|
||||
if( ImGui::IsItemClicked( 1 ) )
|
||||
{
|
||||
ViewDispatch( file, symbol->line, m_sampleParents.symAddr );
|
||||
@ -15643,6 +15671,10 @@ void View::DrawSampleParents()
|
||||
{
|
||||
TextDisabledUnformatted( txt );
|
||||
}
|
||||
else if( m_worker.GetCanonicalPointer( entry ) >> 63 != 0 )
|
||||
{
|
||||
TextColoredUnformatted( 0xFF8888FF, txt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted( txt );
|
||||
@ -15666,14 +15698,7 @@ void View::DrawSampleParents()
|
||||
switch( m_showCallstackFrameAddress )
|
||||
{
|
||||
case 0:
|
||||
if( frame.line == 0 )
|
||||
{
|
||||
TextDisabledUnformatted( txt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", txt, frame.line );
|
||||
}
|
||||
TextDisabledUnformatted( LocationToString( txt, frame.line ) );
|
||||
if( ImGui::IsItemClicked() )
|
||||
{
|
||||
ImGui::SetClipboardText( txt );
|
||||
@ -15711,14 +15736,7 @@ void View::DrawSampleParents()
|
||||
if( sym )
|
||||
{
|
||||
const auto symtxt = m_worker.GetString( sym->file );
|
||||
if( sym->line == 0 )
|
||||
{
|
||||
TextDisabledUnformatted( symtxt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled( "%s:%i", symtxt, sym->line );
|
||||
}
|
||||
TextDisabledUnformatted( LocationToString( symtxt, sym->line ) );
|
||||
if( ImGui::IsItemClicked() )
|
||||
{
|
||||
ImGui::SetClipboardText( symtxt );
|
||||
@ -16913,26 +16931,6 @@ const char* View::GetPlotName( const PlotData* plot ) const
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t View::GetZoneColor( const ZoneEvent& ev, uint64_t thread, int depth )
|
||||
{
|
||||
if( m_findZone.show && !m_findZone.match.empty() && m_findZone.match[m_findZone.selMatch] == ev.SrcLoc() )
|
||||
{
|
||||
if( m_findZone.highlight.active )
|
||||
{
|
||||
const auto zt = m_worker.GetZoneEnd( ev ) - ev.Start();
|
||||
if( zt >= m_findZone.highlight.start && zt <= m_findZone.highlight.end )
|
||||
{
|
||||
return 0xFFFFCC66;
|
||||
}
|
||||
}
|
||||
return 0xFF229999;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetRawZoneColor( ev, thread, depth );
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t View::GetThreadColor( uint64_t thread, int depth )
|
||||
{
|
||||
if( m_vd.dynamicColors == 0 ) return 0xFFCC5555;
|
||||
@ -16967,7 +16965,7 @@ uint32_t View::GetSrcLocColor( const SourceLocation& srcloc, int depth )
|
||||
return GetRawSrcLocColor( srcloc, depth );
|
||||
}
|
||||
|
||||
uint32_t View::GetRawZoneColor( const ZoneEvent& ev, uint64_t thread, int depth )
|
||||
uint32_t View::GetZoneColor( const ZoneEvent& ev, uint64_t thread, int depth )
|
||||
{
|
||||
const auto sl = ev.SrcLoc();
|
||||
const auto& srcloc = m_worker.GetSourceLocation( sl );
|
||||
@ -17002,73 +17000,82 @@ uint32_t View::GetZoneColor( const GpuEvent& ev )
|
||||
return color != 0 ? ( color | 0xFF000000 ) : 0xFF222288;
|
||||
}
|
||||
|
||||
uint32_t View::GetRawZoneColor( const GpuEvent& ev )
|
||||
{
|
||||
return GetZoneColor( ev );
|
||||
}
|
||||
|
||||
uint32_t View::GetZoneHighlight( const ZoneEvent& ev, uint64_t thread, int depth )
|
||||
View::ZoneColorData View::GetZoneColorData( const ZoneEvent& ev, uint64_t thread, int depth )
|
||||
{
|
||||
ZoneColorData ret;
|
||||
const auto& srcloc = ev.SrcLoc();
|
||||
if( m_zoneInfoWindow == &ev )
|
||||
{
|
||||
return 0xFF44DD44;
|
||||
ret.color = GetZoneColor( ev, thread, depth );
|
||||
ret.accentColor = 0xFF44DD44;
|
||||
ret.thickness = 3.f;
|
||||
ret.highlight = true;
|
||||
}
|
||||
else if( m_zoneHighlight == &ev )
|
||||
{
|
||||
return 0xFF4444FF;
|
||||
ret.color = GetZoneColor( ev, thread, depth );
|
||||
ret.accentColor = 0xFF4444FF;
|
||||
ret.thickness = 3.f;
|
||||
ret.highlight = true;
|
||||
}
|
||||
else if( m_zoneSrcLocHighlight == ev.SrcLoc() )
|
||||
else if( m_zoneSrcLocHighlight == srcloc )
|
||||
{
|
||||
return 0xFFEEEEEE;
|
||||
ret.color = GetZoneColor( ev, thread, depth );
|
||||
ret.accentColor = 0xFFEEEEEE;
|
||||
ret.thickness = 1.f;
|
||||
ret.highlight = true;
|
||||
}
|
||||
else if( m_findZone.show && !m_findZone.match.empty() && m_findZone.match[m_findZone.selMatch] == srcloc )
|
||||
{
|
||||
uint32_t color = 0xFF229999;
|
||||
if( m_findZone.highlight.active )
|
||||
{
|
||||
const auto zt = m_worker.GetZoneEnd( ev ) - ev.Start();
|
||||
if( zt >= m_findZone.highlight.start && zt <= m_findZone.highlight.end )
|
||||
{
|
||||
color = 0xFFFFCC66;
|
||||
}
|
||||
}
|
||||
ret.color = color;
|
||||
ret.accentColor = HighlightColor( color );
|
||||
ret.thickness = 3.f;
|
||||
ret.highlight = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HighlightColor( GetZoneColor( ev, thread, depth ) );
|
||||
const auto color = GetZoneColor( ev, thread, depth );
|
||||
ret.color = color;
|
||||
ret.accentColor = HighlightColor( color );
|
||||
ret.thickness = 1.f;
|
||||
ret.highlight = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t View::GetZoneHighlight( const GpuEvent& ev )
|
||||
View::ZoneColorData View::GetZoneColorData( const GpuEvent& ev )
|
||||
{
|
||||
ZoneColorData ret;
|
||||
const auto color = GetZoneColor( ev );
|
||||
ret.color = color;
|
||||
if( m_gpuInfoWindow == &ev )
|
||||
{
|
||||
return 0xFF44DD44;
|
||||
ret.accentColor = 0xFF44DD44;
|
||||
ret.thickness = 3.f;
|
||||
ret.highlight = true;
|
||||
}
|
||||
else if( m_gpuHighlight == &ev )
|
||||
{
|
||||
return 0xFF4444FF;
|
||||
ret.accentColor = 0xFF4444FF;
|
||||
ret.thickness = 3.f;
|
||||
ret.highlight = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto color = GetZoneColor( ev );
|
||||
return 0xFF000000 |
|
||||
( std::min<int>( 0xFF, ( ( ( color & 0x00FF0000 ) >> 16 ) + 25 ) ) << 16 ) |
|
||||
( std::min<int>( 0xFF, ( ( ( color & 0x0000FF00 ) >> 8 ) + 25 ) ) << 8 ) |
|
||||
( std::min<int>( 0xFF, ( ( ( color & 0x000000FF ) ) + 25 ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
float View::GetZoneThickness( const ZoneEvent& ev )
|
||||
{
|
||||
if( m_zoneInfoWindow == &ev || m_zoneHighlight == &ev || ( m_findZone.show && !m_findZone.match.empty() && m_findZone.match[m_findZone.selMatch] == ev.SrcLoc() ) )
|
||||
{
|
||||
return 3.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
float View::GetZoneThickness( const GpuEvent& ev )
|
||||
{
|
||||
if( m_gpuInfoWindow == &ev || m_gpuHighlight == &ev )
|
||||
{
|
||||
return 3.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.f;
|
||||
ret.accentColor = HighlightColor( color );
|
||||
ret.thickness = 1.f;
|
||||
ret.highlight = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void View::ZoomToZone( const ZoneEvent& ev )
|
||||
@ -17250,7 +17257,7 @@ void View::ZoneTooltip( const ZoneEvent& ev )
|
||||
ImGui::Separator();
|
||||
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
SmallColorBox( GetThreadColor( tid, 0 ) );
|
||||
ImGui::SameLine();
|
||||
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
|
||||
@ -17317,7 +17324,7 @@ void View::ZoneTooltip( const GpuEvent& ev )
|
||||
ImGui::Separator();
|
||||
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "%s:%i", m_worker.GetString( srcloc.file ), srcloc.line );
|
||||
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
|
||||
SmallColorBox( GetThreadColor( tid, 0 ) );
|
||||
ImGui::SameLine();
|
||||
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
|
||||
@ -17410,6 +17417,10 @@ void View::CallstackTooltip( uint32_t idx )
|
||||
{
|
||||
TextDisabledUnformatted( txt );
|
||||
}
|
||||
else if( m_worker.GetCanonicalPointer( entry ) >> 63 != 0 )
|
||||
{
|
||||
TextColoredUnformatted( 0xFF8888FF, txt );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted( txt );
|
||||
|
@ -138,6 +138,14 @@ private:
|
||||
LastRange
|
||||
};
|
||||
|
||||
struct ZoneColorData
|
||||
{
|
||||
uint32_t color;
|
||||
uint32_t accentColor;
|
||||
float thickness;
|
||||
bool highlight;
|
||||
};
|
||||
|
||||
void InitMemory();
|
||||
void InitTextEditor( ImFont* font );
|
||||
|
||||
@ -222,12 +230,8 @@ private:
|
||||
uint32_t GetRawSrcLocColor( const SourceLocation& srcloc, int depth );
|
||||
uint32_t GetZoneColor( const ZoneEvent& ev, uint64_t thread, int depth );
|
||||
uint32_t GetZoneColor( const GpuEvent& ev );
|
||||
uint32_t GetRawZoneColor( const ZoneEvent& ev, uint64_t thread, int depth );
|
||||
uint32_t GetRawZoneColor( const GpuEvent& ev );
|
||||
uint32_t GetZoneHighlight( const ZoneEvent& ev, uint64_t thread, int depth );
|
||||
uint32_t GetZoneHighlight( const GpuEvent& ev );
|
||||
float GetZoneThickness( const ZoneEvent& ev );
|
||||
float GetZoneThickness( const GpuEvent& ev );
|
||||
ZoneColorData GetZoneColorData( const ZoneEvent& ev, uint64_t thread, int depth );
|
||||
ZoneColorData GetZoneColorData( const GpuEvent& ev );
|
||||
|
||||
void ZoomToZone( const ZoneEvent& ev );
|
||||
void ZoomToZone( const GpuEvent& ev );
|
||||
@ -398,6 +402,7 @@ private:
|
||||
bool m_showUnknownFrames = true;
|
||||
bool m_statSeparateInlines = false;
|
||||
bool m_statShowAddress = false;
|
||||
bool m_statShowKernel = true;
|
||||
bool m_groupChildrenLocations = false;
|
||||
bool m_allocTimeRelativeToZone = true;
|
||||
bool m_ctxSwitchTimeRelativeToZone = true;
|
||||
@ -716,6 +721,8 @@ private:
|
||||
uint64_t symAddr = 0;
|
||||
int sel;
|
||||
} m_sampleParents;
|
||||
|
||||
std::vector<std::pair<int, int>> m_cpuUsageBuf;
|
||||
};
|
||||
|
||||
}
|
||||
|
29
server/TracyWeb.cpp
Normal file
29
server/TracyWeb.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <shellapi.h>
|
||||
#else
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "TracyWeb.hpp"
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
void OpenWebpage( const char* url )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ShellExecuteA( nullptr, nullptr, url, nullptr, nullptr, 0 );
|
||||
#elif defined __APPLE__
|
||||
char buf[1024];
|
||||
sprintf( buf, "open %s", url );
|
||||
system( buf );
|
||||
#else
|
||||
char buf[1024];
|
||||
sprintf( buf, "xdg-open %s", url );
|
||||
system( buf );
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
11
server/TracyWeb.hpp
Normal file
11
server/TracyWeb.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __TRACYWEB_HPP__
|
||||
#define __TRACYWEB_HPP__
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
void OpenWebpage( const char* url );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -27,6 +27,7 @@
|
||||
#include "../common/TracyProtocol.hpp"
|
||||
#include "../common/TracySystem.hpp"
|
||||
#include "../common/TracyYield.hpp"
|
||||
#include "../common/TracyStackFrames.hpp"
|
||||
#include "TracyFileRead.hpp"
|
||||
#include "TracyFileWrite.hpp"
|
||||
#include "TracySort.hpp"
|
||||
@ -236,6 +237,23 @@ static tracy_force_inline void UpdateLockRange( LockMap& lockmap, const LockEven
|
||||
if( range.end < lt ) range.end = lt;
|
||||
}
|
||||
|
||||
template<size_t U>
|
||||
static void ReadHwSampleVec( FileRead& f, SortedVector<Int48, Int48Sort>& vec, Slab<U>& slab )
|
||||
{
|
||||
uint64_t sz;
|
||||
f.Read( sz );
|
||||
if( sz != 0 )
|
||||
{
|
||||
int64_t refTime = 0;
|
||||
vec.reserve_exact( sz, slab );
|
||||
for( uint64_t i=0; i<sz; i++ )
|
||||
{
|
||||
vec[i] = ReadTimeOffset( f, refTime );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LoadProgress Worker::s_loadProgress;
|
||||
|
||||
Worker::Worker( const char* addr, uint16_t port )
|
||||
@ -904,7 +922,15 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
|
||||
{
|
||||
auto td = m_slab.AllocInit<ThreadData>();
|
||||
uint64_t tid;
|
||||
f.Read2( tid, td->count );
|
||||
if( fileVer >= FileVersion( 0, 7, 9 ) )
|
||||
{
|
||||
f.Read3( tid, td->count, td->kernelSampleCnt );
|
||||
}
|
||||
else
|
||||
{
|
||||
f.Read2( tid, td->count );
|
||||
td->kernelSampleCnt = 0;
|
||||
}
|
||||
td->id = tid;
|
||||
m_data.zonesCnt += td->count;
|
||||
if( fileVer < FileVersion( 0, 6, 3 ) )
|
||||
@ -987,17 +1013,25 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
|
||||
for( uint64_t i=0; i<sz; i++ )
|
||||
{
|
||||
auto ctx = m_slab.AllocInit<GpuCtxData>();
|
||||
if( fileVer >= FileVersion( 0, 7, 6 ) )
|
||||
if( fileVer >= FileVersion( 0, 7, 9 ) )
|
||||
{
|
||||
uint8_t calibration;
|
||||
f.Read7( ctx->thread, calibration, ctx->count, ctx->period, ctx->type, ctx->name, ctx->overflow );
|
||||
ctx->hasCalibration = calibration;
|
||||
}
|
||||
else if( fileVer >= FileVersion( 0, 7, 6 ) )
|
||||
{
|
||||
uint8_t calibration;
|
||||
f.Read6( ctx->thread, calibration, ctx->count, ctx->period, ctx->type, ctx->name );
|
||||
ctx->hasCalibration = calibration;
|
||||
ctx->overflow = 0;
|
||||
}
|
||||
else if( fileVer >= FileVersion( 0, 7, 1 ) )
|
||||
{
|
||||
uint8_t calibration;
|
||||
f.Read5( ctx->thread, calibration, ctx->count, ctx->period, ctx->type );
|
||||
ctx->hasCalibration = calibration;
|
||||
ctx->overflow = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1012,6 +1046,7 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
|
||||
ctx->type = ctx->thread == 0 ? GpuContextType::Vulkan : GpuContextType::OpenGl;
|
||||
}
|
||||
ctx->hasCalibration = false;
|
||||
ctx->overflow = 0;
|
||||
}
|
||||
ctx->hasPeriod = ctx->period != 1.f;
|
||||
m_data.gpuCnt += ctx->count;
|
||||
@ -1701,6 +1736,33 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
|
||||
}
|
||||
}
|
||||
|
||||
if( fileVer >= FileVersion( 0, 7, 9 ) )
|
||||
{
|
||||
f.Read( sz );
|
||||
m_data.codeSymbolMap.reserve( sz );
|
||||
for( uint64_t i=0; i<sz; i++ )
|
||||
{
|
||||
uint64_t v1, v2;
|
||||
f.Read2( v1, v2 );
|
||||
m_data.codeSymbolMap.emplace( v1, v2 );
|
||||
}
|
||||
|
||||
f.Read( sz );
|
||||
m_data.hwSamples.reserve( sz );
|
||||
for( uint64_t i=0; i<sz; i++ )
|
||||
{
|
||||
uint64_t addr;
|
||||
f.Read( addr );
|
||||
auto& data = m_data.hwSamples.emplace( addr, HwSampleData {} ).first->second;
|
||||
ReadHwSampleVec( f, data.cycles, m_slab );
|
||||
ReadHwSampleVec( f, data.retired, m_slab );
|
||||
ReadHwSampleVec( f, data.cacheRef, m_slab );
|
||||
ReadHwSampleVec( f, data.cacheMiss, m_slab );
|
||||
ReadHwSampleVec( f, data.branchRetired, m_slab );
|
||||
ReadHwSampleVec( f, data.branchMiss, m_slab );
|
||||
}
|
||||
}
|
||||
|
||||
if( fileVer >= FileVersion( 0, 6, 13 ) )
|
||||
{
|
||||
f.Read( sz );
|
||||
@ -2067,48 +2129,86 @@ uint64_t Worker::GetPidFromTid( uint64_t tid ) const
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Worker::GetCpuUsageAtTime( int64_t time, int& own, int& other ) const
|
||||
void Worker::GetCpuUsage( int64_t t0, double tstep, size_t num, std::vector<std::pair<int, int>>& out )
|
||||
{
|
||||
own = other = 0;
|
||||
if( time < 0 || time > m_data.lastTime ) return;
|
||||
if( out.size() < num ) out.resize( num );
|
||||
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
// Remove this check when real-time ctxUsage contruction is implemented.
|
||||
if( !m_data.ctxUsage.empty() )
|
||||
if( t0 > m_data.lastTime || int64_t( t0 + tstep * num ) < 0 )
|
||||
{
|
||||
const auto test = ( time << 16 ) | 0xFFFF;
|
||||
auto it = std::upper_bound( m_data.ctxUsage.begin(), m_data.ctxUsage.end(), test, [] ( const auto& l, const auto& r ) { return l < r._time_other_own; } );
|
||||
if( it == m_data.ctxUsage.begin() || it == m_data.ctxUsage.end() ) return;
|
||||
--it;
|
||||
own = it->Own();
|
||||
other = it->Other();
|
||||
memset( out.data(), 0, sizeof( int ) * 2 * num );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
int cntOwn = 0;
|
||||
int cntOther = 0;
|
||||
for( int i=0; i<m_data.cpuDataCount; i++ )
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
if( !m_data.ctxUsage.empty() )
|
||||
{
|
||||
auto& cs = m_data.cpuData[i].cs;
|
||||
if( !cs.empty() )
|
||||
auto ptr = out.data();
|
||||
auto itBegin = m_data.ctxUsage.begin();
|
||||
for( size_t i=0; i<num; i++ )
|
||||
{
|
||||
auto it = std::lower_bound( cs.begin(), cs.end(), time, [] ( const auto& l, const auto& r ) { return (uint64_t)l.End() < (uint64_t)r; } );
|
||||
if( it != cs.end() && it->IsEndValid() && it->Start() <= time )
|
||||
const auto time = int64_t( t0 + tstep * i );
|
||||
if( time < 0 || time > m_data.lastTime )
|
||||
{
|
||||
if( GetPidFromTid( DecompressThreadExternal( it->Thread() ) ) == m_pid )
|
||||
ptr->first = 0;
|
||||
ptr->second = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto test = ( time << 16 ) | 0xFFFF;
|
||||
auto it = std::upper_bound( itBegin, m_data.ctxUsage.end(), test, [] ( const auto& l, const auto& r ) { return l < r._time_other_own; } );
|
||||
if( it == m_data.ctxUsage.begin() || it == m_data.ctxUsage.end() )
|
||||
{
|
||||
cntOwn++;
|
||||
ptr->first = 0;
|
||||
ptr->second = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cntOther++;
|
||||
--it;
|
||||
ptr->first = it->Own();
|
||||
ptr->second = it->Other();
|
||||
}
|
||||
itBegin = it;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
memset( out.data(), 0, sizeof( int ) * 2 * num );
|
||||
for( int i=0; i<m_data.cpuDataCount; i++ )
|
||||
{
|
||||
auto& cs = m_data.cpuData[i].cs;
|
||||
if( !cs.empty() )
|
||||
{
|
||||
auto itBegin = cs.begin();
|
||||
auto ptr = out.data();
|
||||
for( size_t i=0; i<num; i++ )
|
||||
{
|
||||
const auto time = int64_t( t0 + tstep * i );
|
||||
if( time > m_data.lastTime ) break;
|
||||
if( time >= 0 )
|
||||
{
|
||||
auto it = std::lower_bound( itBegin, cs.end(), time, [] ( const auto& l, const auto& r ) { return (uint64_t)l.End() < (uint64_t)r; } );
|
||||
if( it == cs.end() ) break;
|
||||
if( it->IsEndValid() && it->Start() <= time )
|
||||
{
|
||||
if( GetPidFromTid( DecompressThreadExternal( it->Thread() ) ) == m_pid )
|
||||
{
|
||||
ptr->first++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr->second++;
|
||||
}
|
||||
}
|
||||
itBegin = it;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
own = cntOwn;
|
||||
other = cntOther;
|
||||
}
|
||||
|
||||
const ContextSwitch* const Worker::GetContextSwitchDataImpl( uint64_t thread )
|
||||
@ -2325,6 +2425,13 @@ uint64_t Worker::GetSymbolForAddress( uint64_t address, uint32_t& offset ) const
|
||||
return it->addr;
|
||||
}
|
||||
|
||||
uint64_t Worker::GetInlineSymbolForAddress( uint64_t address ) const
|
||||
{
|
||||
auto it = m_data.codeSymbolMap.find( address );
|
||||
if( it == m_data.codeSymbolMap.end() ) return 0;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
StringIdx Worker::GetLocationForAddress( uint64_t address, uint32_t& line ) const
|
||||
{
|
||||
auto it = m_data.codeAddressToLocation.find( address );
|
||||
@ -2793,13 +2900,14 @@ void Worker::Exec()
|
||||
m_resolution = TscTime( welcome.resolution );
|
||||
m_pid = welcome.pid;
|
||||
m_samplingPeriod = welcome.samplingPeriod;
|
||||
m_onDemand = welcome.onDemand;
|
||||
m_onDemand = welcome.flags & WelcomeFlag::OnDemand;
|
||||
m_captureProgram = welcome.programName;
|
||||
m_captureTime = welcome.epoch;
|
||||
m_executableTime = welcome.exectime;
|
||||
m_ignoreMemFreeFaults = welcome.onDemand || welcome.isApple;
|
||||
m_ignoreMemFreeFaults = ( welcome.flags & WelcomeFlag::OnDemand ) || ( welcome.flags & WelcomeFlag::IsApple );
|
||||
m_data.cpuArch = (CpuArchitecture)welcome.cpuArch;
|
||||
m_codeTransfer = welcome.codeTransfer;
|
||||
m_codeTransfer = welcome.flags & WelcomeFlag::CodeTransfer;
|
||||
m_combineSamples = welcome.flags & WelcomeFlag::CombineSamples;
|
||||
m_data.cpuId = welcome.cpuId;
|
||||
memcpy( m_data.cpuManufacturer, welcome.cpuManufacturer, 12 );
|
||||
m_data.cpuManufacturer[12] = '\0';
|
||||
@ -2814,7 +2922,7 @@ void Worker::Exec()
|
||||
|
||||
m_hostInfo = welcome.hostInfo;
|
||||
|
||||
if( welcome.onDemand != 0 )
|
||||
if( m_onDemand )
|
||||
{
|
||||
OnDemandPayloadMessage onDemand;
|
||||
if( !m_sock.Read( &onDemand, sizeof( onDemand ), 10, ShouldExit ) )
|
||||
@ -3457,6 +3565,8 @@ ThreadData* Worker::NewThread( uint64_t thread )
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
td->ghostIdx = 0;
|
||||
#endif
|
||||
td->kernelSampleCnt = 0;
|
||||
td->pendingSample.time.Clear();
|
||||
m_data.threads.push_back( td );
|
||||
m_threadMap.emplace( thread, td );
|
||||
m_data.threadDataLast.first = thread;
|
||||
@ -4463,6 +4573,24 @@ bool Worker::Process( const QueueItem& ev )
|
||||
case QueueType::TidToPid:
|
||||
ProcessTidToPid( ev.tidToPid );
|
||||
break;
|
||||
case QueueType::HwSampleCpuCycle:
|
||||
ProcessHwSampleCpuCycle( ev.hwSample );
|
||||
break;
|
||||
case QueueType::HwSampleInstructionRetired:
|
||||
ProcessHwSampleInstructionRetired( ev.hwSample );
|
||||
break;
|
||||
case QueueType::HwSampleCacheReference:
|
||||
ProcessHwSampleCacheReference( ev.hwSample );
|
||||
break;
|
||||
case QueueType::HwSampleCacheMiss:
|
||||
ProcessHwSampleCacheMiss( ev.hwSample );
|
||||
break;
|
||||
case QueueType::HwSampleBranchRetired:
|
||||
ProcessHwSampleBranchRetired( ev.hwSample );
|
||||
break;
|
||||
case QueueType::HwSampleBranchMiss:
|
||||
ProcessHwSampleBranchMiss( ev.hwSample );
|
||||
break;
|
||||
case QueueType::ParamSetup:
|
||||
ProcessParamSetup( ev.paramSetup );
|
||||
break;
|
||||
@ -5347,6 +5475,9 @@ void Worker::ProcessGpuNewContext( const QueueGpuNewContext& ev )
|
||||
gpu->calibratedGpuTime = gpuTime;
|
||||
gpu->calibratedCpuTime = cpuTime;
|
||||
gpu->calibrationMod = 1.;
|
||||
gpu->lastGpuTime = 0;
|
||||
gpu->overflow = 0;
|
||||
gpu->overflowMul = 0;
|
||||
m_data.gpuData.push_back( gpu );
|
||||
m_gpuCtxMap[ev.context] = gpu;
|
||||
}
|
||||
@ -5519,31 +5650,44 @@ void Worker::ProcessGpuTime( const QueueGpuTime& ev )
|
||||
auto ctx = m_gpuCtxMap[ev.context];
|
||||
assert( ctx );
|
||||
|
||||
const int64_t tref = m_refTimeGpu + ev.gpuTime;
|
||||
m_refTimeGpu = tref;
|
||||
const int64_t t = std::max<int64_t>( 0, tref );
|
||||
int64_t tgpu = m_refTimeGpu + ev.gpuTime;
|
||||
m_refTimeGpu = tgpu;
|
||||
|
||||
if( tgpu < ctx->lastGpuTime )
|
||||
{
|
||||
if( ctx->overflow == 0 )
|
||||
{
|
||||
ctx->overflow = uint64_t( 1 ) << ( 64 - TracyLzcnt( ctx->lastGpuTime ) );
|
||||
}
|
||||
ctx->overflowMul++;
|
||||
}
|
||||
ctx->lastGpuTime = tgpu;
|
||||
if( ctx->overflow != 0 )
|
||||
{
|
||||
tgpu += ctx->overflow * ctx->overflowMul;
|
||||
}
|
||||
|
||||
int64_t gpuTime;
|
||||
if( !ctx->hasPeriod )
|
||||
{
|
||||
if( !ctx->hasCalibration )
|
||||
{
|
||||
gpuTime = t + ctx->timeDiff;
|
||||
gpuTime = tgpu + ctx->timeDiff;
|
||||
}
|
||||
else
|
||||
{
|
||||
gpuTime = int64_t( ( t - ctx->calibratedGpuTime ) * ctx->calibrationMod + ctx->calibratedCpuTime );
|
||||
gpuTime = int64_t( ( tgpu - ctx->calibratedGpuTime ) * ctx->calibrationMod + ctx->calibratedCpuTime );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !ctx->hasCalibration )
|
||||
{
|
||||
gpuTime = int64_t( double( ctx->period ) * t ) + ctx->timeDiff; // precision loss
|
||||
gpuTime = int64_t( double( ctx->period ) * tgpu ) + ctx->timeDiff; // precision loss
|
||||
}
|
||||
else
|
||||
{
|
||||
gpuTime = int64_t( ( double( ctx->period ) * t - ctx->calibratedGpuTime ) * ctx->calibrationMod + ctx->calibratedCpuTime );
|
||||
gpuTime = int64_t( ( double( ctx->period ) * tgpu - ctx->calibratedGpuTime ) * ctx->calibrationMod + ctx->calibratedCpuTime );
|
||||
}
|
||||
}
|
||||
|
||||
@ -5554,20 +5698,13 @@ void Worker::ProcessGpuTime( const QueueGpuTime& ev )
|
||||
if( zone->GpuStart() < 0 )
|
||||
{
|
||||
zone->SetGpuStart( gpuTime );
|
||||
if( m_data.lastTime < gpuTime ) m_data.lastTime = gpuTime;
|
||||
ctx->count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( gpuTime < zone->GpuStart() )
|
||||
{
|
||||
auto tmp = zone->GpuStart();
|
||||
std::swap( gpuTime, tmp );
|
||||
zone->SetGpuStart( tmp );
|
||||
}
|
||||
zone->SetGpuEnd( gpuTime );
|
||||
if( m_data.lastTime < gpuTime ) m_data.lastTime = gpuTime;
|
||||
}
|
||||
if( m_data.lastTime < gpuTime ) m_data.lastTime = gpuTime;
|
||||
}
|
||||
|
||||
void Worker::ProcessGpuCalibration( const QueueGpuCalibration& ev )
|
||||
@ -5790,34 +5927,28 @@ void Worker::ProcessCallstack()
|
||||
m_pendingCallstackId = 0;
|
||||
}
|
||||
|
||||
void Worker::ProcessCallstackSample( const QueueCallstackSample& ev )
|
||||
void Worker::ProcessCallstackSampleImpl( const SampleData& sd, ThreadData& td, int64_t t, uint32_t callstack )
|
||||
{
|
||||
assert( m_pendingCallstackId != 0 );
|
||||
assert( sd.time.Val() == t );
|
||||
assert( sd.callstack.Val() == callstack );
|
||||
m_data.samplesCnt++;
|
||||
|
||||
const auto refTime = m_refTimeCtx + ev.time;
|
||||
m_refTimeCtx = refTime;
|
||||
const auto t = TscTime( refTime - m_data.baseTime );
|
||||
const auto& cs = GetCallstack( callstack );
|
||||
const auto& ip = cs[0];
|
||||
if( GetCanonicalPointer( ip ) >> 63 != 0 ) td.kernelSampleCnt++;
|
||||
|
||||
SampleData sd;
|
||||
sd.time.SetVal( t );
|
||||
sd.callstack.SetVal( m_pendingCallstackId );
|
||||
|
||||
auto td = NoticeThread( ev.thread );
|
||||
if( td->samples.empty() )
|
||||
if( td.samples.empty() )
|
||||
{
|
||||
td->samples.push_back( sd );
|
||||
td.samples.push_back( sd );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( td->samples.back().time.Val() < t );
|
||||
td->samples.push_back_non_empty( sd );
|
||||
assert( td.samples.back().time.Val() < t );
|
||||
td.samples.push_back_non_empty( sd );
|
||||
}
|
||||
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
const auto& cs = GetCallstack( m_pendingCallstackId );
|
||||
{
|
||||
const auto& ip = cs[0];
|
||||
auto frame = GetCallstackFrame( ip );
|
||||
if( frame )
|
||||
{
|
||||
@ -5893,19 +6024,91 @@ void Worker::ProcessCallstackSample( const QueueCallstackSample& ev )
|
||||
}
|
||||
}
|
||||
|
||||
const auto framesKnown = UpdateSampleStatistics( m_pendingCallstackId, 1, true );
|
||||
assert( td->samples.size() > td->ghostIdx );
|
||||
if( framesKnown && td->ghostIdx + 1 == td->samples.size() )
|
||||
const auto framesKnown = UpdateSampleStatistics( callstack, 1, true );
|
||||
assert( td.samples.size() > td.ghostIdx );
|
||||
if( framesKnown && td.ghostIdx + 1 == td.samples.size() )
|
||||
{
|
||||
td->ghostIdx++;
|
||||
m_data.ghostCnt += AddGhostZone( cs, &td->ghostZones, t );
|
||||
td.ghostIdx++;
|
||||
m_data.ghostCnt += AddGhostZone( cs, &td.ghostZones, t );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data.ghostZonesPostponed = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Worker::ProcessCallstackSample( const QueueCallstackSample& ev )
|
||||
{
|
||||
assert( m_pendingCallstackId != 0 );
|
||||
const auto callstack = m_pendingCallstackId;
|
||||
m_pendingCallstackId = 0;
|
||||
|
||||
const auto refTime = m_refTimeCtx + ev.time;
|
||||
m_refTimeCtx = refTime;
|
||||
const auto t = TscTime( refTime - m_data.baseTime );
|
||||
|
||||
auto& td = *NoticeThread( ev.thread );
|
||||
|
||||
SampleData sd;
|
||||
sd.time.SetVal( t );
|
||||
sd.callstack.SetVal( callstack );
|
||||
|
||||
if( m_combineSamples )
|
||||
{
|
||||
const auto pendingTime = td.pendingSample.time.Val();
|
||||
if( pendingTime == 0 )
|
||||
{
|
||||
td.pendingSample = sd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pendingTime == t )
|
||||
{
|
||||
const auto& cs1 = GetCallstack( td.pendingSample.callstack.Val() );
|
||||
const auto& cs2 = GetCallstack( callstack );
|
||||
|
||||
const auto sz1 = cs1.size();
|
||||
const auto sz2 = cs2.size();
|
||||
const auto tsz = sz1 + sz2;
|
||||
|
||||
size_t memsize = sizeof( VarArray<CallstackFrameId> ) + tsz * sizeof( CallstackFrameId );
|
||||
auto mem = (char*)m_slab.AllocRaw( memsize );
|
||||
memcpy( mem, cs1.data(), sizeof( CallstackFrameId ) * sz1 );
|
||||
memcpy( mem + sizeof( CallstackFrameId ) * sz1, cs2.data(), sizeof( CallstackFrameId ) * sz2 );
|
||||
|
||||
VarArray<CallstackFrameId>* arr = (VarArray<CallstackFrameId>*)( mem + tsz * sizeof( CallstackFrameId ) );
|
||||
new(arr) VarArray<CallstackFrameId>( tsz, (CallstackFrameId*)mem );
|
||||
|
||||
uint32_t idx;
|
||||
auto it = m_data.callstackMap.find( arr );
|
||||
if( it == m_data.callstackMap.end() )
|
||||
{
|
||||
idx = m_data.callstackPayload.size();
|
||||
m_data.callstackMap.emplace( arr, idx );
|
||||
m_data.callstackPayload.push_back( arr );
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = it->second;
|
||||
m_slab.Unalloc( memsize );
|
||||
}
|
||||
|
||||
sd.callstack.SetVal( idx );
|
||||
ProcessCallstackSampleImpl( sd, td, pendingTime, idx );
|
||||
td.pendingSample.time.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessCallstackSampleImpl( td.pendingSample, td, pendingTime, td.pendingSample.callstack.Val() );
|
||||
td.pendingSample = sd;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessCallstackSampleImpl( sd, td, t, callstack );
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev )
|
||||
@ -5944,9 +6147,30 @@ void Worker::ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySym
|
||||
if( m_callstackFrameStaging )
|
||||
{
|
||||
const auto idx = m_callstackFrameStaging->size - m_pendingCallstackSubframes;
|
||||
const auto file = StringIdx( fitidx );
|
||||
|
||||
if( m_pendingCallstackSubframes > 1 && idx == 0 )
|
||||
{
|
||||
auto fstr = GetString( file );
|
||||
auto flen = strlen( fstr );
|
||||
if( flen >= s_tracySkipSubframesMinLen )
|
||||
{
|
||||
auto ptr = s_tracySkipSubframes;
|
||||
do
|
||||
{
|
||||
if( flen >= ptr->len && memcmp( fstr + flen - ptr->len, ptr->str, ptr->len ) == 0 )
|
||||
{
|
||||
m_pendingCallstackSubframes--;
|
||||
m_callstackFrameStaging->size--;
|
||||
return;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
while( ptr->str );
|
||||
}
|
||||
}
|
||||
|
||||
const auto name = StringIdx( nitidx );
|
||||
const auto file = StringIdx( fitidx );
|
||||
m_callstackFrameStaging->data[idx].name = name;
|
||||
m_callstackFrameStaging->data[idx].file = file;
|
||||
m_callstackFrameStaging->data[idx].line = ev.line;
|
||||
@ -6044,7 +6268,7 @@ void Worker::ProcessSymbolInformation( const QueueSymbolInformation& ev )
|
||||
sd.size.SetVal( it->second.size );
|
||||
m_data.symbolMap.emplace( ev.symAddr, std::move( sd ) );
|
||||
|
||||
if( m_codeTransfer && it->second.size > 0 && it->second.size <= 64*1024 )
|
||||
if( m_codeTransfer && it->second.size > 0 && it->second.size <= 128*1024 && ( ev.symAddr >> 63 ) == 0 )
|
||||
{
|
||||
assert( m_pendingSymbolCode.find( ev.symAddr ) == m_pendingSymbolCode.end() );
|
||||
m_pendingSymbolCode.emplace( ev.symAddr );
|
||||
@ -6098,6 +6322,11 @@ void Worker::ProcessCodeInformation( const QueueCodeInformation& ev )
|
||||
auto cit = m_checkedFileStrings.find( ref );
|
||||
if( cit == m_checkedFileStrings.end() ) CacheSource( ref );
|
||||
}
|
||||
if( ev.symAddr != 0 )
|
||||
{
|
||||
assert( m_data.codeSymbolMap.find( ev.ptr ) == m_data.codeSymbolMap.end() );
|
||||
m_data.codeSymbolMap.emplace( ev.ptr, ev.symAddr );
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::ProcessCrashReport( const QueueCrashReport& ev )
|
||||
@ -6270,6 +6499,54 @@ void Worker::ProcessTidToPid( const QueueTidToPid& ev )
|
||||
if( m_data.tidToPid.find( ev.tid ) == m_data.tidToPid.end() ) m_data.tidToPid.emplace( ev.tid, ev.pid );
|
||||
}
|
||||
|
||||
void Worker::ProcessHwSampleCpuCycle( const QueueHwSample& ev )
|
||||
{
|
||||
const auto time = TscTime( ev.time - m_data.baseTime );
|
||||
auto it = m_data.hwSamples.find( ev.ip );
|
||||
if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first;
|
||||
it->second.cycles.push_back( time );
|
||||
}
|
||||
|
||||
void Worker::ProcessHwSampleInstructionRetired( const QueueHwSample& ev )
|
||||
{
|
||||
const auto time = TscTime( ev.time - m_data.baseTime );
|
||||
auto it = m_data.hwSamples.find( ev.ip );
|
||||
if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first;
|
||||
it->second.retired.push_back( time );
|
||||
}
|
||||
|
||||
void Worker::ProcessHwSampleCacheReference( const QueueHwSample& ev )
|
||||
{
|
||||
const auto time = TscTime( ev.time - m_data.baseTime );
|
||||
auto it = m_data.hwSamples.find( ev.ip );
|
||||
if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first;
|
||||
it->second.cacheRef.push_back( time );
|
||||
}
|
||||
|
||||
void Worker::ProcessHwSampleCacheMiss( const QueueHwSample& ev )
|
||||
{
|
||||
const auto time = TscTime( ev.time - m_data.baseTime );
|
||||
auto it = m_data.hwSamples.find( ev.ip );
|
||||
if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first;
|
||||
it->second.cacheMiss.push_back( time );
|
||||
}
|
||||
|
||||
void Worker::ProcessHwSampleBranchRetired( const QueueHwSample& ev )
|
||||
{
|
||||
const auto time = TscTime( ev.time - m_data.baseTime );
|
||||
auto it = m_data.hwSamples.find( ev.ip );
|
||||
if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first;
|
||||
it->second.branchRetired.push_back( time );
|
||||
}
|
||||
|
||||
void Worker::ProcessHwSampleBranchMiss( const QueueHwSample& ev )
|
||||
{
|
||||
const auto time = TscTime( ev.time - m_data.baseTime );
|
||||
auto it = m_data.hwSamples.find( ev.ip );
|
||||
if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first;
|
||||
it->second.branchMiss.push_back( time );
|
||||
}
|
||||
|
||||
void Worker::ProcessParamSetup( const QueueParamSetup& ev )
|
||||
{
|
||||
CheckString( ev.name );
|
||||
@ -6930,6 +7207,21 @@ void Worker::Disconnect()
|
||||
m_disconnect = true;
|
||||
}
|
||||
|
||||
static void WriteHwSampleVec( FileWrite& f, SortedVector<Int48, Int48Sort>& vec )
|
||||
{
|
||||
uint64_t sz = vec.size();
|
||||
f.Write( &sz, sizeof( sz ) );
|
||||
if( sz != 0 )
|
||||
{
|
||||
if( !vec.is_sorted() ) vec.sort();
|
||||
int64_t refTime = 0;
|
||||
for( auto& v : vec )
|
||||
{
|
||||
WriteTimeOffset( f, refTime, v.Val() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::Write( FileWrite& f, bool fiDict )
|
||||
{
|
||||
DoPostponedWork();
|
||||
@ -7159,6 +7451,7 @@ void Worker::Write( FileWrite& f, bool fiDict )
|
||||
int64_t refTime = 0;
|
||||
f.Write( &thread->id, sizeof( thread->id ) );
|
||||
f.Write( &thread->count, sizeof( thread->count ) );
|
||||
f.Write( &thread->kernelSampleCnt, sizeof( thread->kernelSampleCnt ) );
|
||||
WriteTimeline( f, thread->timeline, refTime );
|
||||
sz = thread->messages.size();
|
||||
f.Write( &sz, sizeof( sz ) );
|
||||
@ -7193,6 +7486,7 @@ void Worker::Write( FileWrite& f, bool fiDict )
|
||||
f.Write( &ctx->period, sizeof( ctx->period ) );
|
||||
f.Write( &ctx->type, sizeof( ctx->type ) );
|
||||
f.Write( &ctx->name, sizeof( ctx->name ) );
|
||||
f.Write( &ctx->overflow, sizeof( ctx->overflow ) );
|
||||
sz = ctx->threadData.size();
|
||||
f.Write( &sz, sizeof( sz ) );
|
||||
for( auto& td : ctx->threadData )
|
||||
@ -7465,6 +7759,27 @@ void Worker::Write( FileWrite& f, bool fiDict )
|
||||
}
|
||||
}
|
||||
|
||||
sz = m_data.codeSymbolMap.size();
|
||||
f.Write( &sz, sizeof( sz ) );
|
||||
for( auto& v : m_data.codeSymbolMap )
|
||||
{
|
||||
f.Write( &v.first, sizeof( v.first ) );
|
||||
f.Write( &v.second, sizeof( v.second ) );
|
||||
}
|
||||
|
||||
sz = m_data.hwSamples.size();
|
||||
f.Write( &sz, sizeof( sz ) );
|
||||
for( auto& v : m_data.hwSamples )
|
||||
{
|
||||
f.Write( &v.first, sizeof( v.first ) );
|
||||
WriteHwSampleVec( f, v.second.cycles );
|
||||
WriteHwSampleVec( f, v.second.retired );
|
||||
WriteHwSampleVec( f, v.second.cacheRef );
|
||||
WriteHwSampleVec( f, v.second.cacheMiss );
|
||||
WriteHwSampleVec( f, v.second.branchRetired );
|
||||
WriteHwSampleVec( f, v.second.branchMiss );
|
||||
}
|
||||
|
||||
sz = m_data.sourceFileCache.size();
|
||||
f.Write( &sz, sizeof( sz ) );
|
||||
for( auto& v : m_data.sourceFileCache )
|
||||
@ -7662,4 +7977,26 @@ Worker::MemoryBlock Worker::GetSourceFileFromCache( const char* file ) const
|
||||
return it->second;
|
||||
}
|
||||
|
||||
HwSampleData* Worker::GetHwSampleData( uint64_t addr )
|
||||
{
|
||||
auto it = m_data.hwSamples.find( addr );
|
||||
if( it == m_data.hwSamples.end() ) return nullptr;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
uint64_t Worker::GetHwSampleCount() const
|
||||
{
|
||||
uint64_t cnt = 0;
|
||||
for( auto& v : m_data.hwSamples )
|
||||
{
|
||||
cnt += v.second.cycles.size();
|
||||
cnt += v.second.retired.size();
|
||||
cnt += v.second.cacheRef.size();
|
||||
cnt += v.second.cacheMiss.size();
|
||||
cnt += v.second.branchRetired.size();
|
||||
cnt += v.second.branchMiss.size();
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -285,6 +285,7 @@ private:
|
||||
Vector<uint64_t> symbolLocInline;
|
||||
int64_t newSymbolsIndex = -1;
|
||||
int64_t newInlineSymbolsIndex = -1;
|
||||
unordered_flat_map<uint64_t, uint64_t> codeSymbolMap;
|
||||
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
unordered_flat_map<VarArray<CallstackFrameId>*, uint32_t, VarArrayHasher<CallstackFrameId>, VarArrayComparator<CallstackFrameId>> parentCallstackMap;
|
||||
@ -356,6 +357,8 @@ private:
|
||||
unordered_flat_map<uint64_t, Vector<uint64_t>> locationCodeAddressList;
|
||||
|
||||
unordered_flat_map<const char*, MemoryBlock, charutil::Hasher, charutil::Comparator> sourceFileCache;
|
||||
|
||||
unordered_flat_map<uint64_t, HwSampleData> hwSamples;
|
||||
};
|
||||
|
||||
struct MbpsBlock
|
||||
@ -447,6 +450,8 @@ public:
|
||||
uint64_t GetGhostZonesCount() const { return m_data.ghostCnt; }
|
||||
uint32_t GetFrameImageCount() const { return (uint32_t)m_data.frameImage.size(); }
|
||||
uint64_t GetStringsCount() const { return m_data.strings.size() + m_data.stringData.size(); }
|
||||
uint64_t GetHwSampleCountAddress() const { return m_data.hwSamples.size(); }
|
||||
uint64_t GetHwSampleCount() const;
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
uint64_t GetChildSamplesCountSyms() const { return m_data.childSamples.size(); }
|
||||
uint64_t GetChildSamplesCountFull() const;
|
||||
@ -463,11 +468,12 @@ public:
|
||||
int GetCpuDataCpuCount() const { return m_data.cpuDataCount; }
|
||||
uint64_t GetPidFromTid( uint64_t tid ) const;
|
||||
const unordered_flat_map<uint64_t, CpuThreadData>& GetCpuThreadData() const { return m_data.cpuThreadData; }
|
||||
void GetCpuUsageAtTime( int64_t time, int& own, int& other ) const;
|
||||
void GetCpuUsage( int64_t t0, double tstep, size_t num, std::vector<std::pair<int, int>>& out );
|
||||
const unordered_flat_map<const char*, MemoryBlock, charutil::Hasher, charutil::Comparator>& GetSourceFileCache() const { return m_data.sourceFileCache; }
|
||||
uint64_t GetSourceFileCacheCount() const { return m_data.sourceFileCache.size(); }
|
||||
uint64_t GetSourceFileCacheSize() const;
|
||||
MemoryBlock GetSourceFileFromCache( const char* file ) const;
|
||||
HwSampleData* GetHwSampleData( uint64_t addr );
|
||||
|
||||
int64_t GetFrameTime( const FrameData& fd, size_t idx ) const;
|
||||
int64_t GetFrameBegin( const FrameData& fd, size_t idx ) const;
|
||||
@ -496,6 +502,8 @@ public:
|
||||
const char* GetSymbolCode( uint64_t sym, uint32_t& len ) const;
|
||||
uint64_t GetSymbolForAddress( uint64_t address ) const;
|
||||
uint64_t GetSymbolForAddress( uint64_t address, uint32_t& offset ) const;
|
||||
uint64_t GetInlineSymbolForAddress( uint64_t address ) const;
|
||||
bool HasInlineSymbolAddresses() const { return !m_data.codeSymbolMap.empty(); }
|
||||
StringIdx GetLocationForAddress( uint64_t address, uint32_t& line ) const;
|
||||
const Vector<uint64_t>* GetAddressesForLocation( uint32_t fileStringIdx, uint32_t line ) const;
|
||||
const uint64_t* GetInlineSymbolList( uint64_t sym, uint32_t len ) const;
|
||||
@ -679,6 +687,12 @@ private:
|
||||
tracy_force_inline void ProcessContextSwitch( const QueueContextSwitch& ev );
|
||||
tracy_force_inline void ProcessThreadWakeup( const QueueThreadWakeup& ev );
|
||||
tracy_force_inline void ProcessTidToPid( const QueueTidToPid& ev );
|
||||
tracy_force_inline void ProcessHwSampleCpuCycle( const QueueHwSample& ev );
|
||||
tracy_force_inline void ProcessHwSampleInstructionRetired( const QueueHwSample& ev );
|
||||
tracy_force_inline void ProcessHwSampleCacheReference( const QueueHwSample& ev );
|
||||
tracy_force_inline void ProcessHwSampleCacheMiss( const QueueHwSample& ev );
|
||||
tracy_force_inline void ProcessHwSampleBranchRetired( const QueueHwSample& ev );
|
||||
tracy_force_inline void ProcessHwSampleBranchMiss( const QueueHwSample& ev );
|
||||
tracy_force_inline void ProcessParamSetup( const QueueParamSetup& ev );
|
||||
tracy_force_inline void ProcessCpuTopology( const QueueCpuTopology& ev );
|
||||
tracy_force_inline void ProcessMemNamePayload( const QueueMemNamePayload& ev );
|
||||
@ -691,6 +705,7 @@ private:
|
||||
tracy_force_inline void ProcessGpuZoneBeginImplCommon( GpuEvent* zone, const QueueGpuZoneBeginLean& ev, bool serial );
|
||||
tracy_force_inline MemEvent* ProcessMemAllocImpl( uint64_t memname, MemData& memdata, const QueueMemAlloc& ev );
|
||||
tracy_force_inline MemEvent* ProcessMemFreeImpl( uint64_t memname, MemData& memdata, const QueueMemFree& ev );
|
||||
tracy_force_inline void ProcessCallstackSampleImpl( const SampleData& sd, ThreadData& td, int64_t t, uint32_t callstack );
|
||||
|
||||
void ZoneStackFailure( uint64_t thread, const ZoneEvent* ev );
|
||||
void ZoneDoubleEndFailure( uint64_t thread, const ZoneEvent* ev );
|
||||
@ -873,6 +888,7 @@ private:
|
||||
bool m_onDemand;
|
||||
bool m_ignoreMemFreeFaults;
|
||||
bool m_codeTransfer;
|
||||
bool m_combineSamples;
|
||||
|
||||
short_ptr<GpuCtxData> m_gpuCtxMap[256];
|
||||
uint32_t m_pendingCallstackId = 0;
|
||||
|
@ -86,6 +86,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\common\TracySocket.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp" />
|
||||
<ClCompile Include="..\..\..\common\TracySystem.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4.cpp" />
|
||||
<ClCompile Include="..\..\..\common\tracy_lz4hc.cpp" />
|
||||
@ -136,6 +137,7 @@
|
||||
<ClInclude Include="..\..\..\common\TracyProtocol.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyQueue.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySocket.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp" />
|
||||
<ClInclude Include="..\..\..\common\TracySystem.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp" />
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4hc.hpp" />
|
||||
|
@ -156,6 +156,9 @@
|
||||
<ClCompile Include="..\..\..\zstd\dictBuilder\zdict.c">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\common\TracyStackFrames.cpp">
|
||||
<Filter>common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\common\tracy_lz4.hpp">
|
||||
@ -338,5 +341,8 @@
|
||||
<ClInclude Include="..\..\..\zstd\dictBuilder\divsufsort.h">
|
||||
<Filter>zstd\dictBuilder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\TracyStackFrames.hpp">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user