I think a browser does not need a VSync but it is failed to enable VSync on the browser too. The browser app spends 1-2% of CPU. I will not limit FPS for the browser because requestAnimationFrame works perfect. The browser limits FPS to 60 and plays animations very smoothly. This problem is actual for Dektop.
This is my example in for Desktop and prints VSync failed but I solved the problem (with 15% CPU) using the limit of FPS from the first message for the topic. Full code:
main.c
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <glad/glad.h>
typedef struct
{
SDL_Window *window;
SDL_GLContext glContext;
GLuint shaderProgram;
GLuint vao, vbo;
} App;
const char *vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPosition;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPosition, 1.0);\n"
"}\n";
const char *fragmentShaderSource =
"#version 330 core\n"
"precision mediump float;\n"
"out vec4 fragColor;\n"
"void main()\n"
"{\n"
" fragColor = vec4(0.75, 1, 0.7, 1.0);\n"
"}\n";
SDL_AppResult SDL_AppInit(void **appState, int argc, char *argv[])
{
App *app = (App *)SDL_malloc(sizeof(App));
*appState = app;
if (!SDL_Init(SDL_INIT_VIDEO))
{
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable MULTISAMPLE
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8); // Can be 2, 4, 8 or 16
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);
app->window = SDL_CreateWindow("SDL3, OpenGL", 380, 380, SDL_WINDOW_OPENGL);
if (!app->window)
{
SDL_Log("Couldn't create the window: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
app->glContext = SDL_GL_CreateContext(app->window);
if (!app->glContext)
{
SDL_Log("Couldn't create the glConext: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
{
SDL_Log("Failed to initialize OpenGL function pointers");
return SDL_APP_FAILURE;
}
int status = SDL_GL_SetSwapInterval(1);
if (status != 0)
{
SDL_Log("VSync failed: %s", SDL_GetError());
}
glClearColor(0.2f, 0.2f, 0.2f, 1.f);
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
app->shaderProgram = glCreateProgram();
glAttachShader(app->shaderProgram, vertexShader);
glAttachShader(app->shaderProgram, fragmentShader);
glLinkProgram(app->shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glUseProgram(app->shaderProgram);
// clang-format off
GLfloat vertexPositions[] = {
-0.5f, -0.5f, 0.f,
0.5f, -0.5f, 0.f,
0.f, 0.5f, 0.f
};
// clang-format on
glGenVertexArrays(1, &app->vao);
glGenBuffers(1, &app->vbo);
glBindVertexArray(app->vao);
glBindBuffer(GL_ARRAY_BUFFER, app->vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void *)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void *appState, SDL_Event *event)
{
App *app = (App *)appState;
switch (event->type)
{
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS;
default:
break;
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appState)
{
App *app = (App *)appState;
// High-precision timing variables
static Uint64 lastFrameTime = 0;
const Uint64 targetFrameTimeNS = 16666666; // ~16.6ms for 60 FPS
Uint64 now = SDL_GetTicksNS();
// If we haven't hit the target time, yield the CPU
if ((now - lastFrameTime) < targetFrameTimeNS)
{
// Sleep to yield control back to the Windows scheduler
// This drops CPU usage from 15% to <1%
SDL_Delay(1);
return SDL_APP_CONTINUE;
}
// Update our last frame time
lastFrameTime = now;
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(app->vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
SDL_GL_SwapWindow(app->window);
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appState, SDL_AppResult result)
{
App *app = (App *)appState;
glDeleteProgram(app->shaderProgram);
SDL_GL_DestroyContext(app->glContext);
SDL_DestroyWindow(app->window);
SDL_Quit();
}