Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.

Tracy supports profiling CPU (Direct support is provided for C, C++, Lua, Python and Fortran integration. At the same time, third-party bindings to many other languages exist on the internet, such as [Rust](https://github.com/nagisa/rust_tracy_client), [Zig](https://github.com/tealsnow/zig-tracy), [C#](https://github.com/clibequilibrium/Tracy-CSharp), [OCaml](https://github.com/imandra-ai/ocaml-tracy), [Odin](https://github.com/oskarnp/odin-tracy), etc.), GPU (All major graphic APIs: OpenGL, Vulkan, Direct3D 11/12, Metal, OpenCL, CUDA.), memory allocations, locks, context switches, automatically attribute screenshots to captured frames, and much more.
Tracy supports profiling CPU (Direct support is provided for C, C++, Lua, Python and Fortran integration. At the same time, third-party bindings to many other languages exist on the internet, such as [Rust](https://github.com/nagisa/rust_tracy_client), [Zig](https://github.com/tealsnow/zig-tracy), [C#](https://github.com/clibequilibrium/Tracy-CSharp), [OCaml](https://github.com/imandra-ai/ocaml-tracy), [Odin](https://github.com/oskarnp/odin-tracy), etc.), GPU (All major graphics/compute APIs: OpenGL, Vulkan, Direct3D 11/12, Metal, OpenCL, CUDA, WebGPU.), memory allocations, locks, context switches, automatically attribute screenshots to captured frames, and much more.

- [Documentation](https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf) for usage and build process instructions
- [Releases](https://github.com/wolfpld/tracy/releases) containing the documentation (`tracy.pdf`) and compiled Windows x64 binaries (`Tracy-<version>.7z`) as assets
Expand Down
164 changes: 164 additions & 0 deletions examples/WebGPUDemo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# CMakeLists.txt — WebGPU spinning triangle demo
#
# macOS:
# clang++ -std=c++17 -ObjC++ spinning_triangle.cpp platform/platform_macos.mm \
# -I/path/to/wgpu/include -L/path/to/wgpu/lib -lwgpu_native \
# -Wl,-rpath,@executable_path \
# -framework Cocoa -framework Metal -framework QuartzCore \
# -framework Foundation -framework IOKit -framework IOSurface \
# -o spinning_triangle
#
# Windows (MSVC):
# cl /std:c++17 spinning_triangle.cpp platform/platform_windows.cpp \
# /I\path\to\wgpu\include \path\to\wgpu\lib\wgpu_native.lib \
# user32.lib gdi32.lib /Fe:spinning_triangle.exe
#
# Linux / Wayland:
# g++ -std=c++17 spinning_triangle.cpp platform/platform_wayland.cpp \
# xdg-shell-protocol.c \
# -I/path/to/wgpu/include -L/path/to/wgpu/lib -lwgpu_native \
# -lwayland-client -o spinning_triangle

cmake_minimum_required(VERSION 3.16)
project(spinning_triangle LANGUAGES C CXX)

# ---------------------------------------------------------------------------
# WebGPU backend — set WGPU_PATH to your wgpu-native or Dawn installation.
# The library name differs between backends:
# wgpu-native → wgpu_native
# Dawn → webgpu_dawn
# ---------------------------------------------------------------------------
set(WGPU_PATH "" CACHE PATH "Root of the WebGPU native installation (contains include/ and lib/)")
set(WGPU_LIB "webgpu_dawn" CACHE STRING "WebGPU library name (wgpu_native or webgpu_dawn)")

if(NOT WGPU_PATH)
message(FATAL_ERROR "Set WGPU_PATH to the root of your WebGPU native installation.")
endif()

# ---------------------------------------------------------------------------
# Tracy root — defaults to two directories above this CMakeLists.txt.
# ---------------------------------------------------------------------------
set(TRACY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..")
option(TRACY_ENABLE "Enable Tracy profiling" ON)

# ---------------------------------------------------------------------------
# macOS quarantine — pre-built WebGPU binaries downloaded from the internet
# carry a com.apple.quarantine extended attribute that prevents dyld from
# loading them ("damaged or incomplete" / Gatekeeper block). Strip it once
# at configure time so the linker and the runtime loader can both access the
# library directory without further user intervention.
# ---------------------------------------------------------------------------
if(APPLE)
execute_process(
COMMAND xattr -dr com.apple.quarantine "${WGPU_PATH}/lib"
)
endif()

# ---------------------------------------------------------------------------
# Platform-specific source and link settings
# ---------------------------------------------------------------------------
set(PLATFORM_GENERATED_INCLUDES "")

if(APPLE)
set(PLATFORM_SOURCES platform/platform_macos.mm)
set(PLATFORM_LIBS
"-framework Cocoa"
"-framework Metal"
"-framework QuartzCore"
"-framework Foundation"
"-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}")
endif()

# ---------------------------------------------------------------------------
# Target
# ---------------------------------------------------------------------------
add_executable(spinning_triangle
spinning_triangle.cpp
"${TRACY_DIR}/public/TracyClient.cpp"
${PLATFORM_SOURCES}
)

# Treat TracyClient.cpp as third-party code — suppress all warnings so that
# upstream changes don't pollute our build output.
if(MSVC)
set_source_files_properties("${TRACY_DIR}/public/TracyClient.cpp"
PROPERTIES COMPILE_FLAGS "/w"
)
else()
set_source_files_properties("${TRACY_DIR}/public/TracyClient.cpp"
PROPERTIES COMPILE_FLAGS "-w"
)
endif()

target_compile_features(spinning_triangle PRIVATE cxx_std_17)

if(TRACY_ENABLE)
target_compile_definitions(spinning_triangle PRIVATE TRACY_ENABLE)
endif()

target_include_directories(spinning_triangle PRIVATE
"${WGPU_PATH}/include"
"${TRACY_DIR}/public"
${PLATFORM_GENERATED_INCLUDES}
)

target_link_directories(spinning_triangle PRIVATE "${WGPU_PATH}/lib")

target_link_libraries(spinning_triangle PRIVATE
${WGPU_LIB}
${PLATFORM_LIBS}
)

# Embed the rpath so the binary finds the WebGPU dylib/so next to itself.
if(APPLE)
set_target_properties(spinning_triangle PROPERTIES
BUILD_RPATH "${WGPU_PATH}/lib"
INSTALL_RPATH "@executable_path"
)
elseif(UNIX)
set_target_properties(spinning_triangle PROPERTIES
BUILD_RPATH "${WGPU_PATH}/lib"
INSTALL_RPATH "$ORIGIN"
)
endif()
23 changes: 23 additions & 0 deletions examples/WebGPUDemo/platform/platform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// platform.h — interface between platform-agnostic code and platform backends
//
// Each platform_*.mm / platform_*.cpp file implements these five functions.
// Exactly one backend must be linked into the final binary.

#pragma once
#include <webgpu/webgpu.h>

// Initialize the windowing system and create a window of the given dimensions.
// Returns true on success.
bool platformInit(int width, int height, const char* title);

// Create a WebGPU surface backed by the platform window.
// Must be called after wgpuCreateInstance() and platformInit().
WGPUSurface platformCreateSurface(WGPUInstance instance);

// Elapsed wall-clock time in seconds since platformInit().
double platformGetTime();

// Enter the platform event/render loop.
// Calls render() each frame at ~60 fps.
// Calls shutdown() exactly once before returning.
void platformRunLoop(void (*render)(), void (*shutdown)());
120 changes: 120 additions & 0 deletions examples/WebGPUDemo/platform/platform_macos.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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;
}];

[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];
}
}
Loading
Loading