Browse Source

Merge pull request #11 from tsoding/opengl-renderer

Quick and rough implementation of the OpenGL renderer
Alexey Kutepov 4 years ago
parent
commit
642c19d3bf
17 changed files with 616 additions and 48 deletions
  1. 32 32
      .github/workflows/ci.yml
  2. 0 6
      Makefile
  3. 13 3
      README.md
  4. 15 0
      build.sh
  5. 8 4
      build_msvc.bat
  6. 28 0
      setup_dependencies.bat
  7. 36 0
      shaders/font.frag
  8. 32 0
      shaders/font.vert
  9. 36 0
      src/file.c
  10. 6 0
      src/file.h
  11. 1 1
      src/font.h
  12. 72 0
      src/gl_extra.c
  13. 13 0
      src/gl_extra.h
  14. 39 0
      src/la.c
  15. 12 0
      src/la.h
  16. 272 1
      src/main.c
  17. 1 1
      src/sdl_extra.h

+ 32 - 32
.github/workflows/ci.yml

@@ -12,10 +12,10 @@ jobs:
       - name: install dependencies
         run: |
           sudo apt-get update
-          sudo apt-get install -qq libsdl2-dev
+          sudo apt-get install -qq libsdl2-dev libglew-dev
       - name: build te
         run: |
-          make
+          ./build.sh
         env:
           CC: gcc
   build-linux-clang:
@@ -25,10 +25,10 @@ jobs:
       - name: install dependencies
         run: |
           sudo apt-get update
-          sudo apt-get install -qq libsdl2-dev
+          sudo apt-get install -qq libsdl2-dev libglew-dev
       - name: build te
         run: |
-          make
+          ./build.sh
         env:
           CC: clang
   build-macos:
@@ -36,35 +36,35 @@ jobs:
     steps:
       - uses: actions/checkout@v1
       - name: install dependencies
-        run: brew install sdl2 pkg-config
+        run: brew install sdl2 pkg-config glew
       - name: build te
         run: |
-          make
+          ./build.sh
         env:
           CC: clang
-  build-windows-msvc:
-    runs-on: windows-2019
-    steps:
-      - uses: actions/checkout@v1
-        # this runs vcvarsall for us, so we get the MSVC toolchain in PATH.
-      - uses: seanmiddleditch/gha-setup-vsdevenv@master
-      - name: download sdl2
-        run: |
-          curl -fsSL -o SDL2-devel-2.0.14-VC.zip https://www.libsdl.org/release/SDL2-devel-2.0.14-VC.zip
-          tar -xf SDL2-devel-2.0.14-VC.zip
-          mv SDL2-2.0.14 SDL2
-      - name: build te
-        shell: cmd
-        run: |
-          ./build_msvc.bat
-      - name: Prepare WindowsBinaries artifacts
-        shell: cmd
-        run: |
-          mkdir winbin
-          copy /B *.exe winbin
-          copy /B *.png winbin
-      - name: Upload WindowsBinaries artifacts
-        uses: actions/upload-artifact@v2
-        with:
-          name: WindowsBinaries
-          path: ./winbin/
+  # TODO(#12): Windows MSVC build is broken
+  # ---
+  # build-windows-msvc:
+  #   runs-on: windows-2019
+  #   steps:
+  #     - uses: actions/checkout@v1
+  #       # this runs vcvarsall for us, so we get the MSVC toolchain in PATH.
+  #     - uses: seanmiddleditch/gha-setup-vsdevenv@master
+  #     - name: Install dependencies
+  #       run: |
+  #         ./setup_dependencies.bat
+  #     - name: build te
+  #       shell: cmd
+  #       run: |
+  #         ./build_msvc.bat
+  #     - name: Prepare WindowsBinaries artifacts
+  #       shell: cmd
+  #       run: |
+  #         mkdir winbin
+  #         copy /B *.exe winbin
+  #         copy /B *.png winbin
+  #     - name: Upload WindowsBinaries artifacts
+  #       uses: actions/upload-artifact@v2
+  #       with:
+  #         name: WindowsBinaries
+  #         path: ./winbin/

