aboutsummaryrefslogtreecommitdiffstats
path: root/examples/pugl_shader_demo.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/pugl_shader_demo.c')
-rw-r--r--examples/pugl_shader_demo.c702
1 files changed, 343 insertions, 359 deletions
diff --git a/examples/pugl_shader_demo.c b/examples/pugl_shader_demo.c
index 8ebbe60..d038b3f 100644
--- a/examples/pugl_shader_demo.c
+++ b/examples/pugl_shader_demo.c
@@ -56,30 +56,28 @@ static const int defaultWidth = 512;
static const int defaultHeight = 512;
static const uintptr_t resizeTimerId = 1u;
-typedef struct
-{
- mat4 projection;
+typedef struct {
+ mat4 projection;
} RectUniforms;
-typedef struct
-{
- const char* programPath;
- PuglWorld* world;
- PuglView* view;
- PuglTestOptions opts;
- size_t numRects;
- Rect* rects;
- Program drawRect;
- GLuint vao;
- GLuint vbo;
- GLuint instanceVbo;
- GLuint ibo;
- double lastDrawDuration;
- double lastFrameEndTime;
- unsigned framesDrawn;
- int glMajorVersion;
- int glMinorVersion;
- int quit;
+typedef struct {
+ const char* programPath;
+ PuglWorld* world;
+ PuglView* view;
+ PuglTestOptions opts;
+ size_t numRects;
+ Rect* rects;
+ Program drawRect;
+ GLuint vao;
+ GLuint vbo;
+ GLuint instanceVbo;
+ GLuint ibo;
+ double lastDrawDuration;
+ double lastFrameEndTime;
+ unsigned framesDrawn;
+ int glMajorVersion;
+ int glMinorVersion;
+ int quit;
} PuglTestApp;
static PuglStatus
@@ -91,393 +89,379 @@ teardownGl(PuglTestApp* app);
static void
onConfigure(PuglView* view, double width, double height)
{
- (void)view;
+ (void)view;
- glEnable(GL_BLEND);
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
- glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glViewport(0, 0, (int)width, (int)height);
+ glEnable(GL_BLEND);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glViewport(0, 0, (int)width, (int)height);
}
static void
onExpose(PuglView* view)
{
- PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
- const PuglRect frame = puglGetFrame(view);
- const float width = (float)frame.width;
- const float height = (float)frame.height;
- const double time = puglGetTime(puglGetWorld(view));
-
- // Construct projection matrix for 2D window surface (in pixels)
- mat4 proj;
- mat4Ortho(proj,
- 0.0f,
- (float)frame.width,
- 0.0f,
- (float)frame.height,
- -1.0f,
- 1.0f);
-
- // Clear and bind everything that is the same for every rect
- glClear(GL_COLOR_BUFFER_BIT);
- glUseProgram(app->drawRect.program);
- glBindVertexArray(app->vao);
-
- for (size_t i = 0; i < app->numRects; ++i) {
- moveRect(&app->rects[i], i, app->numRects, width, height, time);
- }
-
- glBufferData(GL_UNIFORM_BUFFER, sizeof(proj), &proj, GL_STREAM_DRAW);
-
- glBufferSubData(GL_ARRAY_BUFFER,
- 0,
- (GLsizeiptr)(app->numRects * sizeof(Rect)),
- app->rects);
-
- glDrawElementsInstanced(GL_TRIANGLE_STRIP,
- 4,
- GL_UNSIGNED_INT,
- NULL,
- (GLsizei)(app->numRects * 4));
-
- ++app->framesDrawn;
-
- app->lastFrameEndTime = puglGetTime(puglGetWorld(view));
- app->lastDrawDuration = app->lastFrameEndTime - time;
+ PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
+ const PuglRect frame = puglGetFrame(view);
+ const float width = (float)frame.width;
+ const float height = (float)frame.height;
+ const double time = puglGetTime(puglGetWorld(view));
+
+ // Construct projection matrix for 2D window surface (in pixels)
+ mat4 proj;
+ mat4Ortho(
+ proj, 0.0f, (float)frame.width, 0.0f, (float)frame.height, -1.0f, 1.0f);
+
+ // Clear and bind everything that is the same for every rect
+ glClear(GL_COLOR_BUFFER_BIT);
+ glUseProgram(app->drawRect.program);
+ glBindVertexArray(app->vao);
+
+ for (size_t i = 0; i < app->numRects; ++i) {
+ moveRect(&app->rects[i], i, app->numRects, width, height, time);
+ }
+
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(proj), &proj, GL_STREAM_DRAW);
+
+ glBufferSubData(
+ GL_ARRAY_BUFFER, 0, (GLsizeiptr)(app->numRects * sizeof(Rect)), app->rects);
+
+ glDrawElementsInstanced(
+ GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, NULL, (GLsizei)(app->numRects * 4));
+
+ ++app->framesDrawn;
+
+ app->lastFrameEndTime = puglGetTime(puglGetWorld(view));
+ app->lastDrawDuration = app->lastFrameEndTime - time;
}
static PuglStatus
onEvent(PuglView* view, const PuglEvent* event)
{
- PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
-
- printEvent(event, "Event: ", app->opts.verbose);
-
- switch (event->type) {
- case PUGL_CREATE:
- setupGl(app);
- break;
- case PUGL_DESTROY:
- teardownGl(app);
- break;
- case PUGL_CONFIGURE:
- onConfigure(view, event->configure.width, event->configure.height);
- break;
- case PUGL_UPDATE:
- puglPostRedisplay(view);
- break;
- case PUGL_EXPOSE: onExpose(view); break;
- case PUGL_CLOSE: app->quit = 1; break;
- case PUGL_LOOP_ENTER:
- puglStartTimer(view,
- resizeTimerId,
- 1.0 / (double)puglGetViewHint(view, PUGL_REFRESH_RATE));
- break;
- case PUGL_LOOP_LEAVE:
- puglStopTimer(view, resizeTimerId);
- break;
- case PUGL_KEY_PRESS:
- if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
- app->quit = 1;
- }
- break;
- case PUGL_TIMER:
- if (event->timer.id == resizeTimerId) {
- puglPostRedisplay(view);
- }
- break;
- default: break;
- }
-
- return PUGL_SUCCESS;
+ PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
+
+ printEvent(event, "Event: ", app->opts.verbose);
+
+ switch (event->type) {
+ case PUGL_CREATE:
+ setupGl(app);
+ break;
+ case PUGL_DESTROY:
+ teardownGl(app);
+ break;
+ case PUGL_CONFIGURE:
+ onConfigure(view, event->configure.width, event->configure.height);
+ break;
+ case PUGL_UPDATE:
+ puglPostRedisplay(view);
+ break;
+ case PUGL_EXPOSE:
+ onExpose(view);
+ break;
+ case PUGL_CLOSE:
+ app->quit = 1;
+ break;
+ case PUGL_LOOP_ENTER:
+ puglStartTimer(view,
+ resizeTimerId,
+ 1.0 / (double)puglGetViewHint(view, PUGL_REFRESH_RATE));
+ break;
+ case PUGL_LOOP_LEAVE:
+ puglStopTimer(view, resizeTimerId);
+ break;
+ case PUGL_KEY_PRESS:
+ if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
+ app->quit = 1;
+ }
+ break;
+ case PUGL_TIMER:
+ if (event->timer.id == resizeTimerId) {
+ puglPostRedisplay(view);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
}
static Rect*
makeRects(const size_t numRects)
{
- Rect* rects = (Rect*)calloc(numRects, sizeof(Rect));
- for (size_t i = 0; i < numRects; ++i) {
- rects[i] = makeRect(i, (float)defaultWidth);
- }
+ Rect* rects = (Rect*)calloc(numRects, sizeof(Rect));
+ for (size_t i = 0; i < numRects; ++i) {
+ rects[i] = makeRect(i, (float)defaultWidth);
+ }
- return rects;
+ return rects;
}
static char*
loadShader(const char* const programPath, const char* const name)
{
- char* const path = resourcePath(programPath, name);
- fprintf(stderr, "Loading shader %s\n", path);
+ char* const path = resourcePath(programPath, name);
+ fprintf(stderr, "Loading shader %s\n", path);
- FILE* const file = fopen(path, "r");
- if (!file) {
- logError("Failed to open '%s'\n", path);
- return NULL;
- }
+ FILE* const file = fopen(path, "r");
+ if (!file) {
+ logError("Failed to open '%s'\n", path);
+ return NULL;
+ }
- free(path);
- fseek(file, 0, SEEK_END);
- const size_t fileSize = (size_t)ftell(file);
+ free(path);
+ fseek(file, 0, SEEK_END);
+ const size_t fileSize = (size_t)ftell(file);
- fseek(file, 0, SEEK_SET);
- char* source = (char*)calloc(1, fileSize + 1u);
+ fseek(file, 0, SEEK_SET);
+ char* source = (char*)calloc(1, fileSize + 1u);
- fread(source, 1, fileSize, file);
- fclose(file);
+ fread(source, 1, fileSize, file);
+ fclose(file);
- return source;
+ return source;
}
static int
parseOptions(PuglTestApp* app, int argc, char** argv)
{
- char* endptr = NULL;
-
- // Parse command line options
- app->numRects = 1024;
- app->opts = puglParseTestOptions(&argc, &argv);
- if (app->opts.help) {
- return 1;
- }
-
- // Parse number of rectangles argument, if given
- if (argc >= 1) {
- app->numRects = (size_t)strtol(argv[0], &endptr, 10);
- if (endptr != argv[0] + strlen(argv[0])) {
- logError("Invalid number of rectangles: %s\n", argv[0]);
- return 1;
- }
- }
-
- // Parse OpenGL major version argument, if given
- if (argc >= 2) {
- app->glMajorVersion = (int)strtol(argv[1], &endptr, 10);
- if (endptr != argv[1] + strlen(argv[1])) {
- logError("Invalid GL major version: %s\n", argv[1]);
- return 1;
- } else if (app->glMajorVersion == 4) {
- app->glMinorVersion = 2;
- } else if (app->glMajorVersion != 3) {
- logError("Unsupported GL major version %d\n", app->glMajorVersion);
- return 1;
- }
- }
-
- return 0;
+ char* endptr = NULL;
+
+ // Parse command line options
+ app->numRects = 1024;
+ app->opts = puglParseTestOptions(&argc, &argv);
+ if (app->opts.help) {
+ return 1;
+ }
+
+ // Parse number of rectangles argument, if given
+ if (argc >= 1) {
+ app->numRects = (size_t)strtol(argv[0], &endptr, 10);
+ if (endptr != argv[0] + strlen(argv[0])) {
+ logError("Invalid number of rectangles: %s\n", argv[0]);
+ return 1;
+ }
+ }
+
+ // Parse OpenGL major version argument, if given
+ if (argc >= 2) {
+ app->glMajorVersion = (int)strtol(argv[1], &endptr, 10);
+ if (endptr != argv[1] + strlen(argv[1])) {
+ logError("Invalid GL major version: %s\n", argv[1]);
+ return 1;
+ } else if (app->glMajorVersion == 4) {
+ app->glMinorVersion = 2;
+ } else if (app->glMajorVersion != 3) {
+ logError("Unsupported GL major version %d\n", app->glMajorVersion);
+ return 1;
+ }
+ }
+
+ return 0;
}
static void
setupPugl(PuglTestApp* app)
{
- // Create world, view, and rect data
- app->world = puglNewWorld(PUGL_PROGRAM, 0);
- app->view = puglNewView(app->world);
- app->rects = makeRects(app->numRects);
-
- // Set up world and view
- puglSetClassName(app->world, "PuglGL3Demo");
- puglSetWindowTitle(app->view, "Pugl OpenGL 3");
- puglSetDefaultSize(app->view, defaultWidth, defaultHeight);
- puglSetMinSize(app->view, defaultWidth / 4, defaultHeight / 4);
- puglSetAspectRatio(app->view, 1, 1, 16, 9);
- puglSetBackend(app->view, puglGlBackend());
- puglSetViewHint(app->view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE);
- puglSetViewHint(app->view, PUGL_USE_DEBUG_CONTEXT, app->opts.errorChecking);
- puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MAJOR, app->glMajorVersion);
- puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MINOR, app->glMinorVersion);
- puglSetViewHint(app->view, PUGL_RESIZABLE, app->opts.resizable);
- puglSetViewHint(app->view, PUGL_SAMPLES, app->opts.samples);
- puglSetViewHint(app->view, PUGL_DOUBLE_BUFFER, app->opts.doubleBuffer);
- puglSetViewHint(app->view, PUGL_SWAP_INTERVAL, app->opts.sync);
- puglSetViewHint(app->view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);
- puglSetHandle(app->view, app);
- puglSetEventFunc(app->view, onEvent);
+ // Create world, view, and rect data
+ app->world = puglNewWorld(PUGL_PROGRAM, 0);
+ app->view = puglNewView(app->world);
+ app->rects = makeRects(app->numRects);
+
+ // Set up world and view
+ puglSetClassName(app->world, "PuglGL3Demo");
+ puglSetWindowTitle(app->view, "Pugl OpenGL 3");
+ puglSetDefaultSize(app->view, defaultWidth, defaultHeight);
+ puglSetMinSize(app->view, defaultWidth / 4, defaultHeight / 4);
+ puglSetAspectRatio(app->view, 1, 1, 16, 9);
+ puglSetBackend(app->view, puglGlBackend());
+ puglSetViewHint(app->view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE);
+ puglSetViewHint(app->view, PUGL_USE_DEBUG_CONTEXT, app->opts.errorChecking);
+ puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MAJOR, app->glMajorVersion);
+ puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MINOR, app->glMinorVersion);
+ puglSetViewHint(app->view, PUGL_RESIZABLE, app->opts.resizable);
+ puglSetViewHint(app->view, PUGL_SAMPLES, app->opts.samples);
+ puglSetViewHint(app->view, PUGL_DOUBLE_BUFFER, app->opts.doubleBuffer);
+ puglSetViewHint(app->view, PUGL_SWAP_INTERVAL, app->opts.sync);
+ puglSetViewHint(app->view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);
+ puglSetHandle(app->view, app);
+ puglSetEventFunc(app->view, onEvent);
}
static PuglStatus
setupGl(PuglTestApp* app)
{
- // Load GL functions via GLAD
- if (!gladLoadGLLoader((GLADloadproc)&puglGetProcAddress)) {
- logError("Failed to load GL\n");
- return PUGL_FAILURE;
- }
-
- const char* const headerFile = (app->glMajorVersion == 3
- ? "shaders/header_330.glsl"
- : "shaders/header_420.glsl");
-
- // Load shader sources
- char* const headerSource = loadShader(app->programPath, headerFile);
-
- char* const vertexSource = loadShader(app->programPath,
- "shaders/rect.vert");
-
- char* const fragmentSource = loadShader(app->programPath,
- "shaders/rect.frag");
-
- if (!vertexSource || !fragmentSource) {
- logError("Failed to load shader sources\n");
- return PUGL_FAILURE;
- }
-
- // Compile rectangle shaders and program
- app->drawRect = compileProgram(headerSource, vertexSource, fragmentSource);
- free(fragmentSource);
- free(vertexSource);
- free(headerSource);
- if (!app->drawRect.program) {
- return PUGL_FAILURE;
- }
-
- // Get location of rectangle shader uniform block
- const GLuint globalsIndex = glGetUniformBlockIndex(app->drawRect.program,
- "UniformBufferObject");
-
- // Generate/bind a uniform buffer for setting rectangle properties
- GLuint uboHandle = 0;
- glGenBuffers(1, &uboHandle);
- glBindBuffer(GL_UNIFORM_BUFFER, uboHandle);
- glBindBufferBase(GL_UNIFORM_BUFFER, globalsIndex, uboHandle);
-
- // Generate/bind a VAO to track state
- glGenVertexArrays(1, &app->vao);
- glBindVertexArray(app->vao);
-
- // Generate/bind a VBO to store vertex position data
- glGenBuffers(1, &app->vbo);
- glBindBuffer(GL_ARRAY_BUFFER, app->vbo);
- glBufferData(GL_ARRAY_BUFFER,
- sizeof(rectVertices),
- rectVertices,
- GL_STATIC_DRAW);
-
- // Attribute 0 is position, 2 floats from the VBO
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL);
-
- // Generate/bind a VBO to store instance attribute data
- glGenBuffers(1, &app->instanceVbo);
- glBindBuffer(GL_ARRAY_BUFFER, app->instanceVbo);
- glBufferData(GL_ARRAY_BUFFER,
- (GLsizeiptr)(app->numRects * sizeof(Rect)),
- app->rects,
- GL_STREAM_DRAW);
-
- // Attribute 1 is Rect::position
- glEnableVertexAttribArray(1);
- glVertexAttribDivisor(1, 4);
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), NULL);
-
- // Attribute 2 is Rect::size
- glEnableVertexAttribArray(2);
- glVertexAttribDivisor(2, 4);
- glVertexAttribPointer(2,
- 2,
- GL_FLOAT,
- GL_FALSE,
- sizeof(Rect),
- (const void*)offsetof(Rect, size));
-
- // Attribute 3 is Rect::fillColor
- glEnableVertexAttribArray(3);
- glVertexAttribDivisor(3, 4);
- glVertexAttribPointer(3,
- 4,
- GL_FLOAT,
- GL_FALSE,
- sizeof(Rect),
- (const void*)offsetof(Rect, fillColor));
-
- // Set up the IBO to index into the VBO
- glGenBuffers(1, &app->ibo);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, app->ibo);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,
- sizeof(rectIndices),
- rectIndices,
- GL_STATIC_DRAW);
-
- return PUGL_SUCCESS;
+ // Load GL functions via GLAD
+ if (!gladLoadGLLoader((GLADloadproc)&puglGetProcAddress)) {
+ logError("Failed to load GL\n");
+ return PUGL_FAILURE;
+ }
+
+ const char* const headerFile =
+ (app->glMajorVersion == 3 ? "shaders/header_330.glsl"
+ : "shaders/header_420.glsl");
+
+ // Load shader sources
+ char* const headerSource = loadShader(app->programPath, headerFile);
+
+ char* const vertexSource = loadShader(app->programPath, "shaders/rect.vert");
+
+ char* const fragmentSource =
+ loadShader(app->programPath, "shaders/rect.frag");
+
+ if (!vertexSource || !fragmentSource) {
+ logError("Failed to load shader sources\n");
+ return PUGL_FAILURE;
+ }
+
+ // Compile rectangle shaders and program
+ app->drawRect = compileProgram(headerSource, vertexSource, fragmentSource);
+ free(fragmentSource);
+ free(vertexSource);
+ free(headerSource);
+ if (!app->drawRect.program) {
+ return PUGL_FAILURE;
+ }
+
+ // Get location of rectangle shader uniform block
+ const GLuint globalsIndex =
+ glGetUniformBlockIndex(app->drawRect.program, "UniformBufferObject");
+
+ // Generate/bind a uniform buffer for setting rectangle properties
+ GLuint uboHandle = 0;
+ glGenBuffers(1, &uboHandle);
+ glBindBuffer(GL_UNIFORM_BUFFER, uboHandle);
+ glBindBufferBase(GL_UNIFORM_BUFFER, globalsIndex, uboHandle);
+
+ // Generate/bind a VAO to track state
+ glGenVertexArrays(1, &app->vao);
+ glBindVertexArray(app->vao);
+
+ // Generate/bind a VBO to store vertex position data
+ glGenBuffers(1, &app->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, app->vbo);
+ glBufferData(
+ GL_ARRAY_BUFFER, sizeof(rectVertices), rectVertices, GL_STATIC_DRAW);
+
+ // Attribute 0 is position, 2 floats from the VBO
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL);
+
+ // Generate/bind a VBO to store instance attribute data
+ glGenBuffers(1, &app->instanceVbo);
+ glBindBuffer(GL_ARRAY_BUFFER, app->instanceVbo);
+ glBufferData(GL_ARRAY_BUFFER,
+ (GLsizeiptr)(app->numRects * sizeof(Rect)),
+ app->rects,
+ GL_STREAM_DRAW);
+
+ // Attribute 1 is Rect::position
+ glEnableVertexAttribArray(1);
+ glVertexAttribDivisor(1, 4);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), NULL);
+
+ // Attribute 2 is Rect::size
+ glEnableVertexAttribArray(2);
+ glVertexAttribDivisor(2, 4);
+ glVertexAttribPointer(
+ 2, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (const void*)offsetof(Rect, size));
+
+ // Attribute 3 is Rect::fillColor
+ glEnableVertexAttribArray(3);
+ glVertexAttribDivisor(3, 4);
+ glVertexAttribPointer(3,
+ 4,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof(Rect),
+ (const void*)offsetof(Rect, fillColor));
+
+ // Set up the IBO to index into the VBO
+ glGenBuffers(1, &app->ibo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, app->ibo);
+ glBufferData(
+ GL_ELEMENT_ARRAY_BUFFER, sizeof(rectIndices), rectIndices, GL_STATIC_DRAW);
+
+ return PUGL_SUCCESS;
}
static void
teardownGl(PuglTestApp* app)
{
- glDeleteBuffers(1, &app->ibo);
- glDeleteBuffers(1, &app->vbo);
- glDeleteBuffers(1, &app->instanceVbo);
- glDeleteVertexArrays(1, &app->vao);
- deleteProgram(app->drawRect);
+ glDeleteBuffers(1, &app->ibo);
+ glDeleteBuffers(1, &app->vbo);
+ glDeleteBuffers(1, &app->instanceVbo);
+ glDeleteVertexArrays(1, &app->vao);
+ deleteProgram(app->drawRect);
}
int
main(int argc, char** argv)
{
- PuglTestApp app = {0};
-
- app.programPath = argv[0];
- app.glMajorVersion = 3;
- app.glMinorVersion = 3;
-
- // Parse command line options
- if (parseOptions(&app, argc, argv)) {
- puglPrintTestUsage("pugl_shader_demo", "[NUM_RECTS] [GL_MAJOR]");
- return 1;
- }
-
- // Create and configure world and view
- setupPugl(&app);
-
- // Create window (which will send a PUGL_CREATE event)
- const PuglStatus st = puglRealize(app.view);
- if (st) {
- return logError("Failed to create window (%s)\n", puglStrerror(st));
- }
-
- // Show window
- printViewHints(app.view);
- puglShow(app.view);
-
- // Calculate ideal frame duration to drive the main loop at a good rate
- const int refreshRate = puglGetViewHint(app.view, PUGL_REFRESH_RATE);
- const double frameDuration = 1.0 / (double)refreshRate;
-
- // Grind away, drawing continuously
- const double startTime = puglGetTime(app.world);
- PuglFpsPrinter fpsPrinter = {startTime};
- while (!app.quit) {
- /* To minimize input latency and get smooth performance during window
- resizing, we want to poll for events as long as possible before
- starting to draw the next frame. This ensures that as many events
- are consumed as possible before starting to draw, or, equivalently,
- that the next rendered frame represents the latest events possible.
- This is particularly important for mouse input and "live" window
- resizing, where many events tend to pile up within a frame.
-
- To do this, we keep track of the time when the last frame was
- finished drawing, and how long it took to expose (and assume this is
- relatively stable). Then, we can calculate how much time there is
- from now until the time when we should start drawing to not miss the
- deadline, and use that as the timeout for puglUpdate().
- */
-
- const double now = puglGetTime(app.world);
- const double nextFrameEndTime = app.lastFrameEndTime + frameDuration;
- const double nextExposeTime = nextFrameEndTime - app.lastDrawDuration;
- const double timeUntilNext = nextExposeTime - now;
- const double timeout = app.opts.sync ? timeUntilNext : 0.0;
-
- puglUpdate(app.world, fmax(0.0, timeout));
- puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
- }
-
- // Destroy window (which will send a PUGL_DESTROY event)
- puglFreeView(app.view);
-
- // Free everything else
- puglFreeWorld(app.world);
- free(app.rects);
-
- return 0;
+ PuglTestApp app = {0};
+
+ app.programPath = argv[0];
+ app.glMajorVersion = 3;
+ app.glMinorVersion = 3;
+
+ // Parse command line options
+ if (parseOptions(&app, argc, argv)) {
+ puglPrintTestUsage("pugl_shader_demo", "[NUM_RECTS] [GL_MAJOR]");
+ return 1;
+ }
+
+ // Create and configure world and view
+ setupPugl(&app);
+
+ // Create window (which will send a PUGL_CREATE event)
+ const PuglStatus st = puglRealize(app.view);
+ if (st) {
+ return logError("Failed to create window (%s)\n", puglStrerror(st));
+ }
+
+ // Show window
+ printViewHints(app.view);
+ puglShow(app.view);
+
+ // Calculate ideal frame duration to drive the main loop at a good rate
+ const int refreshRate = puglGetViewHint(app.view, PUGL_REFRESH_RATE);
+ const double frameDuration = 1.0 / (double)refreshRate;
+
+ // Grind away, drawing continuously
+ const double startTime = puglGetTime(app.world);
+ PuglFpsPrinter fpsPrinter = {startTime};
+ while (!app.quit) {
+ /* To minimize input latency and get smooth performance during window
+ resizing, we want to poll for events as long as possible before
+ starting to draw the next frame. This ensures that as many events
+ are consumed as possible before starting to draw, or, equivalently,
+ that the next rendered frame represents the latest events possible.
+ This is particularly important for mouse input and "live" window
+ resizing, where many events tend to pile up within a frame.
+
+ To do this, we keep track of the time when the last frame was
+ finished drawing, and how long it took to expose (and assume this is
+ relatively stable). Then, we can calculate how much time there is
+ from now until the time when we should start drawing to not miss the
+ deadline, and use that as the timeout for puglUpdate().
+ */
+
+ const double now = puglGetTime(app.world);
+ const double nextFrameEndTime = app.lastFrameEndTime + frameDuration;
+ const double nextExposeTime = nextFrameEndTime - app.lastDrawDuration;
+ const double timeUntilNext = nextExposeTime - now;
+ const double timeout = app.opts.sync ? timeUntilNext : 0.0;
+
+ puglUpdate(app.world, fmax(0.0, timeout));
+ puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
+ }
+
+ // Destroy window (which will send a PUGL_DESTROY event)
+ puglFreeView(app.view);
+
+ // Free everything else
+ puglFreeWorld(app.world);
+ free(app.rects);
+
+ return 0;
}