diff options
author | David Robillard <d@drobilla.net> | 2020-04-04 13:36:47 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-04-04 13:36:47 +0200 |
commit | 20fd80c8f20d0d6bda660bd9a273e0c4a78cb9ac (patch) | |
tree | f144e9498a0337bef0d20c9004b024e26a9d2b32 /examples/pugl_gl3_demo.c | |
parent | 11800b6179458eb962cd1862e4053efd7f28c2f4 (diff) | |
download | pugl-20fd80c8f20d0d6bda660bd9a273e0c4a78cb9ac.tar.gz pugl-20fd80c8f20d0d6bda660bd9a273e0c4a78cb9ac.tar.bz2 pugl-20fd80c8f20d0d6bda660bd9a273e0c4a78cb9ac.zip |
Shader Demo: Support both GL 3 and 4
Diffstat (limited to 'examples/pugl_gl3_demo.c')
-rw-r--r-- | examples/pugl_gl3_demo.c | 403 |
1 files changed, 0 insertions, 403 deletions
diff --git a/examples/pugl_gl3_demo.c b/examples/pugl_gl3_demo.c deleted file mode 100644 index 7b2a49f..0000000 --- a/examples/pugl_gl3_demo.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - Copyright 2012-2020 David Robillard <http://drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl_gl3_demo.c An example of drawing with OpenGL 3. - - This is an example of using OpenGL for pixel-perfect 2D drawing. It uses - pixel coordinates for positions and sizes so that things work roughly like a - typical 2D graphics API. - - The program draws a bunch of rectangles with borders, using instancing. - Each rectangle has origin, size, and fill color attributes, which are shared - for all four vertices. On each frame, a single buffer with all the - rectangle data is sent to the GPU, and everything is drawn with a single - draw call. - - This is not particularly realistic or optimal, but serves as a decent rough - benchmark for how much simple geometry you can draw. The number of - rectangles can be given on the command line. For reference, it begins to - struggle to maintain 60 FPS on my machine (1950x + Vega64) with more than - about 100000 rectangles. -*/ - -#include "demo_utils.h" -#include "rects.h" -#include "shader_utils.h" -#include "test/test_utils.h" - -#include "glad/glad.h" - -#include "pugl/gl.h" -#include "pugl/pugl.h" -#include "pugl/pugl_gl.h" - -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -static const int defaultWidth = 512; -static const int defaultHeight = 512; - -typedef struct -{ - mat4 projection; -} RectUniforms; - -typedef struct -{ - PuglTestOptions opts; - PuglWorld* world; - PuglView* view; - size_t numRects; - Rect* rects; - Program drawRect; - GLuint vao; - GLuint vbo; - GLuint instanceVbo; - GLuint ibo; - unsigned framesDrawn; - int quit; -} PuglTestApp; - -static PuglStatus -setupGl(PuglTestApp* app); - -static void -teardownGl(PuglTestApp* app); - -static void -onConfigure(PuglView* view, double width, double height) -{ - (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); -} - -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; -} - -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_KEY_PRESS: - if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) { - app->quit = 1; - } - 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); - } - - return rects; -} - -static char* -loadShader(const char* const path) -{ - FILE* const file = fopen(path, "r"); - if (!file) { - logError("Failed to open '%s'\n", path); - return NULL; - } - - 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); - - fread(source, 1, fileSize, file); - fclose(file); - - return source; -} - -static int -parseOptions(PuglTestApp* app, int argc, char** argv) -{ - // 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) { - char* endptr = NULL; - - app->numRects = (size_t)strtol(argv[0], &endptr, 10); - if (endptr != argv[0] + strlen(argv[0])) { - return 1; - } - } - - return 0; -} - -static void -setupPugl(PuglTestApp* app, const PuglRect frame) -{ - // 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"); - puglSetFrame(app->view, frame); - 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, 3); - puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MINOR, 3); - 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; - } - - // Load shader sources - char* const headerSource = loadShader("shaders/header_330.glsl"); - char* const vertexSource = loadShader("shaders/rect.vert"); - char* const fragmentSource = loadShader("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; - 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); -} - -int -main(int argc, char** argv) -{ - PuglTestApp app; - memset(&app, 0, sizeof(app)); - - const PuglRect frame = {0, 0, defaultWidth, defaultHeight}; - - // Parse command line options - if (parseOptions(&app, argc, argv)) { - puglPrintTestUsage("pugl_gl3_demo", "[NUM_RECTS]"); - return 1; - } - - // Create and configure world and view - setupPugl(&app, frame); - - // 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 - puglShowWindow(app.view); - - // Grind away, drawing continuously - PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)}; - while (!app.quit) { - puglUpdate(app.world, 0.0); - 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; -} |