+ 0 - 6
Makefile

@@ -1,6 +0,0 @@
-CFLAGS=-Wall -Wextra -std=c11 -pedantic -ggdb `pkg-config --cflags sdl2`
-LIBS=`pkg-config --libs sdl2` -lm
-SRCS=src/main.c src/la.c src/editor.c src/font.c src/sdl_extra.c
-
-te: $(SRCS)
-	$(CC) $(CFLAGS) -o te $(SRCS) $(LIBS)

+ 13 - 3
README.md

@@ -1,8 +1,18 @@
 # Text Editor
 
-# Quick Start
+# Quick Start 
+
+## POSIX
+
+```console
+$ ./build.sh
+$ ./te src\main.c
+```
+
+## Windows MSVC
 
 ```console
-$ make
-$ ./te main.c
+> .\setup_dependencies.bat
+> .\build_msvc.bat
+> .\te src\main.c
 ```

+ 15 - 0
build.sh

@@ -0,0 +1,15 @@
+#!/bin/sh
+
+set -xe
+
+CC="${CXX:-cc}"
+PKGS="sdl2 glew"
+CFLAGS="-Wall -Wextra -std=c11 -pedantic -ggdb"
+LIBS=-lm
+SRC="src/main.c src/la.c src/editor.c src/font.c src/sdl_extra.c src/file.c src/gl_extra.c"
+
+if [ `uname` = "Darwin" ]; then
+    CFLAGS+=" -framework OpenGL"
+fi
+
+$CC $CFLAGS `pkg-config --cflags $PKGS` -o te $SRC $LIBS `pkg-config --libs $PKGS`

+ 8 - 4
build_msvc.bat

@@ -1,8 +1,12 @@
 @echo off
 rem launch this from msvc-enabled console
 
-set CFLAGS=/W4 /WX /std:c11 /wd4996 /FC /TC /Zi /nologo
-set INCLUDES=/I SDL2\include
-set LIBS=SDL2\lib\x64\SDL2.lib SDL2\lib\x64\SDL2main.lib Shell32.lib
+set CFLAGS=/W4 /WX /std:c11 /wd4996 /wd5105 /FC /TC /Zi /nologo
+set INCLUDES=/I dependencies\SDL2\include /I dependencies\GLFW\include /I dependencies\GLEW\include
+set LIBS=dependencies\SDL2\lib\x64\SDL2.lib ^
+         dependencies\SDL2\lib\x64\SDL2main.lib ^
+         dependencies\GLFW\lib\glfw3.lib ^
+         dependencies\GLEW\lib\glew32s.lib ^
+         opengl32.lib User32.lib Gdi32.lib Shell32.lib
 
-cl.exe %CFLAGS% %INCLUDES% /Fete src\main.c src\la.c src\editor.c src\font.c src\sdl_extra.c /link %LIBS% -SUBSYSTEM:windows
+cl.exe %CFLAGS% %INCLUDES% /Fete src\main.c src\la.c src\editor.c src\font.c src\sdl_extra.c src\file.c src\gl_extra.c /link %LIBS% -SUBSYSTEM:windows

+ 28 - 0
setup_dependencies.bat

