aboutsummaryrefslogtreecommitdiff
path: root/pacc/pacc-gl.c
diff options
context:
space:
mode:
Diffstat (limited to 'pacc/pacc-gl.c')
-rw-r--r--pacc/pacc-gl.c505
1 files changed, 505 insertions, 0 deletions
diff --git a/pacc/pacc-gl.c b/pacc/pacc-gl.c
new file mode 100644
index 0000000..e6ed0a1
--- /dev/null
+++ b/pacc/pacc-gl.c
@@ -0,0 +1,505 @@
+#include "pacc-gl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "pacc-gl-inc.h"
+
+#ifdef NDEBUG
+#define DPRINTF(fmt, ...)
+#else
+#include <stdio.h>
+#define DPRINTF(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
+#endif
+
+/*
+ OpenGL versions:
+ OpenGL 2.0
+ OpenGL 3.2 core (#define PACC_GL_3)
+ OpenGL ES 2.0 (#define PACC_GL_ES)
+ OpenGL ES 3.0 (#define PACC_GL_ES, #define PACC_GL_3)
+
+ Shader languages:
+ GLSL 1.10 / GLSL ES 1.00
+*/
+
+#ifdef PACC_GL_ES
+#include "glsl/esheader.inc"
+#else
+#include "glsl/dsheader.inc"
+#endif
+
+#include "glsl/blit.vert.inc"
+#include "glsl/copy.frag.inc"
+#include "glsl/color.frag.inc"
+#include "glsl/color_trans.frag.inc"
+
+struct pacc_ctx {
+ int w;
+ int h;
+ uint8_t pal[256*3];
+ bool pal_changed;
+ uint8_t clearcolor[3];
+ GLuint tex_pal;
+ GLuint progs[pacc_mode_count];
+ GLint uni_color, uni_color_trans;
+ uint8_t color;
+ bool color_changed;
+ enum pacc_mode curr_mode;
+};
+
+struct pacc_buf {
+ GLuint buf_obj;
+ GLuint va_obj; // for OpenGL 3.2 Core
+ GLfloat *buf;
+ struct pacc_tex *tex;
+ int len;
+ int buflen;
+ GLenum usage;
+ bool changed;
+};
+
+struct pacc_tex {
+ GLuint tex_obj;
+ int w, h;
+ bool changed;
+ uint8_t *buf;
+};
+
+enum {
+ VAI_COORD,
+};
+
+enum {
+ PACC_BUF_DEF_LEN = 32,
+ PRINTBUFLEN = 160,
+};
+
+static void pacc_delete(struct pacc_ctx *pc) {
+ if (pc) {
+ glDeleteTextures(1, &pc->tex_pal);
+ for (int i = 0; i < pacc_mode_count; i++) {
+ glDeleteProgram(pc->progs[i]);
+ }
+ free(pc);
+ }
+}
+
+static GLuint compile_shader(const uint8_t *ss, GLenum type) {
+ GLuint s = glCreateShader(type);
+ if (!s) goto err;
+ const char *sourcelist[2] = {
+#ifdef PACC_GL_ES
+ (const char *)esheader,
+#else
+ (const char *)dsheader,
+#endif
+ (const char *)ss
+ };
+ glShaderSource(s, 2, sourcelist, 0);
+ glCompileShader(s);
+ GLint res;
+ glGetShaderiv(s, GL_COMPILE_STATUS, &res);
+ if (!res) {
+#ifndef NDEBUG
+ glGetShaderiv(s, GL_INFO_LOG_LENGTH, &res);
+ char *msgbuf = malloc(res);
+ if (msgbuf) {
+ glGetShaderInfoLog(s, res, 0, msgbuf);
+ DPRINTF("%s shader error: \n%s\n", type == GL_VERTEX_SHADER ? "vertex" : "fragment", msgbuf);
+ DPRINTF("s:\n%s\n", ss);
+ free(msgbuf);
+ }
+#endif
+ goto err;
+ }
+ return s;
+err:
+ glDeleteShader(s);
+ return 0;
+}
+
+static GLuint compile_and_link(const uint8_t *vss, const uint8_t *fss) {
+ GLuint p = 0, vs = 0, fs = 0;
+ p = glCreateProgram();
+ if (!p) goto err;
+ vs = compile_shader(vss, GL_VERTEX_SHADER);
+ if (!vs) goto err;
+ fs = compile_shader(fss, GL_FRAGMENT_SHADER);
+ if (!fs) goto err;
+ glAttachShader(p, vs);
+ glAttachShader(p, fs);
+ glBindAttribLocation(p, VAI_COORD, "coord");
+ glLinkProgram(p);
+ GLint res;
+ glGetProgramiv(p, GL_LINK_STATUS, &res);
+ if (!res) {
+#ifndef NDEBUG
+ glGetProgramiv(p, GL_INFO_LOG_LENGTH, &res);
+ char *msgbuf = malloc(res);
+ if (msgbuf) {
+ glGetProgramInfoLog(p, res, 0, msgbuf);
+ DPRINTF("program link error: \n%s\n", msgbuf);
+ DPRINTF("vs:\n%s\n", vss);
+ DPRINTF("fs:\n%s\n", fss);
+ free(msgbuf);
+ }
+#endif
+ goto err;
+ }
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+ glUseProgram(p);
+ glUniform1i(glGetUniformLocation(p, "tex"), 1);
+ return p;
+err:
+ glDeleteProgram(p);
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+ return 0;
+}
+
+static void pacc_buf_delete(struct pacc_buf *pb) {
+ if (pb) {
+ free(pb->buf);
+ glDeleteBuffers(1, &pb->buf_obj);
+#ifdef PACC_GL_3
+ glDeleteVertexArrays(1, &pb->va_obj);
+#endif
+ free(pb);
+ }
+}
+
+static struct pacc_buf *pacc_gen_buf(
+ struct pacc_ctx *pc, struct pacc_tex *pt, enum pacc_buf_mode mode) {
+ struct pacc_buf *pb = malloc(sizeof(*pb));
+ if (!pb) goto err;
+ *pb = (struct pacc_buf) {
+ .buflen = PACC_BUF_DEF_LEN,
+ .tex = pt,
+ .usage = (mode == pacc_buf_mode_static) ?
+ GL_STATIC_DRAW : GL_STREAM_DRAW,
+ };
+ pb->buf = malloc(sizeof(*pb->buf) * pb->buflen);
+ if (!pb->buf) goto err;
+ glGenBuffers(1, &pb->buf_obj);
+ if (!pb->buf_obj) goto err;
+#ifdef PACC_GL_3
+ glGenVertexArrays(1, &pb->va_obj);
+ if (!pb->va_obj) goto err;
+ glBindVertexArray(pb->va_obj);
+ glEnableVertexAttribArray(VAI_COORD);
+ glBindBuffer(GL_ARRAY_BUFFER, pb->buf_obj);
+ glVertexAttribPointer(VAI_COORD, 4, GL_FLOAT, GL_FALSE, 0, 0);
+#endif
+ return pb;
+err:
+ pacc_buf_delete(pb);
+ return 0;
+}
+
+static bool buf_reserve(struct pacc_buf *pb, int len) {
+ if (pb->len + len > pb->buflen) {
+ int newlen = pb->buflen;
+ while (pb->len + len > newlen) newlen *= 2;
+ GLfloat *newbuf = realloc(pb->buf, newlen * sizeof(pb->buf[0]));
+ if (!newbuf) return false;
+ pb->buflen = newlen;
+ pb->buf = newbuf;
+ }
+ return true;
+}
+
+static void pacc_calc_scale(float *ret, int w, int h, int wdest, int hdest) {
+ ret[0] = ((float)w) / wdest;
+ ret[1] = ((float)h) / hdest;
+}
+
+static void pacc_calc_off_tex(float *ret,
+ int tw, int th, int xsrc, int ysrc) {
+ ret[0] = ((float)xsrc) / tw;
+ ret[1] = ((float)ysrc) / th;
+}
+
+static void pacc_calc_off(
+ float *ret, int xdest, int ydest, int w, int h, int wdest, int hdest) {
+ ret[0] = ((float)(xdest * 2 + w - wdest)) / wdest;
+ ret[1] = ((float)(ydest * 2 + h - hdest)) / hdest;
+}
+
+static void pacc_buf_rect_off(
+ const struct pacc_ctx *pc, struct pacc_buf *pb,
+ int x, int y, int w, int h, int xoff, int yoff) {
+ float scale[2];
+ float off[2];
+ float tscale[2];
+ float toff[2];
+ pacc_calc_off(off, x, y, w, h, pc->w, pc->h);
+ pacc_calc_scale(scale, w, h, pc->w, pc->h);
+ pacc_calc_off_tex(toff, pb->tex->w, pb->tex->h, xoff, yoff);
+ pacc_calc_scale(tscale, w, h, pb->tex->w, pb->tex->h);
+ GLfloat coord[16] = {
+ -1.0f * scale[0] + off[0], -1.0f * scale[1] - off[1],
+ 0.0f * tscale[0] + toff[0], 1.0f * tscale[1] + toff[1],
+
+ -1.0f * scale[0] + off[0], 1.0f * scale[1] - off[1],
+ 0.0f * tscale[0] + toff[0], 0.0f * tscale[1] + toff[1],
+
+ 1.0f * scale[0] + off[0], -1.0f * scale[1] - off[1],
+ 1.0f * tscale[0] + toff[0], 1.0f * tscale[1] + toff[1],
+
+ 1.0f * scale[0] + off[0], 1.0f * scale[1] - off[1],
+ 1.0f * tscale[0] + toff[0], 0.0f * tscale[1] + toff[1],
+ };
+ if (!buf_reserve(pb, 24)) return;
+ int indices[6] = {0, 1, 2, 2, 1, 3};
+ for (int i = 0; i < 6; i++) {
+ for (int j = 0; j < 4; j++) {
+ pb->buf[pb->len+i*4+j] = coord[indices[i]*4+j];
+ }
+ }
+ pb->len += 24;
+ pb->changed = true;
+}
+
+static void pacc_buf_vprintf(
+ const struct pacc_ctx *pc, struct pacc_buf *pb,
+ int x, int y, const char *fmt, va_list ap) {
+ uint8_t printbuf[PRINTBUFLEN+1];
+ vsnprintf((char *)printbuf, sizeof(printbuf), fmt, ap);
+ int len = strlen((const char *)printbuf);
+ float scale[2];
+ float off[2];
+ int w = pb->tex->w / 256;
+ int h = pb->tex->h;
+ pacc_calc_scale(scale, w, h, pc->w, pc->h);
+ pacc_calc_off(off, x, y, w, h, pc->w, pc->h);
+ if (!buf_reserve(pb, len*24)) return;
+ GLfloat *coords = pb->buf + pb->len;
+ for (int i = 0; i < len; i++) {
+ coords[24*i+0*4+0] = (-1.0f + 2.0f*i) * scale[0] + off[0];
+ coords[24*i+0*4+1] = -1.0f * scale[1] - off[1];
+ coords[24*i+1*4+0] = coords[24*i+4*4+0] = (-1.0f + 2.0f*i) * scale[0] + off[0];
+ coords[24*i+1*4+1] = coords[24*i+4*4+1] = 1.0f * scale[1] - off[1];
+ coords[24*i+2*4+0] = coords[24*i+3*4+0] = (1.0f + 2.0f*i) * scale[0] + off[0];
+ coords[24*i+2*4+1] = coords[24*i+3*4+1] = -1.0f * scale[1] - off[1];
+ coords[24*i+5*4+0] = (1.0f + 2.0f*i) * scale[0] + off[0];
+ coords[24*i+5*4+1] = 1.0f * scale[1] - off[1];
+ coords[24*i+0*4+2] = ((float)printbuf[i]) / 256.0f;
+ coords[24*i+0*4+3] = 1.0f;
+ coords[24*i+1*4+2] = coords[24*i+4*4+2] = ((float)printbuf[i]) / 256.0f;
+ coords[24*i+1*4+3] = coords[24*i+4*4+3] = 0.0f;
+ coords[24*i+2*4+2] = coords[24*i+3*4+2] = ((float)(printbuf[i]+1)) / 256.0f;
+ coords[24*i+2*4+3] = coords[24*i+3*4+3] = 1.0f;
+ coords[24*i+5*4+2] = ((float)(printbuf[i]+1)) / 256.0f;
+ coords[24*i+5*4+3] = 0.0f;
+ }
+ pb->len += len * 24;
+ pb->changed = true;
+}
+
+static void pacc_buf_printf(
+ const struct pacc_ctx *pc, struct pacc_buf *pb,
+ int x, int y, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ pacc_buf_vprintf(pc, pb, x, y, fmt, ap);
+ va_end(ap);
+}
+
+static void pacc_buf_rect(
+ const struct pacc_ctx *pc, struct pacc_buf *pb,
+ int x, int y, int w, int h) {
+ pacc_buf_rect_off(pc, pb, x, y, w, h, 0, 0);
+}
+
+static void pacc_buf_clear(struct pacc_buf *pb) {
+ pb->len = 0;
+ pb->changed = true;
+}
+
+static void pacc_palette(struct pacc_ctx *pc, const uint8_t *rgb, int colors) {
+ memcpy(pc->pal, rgb, colors*3);
+ pc->pal_changed = true;
+}
+
+static void pacc_color(struct pacc_ctx *pc, uint8_t pal) {
+ pc->color = pal;
+ pc->color_changed = true;
+}
+
+static void pacc_begin_clear(struct pacc_ctx *pc) {
+ if (pc->pal_changed) {
+ glActiveTexture(GL_TEXTURE0);
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0, GL_RGB,
+ 256, 1,
+ 0, GL_RGB,
+ GL_UNSIGNED_BYTE, pc->pal);
+ pc->pal_changed = false;
+ glActiveTexture(GL_TEXTURE1);
+ }
+ if (memcmp(pc->clearcolor, pc->pal, 3)) {
+ memcpy(pc->clearcolor, pc->pal, 3);
+ glClearColor(
+ pc->clearcolor[0] / 255.f,
+ pc->clearcolor[1] / 255.f,
+ pc->clearcolor[2] / 255.f,
+ 1.0f);
+ }
+ glClear(GL_COLOR_BUFFER_BIT);
+ pc->curr_mode = pacc_mode_count;
+}
+
+static void pacc_draw(struct pacc_ctx *pc, struct pacc_buf *pb, enum pacc_mode mode) {
+ if (!pb->len) return;
+ if (mode >= pacc_mode_count) return;
+ if (pc->curr_mode != mode) {
+ glUseProgram(pc->progs[mode]);
+ pc->curr_mode = mode;
+ }
+ if (mode != pacc_mode_copy && pc->color_changed) {
+ glUniform1f(pc->uni_color, pc->color / 255.f);
+ glUniform1f(pc->uni_color_trans, pc->color / 255.f);
+ pc->color_changed = false;
+ }
+ glBindTexture(GL_TEXTURE_2D, pb->tex->tex_obj);
+ if (pb->tex->changed) {
+ GLint format;
+#if PACC_GL_3
+ format = GL_RED;
+#else
+ format = GL_LUMINANCE;
+#endif
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0, format,
+ pb->tex->w, pb->tex->h,
+ 0, format, GL_UNSIGNED_BYTE,
+ pb->tex->buf);
+ pb->tex->changed = false;
+ }
+ if (pb->changed) {
+ glBindBuffer(GL_ARRAY_BUFFER, pb->buf_obj);
+ glBufferData(
+ GL_ARRAY_BUFFER,
+ pb->len * sizeof(pb->buf[0]), pb->buf,
+ pb->usage);
+ pb->changed = false;
+ }
+#ifdef PACC_GL_3
+ glBindVertexArray(pb->va_obj);
+#else
+ glEnableVertexAttribArray(VAI_COORD);
+ glBindBuffer(GL_ARRAY_BUFFER, pb->buf_obj);
+ glVertexAttribPointer(VAI_COORD, 4, GL_FLOAT, GL_FALSE, 0, 0);
+#endif
+ glDrawArrays(GL_TRIANGLES, 0, pb->len / 4);
+}
+
+static uint8_t *pacc_tex_lock(struct pacc_tex *pt) {
+ return pt->buf;
+}
+
+static void pacc_tex_unlock(struct pacc_tex *pt) {
+ pt->changed = true;
+}
+
+static void pacc_tex_delete(struct pacc_tex *pt) {
+ if (pt) {
+ glDeleteTextures(1, &pt->tex_obj);
+ free(pt->buf);
+ free(pt);
+ }
+}
+
+static struct pacc_tex *pacc_gen_tex(struct pacc_ctx *pc, int w, int h) {
+ struct pacc_tex *pt = malloc(sizeof(*pt));
+ if (!pt) goto err;
+ *pt = (struct pacc_tex) {
+ .w = w,
+ .h = h,
+ .buf = calloc(w*h, 1),
+ };
+ if (!pt->buf) goto err;
+ glGenTextures(1, &pt->tex_obj);
+ glBindTexture(GL_TEXTURE_2D, pt->tex_obj);
+ GLint format;
+#ifdef PACC_GL_3
+ format = GL_RED;
+#else
+ format = GL_LUMINANCE;
+#endif
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0, format,
+ w, h, 0,
+ format, GL_UNSIGNED_BYTE,
+ pt->buf);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ if (w & (w - 1)) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ }
+ if (h & (h - 1)) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ return pt;
+err:
+ pacc_tex_delete(pt);
+ return 0;
+}
+
+static struct pacc_vtable pacc_gl_vtable = {
+ .pacc_delete = pacc_delete,
+ .gen_buf = pacc_gen_buf,
+ .gen_tex = pacc_gen_tex,
+ .buf_delete = pacc_buf_delete,
+ .tex_lock = pacc_tex_lock,
+ .tex_unlock = pacc_tex_unlock,
+ .tex_delete = pacc_tex_delete,
+ .buf_rect = pacc_buf_rect,
+ .buf_rect_off = pacc_buf_rect_off,
+ .buf_vprintf = pacc_buf_vprintf,
+ .buf_printf = pacc_buf_printf,
+ .buf_clear = pacc_buf_clear,
+ .palette = pacc_palette,
+ .color = pacc_color,
+ .begin_clear = pacc_begin_clear,
+ .draw = pacc_draw,
+};
+
+struct pacc_ctx *pacc_init_gl(int w, int h, struct pacc_vtable *vt) {
+ struct pacc_ctx *pc = malloc(sizeof(*pc));
+ if (!pc) goto err;
+ *pc = (struct pacc_ctx) {
+ .w = w,
+ .h = h,
+ };
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glGenTextures(1, &pc->tex_pal);
+ glBindTexture(GL_TEXTURE_2D, pc->tex_pal);
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0, GL_RGB,
+ 256, 1, 0,
+ GL_RGB, GL_UNSIGNED_BYTE,
+ pc->pal);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ pc->progs[pacc_mode_copy] = compile_and_link(blit_vert, copy_frag);
+ if (!pc->progs[pacc_mode_copy]) goto err;
+ pc->progs[pacc_mode_color] = compile_and_link(blit_vert, color_frag);
+ if (!pc->progs[pacc_mode_color]) goto err;
+ pc->progs[pacc_mode_color_trans] = compile_and_link(blit_vert, color_trans_frag);
+ if (!pc->progs[pacc_mode_color_trans]) goto err;
+ pc->uni_color = glGetUniformLocation(pc->progs[pacc_mode_color], "color");
+ pc->uni_color_trans = glGetUniformLocation(pc->progs[pacc_mode_color_trans], "color");
+ glActiveTexture(GL_TEXTURE1);
+ *vt = pacc_gl_vtable;
+ return pc;
+err:
+ pacc_delete(pc);
+ return 0;
+}
+