Compare commits

..

1 Commits

Author SHA1 Message Date
Marcos Slomp
9402d295f9 allow OpenGL on MacOS when possible 2026-06-10 14:06:35 -07:00
5 changed files with 33 additions and 352 deletions

View File

@@ -1,84 +0,0 @@
# 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 — 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_LIBS "-framework Cocoa" "-framework OpenGL"
"-framework CoreVideo" "-framework IOKit")
elseif(WIN32)
find_package(GLEW REQUIRED)
set(PLATFORM_LIBS opengl32 user32 gdi32 GLEW::GLEW)
else()
find_package(GLEW REQUIRED)
find_package(X11 REQUIRED)
set(PLATFORM_LIBS X11::X11 GL GLEW::GLEW)
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"
${PLATFORM_INCLUDES}
)
target_link_libraries(gl_spinning_triangle PRIVATE ${PLATFORM_LIBS})

View File

@@ -1,41 +0,0 @@
// 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();
// 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.
void platformRunLoop(void (*render)(), void (*shutdown)());

View File

@@ -1,73 +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 <RGFW.h>
#include <chrono>
#include <cstdio>
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<double>(
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;
}

View File

@@ -1,136 +0,0 @@
// 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);
float scaleX, scaleY;
platformGetPixelDensityScale(&scaleX, &scaleY);
glViewport(0, 0, (int)(kWidth * scaleX), (int)(kHeight * scaleY));
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;
}

View File

@@ -1,12 +1,7 @@
#ifndef __TRACYOPENGL_HPP__
#define __TRACYOPENGL_HPP__
#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
#if !defined TRACY_ENABLE
#define TracyGpuContext
#define TracyGpuContextName(x,y)
@@ -36,6 +31,10 @@ public:
#else
#if (defined __APPLE__ && !TARGET_OS_OSX)
#error Apple devices do not support OpenGL (except on MacOS where it's deprecated).
#endif
#include <atomic>
#include <assert.h>
#include <stdlib.h>
@@ -99,24 +98,25 @@ class GpuCtx
public:
GpuCtx()
: m_context( GetGpuCtxCounter().fetch_add( 1, std::memory_order_relaxed ) )
: m_context( 255 )
, m_head( 0 )
, m_tail( 0 )
{
ZoneScopedC( Color::Red4 );
assert( m_context != 255 );
GLint bits;
GLint bits = 0;
glGetQueryiv( GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &bits );
assert( bits > 0 );
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,
Profiler::LogString( MessageSourceType::Tracy, MessageSeverity::Error, Color::Red4, 0,
"OpenGL driver does not implement GL_TIMESTAMP precision." );
return;
}
assert( bits > 0 );
glGenQueries( QueryCount, m_query );
int64_t tgpu;
glGetInteger64v( GL_TIMESTAMP, &tgpu );
@@ -130,7 +130,8 @@ public:
m_prevCalibration = GetHostTimeNs();
#endif
glGenQueries( QueryCount, m_query );
assert( m_context != 255 );
m_context = GetGpuCtxCounter().fetch_add( 1, std::memory_order_relaxed );
const float period = 1.f;
const auto thread = GetThreadHandle();
@@ -156,6 +157,8 @@ public:
void Name( const char* name, uint16_t len )
{
if ( !Valid() ) return;
auto ptr = (char*)tracy_malloc( len );
memcpy( ptr, name, len );
@@ -171,6 +174,8 @@ public:
void Collect()
{
if ( !Valid() ) return;
ZoneScopedC( Color::Red4 );
#ifdef TRACY_ON_DEMAND
@@ -259,6 +264,11 @@ private:
return m_context;
}
tracy_force_inline bool Valid() const
{
return m_context != 255;
}
unsigned int m_query[QueryCount];
uint8_t m_context;
@@ -280,7 +290,7 @@ public:
: m_active( is_active )
#endif
{
if( !m_active ) return;
if( !m_active || !Valid() ) return;
const auto queryId = GetGpuCtx().ptr->NextQueryId();
glQueryCounter( GetGpuCtx().ptr->TranslateOpenGlQueryId( queryId ), GL_TIMESTAMP );
@@ -301,7 +311,7 @@ public:
: m_active( is_active )
#endif
{
if( !m_active ) return;
if( !m_active || !Valid() ) return;
const auto queryId = GetGpuCtx().ptr->NextQueryId();
glQueryCounter( GetGpuCtx().ptr->TranslateOpenGlQueryId( queryId ), GL_TIMESTAMP );
@@ -328,7 +338,7 @@ public:
: m_active( is_active )
#endif
{
if( !m_active ) return;
if( !m_active || !Valid() ) return;
const auto queryId = GetGpuCtx().ptr->NextQueryId();
glQueryCounter( GetGpuCtx().ptr->TranslateOpenGlQueryId( queryId ), GL_TIMESTAMP );
@@ -350,7 +360,7 @@ public:
: m_active( is_active )
#endif
{
if( !m_active ) return;
if( !m_active || !Valid() ) return;
const auto queryId = GetGpuCtx().ptr->NextQueryId();
glQueryCounter( GetGpuCtx().ptr->TranslateOpenGlQueryId( queryId ), GL_TIMESTAMP );
@@ -373,7 +383,7 @@ public:
tracy_force_inline ~GpuCtxScope()
{
if( !m_active ) return;
if( !m_active || !Valid() ) return;
const auto queryId = GetGpuCtx().ptr->NextQueryId();
glQueryCounter( GetGpuCtx().ptr->TranslateOpenGlQueryId( queryId ), GL_TIMESTAMP );
@@ -386,6 +396,11 @@ public:
TracyLfqCommit;
}
tracy_force_inline bool Valid()
{
return GetGpuCtx().ptr->Valid();
}
private:
const bool m_active;
};