@@ -0,0 +1,28 @@
+@echo off
+
+curl -fsSL -o SDL2-devel-2.0.12-VC.zip https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip
+tar -xf SDL2-devel-2.0.12-VC.zip
+if not exist dependencies\ mkdir dependencies\
+move SDL2-2.0.12 dependencies\SDL2
+del SDL2-devel-2.0.12-VC.zip
+if not exist dependencies\SDL2\temp\ mkdir dependencies\SDL2\temp\
+move dependencies\SDL2\include dependencies\SDL2\temp\SDL2
+move dependencies\SDL2\temp dependencies\SDL2\include
+
+curl -fsSL -o glfw-3.3.2.bin.WIN64.zip https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.bin.WIN64.zip
+tar -xf glfw-3.3.2.bin.WIN64.zip
+if not exist dependencies\GLFW\lib\ mkdir dependencies\GLFW\lib\
+move glfw-3.3.2.bin.WIN64\lib-vc2019\glfw3.lib dependencies\GLFW\lib\glfw3.lib
+if not exist dependencies\GLFW\include\GLFW mkdir dependencies\GLFW\include\GLFW
+move glfw-3.3.2.bin.WIN64\include\GLFW\glfw3.h dependencies\GLFW\include\GLFW\glfw3.h
+del glfw-3.3.2.bin.WIN64.zip
+rmdir /s /q glfw-3.3.2.bin.WIN64
+
+curl -fsSL -o glew-2.1.0-win32.zip https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0-win32.zip/download
+tar -xf glew-2.1.0-win32.zip
+if not exist dependencies\GLEW\lib\ mkdir dependencies\GLEW\lib\
+move glew-2.1.0\lib\Release\x64\glew32s.lib dependencies\GLEW\lib\glew32s.lib
+if not exist dependencies\GLEW\include\GL\ mkdir dependencies\GLEW\include\GL\
+move glew-2.1.0\include\GL\glew.h dependencies\GLEW\include\GL\glew.h
+del glew-2.1.0-win32.zip
+rmdir /s /q glew-2.1.0

+ 36 - 0
shaders/font.frag

@@ -0,0 +1,36 @@
+#version 330 core
+
+#define FONT_WIDTH 128
+#define FONT_HEIGHT 64
+#define FONT_COLS 18
+#define FONT_ROWS 7
+#define FONT_CHAR_WIDTH  (FONT_WIDTH  / FONT_COLS)
+#define FONT_CHAR_HEIGHT (FONT_HEIGHT / FONT_ROWS)
+#define FONT_CHAR_WIDTH_UV  (float(FONT_CHAR_WIDTH) / float(FONT_WIDTH))
+#define FONT_CHAR_HEIGHT_UV (float(FONT_CHAR_HEIGHT) / float(FONT_HEIGHT))
+
+#define ASCII_DISPLAY_LOW 32
+#define ASCII_DISPLAY_HIGH 126
+
+uniform sampler2D font;
+uniform float time;
+
+in vec2 uv;
+in float glyph_ch;
+in vec4 glyph_color;
+
+void main() {
+    int ch = int(glyph_ch);
+    if (!(ASCII_DISPLAY_LOW <= ch && ch <= ASCII_DISPLAY_HIGH)) {
+        ch = 63;
+    }
+
+    int index = ch - 32;
+    float x = float(index % FONT_COLS) * FONT_CHAR_WIDTH_UV;
+    float y = float(index / FONT_COLS) * FONT_CHAR_HEIGHT_UV;
+    vec2 pos = vec2(x, y + FONT_CHAR_HEIGHT_UV);
+    vec2 size = vec2(FONT_CHAR_WIDTH_UV, -FONT_CHAR_HEIGHT_UV);
+    vec2 t = pos + size * uv;
+
+    gl_FragColor = texture(font, t)* glyph_color;
+}

+ 32 - 0
shaders/font.vert

@@ -0,0 +1,32 @@
+#version 330 core
+
+#define FONT_WIDTH 128
+#define FONT_HEIGHT 64
+#define FONT_COLS 18
+#define FONT_ROWS 7
+#define FONT_CHAR_WIDTH  (FONT_WIDTH  / FONT_COLS)
+#define FONT_CHAR_HEIGHT (FONT_HEIGHT / FONT_ROWS)
+
+uniform vec2 resolution;
+
+layout(location = 0) in vec2 pos;
+layout(location = 1) in float scale;
+layout(location = 2) in float ch;
+layout(location = 3) in vec4 color;
+
+out vec2 uv;
+out float glyph_ch;
+out vec4 glyph_color;
+
+vec2 project_point(vec2 point)
+{
+    return 2.0 * point / resolution;
+}
+
+void main() {
+    uv = vec2(float(gl_VertexID & 1), float((gl_VertexID >> 1) & 1));
+    vec2 char_size = vec2(float(FONT_CHAR_WIDTH), float(FONT_CHAR_HEIGHT));
+    gl_Position = vec4(project_point(uv * char_size * scale + pos), 0.0, 1.0);
+    glyph_ch = ch;
+    glyph_color = color;
+}

