From 1b207d3e2abfd6eea69c04285b0c4c64ddb28366 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Wed, 10 Jun 2026 14:14:54 -0700 Subject: [PATCH 01/12] adding OpenGL example (spinning triangle) --- examples/opengl/triangle/CMakeLists.txt | 75 ++++++++ examples/opengl/triangle/platform/platform.h | 37 ++++ .../triangle/platform/platform_macos.mm | 113 +++++++++++++ .../triangle/platform/platform_windows.cpp | 160 ++++++++++++++++++ .../opengl/triangle/platform/platform_x11.cpp | 149 ++++++++++++++++ .../opengl/triangle/spinning_triangle.cpp | 134 +++++++++++++++ 6 files changed, 668 insertions(+) create mode 100644 examples/opengl/triangle/CMakeLists.txt create mode 100644 examples/opengl/triangle/platform/platform.h create mode 100644 examples/opengl/triangle/platform/platform_macos.mm create mode 100644 examples/opengl/triangle/platform/platform_windows.cpp create mode 100644 examples/opengl/triangle/platform/platform_x11.cpp create mode 100644 examples/opengl/triangle/spinning_triangle.cpp diff --git a/examples/opengl/triangle/CMakeLists.txt b/examples/opengl/triangle/CMakeLists.txt new file mode 100644 index 00000000..8b194ec8 --- /dev/null +++ b/examples/opengl/triangle/CMakeLists.txt @@ -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= -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}) diff --git a/examples/opengl/triangle/platform/platform.h b/examples/opengl/triangle/platform/platform.h new file mode 100644 index 00000000..2d1def55 --- /dev/null +++ b/examples/opengl/triangle/platform/platform.h @@ -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 +#else +# include +#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)()); diff --git a/examples/opengl/triangle/platform/platform_macos.mm b/examples/opengl/triangle/platform/platform_macos.mm new file mode 100644 index 00000000..ca496061 --- /dev/null +++ b/examples/opengl/triangle/platform/platform_macos.mm @@ -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 +#import +#include +#include +#include "platform.h" + +static NSOpenGLView* sGLView = nullptr; +static CFAbsoluteTime sStartTime = 0; +static void (*sRenderCb)() = nullptr; +static void (*sShutdownCb)() = nullptr; + +@interface AppDelegate : NSObject +@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]; } +} diff --git a/examples/opengl/triangle/platform/platform_windows.cpp b/examples/opengl/triangle/platform/platform_windows.cpp new file mode 100644 index 00000000..736bd7e9 --- /dev/null +++ b/examples/opengl/triangle/platform/platform_windows.cpp @@ -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 /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 +#include +#include +#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); +} diff --git a/examples/opengl/triangle/platform/platform_x11.cpp b/examples/opengl/triangle/platform/platform_x11.cpp new file mode 100644 index 00000000..3009ebb9 --- /dev/null +++ b/examples/opengl/triangle/platform/platform_x11.cpp @@ -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 +#include +#include +#include +#include "platform.h" + +// GLX_ARB_create_context token values (from ) +#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); +} diff --git a/examples/opengl/triangle/spinning_triangle.cpp b/examples/opengl/triangle/spinning_triangle.cpp new file mode 100644 index 00000000..84ec43d6 --- /dev/null +++ b/examples/opengl/triangle/spinning_triangle.cpp @@ -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 +#include + +#include +#include + +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; +} From e83429c926d0665d76a21d5f0fc069f97f0ae673 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Wed, 10 Jun 2026 18:38:48 -0700 Subject: [PATCH 02/12] replacing the various platform layers by RGFW --- examples/opengl/triangle/CMakeLists.txt | 31 ++-- examples/opengl/triangle/platform/platform.h | 4 + .../triangle/platform/platform_macos.mm | 113 ------------- .../triangle/platform/platform_rgfw.cpp | 73 ++++++++ .../triangle/platform/platform_windows.cpp | 160 ------------------ .../opengl/triangle/platform/platform_x11.cpp | 149 ---------------- .../opengl/triangle/spinning_triangle.cpp | 4 +- 7 files changed, 100 insertions(+), 434 deletions(-) delete mode 100644 examples/opengl/triangle/platform/platform_macos.mm create mode 100644 examples/opengl/triangle/platform/platform_rgfw.cpp delete mode 100644 examples/opengl/triangle/platform/platform_windows.cpp delete mode 100644 examples/opengl/triangle/platform/platform_x11.cpp diff --git a/examples/opengl/triangle/CMakeLists.txt b/examples/opengl/triangle/CMakeLists.txt index 8b194ec8..1ff8da16 100644 --- a/examples/opengl/triangle/CMakeLists.txt +++ b/examples/opengl/triangle/CMakeLists.txt @@ -23,23 +23,29 @@ option(TRACY_ENABLE "Enable Tracy profiling" option(TRACY_OPENGL_AUTO_CALIBRATION "Enable periodic GPU/CPU recalibration" ON) # --------------------------------------------------------------------------- -# Platform-specific sources and link settings +# Platform — RGFW (cross-platform windowing, fetched automatically) # --------------------------------------------------------------------------- +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 OpenGL") - set_source_files_properties(platform/platform_macos.mm - PROPERTIES COMPILE_FLAGS "-ObjC++" - ) + set(PLATFORM_LIBS "-framework Cocoa" "-framework OpenGL" + "-framework CoreVideo" "-framework IOKit") elseif(WIN32) find_package(GLEW REQUIRED) - set(PLATFORM_SOURCES platform/platform_windows.cpp) - set(PLATFORM_LIBS opengl32 GLEW::GLEW) + set(PLATFORM_LIBS opengl32 user32 gdi32 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) + set(PLATFORM_LIBS X11::X11 GL GLEW::GLEW) endif() # --------------------------------------------------------------------------- @@ -71,5 +77,8 @@ 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_include_directories(gl_spinning_triangle PRIVATE + "${TRACY_DIR}/public" + ${PLATFORM_INCLUDES} +) target_link_libraries(gl_spinning_triangle PRIVATE ${PLATFORM_LIBS}) diff --git a/examples/opengl/triangle/platform/platform.h b/examples/opengl/triangle/platform/platform.h index 2d1def55..a82280cb 100644 --- a/examples/opengl/triangle/platform/platform.h +++ b/examples/opengl/triangle/platform/platform.h @@ -31,6 +31,10 @@ double platformGetTime(); // Swap front and back buffers (present the rendered frame). void platformSwapBuffers(); +// Pixel scaling factor relative to the logical window size (1.0 on non-HiDPI displays). +// Must be called after platformInit(). +void platformGetPixelDensityScale(float* x, float* y); + // Enter the platform event/render loop. // Calls render() each frame at ~60 fps. // Calls shutdown() exactly once before returning. diff --git a/examples/opengl/triangle/platform/platform_macos.mm b/examples/opengl/triangle/platform/platform_macos.mm deleted file mode 100644 index ca496061..00000000 --- a/examples/opengl/triangle/platform/platform_macos.mm +++ /dev/null @@ -1,113 +0,0 @@ -// 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 -#import -#include -#include -#include "platform.h" - -static NSOpenGLView* sGLView = nullptr; -static CFAbsoluteTime sStartTime = 0; -static void (*sRenderCb)() = nullptr; -static void (*sShutdownCb)() = nullptr; - -@interface AppDelegate : NSObject -@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]; } -} diff --git a/examples/opengl/triangle/platform/platform_rgfw.cpp b/examples/opengl/triangle/platform/platform_rgfw.cpp new file mode 100644 index 00000000..f37d2407 --- /dev/null +++ b/examples/opengl/triangle/platform/platform_rgfw.cpp @@ -0,0 +1,73 @@ +// platform_rgfw.cpp — RGFW windowing backend (cross-platform) +// https://github.com/ColleagueRiley/RGFW + +#include "platform.h" // GL headers first (gl3.h / glew.h) so RGFW sees guards set + +#define RGFW_OPENGL +#define RGFW_IMPLEMENTATION +#include + +#include +#include + +static RGFW_window* sWin = nullptr; +static std::chrono::steady_clock::time_point sStartTime; + +bool platformInit(int width, int height, const char* title) { + RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL(); + hints->major = 3; + hints->minor = 3; + RGFW_setGlobalHints_OpenGL(hints); + + sWin = RGFW_createWindow(title, 0, 0, width, height, + RGFW_windowCenter | RGFW_windowOpenGL); + if (!sWin) { + fprintf(stderr, "RGFW: failed to create window\n"); + return false; + } + RGFW_window_makeCurrentContext_OpenGL(sWin); + RGFW_window_swapInterval_OpenGL(sWin, 1); + RGFW_window_setExitKey(sWin, RGFW_keyEscape); + + sStartTime = std::chrono::steady_clock::now(); + return true; +} + +bool platformInitGL() { +#ifndef __APPLE__ + glewExperimental = GL_TRUE; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to initialize GLEW\n"); + return false; + } +#endif + return true; +} + +double platformGetTime() { + return std::chrono::duration( + std::chrono::steady_clock::now() - sStartTime).count(); +} + +void platformSwapBuffers() { RGFW_window_swapBuffers_OpenGL(sWin); } + +void platformGetPixelDensityScale(float* x, float* y) { + i32 pw, ph; + RGFW_window_getSizeInPixels(sWin, &pw, &ph); + *x = (float)pw / (float)sWin->w; + *y = (float)ph / (float)sWin->h; +} + +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; +} diff --git a/examples/opengl/triangle/platform/platform_windows.cpp b/examples/opengl/triangle/platform/platform_windows.cpp deleted file mode 100644 index 736bd7e9..00000000 --- a/examples/opengl/triangle/platform/platform_windows.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// 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 /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 -#include -#include -#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); -} diff --git a/examples/opengl/triangle/platform/platform_x11.cpp b/examples/opengl/triangle/platform/platform_x11.cpp deleted file mode 100644 index 3009ebb9..00000000 --- a/examples/opengl/triangle/platform/platform_x11.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// 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 -#include -#include -#include -#include "platform.h" - -// GLX_ARB_create_context token values (from ) -#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); -} diff --git a/examples/opengl/triangle/spinning_triangle.cpp b/examples/opengl/triangle/spinning_triangle.cpp index 84ec43d6..dd826295 100644 --- a/examples/opengl/triangle/spinning_triangle.cpp +++ b/examples/opengl/triangle/spinning_triangle.cpp @@ -98,7 +98,9 @@ static int initGL() { glBindVertexArray(gVao); glClearColor(0.05f, 0.05f, 0.08f, 1.0f); - glViewport(0, 0, kWidth, kHeight); + float scaleX, scaleY; + platformGetPixelDensityScale(&scaleX, &scaleY); + glViewport(0, 0, (int)(kWidth * scaleX), (int)(kHeight * scaleY)); return 0; } From eb88c6eba057296b1a3ebd27c250045ff6923949 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Wed, 10 Jun 2026 18:52:09 -0700 Subject: [PATCH 03/12] adding warning about TracyOpenGL usage on Apple devices --- public/tracy/TracyOpenGL.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/tracy/TracyOpenGL.hpp b/public/tracy/TracyOpenGL.hpp index f9e35b15..31f3f39e 100644 --- a/public/tracy/TracyOpenGL.hpp +++ b/public/tracy/TracyOpenGL.hpp @@ -1,7 +1,12 @@ #ifndef __TRACYOPENGL_HPP__ #define __TRACYOPENGL_HPP__ -#if !defined TRACY_ENABLE || defined __APPLE__ +#ifdef __APPLE__ +#define TRACY_OPENGL_DISABLE +#warning "OpenGL support on Apple devices is deprecated or unavailable." +#endif + +#if !defined TRACY_ENABLE || defined TRACY_OPENGL_DISABLE #define TracyGpuContext #define TracyGpuContextName(x,y) From d98608b02266effb0b19e03177a4269a54720ff2 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Wed, 10 Jun 2026 18:52:57 -0700 Subject: [PATCH 04/12] issue a Tracy warning message when timestamp queries are supported but not properly implemented --- public/tracy/TracyOpenGL.hpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/public/tracy/TracyOpenGL.hpp b/public/tracy/TracyOpenGL.hpp index 31f3f39e..c621b63f 100644 --- a/public/tracy/TracyOpenGL.hpp +++ b/public/tracy/TracyOpenGL.hpp @@ -105,15 +105,21 @@ public: { assert( m_context != 255 ); - glGenQueries( QueryCount, m_query ); + GLint bits; + glGetQueryiv( GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &bits ); + if( bits == 0 ) + { + // all timestamp queries would resolve to 0 (and produce 0ns GPU zones). + // (this is the case for many TBDR GPUs, including Apple Silicon) + Profiler::LogString( MessageSourceType::Tracy, MessageSeverity::Warning, Color::Tomato, 0, + "OpenGL driver does not implement GL_TIMESTAMP precision." ); + } + assert( bits > 0 ); int64_t tgpu; glGetInteger64v( GL_TIMESTAMP, &tgpu ); int64_t tcpu = Profiler::GetTime(); - GLint bits; - glGetQueryiv( GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &bits ); - #ifdef TRACY_OPENGL_AUTO_CALIBRATION // The anchor above is never refreshed; advertise calibration and emit periodic // GpuCalibration events to correct CPU/GPU drift (see Recalibrate). Opt-in, @@ -122,6 +128,8 @@ public: m_prevCalibration = GetHostTimeNs(); #endif + glGenQueries( QueryCount, m_query ); + const float period = 1.f; const auto thread = GetThreadHandle(); TracyLfqPrepare( QueueType::GpuNewContext ); From debda1df5592ea3808b6cb4998d5e60856102ba8 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Wed, 10 Jun 2026 18:56:16 -0700 Subject: [PATCH 05/12] scoping the GpuCtx constructor --- public/tracy/TracyOpenGL.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/tracy/TracyOpenGL.hpp b/public/tracy/TracyOpenGL.hpp index c621b63f..0fca6d65 100644 --- a/public/tracy/TracyOpenGL.hpp +++ b/public/tracy/TracyOpenGL.hpp @@ -103,6 +103,8 @@ public: , m_head( 0 ) , m_tail( 0 ) { + ZoneScopedC( Color::Red4 ); + assert( m_context != 255 ); GLint bits; From a2555fbb33b44905a67d6f95232be9468bf6230d Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Thu, 11 Jun 2026 07:37:58 -0700 Subject: [PATCH 06/12] fixing Windows/Linux build --- examples/opengl/triangle/CMakeLists.txt | 28 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/examples/opengl/triangle/CMakeLists.txt b/examples/opengl/triangle/CMakeLists.txt index 1ff8da16..4088ec99 100644 --- a/examples/opengl/triangle/CMakeLists.txt +++ b/examples/opengl/triangle/CMakeLists.txt @@ -4,12 +4,12 @@ # cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja . # cmake --build build/ninja # -# Linux (requires libx11-dev libgl1-mesa-dev libglew-dev): +# Linux (requires libx11-dev libgl1-mesa-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= -B build/ninja . +# Windows: +# cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja . # cmake --build build/ninja cmake_minimum_required(VERSION 3.16) @@ -33,6 +33,20 @@ FetchContent_Declare(rgfw ) FetchContent_MakeAvailable(rgfw) +# --------------------------------------------------------------------------- +# GL extension loader — GLEW (Windows + Linux, fetched automatically) +# --------------------------------------------------------------------------- +if(NOT APPLE) + set(glew-cmake_BUILD_SHARED OFF CACHE BOOL "" FORCE) + set(ONLY_LIBS ON CACHE BOOL "" FORCE) + FetchContent_Declare(glew + GIT_REPOSITORY https://github.com/Perlmint/glew-cmake.git + GIT_TAG master # pin to a specific commit for reproducible builds + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(glew) +endif() + set(PLATFORM_SOURCES platform/platform_rgfw.cpp) set(PLATFORM_INCLUDES ${rgfw_SOURCE_DIR}) @@ -40,12 +54,10 @@ if(APPLE) set(PLATFORM_LIBS "-framework Cocoa" "-framework OpenGL" "-framework CoreVideo" "-framework IOKit") elseif(WIN32) - find_package(GLEW REQUIRED) - set(PLATFORM_LIBS opengl32 user32 gdi32 GLEW::GLEW) + set(PLATFORM_LIBS opengl32 user32 gdi32 libglew_static) else() - find_package(GLEW REQUIRED) - find_package(X11 REQUIRED) - set(PLATFORM_LIBS X11::X11 GL GLEW::GLEW) + find_package(X11 REQUIRED) + set(PLATFORM_LIBS X11::X11 GL libglew_static) endif() # --------------------------------------------------------------------------- From daba5acfbc2fa46c32564b6f9201b19b62091e71 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Fri, 12 Jun 2026 07:31:03 -0700 Subject: [PATCH 07/12] more explicit compiler warning message --- public/tracy/TracyOpenGL.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/tracy/TracyOpenGL.hpp b/public/tracy/TracyOpenGL.hpp index 0fca6d65..36b2a75f 100644 --- a/public/tracy/TracyOpenGL.hpp +++ b/public/tracy/TracyOpenGL.hpp @@ -3,7 +3,7 @@ #ifdef __APPLE__ #define TRACY_OPENGL_DISABLE -#warning "OpenGL support on Apple devices is deprecated or unavailable." +#warning "OpenGL timestamps are unreliable on Apple devices that still run OpenGL." #endif #if !defined TRACY_ENABLE || defined TRACY_OPENGL_DISABLE From 832234838bd4f1fb54ba15c91ebd24ab5c0ea4fc Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Fri, 12 Jun 2026 07:33:06 -0700 Subject: [PATCH 08/12] better comments and messages --- examples/opengl/triangle/CMakeLists.txt | 4 ---- examples/opengl/triangle/platform/platform.h | 4 ---- .../opengl/triangle/spinning_triangle.cpp | 23 +++++++++++++------ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/examples/opengl/triangle/CMakeLists.txt b/examples/opengl/triangle/CMakeLists.txt index 4088ec99..cb1d21fb 100644 --- a/examples/opengl/triangle/CMakeLists.txt +++ b/examples/opengl/triangle/CMakeLists.txt @@ -20,7 +20,6 @@ project(gl_spinning_triangle LANGUAGES C CXX) # --------------------------------------------------------------------------- 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 — RGFW (cross-platform windowing, fetched automatically) @@ -85,9 +84,6 @@ 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" diff --git a/examples/opengl/triangle/platform/platform.h b/examples/opengl/triangle/platform/platform.h index a82280cb..4513e9f9 100644 --- a/examples/opengl/triangle/platform/platform.h +++ b/examples/opengl/triangle/platform/platform.h @@ -6,10 +6,6 @@ #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 #else # include diff --git a/examples/opengl/triangle/spinning_triangle.cpp b/examples/opengl/triangle/spinning_triangle.cpp index dd826295..2c4f728c 100644 --- a/examples/opengl/triangle/spinning_triangle.cpp +++ b/examples/opengl/triangle/spinning_triangle.cpp @@ -1,14 +1,23 @@ // 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 -#include +#ifdef __APPLE__ +// NOTE: OpenGL is only available on MacOS (no iOS support) +// Including and using anything related to OpenGL on Apple (like ) +// will emit deprecation warnings, unless GL_SILENCE_DEPRECATION is defined +#define GL_SILENCE_DEPRECATION +// NOTE: TracyOpenGL.hpp will not work as expected even on Apple devices that +// support OpenGL, because the OpenGL drivers do not implement ARB_timer_query +// properly (querying GL_TIMESTAMP always resolves to 0). TracyOpenGL.hpp will +// emit a compiler warning, and a Tracy message to the trace/profiler, but the +// program will still run. +#endif + +#include "platform/platform.h" // also includes OpenGL headers #include + +// NOTE: opt-in toggle for periodic recalibrations during Collect() +#define TRACY_OPENGL_AUTO_CALIBRATION #include static const int kWidth = 800; From 39dc6883401cef989c56d62a0037ac8cefa7f814 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Fri, 12 Jun 2026 08:44:46 -0700 Subject: [PATCH 09/12] adding Xrandr dependency --- examples/opengl/triangle/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/opengl/triangle/CMakeLists.txt b/examples/opengl/triangle/CMakeLists.txt index cb1d21fb..342c6075 100644 --- a/examples/opengl/triangle/CMakeLists.txt +++ b/examples/opengl/triangle/CMakeLists.txt @@ -56,7 +56,10 @@ elseif(WIN32) set(PLATFORM_LIBS opengl32 user32 gdi32 libglew_static) else() find_package(X11 REQUIRED) - set(PLATFORM_LIBS X11::X11 GL libglew_static) + if(NOT X11_Xrandr_FOUND) + message(FATAL_ERROR "Xrandr not found — install libxrandr-dev") + endif() + set(PLATFORM_LIBS X11::X11 X11::Xrandr GL libglew_static) endif() # --------------------------------------------------------------------------- From 3f203806e2da305b44b95e561b83faa0bb5401e2 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Fri, 12 Jun 2026 13:00:33 -0700 Subject: [PATCH 10/12] X11 workaround check --- .../triangle/platform/platform_rgfw.cpp | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/examples/opengl/triangle/platform/platform_rgfw.cpp b/examples/opengl/triangle/platform/platform_rgfw.cpp index f37d2407..4275bd2f 100644 --- a/examples/opengl/triangle/platform/platform_rgfw.cpp +++ b/examples/opengl/triangle/platform/platform_rgfw.cpp @@ -10,10 +10,34 @@ #include #include +#if defined(__linux__) +#include +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; + } + RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL(); hints->major = 3; hints->minor = 3; @@ -22,7 +46,7 @@ bool platformInit(int width, int height, const char* title) { sWin = RGFW_createWindow(title, 0, 0, width, height, RGFW_windowCenter | RGFW_windowOpenGL); if (!sWin) { - fprintf(stderr, "RGFW: failed to create window\n"); + fprintf(stderr, "ERROR: failed to create window\n"); return false; } RGFW_window_makeCurrentContext_OpenGL(sWin); From ee0c73bf25a403d498a072cd4cb1335ddbb0df81 Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Sun, 14 Jun 2026 11:24:14 -0700 Subject: [PATCH 11/12] switch to SDL2 (no cmake fetch, just find_package) --- examples/opengl/triangle/CMakeLists.txt | 28 ++---- .../triangle/platform/platform_rgfw.cpp | 97 ------------------- .../triangle/platform/platform_sdl2.cpp | 89 +++++++++++++++++ 3 files changed, 97 insertions(+), 117 deletions(-) delete mode 100644 examples/opengl/triangle/platform/platform_rgfw.cpp create mode 100644 examples/opengl/triangle/platform/platform_sdl2.cpp diff --git a/examples/opengl/triangle/CMakeLists.txt b/examples/opengl/triangle/CMakeLists.txt index 342c6075..47f224e2 100644 --- a/examples/opengl/triangle/CMakeLists.txt +++ b/examples/opengl/triangle/CMakeLists.txt @@ -4,7 +4,7 @@ # cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja . # cmake --build build/ninja # -# Linux (requires libx11-dev libgl1-mesa-dev): +# Linux (requires libsdl2-dev libgl1-mesa-dev): # cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja . # cmake --build build/ninja # @@ -22,20 +22,15 @@ set(TRACY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../..") option(TRACY_ENABLE "Enable Tracy profiling" ON) # --------------------------------------------------------------------------- -# Platform — RGFW (cross-platform windowing, fetched automatically) +# Platform — SDL2 (cross-platform windowing, must be installed on the system) # --------------------------------------------------------------------------- -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) +find_package(SDL2 REQUIRED) # --------------------------------------------------------------------------- # GL extension loader — GLEW (Windows + Linux, fetched automatically) # --------------------------------------------------------------------------- if(NOT APPLE) + include(FetchContent) set(glew-cmake_BUILD_SHARED OFF CACHE BOOL "" FORCE) set(ONLY_LIBS ON CACHE BOOL "" FORCE) FetchContent_Declare(glew @@ -46,20 +41,14 @@ if(NOT APPLE) FetchContent_MakeAvailable(glew) endif() -set(PLATFORM_SOURCES platform/platform_rgfw.cpp) -set(PLATFORM_INCLUDES ${rgfw_SOURCE_DIR}) +set(PLATFORM_SOURCES platform/platform_sdl2.cpp) if(APPLE) - set(PLATFORM_LIBS "-framework Cocoa" "-framework OpenGL" - "-framework CoreVideo" "-framework IOKit") + set(PLATFORM_LIBS SDL2::SDL2 "-framework OpenGL") elseif(WIN32) - set(PLATFORM_LIBS opengl32 user32 gdi32 libglew_static) + set(PLATFORM_LIBS SDL2::SDL2 opengl32 libglew_static) else() - 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 GL libglew_static) + set(PLATFORM_LIBS SDL2::SDL2 GL libglew_static) endif() # --------------------------------------------------------------------------- @@ -90,6 +79,5 @@ endif() target_include_directories(gl_spinning_triangle PRIVATE "${TRACY_DIR}/public" - ${PLATFORM_INCLUDES} ) target_link_libraries(gl_spinning_triangle PRIVATE ${PLATFORM_LIBS}) diff --git a/examples/opengl/triangle/platform/platform_rgfw.cpp b/examples/opengl/triangle/platform/platform_rgfw.cpp deleted file mode 100644 index 4275bd2f..00000000 --- a/examples/opengl/triangle/platform/platform_rgfw.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// platform_rgfw.cpp — RGFW windowing backend (cross-platform) -// https://github.com/ColleagueRiley/RGFW - -#include "platform.h" // GL headers first (gl3.h / glew.h) so RGFW sees guards set - -#define RGFW_OPENGL -#define RGFW_IMPLEMENTATION -#include - -#include -#include - -#if defined(__linux__) -#include -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; - } - - RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL(); - hints->major = 3; - hints->minor = 3; - RGFW_setGlobalHints_OpenGL(hints); - - sWin = RGFW_createWindow(title, 0, 0, width, height, - RGFW_windowCenter | RGFW_windowOpenGL); - if (!sWin) { - fprintf(stderr, "ERROR: failed to create window\n"); - return false; - } - RGFW_window_makeCurrentContext_OpenGL(sWin); - RGFW_window_swapInterval_OpenGL(sWin, 1); - RGFW_window_setExitKey(sWin, RGFW_keyEscape); - - sStartTime = std::chrono::steady_clock::now(); - return true; -} - -bool platformInitGL() { -#ifndef __APPLE__ - glewExperimental = GL_TRUE; - if (glewInit() != GLEW_OK) { - fprintf(stderr, "Failed to initialize GLEW\n"); - return false; - } -#endif - return true; -} - -double platformGetTime() { - return std::chrono::duration( - std::chrono::steady_clock::now() - sStartTime).count(); -} - -void platformSwapBuffers() { RGFW_window_swapBuffers_OpenGL(sWin); } - -void platformGetPixelDensityScale(float* x, float* y) { - i32 pw, ph; - RGFW_window_getSizeInPixels(sWin, &pw, &ph); - *x = (float)pw / (float)sWin->w; - *y = (float)ph / (float)sWin->h; -} - -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; -} diff --git a/examples/opengl/triangle/platform/platform_sdl2.cpp b/examples/opengl/triangle/platform/platform_sdl2.cpp new file mode 100644 index 00000000..f6e95dd6 --- /dev/null +++ b/examples/opengl/triangle/platform/platform_sdl2.cpp @@ -0,0 +1,89 @@ +// platform_sdl2.cpp — SDL2 windowing backend (cross-platform) + +#include "platform.h" // GL headers first (gl3.h / glew.h) so SDL sees guards set + +#define SDL_MAIN_HANDLED // we don't want SDL_main +#include + +#include +#include + +static SDL_Window* sWin = nullptr; +static SDL_GLContext sCtx = nullptr; +static std::chrono::steady_clock::time_point sStartTime; + +bool platformInit(int width, int height, const char* title) { + SDL_SetMainReady(); + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "ERROR: SDL_Init failed: %s\n", SDL_GetError()); + return false; + } + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + sWin = SDL_CreateWindow(title, + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + width, height, + SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI); + if (!sWin) { + fprintf(stderr, "ERROR: SDL_CreateWindow failed: %s\n", SDL_GetError()); + SDL_Quit(); + return false; + } + + sCtx = SDL_GL_CreateContext(sWin); + if (!sCtx) { + fprintf(stderr, "ERROR: SDL_GL_CreateContext failed: %s\n", SDL_GetError()); + SDL_DestroyWindow(sWin); + SDL_Quit(); + return false; + } + + SDL_GL_SetSwapInterval(1); + sStartTime = std::chrono::steady_clock::now(); + return true; +} + +bool platformInitGL() { +#ifndef __APPLE__ + glewExperimental = GL_TRUE; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to initialize GLEW\n"); + return false; + } +#endif + return true; +} + +double platformGetTime() { + return std::chrono::duration( + std::chrono::steady_clock::now() - sStartTime).count(); +} + +void platformSwapBuffers() { SDL_GL_SwapWindow(sWin); } + +void platformGetPixelDensityScale(float* x, float* y) { + int pw, ph, ww, wh; + SDL_GL_GetDrawableSize(sWin, &pw, &ph); + SDL_GetWindowSize(sWin, &ww, &wh); + *x = (ww > 0) ? (float)pw / (float)ww : 1.0f; + *y = (wh > 0) ? (float)ph / (float)wh : 1.0f; +} + +void platformRunLoop(void (*render)(), void (*shutdown)()) { + bool running = true; + while (running) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) running = false; + if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) running = false; + } + if (running) render(); + } + shutdown(); + SDL_GL_DeleteContext(sCtx); + SDL_DestroyWindow(sWin); + SDL_Quit(); +} From 17e13bc2e09248a86e6cae4163de185818dea51c Mon Sep 17 00:00:00 2001 From: Marcos Slomp Date: Sun, 14 Jun 2026 12:18:06 -0700 Subject: [PATCH 12/12] SDL2 -> SDL3 --- examples/opengl/triangle/CMakeLists.txt | 14 ++++++------ .../{platform_sdl2.cpp => platform_sdl3.cpp} | 22 ++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) rename examples/opengl/triangle/platform/{platform_sdl2.cpp => platform_sdl3.cpp} (78%) diff --git a/examples/opengl/triangle/CMakeLists.txt b/examples/opengl/triangle/CMakeLists.txt index 47f224e2..0d4a93d5 100644 --- a/examples/opengl/triangle/CMakeLists.txt +++ b/examples/opengl/triangle/CMakeLists.txt @@ -4,7 +4,7 @@ # cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja . # cmake --build build/ninja # -# Linux (requires libsdl2-dev libgl1-mesa-dev): +# Linux (requires libsdl3-dev libgl1-mesa-dev): # cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -B build/ninja . # cmake --build build/ninja # @@ -22,9 +22,9 @@ set(TRACY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../..") option(TRACY_ENABLE "Enable Tracy profiling" ON) # --------------------------------------------------------------------------- -# Platform — SDL2 (cross-platform windowing, must be installed on the system) +# Platform — SDL3 (cross-platform windowing, must be installed on the system) # --------------------------------------------------------------------------- -find_package(SDL2 REQUIRED) +find_package(SDL3 REQUIRED) # --------------------------------------------------------------------------- # GL extension loader — GLEW (Windows + Linux, fetched automatically) @@ -41,14 +41,14 @@ if(NOT APPLE) FetchContent_MakeAvailable(glew) endif() -set(PLATFORM_SOURCES platform/platform_sdl2.cpp) +set(PLATFORM_SOURCES platform/platform_sdl3.cpp) if(APPLE) - set(PLATFORM_LIBS SDL2::SDL2 "-framework OpenGL") + set(PLATFORM_LIBS SDL3::SDL3 "-framework OpenGL") elseif(WIN32) - set(PLATFORM_LIBS SDL2::SDL2 opengl32 libglew_static) + set(PLATFORM_LIBS SDL3::SDL3 opengl32 libglew_static) else() - set(PLATFORM_LIBS SDL2::SDL2 GL libglew_static) + set(PLATFORM_LIBS SDL3::SDL3 GL libglew_static) endif() # --------------------------------------------------------------------------- diff --git a/examples/opengl/triangle/platform/platform_sdl2.cpp b/examples/opengl/triangle/platform/platform_sdl3.cpp similarity index 78% rename from examples/opengl/triangle/platform/platform_sdl2.cpp rename to examples/opengl/triangle/platform/platform_sdl3.cpp index f6e95dd6..99fe418a 100644 --- a/examples/opengl/triangle/platform/platform_sdl2.cpp +++ b/examples/opengl/triangle/platform/platform_sdl3.cpp @@ -1,9 +1,8 @@ -// platform_sdl2.cpp — SDL2 windowing backend (cross-platform) - +// platform_sdl3.cpp — SDL3 windowing backend (cross-platform) #include "platform.h" // GL headers first (gl3.h / glew.h) so SDL sees guards set #define SDL_MAIN_HANDLED // we don't want SDL_main -#include +#include #include #include @@ -13,8 +12,7 @@ static SDL_GLContext sCtx = nullptr; static std::chrono::steady_clock::time_point sStartTime; bool platformInit(int width, int height, const char* title) { - SDL_SetMainReady(); - if (SDL_Init(SDL_INIT_VIDEO) != 0) { + if (!SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "ERROR: SDL_Init failed: %s\n", SDL_GetError()); return false; } @@ -23,15 +21,13 @@ bool platformInit(int width, int height, const char* title) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - sWin = SDL_CreateWindow(title, - SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - width, height, - SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI); + sWin = SDL_CreateWindow(title, width, height, SDL_WINDOW_OPENGL); if (!sWin) { fprintf(stderr, "ERROR: SDL_CreateWindow failed: %s\n", SDL_GetError()); SDL_Quit(); return false; } + SDL_SetWindowPosition(sWin, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); sCtx = SDL_GL_CreateContext(sWin); if (!sCtx) { @@ -66,7 +62,7 @@ void platformSwapBuffers() { SDL_GL_SwapWindow(sWin); } void platformGetPixelDensityScale(float* x, float* y) { int pw, ph, ww, wh; - SDL_GL_GetDrawableSize(sWin, &pw, &ph); + SDL_GetWindowSizeInPixels(sWin, &pw, &ph); SDL_GetWindowSize(sWin, &ww, &wh); *x = (ww > 0) ? (float)pw / (float)ww : 1.0f; *y = (wh > 0) ? (float)ph / (float)wh : 1.0f; @@ -77,13 +73,13 @@ void platformRunLoop(void (*render)(), void (*shutdown)()) { while (running) { SDL_Event e; while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) running = false; - if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) running = false; + if (e.type == SDL_EVENT_QUIT) running = false; + if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) running = false; } if (running) render(); } shutdown(); - SDL_GL_DeleteContext(sCtx); + SDL_GL_DestroyContext(sCtx); SDL_DestroyWindow(sWin); SDL_Quit(); }