mirror of
https://github.com/wolfpld/tracy.git
synced 2026-09-03 17:08:29 +00:00
adding OpenGL example (spinning triangle)
This commit is contained in:
75
examples/opengl/triangle/CMakeLists.txt
Normal file
75
examples/opengl/triangle/CMakeLists.txt
Normal file
@@ -0,0 +1,75 @@
|
||||
# CMakeLists.txt — OpenGL spinning triangle demo
|
||||
#
|
||||
# macOS:
|
||||
# cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja .
|
||||
# cmake --build build/ninja
|
||||
#
|
||||
# Linux (requires libx11-dev libgl1-mesa-dev libglew-dev):
|
||||
# cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja .
|
||||
# cmake --build build/ninja
|
||||
#
|
||||
# Windows (MSVC, requires GLEW):
|
||||
# cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DGLEW_ROOT=<path> -B build/ninja .
|
||||
# cmake --build build/ninja
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(gl_spinning_triangle LANGUAGES C CXX)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tracy root — defaults to three directories above this CMakeLists.txt.
|
||||
# ---------------------------------------------------------------------------
|
||||
set(TRACY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../..")
|
||||
option(TRACY_ENABLE "Enable Tracy profiling" ON)
|
||||
option(TRACY_OPENGL_AUTO_CALIBRATION "Enable periodic GPU/CPU recalibration" ON)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform-specific sources and link settings
|
||||
# ---------------------------------------------------------------------------
|
||||
if(APPLE)
|
||||
set(PLATFORM_SOURCES platform/platform_macos.mm)
|
||||
set(PLATFORM_LIBS "-framework Cocoa" "-framework OpenGL")
|
||||
set_source_files_properties(platform/platform_macos.mm
|
||||
PROPERTIES COMPILE_FLAGS "-ObjC++"
|
||||
)
|
||||
elseif(WIN32)
|
||||
find_package(GLEW REQUIRED)
|
||||
set(PLATFORM_SOURCES platform/platform_windows.cpp)
|
||||
set(PLATFORM_LIBS opengl32 GLEW::GLEW)
|
||||
else()
|
||||
find_package(GLEW REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
set(PLATFORM_SOURCES platform/platform_x11.cpp)
|
||||
set(PLATFORM_LIBS GLEW::GLEW X11::X11 GL)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Target
|
||||
# ---------------------------------------------------------------------------
|
||||
add_executable(gl_spinning_triangle
|
||||
spinning_triangle.cpp
|
||||
"${TRACY_DIR}/public/TracyClient.cpp"
|
||||
${PLATFORM_SOURCES}
|
||||
)
|
||||
|
||||
# Suppress upstream warnings from TracyClient.cpp
|
||||
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(gl_spinning_triangle PRIVATE cxx_std_17)
|
||||
|
||||
if(TRACY_ENABLE)
|
||||
target_compile_definitions(gl_spinning_triangle PRIVATE TRACY_ENABLE)
|
||||
endif()
|
||||
if(TRACY_OPENGL_AUTO_CALIBRATION)
|
||||
target_compile_definitions(gl_spinning_triangle PRIVATE TRACY_OPENGL_AUTO_CALIBRATION)
|
||||
endif()
|
||||
|
||||
target_include_directories(gl_spinning_triangle PRIVATE "${TRACY_DIR}/public")
|
||||
target_link_libraries(gl_spinning_triangle PRIVATE ${PLATFORM_LIBS})
|
||||
37
examples/opengl/triangle/platform/platform.h
Normal file
37
examples/opengl/triangle/platform/platform.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// platform.h — interface between platform-agnostic code and platform backends
|
||||
//
|
||||
// Each platform_*.mm / platform_*.cpp file implements these four functions.
|
||||
// Exactly one backend must be linked into the final binary.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __APPLE__
|
||||
// OpenGL is only available on MacOS (no iOS support)
|
||||
// Anything from gl3.h will spew deprecation warnings when used,
|
||||
// unless GL_SILENCE_DEPRECATION has been defined beforehand
|
||||
//# define GL_SILENCE_DEPRECATION
|
||||
# include <OpenGL/gl3.h>
|
||||
#else
|
||||
# include <GL/glew.h>
|
||||
#endif
|
||||
|
||||
// Initialize the windowing system, create a window, and make an OpenGL 3.3
|
||||
// Core Profile context current on the calling thread.
|
||||
// Returns true on success.
|
||||
bool platformInit(int width, int height, const char* title);
|
||||
|
||||
// Load OpenGL function pointers (no-op on macOS where the framework exports them directly).
|
||||
// Must be called after platformInit() while the GL context is current.
|
||||
// Returns true on success.
|
||||
bool platformInitGL();
|
||||
|
||||
// Elapsed wall-clock time in seconds since platformInit().
|
||||
double platformGetTime();
|
||||
|
||||
// Swap front and back buffers (present the rendered frame).
|
||||
void platformSwapBuffers();
|
||||
|
||||
// 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)());
|
||||
113
examples/opengl/triangle/platform/platform_macos.mm
Normal file
113
examples/opengl/triangle/platform/platform_macos.mm
Normal file
@@ -0,0 +1,113 @@
|
||||
// platform_macos.mm — macOS backend (Cocoa + NSOpenGLView)
|
||||
//
|
||||
// Note: OpenGL is deprecated on macOS 10.14+, but remains functional.
|
||||
// Tracy's TracyGpuContext is a no-op on Apple platforms.
|
||||
//
|
||||
// Compile flags:
|
||||
// clang++ -std=c++17 -ObjC++ spinning_triangle.cpp platform/platform_macos.mm \
|
||||
// -framework Cocoa -framework OpenGL -o gl_spinning_triangle
|
||||
|
||||
// OpenGL is only available on MacOS (no iOS support)
|
||||
// Anything from Cocoa/OpenGL will spew deprecation warnings when used,
|
||||
// unless GL_SILENCE_DEPRECATION has been defined beforehand
|
||||
//#define GL_SILENCE_DEPRECATION
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#include <CoreFoundation/CFDate.h>
|
||||
#include <cstdio>
|
||||
#include "platform.h"
|
||||
|
||||
static NSOpenGLView* sGLView = nullptr;
|
||||
static CFAbsoluteTime sStartTime = 0;
|
||||
static void (*sRenderCb)() = nullptr;
|
||||
static void (*sShutdownCb)() = nullptr;
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
@property (strong) NSWindow* window;
|
||||
@property (strong) NSTimer* timer;
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
|
||||
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) { [NSApp terminate:nil]; return nil; }
|
||||
return event;
|
||||
}];
|
||||
|
||||
[self.window makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
- (void)tick:(NSTimer*)t {
|
||||
if (sRenderCb) {
|
||||
[[sGLView openGLContext] makeCurrentContext];
|
||||
sRenderCb();
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app { return YES; }
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification*)notification {
|
||||
[self.timer invalidate];
|
||||
if (sShutdownCb) sShutdownCb();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
bool platformInit(int width, int height, const char* title) {
|
||||
[NSApplication sharedApplication];
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
NSOpenGLPixelFormatAttribute attrs[] = {
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
||||
NSOpenGLPFAColorSize, 24,
|
||||
NSOpenGLPFAAlphaSize, 8,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAAccelerated,
|
||||
0
|
||||
};
|
||||
NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
|
||||
if (!fmt) { fprintf(stderr, "Failed to create NSOpenGLPixelFormat\n"); return false; }
|
||||
|
||||
NSRect frame = NSMakeRect(200, 200, width, height);
|
||||
sGLView = [[NSOpenGLView alloc] initWithFrame:frame pixelFormat:fmt];
|
||||
if (!sGLView) { fprintf(stderr, "Failed to create NSOpenGLView\n"); return false; }
|
||||
|
||||
NSWindow* window = [[NSWindow alloc]
|
||||
initWithContentRect:frame
|
||||
styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable)
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
[window setTitle:[NSString stringWithUTF8String:title]];
|
||||
[window setContentView:sGLView];
|
||||
|
||||
[[sGLView openGLContext] makeCurrentContext];
|
||||
|
||||
AppDelegate* del = [[AppDelegate alloc] init];
|
||||
del.window = window;
|
||||
[NSApp setDelegate:del];
|
||||
|
||||
sStartTime = CFAbsoluteTimeGetCurrent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool platformInitGL() { return true; }
|
||||
|
||||
double platformGetTime() { return CFAbsoluteTimeGetCurrent() - sStartTime; }
|
||||
|
||||
void platformSwapBuffers() { [[sGLView openGLContext] flushBuffer]; }
|
||||
|
||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
||||
sRenderCb = render;
|
||||
sShutdownCb = shutdown;
|
||||
@autoreleasepool { [NSApp run]; }
|
||||
}
|
||||
160
examples/opengl/triangle/platform/platform_windows.cpp
Normal file
160
examples/opengl/triangle/platform/platform_windows.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
// platform_windows.cpp — Windows backend (Win32 + WGL)
|
||||
//
|
||||
// Creates a WGL 3.3 Core Profile OpenGL context.
|
||||
// GLEW must be initialized by the caller (initGL) after platformInit() returns.
|
||||
//
|
||||
// Compile flags (MSVC):
|
||||
// cl /std:c++17 spinning_triangle.cpp platform/platform_windows.cpp \
|
||||
// /I<glew-include> <glew-lib>/glew32s.lib opengl32.lib \
|
||||
// user32.lib gdi32.lib /Fe:gl_spinning_triangle.exe
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <GL/gl.h>
|
||||
#include <cstdio>
|
||||
#include "platform.h"
|
||||
|
||||
// WGL_ARB_create_context token values
|
||||
#ifndef WGL_CONTEXT_MAJOR_VERSION_ARB
|
||||
# define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
# define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
# define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||
# define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||
#endif
|
||||
|
||||
typedef HGLRC (WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int*);
|
||||
|
||||
static HWND sHwnd = nullptr;
|
||||
static HDC sDC = nullptr;
|
||||
static HGLRC sGLRC = nullptr;
|
||||
static bool sRunning = false;
|
||||
static LARGE_INTEGER sFreq = {};
|
||||
static LARGE_INTEGER sStart = {};
|
||||
static void (*sRenderCb)() = nullptr;
|
||||
static void (*sShutdownCb)() = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool platformInit(int width, int height, const char* title) {
|
||||
WNDCLASSEXA wc = {};
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
||||
wc.lpfnWndProc = wndProc;
|
||||
wc.hInstance = GetModuleHandleA(nullptr);
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.lpszClassName = "GLSpinningTriangle";
|
||||
if (!RegisterClassExA(&wc)) { fprintf(stderr, "RegisterClassExA failed\n"); return false; }
|
||||
|
||||
RECT rect = { 0, 0, width, height };
|
||||
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX), FALSE);
|
||||
sHwnd = CreateWindowExA(0, "GLSpinningTriangle", 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\n"); return false; }
|
||||
|
||||
sDC = GetDC(sHwnd);
|
||||
|
||||
// Create a legacy context to get wglCreateContextAttribsARB, then replace it.
|
||||
PIXELFORMATDESCRIPTOR pfd = {};
|
||||
pfd.nSize = sizeof(pfd);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||
pfd.cColorBits = 32;
|
||||
SetPixelFormat(sDC, ChoosePixelFormat(sDC, &pfd), &pfd);
|
||||
HGLRC dummy = wglCreateContext(sDC);
|
||||
wglMakeCurrent(sDC, dummy);
|
||||
|
||||
auto wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)
|
||||
wglGetProcAddress("wglCreateContextAttribsARB");
|
||||
|
||||
wglMakeCurrent(nullptr, nullptr);
|
||||
wglDeleteContext(dummy);
|
||||
|
||||
if (!wglCreateContextAttribsARB) {
|
||||
fprintf(stderr, "WGL_ARB_create_context not supported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int attribs[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
0
|
||||
};
|
||||
sGLRC = wglCreateContextAttribsARB(sDC, nullptr, attribs);
|
||||
if (!sGLRC) { fprintf(stderr, "wglCreateContextAttribsARB failed\n"); return false; }
|
||||
wglMakeCurrent(sDC, sGLRC);
|
||||
|
||||
ShowWindow(sHwnd, SW_SHOW);
|
||||
UpdateWindow(sHwnd);
|
||||
|
||||
QueryPerformanceFrequency(&sFreq);
|
||||
QueryPerformanceCounter(&sStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool platformInitGL() {
|
||||
glewExperimental = GL_TRUE;
|
||||
if (glewInit() != GLEW_OK) {
|
||||
fprintf(stderr, "Failed to initialize GLEW\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double platformGetTime() {
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
return (double)(now.QuadPart - sStart.QuadPart) / (double)sFreq.QuadPart;
|
||||
}
|
||||
|
||||
void platformSwapBuffers() { SwapBuffers(sDC); }
|
||||
|
||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
||||
static const double kFrameTime = 1.0 / 60.0;
|
||||
sRenderCb = render;
|
||||
sShutdownCb = shutdown;
|
||||
sRunning = true;
|
||||
|
||||
while (sRunning) {
|
||||
double frameStart = platformGetTime();
|
||||
|
||||
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();
|
||||
|
||||
double elapsed = platformGetTime() - frameStart;
|
||||
if (elapsed < kFrameTime) {
|
||||
DWORD ms = (DWORD)((kFrameTime - elapsed) * 1000.0);
|
||||
if (ms > 0) Sleep(ms);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown();
|
||||
wglMakeCurrent(nullptr, nullptr);
|
||||
wglDeleteContext(sGLRC);
|
||||
ReleaseDC(sHwnd, sDC);
|
||||
DestroyWindow(sHwnd);
|
||||
}
|
||||
149
examples/opengl/triangle/platform/platform_x11.cpp
Normal file
149
examples/opengl/triangle/platform/platform_x11.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
// platform_x11.cpp — Linux/X11 backend (GLX)
|
||||
//
|
||||
// Creates a GLX 3.3 Core Profile OpenGL context.
|
||||
// GLEW must be initialized by the caller (initGL) after platformInit() returns.
|
||||
//
|
||||
// Dependencies: libX11, libGL, libGLEW
|
||||
//
|
||||
// Compile flags:
|
||||
// g++ -std=c++17 spinning_triangle.cpp platform/platform_x11.cpp \
|
||||
// -lX11 -lGL -lGLEW -o gl_spinning_triangle
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <GL/glx.h>
|
||||
#include <cstdio>
|
||||
#include <time.h>
|
||||
#include "platform.h"
|
||||
|
||||
// GLX_ARB_create_context token values (from <GL/glxext.h>)
|
||||
#ifndef GLX_CONTEXT_MAJOR_VERSION_ARB
|
||||
# define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
# define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
# define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||
# define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||
#endif
|
||||
|
||||
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
|
||||
|
||||
static Display* sDpy = nullptr;
|
||||
static Window sWin = 0;
|
||||
static GLXContext sCtx = nullptr;
|
||||
static Atom sWmDelete = 0;
|
||||
static bool sRunning = false;
|
||||
static struct timespec sStart = {};
|
||||
static void (*sRenderCb)() = nullptr;
|
||||
static void (*sShutdownCb)() = nullptr;
|
||||
|
||||
bool platformInit(int width, int height, const char* title) {
|
||||
sDpy = XOpenDisplay(nullptr);
|
||||
if (!sDpy) { fprintf(stderr, "Cannot open X display\n"); return false; }
|
||||
|
||||
const int fbAttribs[] = {
|
||||
GLX_X_RENDERABLE, True,
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
GLX_RED_SIZE, 8,
|
||||
GLX_GREEN_SIZE, 8,
|
||||
GLX_BLUE_SIZE, 8,
|
||||
None
|
||||
};
|
||||
int fbCount = 0;
|
||||
GLXFBConfig* fbc = glXChooseFBConfig(sDpy, DefaultScreen(sDpy), fbAttribs, &fbCount);
|
||||
if (!fbc || fbCount == 0) { fprintf(stderr, "No suitable GLXFBConfig\n"); return false; }
|
||||
|
||||
XVisualInfo* vi = glXGetVisualFromFBConfig(sDpy, fbc[0]);
|
||||
|
||||
XSetWindowAttributes swa = {};
|
||||
swa.colormap = XCreateColormap(sDpy, DefaultRootWindow(sDpy), vi->visual, AllocNone);
|
||||
swa.event_mask = ExposureMask | KeyPressMask;
|
||||
sWin = XCreateWindow(sDpy, DefaultRootWindow(sDpy),
|
||||
200, 200, width, height, 0, vi->depth, InputOutput, vi->visual,
|
||||
CWColormap | CWEventMask, &swa);
|
||||
XFree(vi);
|
||||
|
||||
XStoreName(sDpy, sWin, title);
|
||||
XMapWindow(sDpy, sWin);
|
||||
|
||||
sWmDelete = XInternAtom(sDpy, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(sDpy, sWin, &sWmDelete, 1);
|
||||
|
||||
auto glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
|
||||
glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
|
||||
if (!glXCreateContextAttribsARB) {
|
||||
fprintf(stderr, "glXCreateContextAttribsARB not found\n");
|
||||
XFree(fbc);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int ctxAttribs[] = {
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
None
|
||||
};
|
||||
sCtx = glXCreateContextAttribsARB(sDpy, fbc[0], nullptr, True, ctxAttribs);
|
||||
XFree(fbc);
|
||||
if (!sCtx) { fprintf(stderr, "Failed to create GLX context\n"); return false; }
|
||||
|
||||
glXMakeCurrent(sDpy, sWin, sCtx);
|
||||
clock_gettime(CLOCK_MONOTONIC, &sStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool platformInitGL() {
|
||||
glewExperimental = GL_TRUE;
|
||||
if (glewInit() != GLEW_OK) {
|
||||
fprintf(stderr, "Failed to initialize GLEW\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double platformGetTime() {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
return (double)(now.tv_sec - sStart.tv_sec) + (double)(now.tv_nsec - sStart.tv_nsec) * 1e-9;
|
||||
}
|
||||
|
||||
void platformSwapBuffers() { glXSwapBuffers(sDpy, sWin); }
|
||||
|
||||
void platformRunLoop(void (*render)(), void (*shutdown)()) {
|
||||
static const long kFrameNs = 1000000000L / 60;
|
||||
sRenderCb = render;
|
||||
sShutdownCb = shutdown;
|
||||
sRunning = true;
|
||||
|
||||
while (sRunning) {
|
||||
struct timespec frameStart;
|
||||
clock_gettime(CLOCK_MONOTONIC, &frameStart);
|
||||
|
||||
while (XPending(sDpy)) {
|
||||
XEvent ev;
|
||||
XNextEvent(sDpy, &ev);
|
||||
if (ev.type == KeyPress) { sRunning = false; break; }
|
||||
if (ev.type == ClientMessage && (Atom)ev.xclient.data.l[0] == sWmDelete) {
|
||||
sRunning = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sRunning) render();
|
||||
|
||||
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();
|
||||
glXMakeCurrent(sDpy, None, nullptr);
|
||||
glXDestroyContext(sDpy, sCtx);
|
||||
XDestroyWindow(sDpy, sWin);
|
||||
XCloseDisplay(sDpy);
|
||||
}
|
||||
134
examples/opengl/triangle/spinning_triangle.cpp
Normal file
134
examples/opengl/triangle/spinning_triangle.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
// spinning_triangle.cpp — OpenGL spinning triangle demo with Tracy GPU profiling.
|
||||
//
|
||||
// Tracy GPU zones are active on non-Apple platforms when TRACY_ENABLE is defined.
|
||||
// TRACY_OPENGL_AUTO_CALIBRATION (enabled by default in CMakeLists.txt) enables
|
||||
// periodic GPU/CPU drift correction via glGetInteger64v(GL_TIMESTAMP).
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
#include <tracy/Tracy.hpp>
|
||||
#include <tracy/TracyOpenGL.hpp>
|
||||
|
||||
static const int kWidth = 800;
|
||||
static const int kHeight = 600;
|
||||
|
||||
static GLuint gProgram = 0;
|
||||
static GLuint gVao = 0;
|
||||
static GLint gAngleLoc = -1;
|
||||
|
||||
// Vertex colors and positions are baked in; rotation is driven by a uniform.
|
||||
static const char* kVertSrc = R"(
|
||||
#version 150 core
|
||||
uniform float uAngle;
|
||||
const vec2 kPos[3] = vec2[3](
|
||||
vec2( 0.0, 0.5 ),
|
||||
vec2(-0.433, -0.25 ),
|
||||
vec2( 0.433, -0.25 )
|
||||
);
|
||||
const vec3 kCol[3] = vec3[3](
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
out vec3 vColor;
|
||||
void main() {
|
||||
float c = cos(uAngle);
|
||||
float s = sin(uAngle);
|
||||
vec2 p = kPos[gl_VertexID];
|
||||
gl_Position = vec4(p.x*c - p.y*s, p.x*s + p.y*c, 0.0, 1.0);
|
||||
vColor = kCol[gl_VertexID];
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* kFragSrc = R"(
|
||||
#version 150 core
|
||||
in vec3 vColor;
|
||||
out vec4 fragColor;
|
||||
void main() { fragColor = vec4(vColor, 1.0); }
|
||||
)";
|
||||
|
||||
static GLuint compileShader(GLenum type, const char* src) {
|
||||
GLuint s = glCreateShader(type);
|
||||
glShaderSource(s, 1, &src, nullptr);
|
||||
glCompileShader(s);
|
||||
GLint ok = 0;
|
||||
glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok) {
|
||||
char log[512];
|
||||
glGetShaderInfoLog(s, sizeof(log), nullptr, log);
|
||||
fprintf(stderr, "Shader compile error: %s\n", log);
|
||||
glDeleteShader(s);
|
||||
return 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static int initGL() {
|
||||
if (!platformInitGL()) return 1;
|
||||
|
||||
TracyGpuContext;
|
||||
TracyGpuContextName("OpenGL", 6);
|
||||
|
||||
GLuint vert = compileShader(GL_VERTEX_SHADER, kVertSrc);
|
||||
GLuint frag = compileShader(GL_FRAGMENT_SHADER, kFragSrc);
|
||||
if (!vert || !frag) return 1;
|
||||
|
||||
gProgram = glCreateProgram();
|
||||
glAttachShader(gProgram, vert);
|
||||
glAttachShader(gProgram, frag);
|
||||
glLinkProgram(gProgram);
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
|
||||
GLint ok = 0;
|
||||
glGetProgramiv(gProgram, GL_LINK_STATUS, &ok);
|
||||
if (!ok) {
|
||||
char log[512];
|
||||
glGetProgramInfoLog(gProgram, sizeof(log), nullptr, log);
|
||||
fprintf(stderr, "Program link error: %s\n", log);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gAngleLoc = glGetUniformLocation(gProgram, "uAngle");
|
||||
|
||||
// Core profile requires a bound VAO even with no vertex attributes.
|
||||
glGenVertexArrays(1, &gVao);
|
||||
glBindVertexArray(gVao);
|
||||
|
||||
glClearColor(0.05f, 0.05f, 0.08f, 1.0f);
|
||||
glViewport(0, 0, kWidth, kHeight);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void renderFrame() {
|
||||
ZoneScoped;
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glUseProgram(gProgram);
|
||||
|
||||
{
|
||||
TracyGpuZone("triangle draw");
|
||||
glUniform1f(gAngleLoc, (float)platformGetTime());
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
platformSwapBuffers();
|
||||
TracyGpuCollect;
|
||||
}
|
||||
|
||||
static void shutdown() {
|
||||
fprintf(stderr, "application is shutting down...\n");
|
||||
glDeleteVertexArrays(1, &gVao);
|
||||
glDeleteProgram(gProgram);
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (!platformInit(kWidth, kHeight, "OpenGL Spinning Triangle"))
|
||||
return 1;
|
||||
if (initGL() != 0)
|
||||
return 2;
|
||||
platformRunLoop(renderFrame, shutdown);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user