+ 36 - 0
src/file.c

@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "./file.h"
+
+char *slurp_file(const char *file_path)
+{
+#define SLURP_FILE_PANIC \
+    do { \
+        fprintf(stderr, "Could not read file `%s`: %s\n", file_path, strerror(errno)); \
+        exit(1); \
+    } while (0)
+
+    FILE *f = fopen(file_path, "r");
+    if (f == NULL) SLURP_FILE_PANIC;
+    if (fseek(f, 0, SEEK_END) < 0) SLURP_FILE_PANIC;
+
+    long size = ftell(f);
+    if (size < 0) SLURP_FILE_PANIC;
+
+    char *buffer = malloc(size + 1);
+    if (buffer == NULL) SLURP_FILE_PANIC;
+
+    if (fseek(f, 0, SEEK_SET) < 0) SLURP_FILE_PANIC;
+
+    fread(buffer, 1, size, f);
+    if (ferror(f) < 0) SLURP_FILE_PANIC;
+
+    buffer[size] = '\0';
+
+    if (fclose(f) < 0) SLURP_FILE_PANIC;
+
+    return buffer;
+#undef SLURP_FILE_PANIC
+}

+ 6 - 0
src/file.h

@@ -0,0 +1,6 @@
+#ifndef FILE_H_
+#define FILE_H_
+
+char *slurp_file(const char *file_path);
+
+#endif // FILE_H_

+ 1 - 1
src/font.h

@@ -1,7 +1,7 @@
 #ifndef FONT_H_
 #define FONT_H_
 
-#include <SDL.h>
+#include <SDL2/SDL.h>
 
 #include "./la.h"
 

+ 72 - 0
src/gl_extra.c

@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "file.h"
+#include "gl_extra.h"
+
+const char *shader_type_as_cstr(GLuint shader)
+{
+    switch (shader) {
+    case GL_VERTEX_SHADER:
+        return "GL_VERTEX_SHADER";
+    case GL_FRAGMENT_SHADER:
+        return "GL_FRAGMENT_SHADER";
+    default:
+        return "(Unknown)";
+    }
+}
+
+bool compile_shader_source(const GLchar *source, GLenum shader_type, GLuint *shader)
+{
+    *shader = glCreateShader(shader_type);
+    glShaderSource(*shader, 1, &source, NULL);
+    glCompileShader(*shader);
+
+    GLint compiled = 0;
+    glGetShaderiv(*shader, GL_COMPILE_STATUS, &compiled);
+
+    if (!compiled) {
+        GLchar message[1024];
+        GLsizei message_size = 0;
+        glGetShaderInfoLog(*shader, sizeof(message), &message_size, message);
+        fprintf(stderr, "ERROR: could not compile %s\n", shader_type_as_cstr(shader_type));
+        fprintf(stderr, "%.*s\n", message_size, message);
+        return false;
+    }
+
+    return true;
+}
+
+bool compile_shader_file(const char *file_path, GLenum shader_type, GLuint *shader)
+{
+    char *source = slurp_file(file_path);
+    bool ok = compile_shader_source(source, shader_type, shader);
+    if (!ok) {
+        fprintf(stderr, "ERROR: failed to compile `%s` shader file\n", file_path);
+    }
+    free(source);
+    return ok;
+}
+
+bool link_program(GLuint vert_shader, GLuint frag_shader, GLuint *program)
+{
+    *program = glCreateProgram();
+
+    glAttachShader(*program, vert_shader);
+    glAttachShader(*program, frag_shader);
+    glLinkProgram(*program);
+
+    GLint linked = 0;
+    glGetProgramiv(*program, GL_LINK_STATUS, &linked);
+    if (!linked) {
+        GLsizei message_size = 0;
+        GLchar message[1024];
+
+        glGetProgramInfoLog(*program, sizeof(message), &message_size, message);
+        fprintf(stderr, "Program Linking: %.*s\n", message_size, message);
+    }
+
+    glDeleteShader(vert_shader);
+    glDeleteShader(frag_shader);
+
+    return program;
+}

