commit 3ebae2846b321dd230a486e74f074feac71dccbf
parent f26cacbbdbb5503d926d48470c2ae49538bbc40d
Author: krasjet
Date: 2020-12-28 16:10Z
glfw: some more perf improvements
Diffstat:
9 files changed, 182 insertions(+), 36 deletions(-)
diff --git a/glfw/Makefile b/glfw/Makefile
@@ -11,6 +11,7 @@ BINS = \
simple \
fb \
fb_compat \
+ fb_compat_min \
fb_pbo \
fb_frag \
triangle \
@@ -31,6 +32,7 @@ LDLIBS.fb_frag = -ldl
LIBS.fb_frag = $(LIBGLAD)
LDLIBS.fb_compat = -lm
+LDLIBS.fb_compat_min = -lm
triangle: $(LIBGLAD)
LDLIBS.triangle = -ldl
diff --git a/glfw/README b/glfw/README
@@ -17,25 +17,29 @@ fb.c:
but note that these keywords are deprecated, so they are not forward
compatible.
-fb_compat.c:
+fb_compat.c, fb_compat_min.c:
render software generated framebuffer (backward compat)
- fb_compat uses the deprecated fixed pipeline OpenGL to render the framebuffer
- to screen as a texture on a screen-filling quad. It is usually not the
- recommended approach nowadays. However, compared to core profile OpenGL, it
- has some advantages:
+ fb_compat and fb_compat_min uses the deprecated fixed pipeline OpenGL to
+ render the framebuffer to screen as a texture on a screen-filling quad. It is
+ usually not the recommended approach nowadays. However, compared to core
+ profile OpenGL, it has some advantages:
1. it's simple (100+ lines less than fb.c), and fast enough for our purposes
(only render a quad)
2. it does not require a OpenGL loading library (GLAD, GLEW, etc.) to use
core-profile features, so you don't need anything other than GLFW to link
- the program. It makes the binary much smaller.
+ the program. It makes the binary much smaller (only 15KB after strip).
3. it's backward compatible with old hardware (OpenGL 1.1+, you can't get any
lower than this)
Therefore, I decide to include it as a reference, though do not use it if you
want forward compatibility.
+ fb_compat_min differs from fb_compat in that it uses no buffering and needs
+ to upload vertices one by one. It's simple, and makes no performance
+ differences for our purposes. Use the one you appreciate the most.
+
fb_pbo.c:
render software generated framebuffer (with PBO)
@@ -92,7 +96,7 @@ to generate `config.mk`, then use
to build the binary.
Notes
-=====
+-----
fb, fb_compat, fb_pbo:
If you want to use other color formats, e.g. RGB with 8-bit for each channel,
@@ -106,6 +110,22 @@ fb, fb_compat, fb_pbo:
I would recommend adding an alpha channel even if you don't need it.
Memory misalignment can be a hit to performance.
+Summary of fb
+-------------
+
+Performance
+
+ fb_frag >>> fb_pbo >> fb > fb_compat > fb_compat_min
+
+Complexity
+
+ fb_pbo > fb > fb_frag >> fb_compat > fb_compat_min
+
+Remember that fb_frag only supports images of the kind I(x,y).
+
+I would prefer fb for forward compatibility, fb_compat for backward
+compatibility, and fb_frag only for fragment shader experiments.
+
See also
--------
diff --git a/glfw/fb.c b/glfw/fb.c
@@ -98,10 +98,7 @@ glfw_init(void)
-1.f, -1.f, 0.f, 0.f,
1.f, -1.f, 1.f, 0.f,
-1.f, 1.f, 0.f, 1.f,
- /* ^ bottom-left v upper-right */
- 1.f, -1.f, 1.f, 0.f,
1.f, 1.f, 1.f, 1.f,
- -1.f, 1.f, 0.f, 1.f
};
static const GLchar * vs_src =
"#version 150\n"
@@ -249,7 +246,7 @@ redraw(struct Pixel *fb) {
GL_RGBA, GL_UNSIGNED_BYTE, fb);
/* then draw the screen-filling quad */
- glDrawArrays(GL_TRIANGLES, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
/* present new frame */
glfwSwapBuffers(win);
diff --git a/glfw/fb_compat.c b/glfw/fb_compat.c
@@ -40,10 +40,7 @@ glfw_init(void)
-1.f, -1.f, 0.f, 0.f,
1.f, -1.f, 1.f, 0.f,
-1.f, 1.f, 0.f, 1.f,
- /* ^ bottom-left v upper-right */
- 1.f, -1.f, 1.f, 0.f,
1.f, 1.f, 1.f, 1.f,
- -1.f, 1.f, 0.f, 1.f
};
if (!glfwInit()) {
@@ -130,7 +127,7 @@ redraw(struct Pixel *fb) {
GL_RGBA, GL_UNSIGNED_BYTE, fb);
/* then draw the screen-filling quad */
- glDrawArrays(GL_TRIANGLES, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
/* present new frame */
glfwSwapBuffers(win);
diff --git a/glfw/fb_compat_min.c b/glfw/fb_compat_min.c
@@ -0,0 +1,135 @@
+/* fb_compat_min.c: render software generated framebuffer
+ * (backward compat, minimal) */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <GLFW/glfw3.h>
+
+/* pixel format defined by GL_RGBA */
+struct Pixel {
+ GLubyte r; GLubyte b; GLubyte g; GLubyte a;
+};
+
+/* global data, you might want to encapsulate them into a struct */
+enum {
+ width = 128, height = 128, scale = 2
+};
+static GLFWwindow *win = NULL;
+
+static void
+die(const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+}
+
+/* handle resize events */
+static void
+resize_cb(GLFWwindow *win, int width, int height)
+{
+ (void) win;
+ glViewport(0, 0, width, height);
+}
+
+static void
+glfw_init(void)
+{
+ if (!glfwInit()) {
+ fputs("fail to init glfw", stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ /* create main window (not resizable) */
+ glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
+ win = glfwCreateWindow(width*scale, height*scale, "pattern", NULL, NULL);
+ if (!win)
+ die("fail to create window");
+
+ /* set opengl drawing context to main window */
+ glfwMakeContextCurrent(win);
+ /* some window managers ignore the resizable hint,
+ * so we still need to handle resize events */
+ glfwSetWindowSizeCallback(win, resize_cb);
+
+ /* enable texture and set up parameters */
+ glEnable(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ /* allocate texture memory for later use */
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ width, height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+}
+
+static int
+done(void) {
+ glfwPollEvents();
+ return glfwWindowShouldClose(win);
+}
+
+static void
+glfw_finish(void)
+{
+ glfwTerminate();
+}
+
+static void
+render(struct Pixel *fb, double t)
+{
+ float cx = width / 2.0, cy = height / 2.0;
+
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ float xx = x - cx, yy = y - cy;
+ float shade = 0.5 + 0.5 * sin(xx*xx + yy*yy + 6.2832*t);
+ fb[y*width + x].r = 255 * shade;
+ fb[y*width + x].g = 255 * shade;
+ fb[y*width + x].b = 255 * shade;
+ fb[y*width + x].a = 255;
+ }
+ }
+}
+
+/* render and redraw frame */
+static void
+redraw(struct Pixel *fb) {
+ /* clear previous frame */
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ /* generate new frame to framebuffer */
+ render(fb, glfwGetTime());
+
+ /* update texture (we use subimage here to avoid
+ * allocating a new texture memory) */
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, 0, width, height,
+ GL_RGBA, GL_UNSIGNED_BYTE, fb);
+
+ /* then draw the screen-filling quad */
+ glBegin(GL_TRIANGLE_STRIP);
+ glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0, -1.0);
+ glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0, -1.0);
+ glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0, 1.0);
+ glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0, 1.0);
+ glEnd();
+
+ /* present new frame */
+ glfwSwapBuffers(win);
+}
+
+int
+main(void)
+{
+ struct Pixel fb[width*height];
+
+ glfw_init();
+ while (!done()) {
+ redraw(fb);
+ }
+ glfw_finish();
+ return 0;
+}
diff --git a/glfw/fb_frag.c b/glfw/fb_frag.c
@@ -19,6 +19,7 @@ static GLuint vao, vbo;
#define GLSL(ver, ...) "#version " #ver "\n" #__VA_ARGS__
static const GLchar * vs_src = GLSL(150,
in vec2 pos;
+
void main() {
gl_Position = vec4(pos, 0.f, 1.f);
}
@@ -117,10 +118,7 @@ glfw_init(void)
-1.f, -1.f,
1.f, -1.f,
-1.f, 1.f,
- /* ^left vright */
- 1.f, -1.f,
1.f, 1.f,
- -1.f, 1.f,
};
if (!glfwInit()) {
@@ -206,7 +204,7 @@ redraw(void) {
/* update uniform */
glUniform1f(glGetUniformLocation(program, "t"), glfwGetTime());
/* then draw the screen-filling quad */
- glDrawArrays(GL_TRIANGLES, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
/* present new frame */
glfwSwapBuffers(win);
diff --git a/glfw/fb_pbo.c b/glfw/fb_pbo.c
@@ -98,10 +98,7 @@ glfw_init(void)
-1.f, -1.f, 0.f, 0.f,
1.f, -1.f, 1.f, 0.f,
-1.f, 1.f, 0.f, 1.f,
- /* ^ bottom-left v upper-right */
- 1.f, -1.f, 1.f, 0.f,
1.f, 1.f, 1.f, 1.f,
- -1.f, 1.f, 0.f, 1.f
};
static const GLchar * vs_src =
"#version 150\n"
@@ -278,7 +275,7 @@ redraw(void) {
}
}
/* draw the screen-filling quad */
- glDrawArrays(GL_TRIANGLES, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
/* present new frame */
glfwSwapBuffers(win);
diff --git a/glfw/simple.c b/glfw/simple.c
@@ -16,7 +16,7 @@ int
main(void)
{
const int width = 256, height = 256;
- GLFWwindow *window = NULL;
+ GLFWwindow *win = NULL;
/* init glfw */
if (!glfwInit()) {
@@ -26,17 +26,17 @@ main(void)
/* create main window (not resizable) */
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
- window = glfwCreateWindow(width, height, "simple", NULL, NULL);
- if (!window)
+ win = glfwCreateWindow(width, height, "simple", NULL, NULL);
+ if (!win)
die("fail to create window");
/* set opengl drawing context to main window */
- glfwMakeContextCurrent(window);
+ glfwMakeContextCurrent(win);
- while (!glfwWindowShouldClose(window)) {
+ while (!glfwWindowShouldClose(win)) {
glClear(GL_COLOR_BUFFER_BIT);
- glfwSwapBuffers(window);
+ glfwSwapBuffers(win);
glfwPollEvents();
}
diff --git a/glfw/triangle_compat.c b/glfw/triangle_compat.c
@@ -24,7 +24,7 @@ int
main(void)
{
const int width = 256, height = 256;
- GLFWwindow *window = NULL;
+ GLFWwindow *win = NULL;
if (!glfwInit()) {
fputs("fail to init glfw", stderr);
@@ -33,16 +33,16 @@ main(void)
/* create main window (not resizable) */
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
- window = glfwCreateWindow(width, height, "trig", NULL, NULL);
- if (!window)
+ win = glfwCreateWindow(width, height, "trig", NULL, NULL);
+ if (!win)
die("fail to create window");
/* set opengl drawing context to main window */
- glfwMakeContextCurrent(window);
+ glfwMakeContextCurrent(win);
- glfwSetWindowSizeCallback(window, resize_cb);
+ glfwSetWindowSizeCallback(win, resize_cb);
- while (!glfwWindowShouldClose(window)) {
+ while (!glfwWindowShouldClose(win)) {
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -53,7 +53,7 @@ main(void)
glVertex2f( 0.0f, 0.5f);
glEnd();
- glfwSwapBuffers(window);
+ glfwSwapBuffers(win);
glfwPollEvents();
}