mirror of
https://github.com/wolfpld/tracy.git
synced 2026-06-13 10:49:05 +00:00
Compare commits
28 Commits
slomp/trac
...
slomp/webg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f68dbcbbc | ||
|
|
3c90c2d7f8 | ||
|
|
7e572c55fb | ||
|
|
07bfe3465e | ||
|
|
0544440a34 | ||
|
|
f622b97436 | ||
|
|
dfded9d55d | ||
|
|
7180ea381f | ||
|
|
0c74658dd3 | ||
|
|
cc091a99a2 | ||
|
|
f89709e99e | ||
|
|
a4c5f15312 | ||
|
|
3455fd9f82 | ||
|
|
9ab39d8af3 | ||
|
|
bfab6d03f4 | ||
|
|
66e4f5cef7 | ||
|
|
7637971e9e | ||
|
|
4e3cffc4ba | ||
|
|
3956616fc2 | ||
|
|
d34c45fa5a | ||
|
|
8fe5a511c9 | ||
|
|
afdd2e2f81 | ||
|
|
3c1b1b2f80 | ||
|
|
992134f85e | ||
|
|
37bc986584 | ||
|
|
feb4e7c989 | ||
|
|
4a8fe6f56e | ||
|
|
a960a25285 |
@@ -78,12 +78,20 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform-specific source and link settings
|
||||
# Platform — RGFW (cross-platform windowing, fetched automatically)
|
||||
# ---------------------------------------------------------------------------
|
||||
set(PLATFORM_GENERATED_INCLUDES "")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(rgfw
|
||||
GIT_REPOSITORY https://github.com/ColleagueRiley/RGFW.git
|
||||
GIT_TAG main # pin to a specific commit for reproducible builds
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(rgfw)
|
||||
|
||||
set(PLATFORM_SOURCES platform/platform_rgfw.cpp)
|
||||
set(PLATFORM_INCLUDES ${rgfw_SOURCE_DIR})
|
||||
|
||||
if(APPLE)
|
||||
set(PLATFORM_SOURCES platform/platform_macos.mm)
|
||||
set(PLATFORM_LIBS
|
||||
"-framework Cocoa"
|
||||
"-framework Metal"
|
||||
@@ -92,45 +100,14 @@ if(APPLE)
|
||||
"-framework IOKit"
|
||||
"-framework IOSurface"
|
||||
)
|
||||
set_source_files_properties(platform/platform_macos.mm
|
||||
PROPERTIES COMPILE_FLAGS "-ObjC++"
|
||||
)
|
||||
elseif(WIN32)
|
||||
set(PLATFORM_SOURCES platform/platform_windows.cpp)
|
||||
set(PLATFORM_LIBS user32 gdi32)
|
||||
else()
|
||||
# Linux / Wayland — generate xdg-shell protocol glue via wayland-scanner.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols)
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
||||
find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
|
||||
|
||||
set(XDG_SHELL_XML "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml")
|
||||
set(XDG_SHELL_H "${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.h")
|
||||
set(XDG_SHELL_C "${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-protocol.c")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${XDG_SHELL_H}"
|
||||
COMMAND "${WAYLAND_SCANNER}" client-header "${XDG_SHELL_XML}" "${XDG_SHELL_H}"
|
||||
DEPENDS "${XDG_SHELL_XML}"
|
||||
COMMENT "Generating xdg-shell-client-protocol.h"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT "${XDG_SHELL_C}"
|
||||
COMMAND "${WAYLAND_SCANNER}" private-code "${XDG_SHELL_XML}" "${XDG_SHELL_C}"
|
||||
DEPENDS "${XDG_SHELL_XML}"
|
||||
COMMENT "Generating xdg-shell-protocol.c"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
set(PLATFORM_SOURCES
|
||||
platform/platform_wayland.cpp
|
||||
"${XDG_SHELL_C}"
|
||||
"${XDG_SHELL_H}"
|
||||
)
|
||||
set(PLATFORM_LIBS wayland-client)
|
||||
set(PLATFORM_GENERATED_INCLUDES "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
find_package(X11 REQUIRED)
|
||||
if(NOT X11_Xrandr_FOUND)
|
||||
message(FATAL_ERROR "Xrandr not found — install libxrandr-dev")
|
||||
endif()
|
||||
set(PLATFORM_LIBS X11::X11 X11::Xrandr)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -163,7 +140,7 @@ endif()
|
||||
target_include_directories(spinning_triangle PRIVATE
|
||||
"${WGPU_PATH}/include"
|
||||
"${TRACY_DIR}/public"
|
||||
${PLATFORM_GENERATED_INCLUDES}
|
||||
${PLATFORM_INCLUDES}
|
||||
)
|
||||
|
||||
target_link_directories(spinning_triangle PRIVATE "${WGPU_PATH}/lib")
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
// platform_macos.mm — macOS backend (Cocoa + CAMetalLayer)
|
||||
//
|
||||
// Compile flags (see spinning_triangle.cpp header for full invocation):
|
||||
// -ObjC++ -framework Cocoa -framework Metal -framework QuartzCore \
|
||||
// -framework Foundation -framework IOKit -framework IOSurface
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#include <CoreFoundation/CFDate.h>
|
||||
#include <webgpu/webgpu.h>
|
||||
#include "platform.h"
|
||||
|
||||
static CAMetalLayer* sMetalLayer = nullptr;
|
||||
static CFAbsoluteTime sStartTime = 0;
|
||||
static void (*sRenderCb)() = nullptr;
|
||||
static void (*sShutdownCb)() = nullptr;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cocoa app — window, metal layer, render timer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
|
||||
@property (strong) NSWindow* window;
|
||||
@property (strong) NSTimer* timer;
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
|
||||
// ~60 fps render loop
|
||||
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 60.0
|
||||
target:self
|
||||
selector:@selector(tick:)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
|
||||
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown
|
||||
handler:^NSEvent*(NSEvent* event) {
|
||||
if (event.keyCode == 53) { // kVK_Escape
|
||||
[NSApp terminate:nil];
|
||||
return nil;
|
||||
}
|
||||
return event;
|
||||
}];
|
||||
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[self.window makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
- (void)tick:(NSTimer*)t {
|
||||
if (sRenderCb) sRenderCb();
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification*)notification {
|
||||
[self.timer invalidate];
|
||||
if (sShutdownCb) sShutdownCb();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Platform interface implementation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool platformInit(int width, int height, const char* title) {
|
||||
NSApplication* app = [NSApplication sharedApplication];
|
||||
[app setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
NSRect frame = NSMakeRect(200, 200, width, height);
|
||||
NSWindow* window = [[NSWindow alloc]
|
||||
initWithContentRect:frame
|
||||
styleMask:(NSWindowStyleMaskTitled |
|
||||
NSWindowStyleMaskClosable |
|
||||
NSWindowStyleMaskMiniaturizable)
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
[window setTitle:[NSString stringWithUTF8String:title]];
|
||||
|
||||
// Metal-backed layer
|
||||
NSView* contentView = [window contentView];
|
||||
[contentView setWantsLayer:YES];
|
||||
sMetalLayer = [CAMetalLayer layer];
|
||||
sMetalLayer.frame = contentView.bounds;
|
||||
sMetalLayer.contentsScale = [window backingScaleFactor];
|
||||
sMetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
[contentView.layer addSublayer:sMetalLayer];
|
||||
|
||||
AppDelegate* del = [[AppDelegate alloc] init];
|
||||
del.window = window;
|
||||
[app setDelegate:del];
|
||||
|
||||
sStartTime = CFAbsoluteTimeGetCurrent();
|
||||
return true;
|
||||
}
|
||||
|
||||
WGPUSurface platformCreateSurface(WGPUInstance instance) {
|
||||
WGPUSurfaceSourceMetalLayer metalSrc = {};
|
||||
metalSrc.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
|
||||
metalSrc.layer = sMetalLayer;
|
||||
|
||||
WGPUSurfaceDescriptor surfDesc = {};
|
||||
surfDesc.nextInChain = (WGPUChainedStruct*)&metalSrc;
|
||||
return wgpuInstanceCreateSurface(instance, &surfDesc);
|
||||
}
|
||||
|
||||
double platformGetTime() {
|
||||
return CFAbsoluteTimeGetCurrent() - sStartTime;
|
||||
}
|
||||
|
||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
||||
sRenderCb = render;
|
||||
sShutdownCb = shutdown;
|
||||
@autoreleasepool {
|
||||
[[NSApplication sharedApplication] run];
|
||||
}
|
||||
}
|
||||
72
examples/webgpu/triangle/platform/platform_rgfw.cpp
Normal file
72
examples/webgpu/triangle/platform/platform_rgfw.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// platform_rgfw.cpp — RGFW windowing backend for the WebGPU example
|
||||
// https://github.com/ColleagueRiley/RGFW
|
||||
|
||||
#include "platform.h" // webgpu/webgpu.h first so RGFW sees WGPUSurface
|
||||
|
||||
#define RGFW_WEBGPU
|
||||
#define RGFW_IMPLEMENTATION
|
||||
#include <RGFW.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <X11/Xlib.h>
|
||||
static bool platformHasDisplay() {
|
||||
// RGFW workaround: RGFW indiscriminately passes XOpenDisplay(0) unchecked
|
||||
// to X11 functions like XCreateWindow(), which will lead to SIGSEGV.
|
||||
Display* display = XOpenDisplay(0);
|
||||
if (display == nullptr) {
|
||||
fprintf(stderr, "ERROR: failed to open X11 display (is $DISPLAY set?)\n");
|
||||
return false;
|
||||
}
|
||||
XCloseDisplay(display);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static bool platformHasDisplay() {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static RGFW_window* sWin = nullptr;
|
||||
static std::chrono::steady_clock::time_point sStartTime;
|
||||
|
||||
bool platformInit(int width, int height, const char* title) {
|
||||
if (!platformHasDisplay()) {
|
||||
fprintf(stderr, "ERROR: no display found\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sWin = RGFW_createWindow(title, 0, 0, width, height, RGFW_windowCenter);
|
||||
if (!sWin) {
|
||||
fprintf(stderr, "ERROR: failed to create window\n");
|
||||
return false;
|
||||
}
|
||||
RGFW_window_setExitKey(sWin, RGFW_keyEscape);
|
||||
sStartTime = std::chrono::steady_clock::now();
|
||||
return true;
|
||||
}
|
||||
|
||||
WGPUSurface platformCreateSurface(WGPUInstance instance) {
|
||||
return RGFW_window_createSurface_WebGPU(sWin, instance);
|
||||
}
|
||||
|
||||
double platformGetTime() {
|
||||
return std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now() - sStartTime).count();
|
||||
}
|
||||
|
||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
||||
while (RGFW_window_shouldClose(sWin) == RGFW_FALSE) {
|
||||
RGFW_event event;
|
||||
while (RGFW_window_checkEvent(sWin, &event)) {
|
||||
if (event.type == RGFW_windowClose) goto done;
|
||||
}
|
||||
render();
|
||||
}
|
||||
done:
|
||||
shutdown();
|
||||
RGFW_window_close(sWin);
|
||||
sWin = nullptr;
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
// platform_wayland.cpp — Linux/Wayland backend
|
||||
//
|
||||
// Dependencies:
|
||||
// libwayland-client, wayland-protocols (for xdg-shell)
|
||||
//
|
||||
// Generate xdg-shell protocol glue before building:
|
||||
// XML=$(pkg-config --variable=pkgdatadir wayland-protocols)/stable/xdg-shell/xdg-shell.xml
|
||||
// wayland-scanner client-header $XML xdg-shell-client-protocol.h
|
||||
// wayland-scanner private-code $XML xdg-shell-protocol.c
|
||||
//
|
||||
// Compile flags (see spinning_triangle.cpp header for full invocation):
|
||||
// g++ -std=c++17 spinning_triangle.cpp platform_wayland.cpp \
|
||||
// xdg-shell-protocol.c \
|
||||
// -I/path/to/wgpu/include -L/path/to/wgpu/lib -lwgpu_native \
|
||||
// $(pkg-config --cflags --libs wayland-client) \
|
||||
// -o spinning_triangle
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <time.h>
|
||||
#include <webgpu/webgpu.h>
|
||||
#include "platform.h"
|
||||
|
||||
static wl_display* sDisplay = nullptr;
|
||||
static wl_compositor* sCompositor = nullptr;
|
||||
static xdg_wm_base* sWmBase = nullptr;
|
||||
static wl_seat* sSeat = nullptr;
|
||||
static wl_keyboard* sKeyboard = nullptr;
|
||||
static wl_surface* sSurface = nullptr;
|
||||
static xdg_surface* sXdgSurface = nullptr;
|
||||
static xdg_toplevel* sToplevel = nullptr;
|
||||
static bool sConfigured = false;
|
||||
static bool sRunning = false;
|
||||
static struct timespec sStartTime = {};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// xdg_wm_base listener — ping/pong keepalive
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void wmBasePing(void*, xdg_wm_base* wm, uint32_t serial) {
|
||||
xdg_wm_base_pong(wm, serial);
|
||||
}
|
||||
static const xdg_wm_base_listener kWmBaseListener = { wmBasePing };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// xdg_surface listener — acknowledge configure events
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void xdgSurfaceConfigure(void*, xdg_surface* surf, uint32_t serial) {
|
||||
xdg_surface_ack_configure(surf, serial);
|
||||
sConfigured = true;
|
||||
}
|
||||
static const xdg_surface_listener kXdgSurfaceListener = { xdgSurfaceConfigure };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// xdg_toplevel listener — window close / resize
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void toplevelClose(void*, xdg_toplevel*) {
|
||||
sRunning = false;
|
||||
}
|
||||
static void toplevelConfigure(void*, xdg_toplevel*, int32_t, int32_t, wl_array*) {}
|
||||
static const xdg_toplevel_listener kToplevelListener = { toplevelConfigure, toplevelClose };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Keyboard listener — Escape to quit
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void kbdKeymap(void*, wl_keyboard*, uint32_t, int32_t, uint32_t) {}
|
||||
static void kbdEnter(void*, wl_keyboard*, uint32_t, wl_surface*, wl_array*) {}
|
||||
static void kbdLeave(void*, wl_keyboard*, uint32_t, wl_surface*) {}
|
||||
static void kbdKey(void*, wl_keyboard*, uint32_t, uint32_t, uint32_t key, uint32_t state) {
|
||||
// key 1 == KEY_ESC in Linux evdev (linux/input-event-codes.h)
|
||||
if (key == 1 && state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
sRunning = false;
|
||||
}
|
||||
static void kbdModifiers(void*, wl_keyboard*, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) {}
|
||||
static void kbdRepeatInfo(void*, wl_keyboard*, int32_t, int32_t) {}
|
||||
static const wl_keyboard_listener kKbdListener = {
|
||||
kbdKeymap, kbdEnter, kbdLeave, kbdKey, kbdModifiers, kbdRepeatInfo
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// wl_seat listener — grab keyboard capability
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void seatCapabilities(void*, wl_seat* seat, uint32_t caps) {
|
||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !sKeyboard) {
|
||||
sKeyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_add_listener(sKeyboard, &kKbdListener, nullptr);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && sKeyboard) {
|
||||
wl_keyboard_release(sKeyboard);
|
||||
sKeyboard = nullptr;
|
||||
}
|
||||
}
|
||||
static void seatName(void*, wl_seat*, const char*) {}
|
||||
static const wl_seat_listener kSeatListener = { seatCapabilities, seatName };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Registry listener — bind global interfaces
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void registryGlobal(void*, wl_registry* reg,
|
||||
uint32_t name, const char* iface, uint32_t ver) {
|
||||
if (strcmp(iface, wl_compositor_interface.name) == 0)
|
||||
sCompositor = (wl_compositor*)wl_registry_bind(reg, name, &wl_compositor_interface, 4);
|
||||
else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
|
||||
sWmBase = (xdg_wm_base*)wl_registry_bind(reg, name, &xdg_wm_base_interface, 1);
|
||||
xdg_wm_base_add_listener(sWmBase, &kWmBaseListener, nullptr);
|
||||
} else if (strcmp(iface, wl_seat_interface.name) == 0) {
|
||||
sSeat = (wl_seat*)wl_registry_bind(reg, name, &wl_seat_interface, 5);
|
||||
wl_seat_add_listener(sSeat, &kSeatListener, nullptr);
|
||||
}
|
||||
}
|
||||
static void registryGlobalRemove(void*, wl_registry*, uint32_t) {}
|
||||
static const wl_registry_listener kRegistryListener = { registryGlobal, registryGlobalRemove };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Platform interface implementation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool platformInit(int width, int height, const char* title) {
|
||||
sDisplay = wl_display_connect(nullptr);
|
||||
if (!sDisplay) { fprintf(stderr, "Cannot connect to Wayland display\n"); return false; }
|
||||
|
||||
wl_registry* registry = wl_display_get_registry(sDisplay);
|
||||
wl_registry_add_listener(registry, &kRegistryListener, nullptr);
|
||||
|
||||
// Two roundtrips: first to enumerate globals, second for seat capabilities
|
||||
wl_display_roundtrip(sDisplay);
|
||||
wl_display_roundtrip(sDisplay);
|
||||
|
||||
if (!sCompositor) { fprintf(stderr, "No wl_compositor\n"); return false; }
|
||||
if (!sWmBase) { fprintf(stderr, "No xdg_wm_base\n"); return false; }
|
||||
|
||||
sSurface = wl_compositor_create_surface(sCompositor);
|
||||
sXdgSurface = xdg_wm_base_get_xdg_surface(sWmBase, sSurface);
|
||||
sToplevel = xdg_surface_get_toplevel(sXdgSurface);
|
||||
|
||||
xdg_surface_add_listener(sXdgSurface, &kXdgSurfaceListener, nullptr);
|
||||
xdg_toplevel_add_listener(sToplevel, &kToplevelListener, nullptr);
|
||||
xdg_toplevel_set_title(sToplevel, title);
|
||||
xdg_toplevel_set_app_id(sToplevel, "spinning_triangle");
|
||||
|
||||
wl_surface_commit(sSurface);
|
||||
|
||||
// Wait for the compositor to send the first configure
|
||||
while (!sConfigured) wl_display_dispatch(sDisplay);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &sStartTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
WGPUSurface platformCreateSurface(WGPUInstance instance) {
|
||||
WGPUSurfaceSourceWaylandSurface waylandSrc = {};
|
||||
waylandSrc.chain.sType = WGPUSType_SurfaceSourceWaylandSurface;
|
||||
waylandSrc.display = sDisplay;
|
||||
waylandSrc.surface = sSurface;
|
||||
|
||||
WGPUSurfaceDescriptor surfDesc = {};
|
||||
surfDesc.nextInChain = (WGPUChainedStruct*)&waylandSrc;
|
||||
return wgpuInstanceCreateSurface(instance, &surfDesc);
|
||||
}
|
||||
|
||||
double platformGetTime() {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
return (double)(now.tv_sec - sStartTime.tv_sec)
|
||||
+ (double)(now.tv_nsec - sStartTime.tv_nsec) * 1e-9;
|
||||
}
|
||||
|
||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
||||
// Target ~16.67 ms per frame (60 fps)
|
||||
static const long kFrameNs = 1000000000L / 60;
|
||||
|
||||
sRunning = true;
|
||||
while (sRunning) {
|
||||
struct timespec frameStart;
|
||||
clock_gettime(CLOCK_MONOTONIC, &frameStart);
|
||||
|
||||
// Dispatch pending Wayland events without blocking
|
||||
if (wl_display_dispatch_pending(sDisplay) < 0) break;
|
||||
wl_display_flush(sDisplay);
|
||||
|
||||
if (sRunning) render();
|
||||
|
||||
// Sleep for the remainder of the frame budget
|
||||
struct timespec frameEnd;
|
||||
clock_gettime(CLOCK_MONOTONIC, &frameEnd);
|
||||
long elapsed = (frameEnd.tv_sec - frameStart.tv_sec) * 1000000000L
|
||||
+ (frameEnd.tv_nsec - frameStart.tv_nsec);
|
||||
long remaining = kFrameNs - elapsed;
|
||||
if (remaining > 0) {
|
||||
struct timespec ts = { 0, remaining };
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown();
|
||||
|
||||
// Cleanup Wayland objects
|
||||
if (sKeyboard) { wl_keyboard_release(sKeyboard); sKeyboard = nullptr; }
|
||||
if (sToplevel) { xdg_toplevel_destroy(sToplevel); sToplevel = nullptr; }
|
||||
if (sXdgSurface) { xdg_surface_destroy(sXdgSurface); sXdgSurface = nullptr; }
|
||||
if (sSurface) { wl_surface_destroy(sSurface); sSurface = nullptr; }
|
||||
if (sWmBase) { xdg_wm_base_destroy(sWmBase); sWmBase = nullptr; }
|
||||
if (sSeat) { wl_seat_release(sSeat); sSeat = nullptr; }
|
||||
if (sCompositor) { wl_compositor_destroy(sCompositor); sCompositor = nullptr; }
|
||||
wl_display_disconnect(sDisplay);
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// platform_windows.cpp — Windows backend (Win32)
|
||||
//
|
||||
// Compile flags (MSVC, console subsystem):
|
||||
// cl /std:c++17 spinning_triangle.cpp platform_windows.cpp \
|
||||
// /I\path\to\wgpu\include \path\to\wgpu\lib\wgpu_native.lib \
|
||||
// user32.lib gdi32.lib /Fe:spinning_triangle.exe
|
||||
//
|
||||
// MinGW/Clang equivalent:
|
||||
// clang++ -std=c++17 spinning_triangle.cpp platform_windows.cpp \
|
||||
// -I/path/to/wgpu/include -L/path/to/wgpu/lib -lwgpu_native \
|
||||
// -luser32 -lgdi32 -o spinning_triangle.exe
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <webgpu/webgpu.h>
|
||||
#include <stdio.h>
|
||||
#include "platform.h"
|
||||
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#pragma comment(lib, "gdi32.lib")
|
||||
#pragma comment(lib, "dxguid.lib") // Dawn: WKPDID_D3DDebugObjectName
|
||||
#pragma comment(lib, "OneCore") // Dawn: CompareObjectHandles
|
||||
#pragma comment(lib, "ntdll.lib") // wgpu-native: NtReadFile et al.
|
||||
|
||||
static HWND sHwnd = nullptr;
|
||||
static bool sRunning = false;
|
||||
static LARGE_INTEGER sFreq = {};
|
||||
static LARGE_INTEGER sStartTime = {};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Win32 window procedure
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
switch (msg) {
|
||||
case WM_KEYDOWN:
|
||||
if (wp == VK_ESCAPE) { sRunning = false; return 0; }
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
case WM_DESTROY:
|
||||
sRunning = false;
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcA(hwnd, msg, wp, lp);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Platform interface implementation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool platformInit(int width, int height, const char* title) {
|
||||
WNDCLASSEXA wc = {};
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = wndProc;
|
||||
wc.hInstance = GetModuleHandleA(nullptr);
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.lpszClassName = "SpinningTriangle";
|
||||
if (!RegisterClassExA(&wc)) {
|
||||
fprintf(stderr, "RegisterClassExA failed (%lu)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust client area to match the requested dimensions
|
||||
RECT rect = { 0, 0, width, height };
|
||||
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX), FALSE);
|
||||
|
||||
sHwnd = CreateWindowExA(
|
||||
0, "SpinningTriangle", title,
|
||||
WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX),
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
rect.right - rect.left, rect.bottom - rect.top,
|
||||
nullptr, nullptr, GetModuleHandleA(nullptr), nullptr);
|
||||
if (!sHwnd) {
|
||||
fprintf(stderr, "CreateWindowExA failed (%lu)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
ShowWindow(sHwnd, SW_SHOW);
|
||||
UpdateWindow(sHwnd);
|
||||
|
||||
QueryPerformanceFrequency(&sFreq);
|
||||
QueryPerformanceCounter(&sStartTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
WGPUSurface platformCreateSurface(WGPUInstance instance) {
|
||||
WGPUSurfaceSourceWindowsHWND hwndSrc = {};
|
||||
hwndSrc.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
|
||||
hwndSrc.hinstance = GetModuleHandleA(nullptr);
|
||||
hwndSrc.hwnd = sHwnd;
|
||||
|
||||
WGPUSurfaceDescriptor surfDesc = {};
|
||||
surfDesc.nextInChain = (WGPUChainedStruct*)&hwndSrc;
|
||||
return wgpuInstanceCreateSurface(instance, &surfDesc);
|
||||
}
|
||||
|
||||
double platformGetTime() {
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
return (double)(now.QuadPart - sStartTime.QuadPart) / (double)sFreq.QuadPart;
|
||||
}
|
||||
|
||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
||||
// Target ~16.67 ms per frame (60 fps)
|
||||
static const double kFrameTime = 1.0 / 60.0;
|
||||
|
||||
sRunning = true;
|
||||
while (sRunning) {
|
||||
double frameStart = platformGetTime();
|
||||
|
||||
// Drain the Win32 message queue
|
||||
MSG msg;
|
||||
while (PeekMessageA(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT) { sRunning = false; break; }
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageA(&msg);
|
||||
}
|
||||
|
||||
if (sRunning) render();
|
||||
|
||||
// Sleep for the remainder of the frame budget
|
||||
double elapsed = platformGetTime() - frameStart;
|
||||
if (elapsed < kFrameTime) {
|
||||
DWORD ms = (DWORD)((kFrameTime - elapsed) * 1000.0);
|
||||
if (ms > 0) Sleep(ms);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown();
|
||||
if (sHwnd) DestroyWindow(sHwnd);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ The user manual
|
||||
|
||||
**Bartosz Taudul** [\<wolf@nereid.pl\>](mailto:wolf@nereid.pl)
|
||||
|
||||
2026-06-06 <https://github.com/wolfpld/tracy>
|
||||
2026-06-09 <https://github.com/wolfpld/tracy>
|
||||
|
||||
# Quick overview {#quick-overview .unnumbered}
|
||||
|
||||
@@ -1495,6 +1495,12 @@ You also need to periodically collect the GPU events using the `TracyGpuCollect`
|
||||
|
||||
[^49]: Because Apple is unable to implement standards properly.
|
||||
|
||||
##### Calibrated context
|
||||
|
||||
By default, the OpenGL context is uncalibrated: the CPU and GPU clocks are aligned only once, when the context is created, so over long captures the two time domains may drift apart (section [5.4](#options) describes correcting this drift manually). Defining `TRACY_OPENGL_AUTO_CALIBRATION` before including `TracyOpenGL.hpp` enables periodic recalibration instead: roughly once per second Tracy samples the GPU and CPU clocks together and emits a calibration event, allowing the profiler to track and remove the drift automatically.
|
||||
|
||||
This is opt-in because OpenGL exposes no atomic CPU+GPU timestamp query (unlike Vulkan's `VK_EXT_calibrated_timestamps` or Direct3D 12, whose contexts are always calibrated). Recalibration therefore reads the GPU clock with `glGetInteger64v(GL_TIMESTAMP)`, which forces a CPU/GPU synchronization (a pipeline stall) each time it runs. Enable it only when the improved long-capture alignment is worth the periodic stall.
|
||||
|
||||
### Vulkan
|
||||
|
||||
Similarly, for Vulkan support you should include the `public/tracy/TracyVulkan.hpp` header file. Tracing Vulkan devices and queues is a bit more involved, and the Vulkan initialization macro `TracyVkContext(physdev, device, queue, cmdbuf)` returns an instance of `TracyVkCtx` object, which tracks an associated Vulkan queue. Cleanup is performed using the `TracyVkDestroy(ctx)` macro. You may create multiple Vulkan contexts. To set a custom name for the context, use the `TracyVkContextName(ctx, name, size)` macro.
|
||||
@@ -1794,6 +1800,10 @@ By default, tracy client resolves callstack symbols in a background thread at ru
|
||||
|
||||
The generated tracy capture will have callstack frames symbols showing `[unresolved]`. The `update` tool can be used to load that capture, perform symbol resolution offline (by passing `-r`) and writing out a new capture with symbols resolved. By default `update` will use the original shared libraries paths that were recorded in the capture (which assumes running in the same machine or a machine with identical filesystem setup as the one used to run the tracy instrumented application). You can do path substitution with the `-p` option to perform any number of path substitions in order to use symbols located elsewhere.
|
||||
|
||||
By default symbol resolution is performed with the platform's native facility: the DbgHelp library on Windows, and the `addr2line` tool found in `PATH` elsewhere. You can override this with the `-a` option, passing the path to a custom `addr2line`-compatible tool (for instance an `addr2line` from a cross-compilation toolchain, or `llvm-addr2line`). The `-a` option works on all platforms, including Windows, and takes precedence over the platform default.
|
||||
|
||||
Extra arguments can be passed verbatim to the resolution tool with the `-A` option. Tracy records callstack frame offsets relative to the image base, but `addr2line`-compatible tools expect a full virtual address for images that have a non-zero preferred image base (such as PE on Windows or Mach-O on Apple). For these, pass `-A "--relative-address"` so that `llvm-addr2line` or `llvm-symbolizer` adds the image base back. ELF images need no such adjustment.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important**
|
||||
>
|
||||
@@ -1988,6 +1998,39 @@ After you release the lock use the `TracyCLockAfterUnlock` macro:
|
||||
|
||||
You can optionally mark the location of where the lock is held by using the `TracyCLockMark` macro, this should be done after acquiring the lock.
|
||||
|
||||
Similarly, you can use the following macros to mark a shared lock using the C API:
|
||||
|
||||
- `TracyCSharedLockAnnounce(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockTerminate(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockBeforeLock(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockAfterLock(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockAfterUnlock(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockAfterTryLock(lock_ctx, acquired)`
|
||||
|
||||
- `TracyCSharedLockBeforeSharedLock(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockAfterSharedLock(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockAfterSharedUnlock(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockAfterTrySharedLock(lock_ctx, acquired)`
|
||||
|
||||
- `TracyCSharedLockMark(lock_ctx)`
|
||||
|
||||
- `TracyCSharedLockCustomName(lock_ctx, name, size)`
|
||||
|
||||
A shared lock context has to be defined next to the shared lock that it will be marking:
|
||||
|
||||
TracyCSharedLockCtx tracy_shared_lock_ctx;
|
||||
HANDLE shared_lock;
|
||||
|
||||
The same rules apply to shared locks as to regular locks, but you need to use the shared lock macros instead. Lock implementations in classes `Lockable` and `SharedLockable` show how to properly perform context handling.
|
||||
|
||||
### Memory profiling {#cmemoryprofiling}
|
||||
|
||||
Use the following macros in your implementations of `malloc` and `free`:
|
||||
@@ -3582,7 +3625,7 @@ You can freely adjust each time range on the timeline by clicking the left mouse
|
||||
|
||||
Tracy allows adding custom notes to the trace. For example, you may want to mark a region to ignore because the application was out-of-focus or a region where a new user was connecting to the game, which resulted in a frame drop that needs to be investigated.
|
||||
|
||||
Methods of specifying the annotation region are described in section [5.3](#timeranges). When a new annotation is added, a settings window is displayed (section [5.21](#annotationsettings)), allowing you to enter a description.
|
||||
Methods of specifying the annotation region are described in section [5.3](#timeranges). When a new annotation is added, it is assigned a semi-unique random name to make it distinguishable. The settings window is also opened (section [5.21](#annotationsettings)), allowing you to enter your own description of the annotation.
|
||||
|
||||
Annotations are displayed on the timeline, as presented in figure [21](#annotation). Clicking on the circle next to the text description will open the annotation settings window, in which you can modify or remove the region. List of all annotations in the trace is available in the annotations list window described in section [5.22](#annotationlist), which is accessible through the * Tools* button on the control menu.
|
||||
|
||||
@@ -4125,7 +4168,9 @@ The information about the selected memory allocation is displayed in this window
|
||||
|
||||
## Trace information window {#traceinfo}
|
||||
|
||||
This window contains information about the current trace: captured program name, time of the capture, profiler version which performed the capture, and a custom trace description, which you can fill in.
|
||||
This window contains information about the current trace: captured program name, time of the capture, profiler version which performed the capture.
|
||||
|
||||
There's an text entry field for an optional custom description of the trace for you to fill in. This description will appear on the profiler window title bar, or when comparing two traces (section [5.8](#compare)), enabling you to quickly recognize what the trace contains. For some people it's fine to just have *any* semi-unique description to be able to identify a specific trace. For such purposes there's an * Generate name* button, which will set the trace description to an abstract meaningless identifier.
|
||||
|
||||
If the * Public sidecar* option is selected, the file containing trace-specific user settings (see section [9.2](#tracespecific)) will be saved on disk next to the trace file.
|
||||
|
||||
@@ -4159,6 +4204,7 @@ If an application should crash during profiling (section [2.5](#crashhandling))
|
||||
|
||||
-----
|
||||
|
||||
- Dice icon
|
||||
- User Gear icon
|
||||
|
||||
## Zone information window {#zoneinfo}
|
||||
@@ -4562,7 +4608,12 @@ The profiled program is highlighted using green color. Furthermore, the yellow h
|
||||
|
||||
## Annotation settings window {#annotationsettings}
|
||||
|
||||
In this window, you may modify how a timeline annotation (section [5.3.1](#annotatingtrace)) is presented by setting its text description or selecting region highlight color. If the note is no longer needed, you may also remove it here.
|
||||
In this window, you may modify how a timeline annotation (section [5.3.1](#annotatingtrace)) is presented by setting its text description or selecting region highlight color. A random annotation description can be set with the * Generate name* button. If the note is no longer needed, you may also remove it here.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
- Dice icon
|
||||
|
||||
## Annotation list window {#annotationlist}
|
||||
|
||||
|
||||
@@ -4083,7 +4083,7 @@ You can freely adjust each time range on the timeline by clicking the \LMB{}~lef
|
||||
|
||||
Tracy allows adding custom notes to the trace. For example, you may want to mark a region to ignore because the application was out-of-focus or a region where a new user was connecting to the game, which resulted in a frame drop that needs to be investigated.
|
||||
|
||||
Methods of specifying the annotation region are described in section~\ref{timeranges}. When a new annotation is added, a settings window is displayed (section~\ref{annotationsettings}), allowing you to enter a description.
|
||||
Methods of specifying the annotation region are described in section~\ref{timeranges}. When a new annotation is added, it is assigned a semi-unique random name to make it distinguishable. The settings window is also opened (section~\ref{annotationsettings}), allowing you to enter your own description of the annotation.
|
||||
|
||||
Annotations are displayed on the timeline, as presented in figure~\ref{annotation}. Clicking on the circle next to the text description will open the annotation settings window, in which you can modify or remove the region. List of all annotations in the trace is available in the annotations list window described in section~\ref{annotationlist}, which is accessible through the \emph{\faScrewdriverWrench{} Tools} button on the control menu.
|
||||
|
||||
@@ -4594,7 +4594,9 @@ The information about the selected memory allocation is displayed in this window
|
||||
\subsection{Trace information window}
|
||||
\label{traceinfo}
|
||||
|
||||
This window contains information about the current trace: captured program name, time of the capture, profiler version which performed the capture, and a custom trace description, which you can fill in.
|
||||
This window contains information about the current trace: captured program name, time of the capture, profiler version which performed the capture.
|
||||
|
||||
There's an text entry field for an optional custom description of the trace for you to fill in. This description will appear on the profiler window title bar, or when comparing two traces (section~\ref{compare}), enabling you to quickly recognize what the trace contains. For some people it's fine to just have \emph{any} semi-unique description to be able to identify a specific trace. For such purposes there's an \emph{\faDice{}~Generate name} button, which will set the trace description to an abstract meaningless identifier.
|
||||
|
||||
If the \emph{\faUserGear{}~Public sidecar} option is selected, the file containing trace-specific user settings (see section~\ref{tracespecific}) will be saved on disk next to the trace file.
|
||||
|
||||
@@ -4934,7 +4936,7 @@ The profiled program is highlighted using green color. Furthermore, the yellow h
|
||||
\subsection{Annotation settings window}
|
||||
\label{annotationsettings}
|
||||
|
||||
In this window, you may modify how a timeline annotation (section~\ref{annotatingtrace}) is presented by setting its text description or selecting region highlight color. If the note is no longer needed, you may also remove it here.
|
||||
In this window, you may modify how a timeline annotation (section~\ref{annotatingtrace}) is presented by setting its text description or selecting region highlight color. A random annotation description can be set with the \emph{\faDice{}~Generate name} button. If the note is no longer needed, you may also remove it here.
|
||||
|
||||
\subsection{Annotation list window}
|
||||
\label{annotationlist}
|
||||
|
||||
@@ -70,6 +70,7 @@ set(SERVER_FILES
|
||||
TracyMarkdown.cpp
|
||||
TracyMicroArchitecture.cpp
|
||||
TracyMouse.cpp
|
||||
TracyNameGen.cpp
|
||||
TracyProtoHistory.cpp
|
||||
TracySourceContents.cpp
|
||||
TracySourceTokenizer.cpp
|
||||
|
||||
@@ -162,6 +162,15 @@ static ImGuiKey TranslateKeyCode( const char* code )
|
||||
return ImGuiKey_None;
|
||||
}
|
||||
|
||||
static void UpdateKeyModifiers( const EmscriptenKeyboardEvent* e )
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent( ImGuiMod_Ctrl, e->ctrlKey );
|
||||
io.AddKeyEvent( ImGuiMod_Shift, e->shiftKey );
|
||||
io.AddKeyEvent( ImGuiMod_Alt, e->altKey );
|
||||
io.AddKeyEvent( ImGuiMod_Super, e->metaKey );
|
||||
}
|
||||
|
||||
Backend::Backend( const char* title, const std::function<void()>& redraw, const std::function<void(float)>& scaleChanged, const std::function<int(void)>& isBusy, RunQueue* mainThreadTasks )
|
||||
{
|
||||
constexpr EGLint eglConfigAttrib[] = {
|
||||
@@ -243,6 +252,7 @@ Backend::Backend( const char* title, const std::function<void()>& redraw, const
|
||||
return EM_TRUE;
|
||||
} );
|
||||
emscripten_set_keydown_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, [] ( int, const EmscriptenKeyboardEvent* e, void* ) -> EM_BOOL {
|
||||
UpdateKeyModifiers( e );
|
||||
const auto code = TranslateKeyCode( e->code );
|
||||
if( code == ImGuiKey_None ) return EM_FALSE;
|
||||
ImGui::GetIO().AddKeyEvent( code, true );
|
||||
@@ -250,6 +260,7 @@ Backend::Backend( const char* title, const std::function<void()>& redraw, const
|
||||
return EM_TRUE;
|
||||
} );
|
||||
emscripten_set_keyup_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, [] ( int, const EmscriptenKeyboardEvent* e, void* ) -> EM_BOOL {
|
||||
UpdateKeyModifiers( e );
|
||||
const auto code = TranslateKeyCode( e->code );
|
||||
if( code == ImGuiKey_None ) return EM_FALSE;
|
||||
ImGui::GetIO().AddKeyEvent( code, false );
|
||||
|
||||
@@ -290,7 +290,7 @@ static constexpr const uint32_t AsmSyntaxColors[] = {
|
||||
|
||||
[[maybe_unused]] static tracy_force_inline void TooltipIfHovered( const char* text )
|
||||
{
|
||||
if( !ImGui::IsItemHovered() ) return;
|
||||
if( !ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) ) return;
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted( text );
|
||||
ImGui::EndTooltip();
|
||||
|
||||
221
profiler/src/profiler/TracyNameGen.cpp
Normal file
221
profiler/src/profiler/TracyNameGen.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "TracyNameGen.hpp"
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
struct NameBank
|
||||
{
|
||||
const char* const* adjectives;
|
||||
const char* const* nouns;
|
||||
size_t numAdjectives;
|
||||
size_t numNouns;
|
||||
};
|
||||
|
||||
constexpr const char* AnalysisAdjectives[] = {
|
||||
"Granular", "Forensic", "Acute", "Lucid", "Precise",
|
||||
"Deep", "Exact", "Critical", "Analytical", "Transparent",
|
||||
"Subtle", "Sharp", "Rigid", "Focused", "Absolute",
|
||||
"Meticulous", "Spectral", "Diagnostic", "Pervasive", "Introspective",
|
||||
"Systematic", "Optical", "Minute", "Piercing", "Detailed",
|
||||
"Scrutinized", "Clear", "Keen", "Rigorous", "Vast",
|
||||
"Incisive", "Exhaustive", "Lateral", "Prismatic", "Observant"
|
||||
};
|
||||
constexpr const char* AnalysisNouns[] = {
|
||||
"Probe", "Trace", "Lens", "Scope", "Metric",
|
||||
"Insight", "Scan", "Audit", "Point", "Vector",
|
||||
"Signal", "Marker", "Frame", "Detail", "View",
|
||||
"Spectrum", "Snapshot", "Blueprint", "Aperture", "Index",
|
||||
"Radar", "Prism", "Gauge", "Focal", "Pattern",
|
||||
"Echo", "Signature", "Horizon", "Mirror", "Scale",
|
||||
"Telemetry", "Graph", "Stratum", "Artifact", "Aspect"
|
||||
};
|
||||
|
||||
constexpr const char* PerformanceAdjectives[] = {
|
||||
"Swift", "Lean", "Kinetic", "Agile", "Hyper",
|
||||
"Rapid", "Fluid", "Peak", "Instant", "Nimble",
|
||||
"Optimal", "Sonic", "Linear", "Warp", "Turbo",
|
||||
"Frictionless", "Seamless", "Electric", "Blazing", "Aerodynamic",
|
||||
"Quantum", "Prompt", "Direct", "Streamlined", "Volatile",
|
||||
"Highgain", "Rapidfire", "Torrential", "Sleek", "Velocity",
|
||||
"Dynamic", "Active", "Persistent", "Lightweight", "Snappy"
|
||||
};
|
||||
constexpr const char* PerformanceNouns[] = {
|
||||
"Pulse", "Flow", "Cycle", "Burst", "Stream",
|
||||
"Tick", "Glide", "Shift", "Velocity", "Spike",
|
||||
"Pace", "Rhythm", "Drive", "Path", "Edge",
|
||||
"Sprint", "Torrent", "Current", "Surge", "Momentum",
|
||||
"Flux", "Wave", "Accelerator", "Spark", "Jet",
|
||||
"Thrust", "Orbit", "Apex", "Bolt", "Phase",
|
||||
"Rush", "Impact", "Frequency", "Lapse", "Kick"
|
||||
};
|
||||
|
||||
constexpr const char* CoreAdjectives[] = {
|
||||
"Binary", "Raw", "Atomic", "Static", "Core",
|
||||
"Virtual", "Base", "Solid", "Dense", "Opaque",
|
||||
"Primitive", "Native", "Hard", "Stable", "Immutable",
|
||||
"Monolithic", "Bare", "Rigid", "Concrete", "Fundamental",
|
||||
"Discrete", "Fixed", "Heavy", "Latent", "Symmetric",
|
||||
"Implicit", "Explicit", "Cold", "Basic", "Granite",
|
||||
"Stark", "Brute", "Firm", "Stout", "Coarse"
|
||||
};
|
||||
constexpr const char* CoreNouns[] = {
|
||||
"Stack", "Heap", "Node", "Buffer", "Segment",
|
||||
"Thread", "Kernel", "Block", "Page", "Shell",
|
||||
"Layer", "Bit", "Logic", "Port", "Root",
|
||||
"Register", "Pointer", "Address", "Cache", "Opcode",
|
||||
"Slab", "Pipeline", "Bus", "Socket", "Sector",
|
||||
"Vault", "Anchor", "Pillar", "Base", "Primitive",
|
||||
"Offset", "Handle", "Struct", "Memory", "Word"
|
||||
};
|
||||
|
||||
constexpr const char* ModernAdjectives[] = {
|
||||
"Synthetic", "Neural", "Async", "Elastic", "Cloud",
|
||||
"Distributed", "Reactive", "Orbital", "Poly", "Infinite",
|
||||
"Parallel", "Modular", "Virtualized", "Scalable", "Agnostic",
|
||||
"Adaptive", "Hybrid", "Autonomous", "Global", "Synergic",
|
||||
"Omnipresent", "Evolving", "Abstract", "Unified", "Concurrent",
|
||||
"Remote", "Digital", "Cluster", "Ephemeral", "Stateful",
|
||||
"Stateless", "Serverless", "Decoupled", "Fluent", "Native"
|
||||
};
|
||||
constexpr const char* ModernNouns[] = {
|
||||
"Nexus", "Grid", "Matrix", "Vertex", "Sync",
|
||||
"Axiom", "Sphere", "Hub", "Mesh", "Bridge",
|
||||
"Link", "Unit", "Fabric", "Cluster", "Portal",
|
||||
"Ecosystem", "Catalyst", "Interface", "Domain", "Gateway",
|
||||
"Lattice", "Cloud", "Instance", "Schema", "Registry",
|
||||
"Tenant", "Namespace", "Pod", "Stream", "Endpoint",
|
||||
"Payload", "Relay", "Orchestrator", "Broker", "Agent"
|
||||
};
|
||||
|
||||
constexpr const char* FailureAdjectives[] = {
|
||||
"Clumsy", "Wobbly", "Confused", "Chaotic", "Sneaky",
|
||||
"Lazy", "Dizzy", "Broken", "Leaky", "Fragile",
|
||||
"Shaky", "Erratic", "Sleepy", "Lost", "Random",
|
||||
"Glitchy", "Unstable", "Paradoxical", "Cluttery", "Hiccupy",
|
||||
"Wonky", "Flaky", "Stubborn", "Moody", "Nervous",
|
||||
"Fumbling", "Drifting", "Tangled", "Blurred", "Absent",
|
||||
"Haphazard", "Spasmodic", "Clunky", "Jittery", "Bewildered"
|
||||
};
|
||||
constexpr const char* FailureNouns[] = {
|
||||
"Crash", "Bug", "Leak", "Hang", "Timeout",
|
||||
"Panic", "Loop", "Spill", "Hiccup", "Glitch",
|
||||
"Wobble", "Tumble", "Void", "Abyss", "Maze",
|
||||
"Knot", "Static", "Noise", "Drift", "Stumble",
|
||||
"Gap", "Fragment", "Shard", "Spark", "Bubble",
|
||||
"Slip", "Trip", "Fall", "Ghost", "Shadow",
|
||||
"Blur", "Overflow", "Sinkhole", "Echo", "Mirage"
|
||||
};
|
||||
|
||||
constexpr const char* MythicAdjectives[] = {
|
||||
"Mythic", "Arcane", "Ancient", "Eternal", "Sacred",
|
||||
"Divine", "Forgotten", "Elder", "Primordial", "Venerable",
|
||||
"Runic", "Prophetic", "Colossal", "Imperial", "Regal",
|
||||
"Sovereign", "Mystic", "Occult", "Hidden", "Cryptic",
|
||||
"Ethereal", "Celestial", "Gnostic", "Hermetic", "Alchemical",
|
||||
"Astral", "Golden", "Iron", "Bronze", "Obsidian",
|
||||
"Silver", "Timeless", "Boundless", "Omnipotent", "Everlasting"
|
||||
};
|
||||
constexpr const char* MythicNouns[] = {
|
||||
"Aegis", "Helios", "Oracle", "Titan", "Rune",
|
||||
"Lex", "Codex", "Obelisk", "Monolith", "Temple",
|
||||
"Altar", "Scepter", "Crown", "Sigil", "Glyph",
|
||||
"Tome", "Relic", "Artifact", "Sanctum", "Citadel",
|
||||
"Bastion", "Spire", "Pillar", "Throne", "Vault",
|
||||
"Key", "Gate", "Bridge", "Seal", "Pact",
|
||||
"Covenant", "Legacy", "Epoch", "Era", "Myth"
|
||||
};
|
||||
|
||||
constexpr const char* CosmosAdjectives[] = {
|
||||
"Relativistic", "Baryonic", "Intergalactic", "Event-Horizon", "Singular",
|
||||
"Celestial", "Nebular", "Void-Born", "Astral", "Luminous",
|
||||
"Spectral", "Ionized", "Gravitational", "Ecliptic", "Zenithal",
|
||||
"Stellar", "Cosmological", "Parallactic", "Zero-Point", "Dark-Matter",
|
||||
"Radiant", "Orbital", "Supernova", "Hyper-Spatial", "Aetheric",
|
||||
"Cold-Void", "Infinite", "Dimensional", "Crystalline", "Tidal",
|
||||
"Planetary", "Solar", "Lunar", "Galactic", "Oblique"
|
||||
};
|
||||
constexpr const char* CosmosNouns[] = {
|
||||
"Pulsar", "Quasar", "Singularity", "Void", "Nebula",
|
||||
"Horizon", "Apex", "Zenith", "Equinox", "Corona",
|
||||
"Aperture", "Axis", "Parallax", "Cluster", "Constellation",
|
||||
"Vacuum", "Symmetry", "Continuum", "Flux", "Vortex",
|
||||
"Nova", "Eclipse", "Solenoid", "Sphere", "Vector",
|
||||
"Siderostat", "Sextant", "Obliquity", "Precession", "Azimuth",
|
||||
"Wavelength", "Frequency", "Radiance", "Entropy", "Magnitude"
|
||||
};
|
||||
|
||||
constexpr const char* GameAdjectives[] = {
|
||||
"Frame-Locked", "Pixel-Perfect", "Arcade", "Retro", "Hardcore",
|
||||
"Unlocked", "Godlike", "Buffed", "Nerfed", "Overclocked",
|
||||
"Clutch", "Lagless", "Sweaty", "Tryhard", "Broken",
|
||||
"Turbo", "Min-Max", "Rage-Quit", "No-Scope", "Frame-Perfect",
|
||||
"Savescum", "Co-Op", "Modded", "Patched", "Hotfixed",
|
||||
"Debugged", "Optimized", "Smoothed", "Playtest", "Sandbox",
|
||||
"Scripted", "Speedrun", "Cheat-Code", "Invincible", "Flawless"
|
||||
};
|
||||
constexpr const char* GameNouns[] = {
|
||||
"Frame", "Tick", "Sprite", "Polygon", "Shader",
|
||||
"Texture", "Voxel", "Render", "Hitbox", "Hurtbox",
|
||||
"Collision", "Input", "Viewport", "Level", "Checkpoint",
|
||||
"Boss", "Loot", "Quest", "Spawn", "Respawn",
|
||||
"Grind", "Scroll", "Tilemap", "Backdrop", "Rig",
|
||||
"Build", "Frag", "Gib", "Drawcall", "Pass",
|
||||
"Batch", "Delta", "Pool", "Arena", "Worker"
|
||||
};
|
||||
|
||||
constexpr std::array NameBanks = {
|
||||
NameBank { AnalysisAdjectives, AnalysisNouns, sizeof(AnalysisAdjectives) / sizeof(AnalysisAdjectives[0]), sizeof(AnalysisNouns) / sizeof(AnalysisNouns[0]) },
|
||||
NameBank { PerformanceAdjectives, PerformanceNouns, sizeof(PerformanceAdjectives) / sizeof(PerformanceAdjectives[0]), sizeof(PerformanceNouns) / sizeof(PerformanceNouns[0]) },
|
||||
NameBank { CoreAdjectives, CoreNouns, sizeof(CoreAdjectives) / sizeof(CoreAdjectives[0]), sizeof(CoreNouns) / sizeof(CoreNouns[0]) },
|
||||
NameBank { ModernAdjectives, ModernNouns, sizeof(ModernAdjectives) / sizeof(ModernAdjectives[0]), sizeof(ModernNouns) / sizeof(ModernNouns[0]) },
|
||||
NameBank { FailureAdjectives, FailureNouns, sizeof(FailureAdjectives) / sizeof(FailureAdjectives[0]), sizeof(FailureNouns) / sizeof(FailureNouns[0]) },
|
||||
NameBank { MythicAdjectives, MythicNouns, sizeof(MythicAdjectives) / sizeof(MythicAdjectives[0]), sizeof(MythicNouns) / sizeof(MythicNouns[0]) },
|
||||
NameBank { CosmosAdjectives, CosmosNouns, sizeof(CosmosAdjectives) / sizeof(CosmosAdjectives[0]), sizeof(CosmosNouns) / sizeof(CosmosNouns[0]) },
|
||||
NameBank { GameAdjectives, GameNouns, sizeof(GameAdjectives) / sizeof(GameAdjectives[0]), sizeof(GameNouns) / sizeof(GameNouns[0]) },
|
||||
};
|
||||
|
||||
constexpr std::array NameStructure = { "an", "aan", "nn" };
|
||||
|
||||
|
||||
std::string GenerateAbstractName()
|
||||
{
|
||||
std::random_device rd;
|
||||
std::default_random_engine gen( rd() );
|
||||
std::uniform_int_distribution<uint32_t> dist( 0, UINT32_MAX );
|
||||
|
||||
const auto baseBank = NameBanks[dist( gen ) % NameBanks.size()];
|
||||
const char* structure = NameStructure[dist( gen ) % NameStructure.size()];
|
||||
|
||||
std::vector<std::string> parts;
|
||||
while( *structure )
|
||||
{
|
||||
const auto type = *structure++;
|
||||
assert( type == 'a' || type == 'n' );
|
||||
const auto bank = dist( gen ) % 6 == 0 ? NameBanks[dist( gen ) % NameBanks.size()] : baseBank;
|
||||
for(;;)
|
||||
{
|
||||
auto part = std::string( type == 'a' ? bank.adjectives[dist( gen ) % bank.numAdjectives] : bank.nouns[dist( gen ) % bank.numNouns] );
|
||||
if( std::ranges::find( parts, part ) == parts.end() )
|
||||
{
|
||||
parts.emplace_back( std::move( part ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::string ret = parts[0];
|
||||
for( size_t i=1; i<parts.size(); i++ )
|
||||
{
|
||||
ret += " " + parts[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
13
profiler/src/profiler/TracyNameGen.hpp
Normal file
13
profiler/src/profiler/TracyNameGen.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef __TRACYNAMEGEN_HPP__
|
||||
#define __TRACYNAMEGEN_HPP__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
std::string GenerateAbstractName();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -295,6 +295,7 @@ bool UserData::Load()
|
||||
LoadValue( v, "min", a->range.min );
|
||||
LoadValue( v, "max", a->range.max );
|
||||
LoadValue( v, "color", a->color );
|
||||
a->range.active = true;
|
||||
m_annotations.emplace_back( std::move( a ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "TracyImGui.hpp"
|
||||
#include "TracyNameGen.hpp"
|
||||
#include "TracyPrint.hpp"
|
||||
#include "TracyView.hpp"
|
||||
#include "tracy_pdqsort.h"
|
||||
@@ -10,6 +13,7 @@ namespace tracy
|
||||
void View::AddAnnotation( int64_t start, int64_t end )
|
||||
{
|
||||
auto ann = std::make_shared<Annotation>();
|
||||
ann->text = GenerateAbstractName();
|
||||
ann->range.active = true;
|
||||
ann->range.min = start;
|
||||
ann->range.max = end;
|
||||
@@ -52,7 +56,22 @@ void View::DrawSelectedAnnotation()
|
||||
char buf[1024];
|
||||
buf[descsz] = '\0';
|
||||
memcpy( buf, desc, descsz );
|
||||
if( ImGui::InputTextWithHint( "##anndesc", "Describe annotation", buf, 256 ) )
|
||||
|
||||
const char* buttonText = ICON_FA_DICE;
|
||||
auto buttonSize = ImGui::CalcTextSize( buttonText );
|
||||
buttonSize.x += ImGui::GetStyle().FramePadding.x * 2.0f + ImGui::GetStyle().ItemSpacing.x;
|
||||
ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x - buttonSize.x );
|
||||
bool changed = ImGui::InputTextWithHint( "##anndesc", "Describe annotation", buf, 256 );
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( buttonText ) )
|
||||
{
|
||||
changed = true;
|
||||
const auto name = GenerateAbstractName();
|
||||
const auto len = std::min( sizeof( buf ) - 1, name.size() );
|
||||
memcpy( buf, name.c_str(), len );
|
||||
buf[len] = '\0';
|
||||
}
|
||||
if( changed )
|
||||
{
|
||||
m_selectedAnnotation->text.assign( buf );
|
||||
}
|
||||
|
||||
@@ -299,6 +299,22 @@ void View::DrawTimeline()
|
||||
v->range.StartFrame();
|
||||
HandleRange( v->range, timespan, ImGui::GetCursorScreenPos(), w );
|
||||
}
|
||||
if( IsMouseClicked( 0 ) )
|
||||
{
|
||||
const auto ty = ImGui::GetTextLineHeight();
|
||||
for( auto& ann : m_annotations )
|
||||
{
|
||||
if( ann->range.min >= m_vd.zvEnd || ann->range.max <= m_vd.zvStart ) continue;
|
||||
const auto aMin = ( ann->range.min - m_vd.zvStart ) * pxns;
|
||||
const auto aMax = ( ann->range.max - m_vd.zvStart ) * pxns;
|
||||
if( ImGui::IsMouseHoveringRect( linepos + ImVec2( aMin, lineh - ty * 1.5f ), linepos + ImVec2( aMax, lineh ) ) )
|
||||
{
|
||||
m_selectedAnnotation = ann.get();
|
||||
ConsumeMouseEvents( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
HandleTimelineMouse( timespan, ImGui::GetCursorScreenPos(), w );
|
||||
}
|
||||
if( ImGui::IsWindowFocused( ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem ) )
|
||||
@@ -360,9 +376,8 @@ void View::DrawTimeline()
|
||||
bool hover = ImGui::IsWindowHovered() && ImGui::IsMouseHoveringRect( wpos, wpos + ImVec2( w, h ) );
|
||||
draw = ImGui::GetWindowDrawList();
|
||||
|
||||
const auto scale = GetScale();
|
||||
const auto ty = ImGui::GetTextLineHeight();
|
||||
const auto to = 9.f;
|
||||
const auto th = ( ty - to ) * sqrt( 3 ) * 0.5;
|
||||
|
||||
if( m_vd.drawGpuZones )
|
||||
{
|
||||
@@ -415,17 +430,24 @@ void View::DrawTimeline()
|
||||
|
||||
m_lockHighlight = m_nextLockHighlight;
|
||||
|
||||
const auto iconSize = ImGui::CalcTextSize( ICON_FA_NOTE_STICKY );
|
||||
for( auto& ann : m_annotations )
|
||||
{
|
||||
if( ann->range.min < m_vd.zvEnd && ann->range.max > m_vd.zvStart )
|
||||
{
|
||||
uint32_t c0 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x44000000 : 0x22000000 );
|
||||
uint32_t c1 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x66000000 : 0x44000000 );
|
||||
uint32_t c2 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0xCC000000 : 0xAA000000 );
|
||||
draw->AddRectFilled( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ), c0 );
|
||||
DrawLine( draw, linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + 0.5f, 0.5f ), linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + 0.5f, lineh + 0.5f ), ann->range.hiMin ? c2 : c1, ann->range.hiMin ? 2 : 1 );
|
||||
DrawLine( draw, linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns + 0.5f, 0.5f ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns + 0.5f, lineh + 0.5f ), ann->range.hiMax ? c2 : c1, ann->range.hiMax ? 2 : 1 );
|
||||
if( drawMouseLine && ImGui::IsMouseHoveringRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ) ) )
|
||||
uint32_t c0 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x22000000 : 0x11000000 );
|
||||
uint32_t c1 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x88000000 : 0x66000000 );
|
||||
uint32_t c2 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0xDD000000 : 0xBB000000 );
|
||||
|
||||
const auto aMin = ( ann->range.min - m_vd.zvStart ) * pxns;
|
||||
const auto aMax = ( ann->range.max - m_vd.zvStart ) * pxns;
|
||||
|
||||
draw->AddRectFilled( linepos + ImVec2( aMin, 0 ), linepos + ImVec2( aMax, lineh ), c0 );
|
||||
draw->AddRectFilled( linepos + ImVec2( aMin + 1, lineh - ty * 1.5f ), linepos + ImVec2( aMax - 1, lineh ), 0x88000000 );
|
||||
DrawLine( draw, linepos + ImVec2( aMin + 0.5f, 0.5f ), linepos + ImVec2( aMin + 0.5f, lineh + 0.5f ), ann->range.hiMin ? c2 : c1, ann->range.hiMin ? 2 : 1 );
|
||||
DrawLine( draw, linepos + ImVec2( aMax - 0.5f, 0.5f ), linepos + ImVec2( aMax - 0.5f, lineh + 0.5f ), ann->range.hiMax ? c2 : c1, ann->range.hiMax ? 2 : 1 );
|
||||
|
||||
if( drawMouseLine && ImGui::IsMouseHoveringRect( linepos + ImVec2( aMin, 0 ), linepos + ImVec2( aMax, lineh ) ) )
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
if( ann->text.empty() )
|
||||
@@ -442,27 +464,22 @@ void View::DrawTimeline()
|
||||
TextFocused( "Annotation length:", TimeToString( ann->range.max - ann->range.min ) );
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
const auto aw = ( ann->range.max - ann->range.min ) * pxns;
|
||||
if( aw > th * 4 )
|
||||
{
|
||||
draw->AddCircleFilled( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 2, th * 2 ), th, 0x88AABB22 );
|
||||
draw->AddCircle( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 2, th * 2 ), th, 0xAAAABB22 );
|
||||
if( drawMouseLine && IsMouseClicked( 0 ) && ImGui::IsMouseHoveringRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th, th ), linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 3, th * 3 ) ) )
|
||||
{
|
||||
m_selectedAnnotation = ann.get();
|
||||
}
|
||||
|
||||
const auto aw = ( ann->range.max - ann->range.min ) * pxns;
|
||||
if( aw > ty + iconSize.x )
|
||||
{
|
||||
draw->AddText( linepos + ImVec2( aMin + ty * 0.5f, lineh - ty * 1.25f ), ann->color | 0xFF000000, ICON_FA_NOTE_STICKY );
|
||||
if( !ann->text.empty() )
|
||||
{
|
||||
const auto tw = ImGui::CalcTextSize( ann->text.c_str() ).x;
|
||||
if( aw - th*4 > tw )
|
||||
if( aw > ty + iconSize.x + tw )
|
||||
{
|
||||
draw->AddText( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 4, th * 0.5 ), 0xFFFFFFFF, ann->text.c_str() );
|
||||
draw->AddText( linepos + ImVec2( aMin + ty + iconSize.x, lineh - ty * 1.25f ), 0xFFFFFFFF, ann->text.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
draw->PushClipRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ), true );
|
||||
draw->AddText( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 4, th * 0.5 ), 0xFFFFFFFF, ann->text.c_str() );
|
||||
draw->PushClipRect( linepos + ImVec2( aMin + 1, lineh - ty * 1.5f ), linepos + ImVec2( aMax - 1, lineh ) );
|
||||
draw->AddText( linepos + ImVec2( aMin + ty + iconSize.x, lineh - ty * 1.25f ), 0xFFFFFFFF, ann->text.c_str() );
|
||||
draw->PopClipRect();
|
||||
}
|
||||
}
|
||||
@@ -485,7 +502,6 @@ void View::DrawTimeline()
|
||||
draw->AddRect( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x4488DD88 );
|
||||
}
|
||||
|
||||
const auto scale = GetScale();
|
||||
if( m_findZone.range.active && ( m_findZone.show || m_showRanges ) )
|
||||
{
|
||||
const auto px0 = ( m_findZone.range.min - m_vd.zvStart ) * pxns;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "TracyImGui.hpp"
|
||||
#include "TracyNameGen.hpp"
|
||||
#include "TracyPrint.hpp"
|
||||
#include "TracyView.hpp"
|
||||
#include "tracy_pdqsort.h"
|
||||
@@ -55,8 +56,22 @@ void View::DrawInfo()
|
||||
char buf[256];
|
||||
buf[descsz] = '\0';
|
||||
memcpy( buf, desc.c_str(), descsz );
|
||||
ImGui::SetNextItemWidth( -1 );
|
||||
if( ImGui::InputTextWithHint( "##traceDesc", "Enter description of the trace", buf, 256 ) )
|
||||
|
||||
const char* buttonText = ICON_FA_DICE;
|
||||
auto buttonSize = ImGui::CalcTextSize( buttonText );
|
||||
buttonSize.x += ImGui::GetStyle().FramePadding.x * 2.0f + ImGui::GetStyle().ItemSpacing.x;
|
||||
ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x - buttonSize.x );
|
||||
bool changed = ImGui::InputTextWithHint( "##traceDesc", "Enter description of the trace", buf, 256 );
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( buttonText ) )
|
||||
{
|
||||
changed = true;
|
||||
const auto name = GenerateAbstractName();
|
||||
const auto len = std::min( sizeof( buf ) - 1, name.size() );
|
||||
memcpy( buf, name.c_str(), len );
|
||||
buf[len] = '\0';
|
||||
}
|
||||
if( changed )
|
||||
{
|
||||
m_userData.SetDescription( buf );
|
||||
if( m_stcb ) UpdateTitle();
|
||||
|
||||
@@ -524,7 +524,7 @@ static const char* GetHostInfo()
|
||||
auto ptr = buf;
|
||||
#if defined _WIN32
|
||||
# if defined TRACY_WIN32_NO_DESKTOP
|
||||
auto GetVersion = &::GetVersionEx;
|
||||
auto GetVersion = &::GetVersionExW;
|
||||
# else
|
||||
auto GetVersion = (t_RtlGetVersion)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlGetVersion" );
|
||||
# endif
|
||||
@@ -1408,9 +1408,30 @@ namespace
|
||||
// 1a. But s_queue is needed for initialization of variables in point 2.
|
||||
extern moodycamel::ConcurrentQueue<QueueItem> s_queue;
|
||||
|
||||
// A producer token may be created before s_initTime is constructed (the dynamic loader
|
||||
// runs shared object initializers before any of the executable's constructors, and such
|
||||
// an initializer may emit a zone). Remember the time of such an early token creation, so
|
||||
// that the init time can be backdated accordingly and no event timestamp precedes the
|
||||
// trace epoch.
|
||||
static std::atomic<int64_t> s_earlyTokenTime { 0 };
|
||||
static bool s_initTimeConstructed = false;
|
||||
|
||||
// 2. If these variables would be in the .CRT$XCB section, they would be initialized only in main thread.
|
||||
thread_local moodycamel::ProducerToken init_order(107) s_token_detail( s_queue );
|
||||
thread_local ProducerWrapper init_order(108) s_token { s_queue.get_explicit_producer( s_token_detail ) };
|
||||
|
||||
static moodycamel::ConcurrentQueue<QueueItem>::ExplicitProducer* CreateProducerToken()
|
||||
{
|
||||
auto ptr = s_queue.get_explicit_producer( s_token_detail );
|
||||
if( !s_initTimeConstructed )
|
||||
{
|
||||
const auto t = Profiler::GetTime();
|
||||
auto e = s_earlyTokenTime.load( std::memory_order_relaxed );
|
||||
while( ( e == 0 || t < e ) && !s_earlyTokenTime.compare_exchange_weak( e, t, std::memory_order_relaxed ) ) {}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
thread_local ProducerWrapper init_order(108) s_token { CreateProducerToken() };
|
||||
thread_local ThreadHandleWrapper init_order(104) s_threadHandle { detail::GetThreadHandleImpl() };
|
||||
|
||||
# ifdef _MSC_VER
|
||||
@@ -1419,12 +1440,36 @@ thread_local ThreadHandleWrapper init_order(104) s_threadHandle { detail::GetThr
|
||||
# pragma init_seg( ".CRT$XCB" )
|
||||
# endif
|
||||
|
||||
static InitTimeWrapper init_order(101) s_initTime { SetupHwTimer() };
|
||||
static int64_t GetInitTimeImpl()
|
||||
{
|
||||
auto t = SetupHwTimer();
|
||||
const auto e = s_earlyTokenTime.load( std::memory_order_relaxed );
|
||||
if( e != 0 && e < t ) t = e;
|
||||
s_initTimeConstructed = true;
|
||||
return t;
|
||||
}
|
||||
static InitTimeWrapper init_order(101) s_initTime { GetInitTimeImpl() };
|
||||
std::atomic<int> init_order(102) RpInitDone( 0 );
|
||||
std::atomic<int> init_order(102) RpInitLock( 0 );
|
||||
thread_local bool RpThreadInitDone = false;
|
||||
thread_local bool RpThreadShutdown = false;
|
||||
moodycamel::ConcurrentQueue<QueueItem> init_order(103) s_queue( QueuePrealloc );
|
||||
|
||||
# ifndef _MSC_VER
|
||||
// An instrumented shared object may emit zones from its static initializers, which the
|
||||
// dynamic loader runs before any of the executable's constructors, including the
|
||||
// priority-ordered constructor of s_queue above. The main thread producer token (s_token)
|
||||
// is then lazily created against the zero-initialized queue memory, and the queue
|
||||
// constructor subsequently orphans it, making all zones emitted on the main thread
|
||||
// invisible to the consumer. Re-adopt such a producer here. If no zones were emitted up
|
||||
// to this point, this only triggers construction of s_token, which is a no-op repair.
|
||||
struct EarlyMainThreadTokenRepair
|
||||
{
|
||||
EarlyMainThreadTokenRepair() { if( s_token.ptr ) s_queue.readopt_orphaned_producer( s_token.ptr ); }
|
||||
};
|
||||
static EarlyMainThreadTokenRepair init_order(104) s_earlyMainThreadTokenRepair;
|
||||
# endif
|
||||
|
||||
std::atomic<uint32_t> init_order(104) s_lockCounter( 0 );
|
||||
std::atomic<uint8_t> init_order(104) s_gpuCtxCounter( 0 );
|
||||
|
||||
@@ -5257,7 +5302,7 @@ TRACY_API int32_t ___tracy_before_lock_shared_shared_lockable_ctx( struct __trac
|
||||
return static_cast<int32_t>(true);
|
||||
}
|
||||
|
||||
TRACY_API void ___tracy_after_locked_shared_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata )
|
||||
TRACY_API void ___tracy_after_lock_shared_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata )
|
||||
{
|
||||
auto item = tracy::Profiler::QueueSerial();
|
||||
tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockSharedObtain );
|
||||
|
||||
@@ -52,20 +52,8 @@ public:
|
||||
RingBuffer( const RingBuffer& ) = delete;
|
||||
RingBuffer& operator=( const RingBuffer& ) = delete;
|
||||
|
||||
RingBuffer( RingBuffer&& other )
|
||||
{
|
||||
memcpy( (char*)&other, (char*)this, sizeof( RingBuffer ) );
|
||||
m_metadata = nullptr;
|
||||
m_fd = 0;
|
||||
}
|
||||
|
||||
RingBuffer& operator=( RingBuffer&& other )
|
||||
{
|
||||
memcpy( (char*)&other, (char*)this, sizeof( RingBuffer ) );
|
||||
m_metadata = nullptr;
|
||||
m_fd = 0;
|
||||
return *this;
|
||||
}
|
||||
RingBuffer( RingBuffer&& other ) = delete;
|
||||
RingBuffer& operator=( RingBuffer&& other ) = delete;
|
||||
|
||||
bool IsValid() const { return m_metadata != nullptr; }
|
||||
int GetId() const { return m_id; }
|
||||
|
||||
@@ -171,8 +171,8 @@ struct ConcurrentQueueDefaultTraits
|
||||
#if defined(malloc) || defined(free)
|
||||
// Gah, this is 2015, stop defining macros that break standard code already!
|
||||
// Work around malloc/free being special macros:
|
||||
static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); }
|
||||
static inline void WORKAROUND_free(void* ptr) { return free(ptr); }
|
||||
static inline void* WORKAROUND_malloc(size_t size) { return tracy::tracy_malloc(size); }
|
||||
static inline void WORKAROUND_free(void* ptr) { return tracy::tracy_free(ptr); }
|
||||
static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); }
|
||||
static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); }
|
||||
#else
|
||||
@@ -1210,6 +1210,21 @@ private:
|
||||
return static_cast<ExplicitProducer*>(token.producer);
|
||||
}
|
||||
|
||||
// If a producer token is created before the constructor of a statically allocated
|
||||
// queue runs (which may happen due to the undefined order of static initialization
|
||||
// across module boundaries), the constructor will orphan it by resetting the
|
||||
// producer list. Such a producer is functional, as producer creation works on the
|
||||
// zero-initialized queue memory, but the consumer is not able to see the data it
|
||||
// enqueues. This method links the producer back into the list.
|
||||
bool readopt_orphaned_producer(ExplicitProducer* producer)
|
||||
{
|
||||
for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) {
|
||||
if (ptr == static_cast<ProducerBase*>(producer)) return false;
|
||||
}
|
||||
add_producer(static_cast<ProducerBase*>(producer));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
@@ -391,7 +391,7 @@ TRACY_API void ___tracy_after_lock_shared_lockable_ctx( struct __tracy_shared_lo
|
||||
TRACY_API void ___tracy_after_unlock_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata );
|
||||
TRACY_API void ___tracy_after_try_lock_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata, int32_t acquired );
|
||||
TRACY_API int32_t ___tracy_before_lock_shared_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata );
|
||||
TRACY_API void ___tracy_after_locked_shared_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata );
|
||||
TRACY_API void ___tracy_after_lock_shared_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata );
|
||||
TRACY_API void ___tracy_after_unlock_shared_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata );
|
||||
TRACY_API void ___tracy_after_try_lock_shared_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata, int32_t acquired );
|
||||
TRACY_API void ___tracy_mark_shared_lockable_ctx( struct __tracy_shared_lockable_context_data* lockdata, const struct ___tracy_source_location_data* srcloc );
|
||||
@@ -414,7 +414,7 @@ TRACY_API void ___tracy_custom_name_shared_lockable_ctx( struct __tracy_shared_l
|
||||
#define TracyCSharedLockAfterUnlock( lock ) ___tracy_after_unlock_shared_lockable_ctx( lock );
|
||||
#define TracyCSharedLockAfterTryLock( lock, acquired ) ___tracy_after_try_lock_shared_lockable_ctx( lock, acquired );
|
||||
#define TracyCSharedLockBeforeSharedLock( lock ) ___tracy_before_lock_shared_shared_lockable_ctx( lock );
|
||||
#define TracyCSharedLockAfterSharedLock( lock ) ___tracy_after_locked_shared_shared_lockable_ctx( lock );
|
||||
#define TracyCSharedLockAfterSharedLock( lock ) ___tracy_after_lock_shared_shared_lockable_ctx( lock );
|
||||
#define TracyCSharedLockAfterSharedUnlock( lock ) ___tracy_after_unlock_shared_shared_lockable_ctx( lock );
|
||||
#define TracyCSharedLockAfterTrySharedLock( lock, acquired ) ___tracy_after_try_lock_shared_shared_lockable_ctx( lock, acquired );
|
||||
#define TracyCSharedLockMark( lock ) static const struct ___tracy_source_location_data TracyConcat(__tracy_source_location,TracyLine) = { NULL, __func__, TracyFile, (uint32_t)TracyLine, 0 }; ___tracy_mark_shared_lockable_ctx( lock, &TracyConcat(__tracy_source_location,TracyLine) );
|
||||
|
||||
Reference in New Issue
Block a user