+ 13 - 0
src/gl_extra.h

@@ -0,0 +1,13 @@
+#ifndef GL_EXTRA_H_
+#define GL_EXTRA_H_
+
+#define GL_GLEXT_PROTOTYPES
+#include <SDL2/SDL_opengl.h>
+
+#include <stdbool.h>
+
+bool compile_shader_source(const GLchar *source, GLenum shader_type, GLuint *shader);
+bool compile_shader_file(const char *file_path, GLenum shader_type, GLuint *shader);
+bool link_program(GLuint vert_shader, GLuint frag_shader, GLuint *program);
+
+#endif // GL_EXTRA_H_

+ 39 - 0
src/la.c

@@ -28,8 +28,47 @@ Vec2f vec2f_mul(Vec2f a, Vec2f b)
     return vec2f(a.x * b.x, a.y * b.y);
 }
 
+Vec2f vec2f_mul3(Vec2f a, Vec2f b, Vec2f c)
+{
+    return vec2f_mul(vec2f_mul(a, b), c);
+}
+
 Vec2f vec2f_div(Vec2f a, Vec2f b)
 {
     return vec2f(a.x / b.x, a.y / b.y);
 }
 
+Vec4f vec4f(float x, float y, float z, float w)
+{
+    return (Vec4f) {
+        .x = x,
+        .y = y,
+        .z = z,
+        .w = w,
+    };
+}
+
+Vec4f vec4fs(float x)
+{
+    return vec4f(x, x, x, x);
+}
+
+Vec4f vec4f_add(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
+}
+
+Vec4f vec4f_sub(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
+}
+
+Vec4f vec4f_mul(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
+}
+
+Vec4f vec4f_div(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
+}

+ 12 - 0
src/la.h

@@ -10,6 +10,18 @@ Vec2f vec2fs(float x);
 Vec2f vec2f_add(Vec2f a, Vec2f b);
 Vec2f vec2f_sub(Vec2f a, Vec2f b);
 Vec2f vec2f_mul(Vec2f a, Vec2f b);
+Vec2f vec2f_mul3(Vec2f a, Vec2f b, Vec2f c);
 Vec2f vec2f_div(Vec2f a, Vec2f b);
 
+typedef struct {
+    float x, y, z, w;
+} Vec4f;
+
+Vec4f vec4f(float x, float y, float z, float w);
+Vec4f vec4fs(float x);
+Vec4f vec4f_add(Vec4f a, Vec4f b);
+Vec4f vec4f_sub(Vec4f a, Vec4f b);
+Vec4f vec4f_mul(Vec4f a, Vec4f b);
+Vec4f vec4f_div(Vec4f a, Vec4f b);
+
 #endif // LA_H_

+ 272 - 1
src/main.c

@@ -2,7 +2,11 @@
 #include <stdlib.h>
 #include <stdbool.h>
 
-#include <SDL.h>
+#include <SDL2/SDL.h>
+#define GLEW_STATIC
+#include <GL/glew.h>
+#define GL_GLEXT_PROTOTYPES
+#include <SDL2/SDL_opengl.h>
 
 #define STB_IMAGE_IMPLEMENTATION
 #include "./stb_image.h"
@@ -13,6 +17,7 @@
 #include "./editor.h"
 #include "./la.h"
 #include "./sdl_extra.h"
+#include "./gl_extra.h"
 #include "./font.h"
 
 #define SCREEN_WIDTH 800
@@ -70,6 +75,271 @@ void usage(FILE *stream)
 // TODO: Delete line
 // TODO: Split the line on Enter
 
+// #define OPENGL_RENDERER
+
+#ifdef OPENGL_RENDERER
+void MessageCallback(GLenum source,
+                     GLenum type,
+                     GLuint id,
+                     GLenum severity,
+                     GLsizei length,
+                     const GLchar* message,
+                     const void* userParam)
+{
+    (void) source;
+    (void) id;
+    (void) length;
+    (void) userParam;
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+            type, severity, message);
+}
+
+typedef struct {
+    Vec2f pos;
+    float scale;
+    float ch;
+    Vec4f color;
+} Glyph;
+
+typedef enum {
+    GLYPH_ATTR_POS = 0,
+    GLYPH_ATTR_SCALE,
+    GLYPH_ATTR_CH,
+    GLYPH_ATTR_COLOR,
+    COUNT_GLYPH_ATTRS,
+} Glyph_Attr;
+
+typedef struct {
+    size_t offset;
+    size_t comps;
+} Glyph_Attr_Def;
+
+static const Glyph_Attr_Def glyph_attr_defs[COUNT_GLYPH_ATTRS] = {
+    [GLYPH_ATTR_POS]   = {
+        .offset = offsetof(Glyph, pos),
+        .comps = 2,
+    },
+    [GLYPH_ATTR_SCALE] = {
+        .offset = offsetof(Glyph, scale),
+        .comps = 1,
+    },
+    [GLYPH_ATTR_CH]    = {
+        .offset = offsetof(Glyph, ch),
+        .comps = 1,
+    },
+    [GLYPH_ATTR_COLOR] = {
+        .offset = offsetof(Glyph, color),
+        .comps = 4,
+    },
+};
+static_assert(COUNT_GLYPH_ATTRS == 4, "The amount of glyph vertex attributes have changed");
+
+#define GLYPH_BUFFER_CAP 1024
+
+Glyph glyph_buffer[GLYPH_BUFFER_CAP];
+size_t glyph_buffer_count = 0;
+
+void glyph_buffer_push(Glyph glyph)
+{
+    assert(glyph_buffer_count < GLYPH_BUFFER_CAP);
+    glyph_buffer[glyph_buffer_count++] = glyph;
+}
+
+void glyph_buffer_sync(void)
+{
+    glBufferSubData(GL_ARRAY_BUFFER,
+                    0,
+                    glyph_buffer_count * sizeof(Glyph),
+                    glyph_buffer);
+}
+
+void gl_render_text(const char *text, size_t text_size,
+                    Vec2f pos, float scale, Vec4f color)
+{
+    for (size_t i = 0; i < text_size; ++i) {
+        const Vec2f char_size = vec2f(FONT_CHAR_WIDTH, FONT_CHAR_HEIGHT);
+        const Glyph glyph = {
+            .pos = vec2f_add(pos, vec2f_mul3(char_size,
+                                             vec2f((float) i, 0.0f),
+                                             vec2fs(scale))),
+            .scale = scale,
+            .ch = (float) text[i],
+            .color = color
+        };
+        glyph_buffer_push(glyph);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    (void) argc;
+    (void) argv;
+
+    scc(SDL_Init(SDL_INIT_VIDEO));
+
+    SDL_Window *window =
+        scp(SDL_CreateWindow("Text Editor",
+                             0, 0,
+                             SCREEN_WIDTH, SCREEN_HEIGHT,
+                             SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL));
+
+    {
+        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);
+
+        int major;
+        int minor;
+        SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
+        SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
+        printf("GL version %d.%d\n", major, minor);
+    }
+
+    scp(SDL_GL_CreateContext(window));
+
+    if (GLEW_OK != glewInit()) {
+        fprintf(stderr, "Could not initialize GLEW!");
+        exit(1);
+    }
+
+    if (!GLEW_ARB_draw_instanced) {
+        fprintf(stderr, "ARB_draw_instanced is not supported; game may not work properly!!\n");
+        exit(1);
+    }
+
+    if (!GLEW_ARB_instanced_arrays) {
+        fprintf(stderr, "ARB_instanced_arrays is not supported; game may not work properly!!\n");
+        exit(1);
+    }
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    if (GLEW_ARB_debug_output) {
+        glEnable(GL_DEBUG_OUTPUT);
+        glDebugMessageCallback(MessageCallback, 0);
+    } else {
+        fprintf(stderr, "WARNING! GLEW_ARB_debug_output is not available");
+    }
+
+    GLint time_uniform;
+    GLint resolution_uniform;
+
+    // Initialize Shaders
+    {
+        GLuint vert_shader = 0;
+        if (!compile_shader_file("./shaders/font.vert", GL_VERTEX_SHADER, &vert_shader)) {
+            exit(1);
+        }
+        GLuint frag_shader = 0;
+        if (!compile_shader_file("./shaders/font.frag", GL_FRAGMENT_SHADER, &frag_shader)) {
+            exit(1);
+        }
+
+        GLuint program = 0;
+        if (!link_program(vert_shader, frag_shader, &program)) {
+            exit(1);
+        }
+
+        glUseProgram(program);
+
+        time_uniform = glGetUniformLocation(program, "time");
+        resolution_uniform = glGetUniformLocation(program, "resolution");
+
+        glUniform2f(resolution_uniform, SCREEN_WIDTH, SCREEN_HEIGHT);
+    }
+
+    // Init Font Texture
+    {
+        const char *file_path = "charmap-oldschool_white.png";
+        int width, height, n;
+        unsigned char *pixels = stbi_load(file_path, &width, &height, &n, STBI_rgb_alpha);
+        if (pixels == NULL) {
+            fprintf(stderr, "ERROR: could not load file %s: %s\n",
+                    file_path, stbi_failure_reason());
+            exit(1);
+        }
+
+        glActiveTexture(GL_TEXTURE0);
+
+        GLuint font_texture = 0;
+        glGenTextures(1, &font_texture);
+        glBindTexture(GL_TEXTURE_2D, font_texture);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+        glTexImage2D(GL_TEXTURE_2D,
+                     0,
+                     GL_RGBA,
+                     width,
+                     height,
+                     0,
+                     GL_RGBA,
+                     GL_UNSIGNED_BYTE,
+                     pixels);
+    }
+
+    // Init Buffers
+    {
+        GLuint vao = 0;
+        glGenVertexArrays(1, &vao);
+        glBindVertexArray(vao);
+
+        GLuint vbo = 0;
+        glGenBuffers(1, &vbo);
+        glBindBuffer(GL_ARRAY_BUFFER, vbo);
+        glBufferData(GL_ARRAY_BUFFER,
+                     sizeof(glyph_buffer),
+                     glyph_buffer,
+                     GL_DYNAMIC_DRAW);
+
+        for (Glyph_Attr attr = 0; attr < COUNT_GLYPH_ATTRS; ++attr) {
+            glEnableVertexAttribArray(attr);
+            glVertexAttribPointer(
+                attr,
+                glyph_attr_defs[attr].comps,
+                GL_FLOAT,
+                GL_FALSE,
+                sizeof(Glyph),
+                (void*) glyph_attr_defs[attr].offset);
+            glVertexAttribDivisor(attr, 1);
+        }
+    }
+
+    const char *text = "Hello, World";
+    Vec4f color = vec4f(1.0f, 0.0f, 0.0f, 1.0f);
+    gl_render_text(text, strlen(text), vec2fs(0.0f), 5.0f, color);
+    glyph_buffer_sync();
+
+    bool quit = false;
+    while (!quit) {
+        SDL_Event event = {0};
+        while (SDL_PollEvent(&event)) {
+            switch (event.type) {
+            case SDL_QUIT: {
+                quit = true;
+            }
+            break;
+            }
+        }
+
+        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        glUniform1f(time_uniform, (float) SDL_GetTicks() / 1000.0f);
+
+        glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, glyph_buffer_count);
+
+        SDL_GL_SwapWindow(window);
+    }
+
+    return 0;
+}
+#else
 int main(int argc, char **argv)
 {
     const char *file_path = NULL;
@@ -205,3 +475,4 @@ int main(int argc, char **argv)
 
     return 0;
 }
+#endif // OPENGL_RENDERER

+ 1 - 1
src/sdl_extra.h

@@ -1,7 +1,7 @@
 #ifndef SDL_EXTRA_H_
 #define SDL_EXTRA_H_
 
-#include <SDL.h>
+#include <SDL2/SDL.h>
 
 #include "./la.h"