graphics

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 1cfd59c95cf9ab70569af3c237464ee9a93eaa10
parent 557e7a11122ab62f50917fe46ebd0fd4654de9f6
Author: Brian Swetland <swetland@frotz.net>
Date:   Sat,  9 Feb 2013 03:33:49 -0800

multiplatform: add GL3.3/GLES3.0 platform implementation

This breaks the existing API a bit, adding an InputConfig object
which both owns the VAO and when to change that state as well as
manages (inefficiently for now) the GL Program object

Diffstat:
Acommon/glapp.cc | 569+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acommon/glapp.h | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 771 insertions(+), 0 deletions(-)

diff --git a/common/glapp.cc b/common/glapp.cc @@ -0,0 +1,569 @@ +/* Copyright 2013 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "glapp.h" +#include "util.h" + +#if 1 +#define CHECK() do {} while (0) +#else +const char *__gl_error_string(unsigned e) { + static char buf[16]; + switch (e) { + case GL_INVALID_ENUM: return "INVALID ENUM"; + case GL_INVALID_VALUE: return "INVALID VALUE"; + case GL_INVALID_OPERATION: return "INVALID OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID FRAMEBUFFER OPERATION"; + case GL_OUT_OF_MEMORY: return "OUT OF MEMORY"; + default: + sprintf(buf,"0x%08x", e); + return buf; + } +} + +void __check_gl_error(const char *fn, int line) { + unsigned e; + while ((e = glGetError()) != GL_NO_ERROR) + printx("%s:%d: GL ERROR %s\n", fn, line, __gl_error_string(e)); +} +#define CHECK() __check_gl_error(__func__, __LINE__) +#endif + +static void die(const char *fmt, ...) { + fprintf(stderr,"ERROR: %s\n", fmt); + exit(-1); +} + +static void quit(void) { + SDL_Quit(); + exit(0); +} + +void App::handleEvents(void) { + SDL_Event ev; + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_KEYDOWN: + if (ev.key.keysym.sym < 256) + keystate[ev.key.keysym.sym] = 1; + if (ev.key.keysym.sym == SDLK_ESCAPE) + quit(); + break; + case SDL_KEYUP: + if (ev.key.keysym.sym < 256) + keystate[ev.key.keysym.sym] = 0; + break; + case SDL_QUIT: + quit(); + } + } +} + +#if 0 +void App::setOptions(int argc, char **argv) { + char *x; + argc--; + argv++; + while (argc--) { + if (!strcmp("-nosync",argv[0])) { + _vsync = 0; + } else if (isdigit(argv[0][0]) && (x = strchr(argv[0],'x'))) { + _width = atoi(argv[0]); + _height = atoi(x + 1); + } else { + fprintf(stderr,"unknown argument '%s'\n",argv[0]); + } + argv++; + } +} +#endif + +int App::start(void) { + memset(keystate, 0, sizeof(keystate)); + + if (SDL_Init(SDL_INIT_VIDEO)) + die("sdl video init failed"); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32); + + /* enable vsync */ + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, _vsync); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, _vsync); + + if (SDL_SetVideoMode(width, height, 32, + SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL) == NULL) + die("sdl cannot set mode"); + + if (init()) + return -1; + + for (;;) { + handleEvents(); + + unsigned b = SDL_GetRelativeMouseState(&mouseDX, &mouseDY); + mouseBTN = (b & 1) | ((b & 2) << 1) | ((b & 4) >> 1); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + render(); + + if (_vsync) { + SDL_GL_SwapBuffers(); + } else { + glFlush(); + } + } + return -1; +} + +App::App() : width(800), height(600), _vsync(1) { +} + +App::~App() { +} + +int main(int argc, char **argv) { + App *app = createApp(); + app->start(); + app->release(); + return 0; +} + + +// ---- + +static inline void IGNORE(int x) {} + +void printx(const char *fmt, ...) { + char buf[128]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[127] = 0; + va_end(ap); + IGNORE(write(2, buf, strlen(buf))); +} + +void printmtx(float *m, const char *name) { + printx("| %8.4f %8.4f %8.4f %8.4f | \"%s\"\n", m[0], m[1], m[2], m[3], name); + printx("| %8.4f %8.4f %8.4f %8.4f |\n", m[4], m[5], m[6], m[7]); + printx("| %8.4f %8.4f %8.4f %8.4f |\n", m[8], m[9], m[10], m[11]); + printx("| %8.4f %8.4f %8.4f %8.4f |\n", m[12], m[13], m[14], m[15]); +} + +int error(const char *fmt, ...) { + char buf[128]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + buf[127] = 0; + printx("ERROR: %s\n", buf); + return -1; +} + +#define OFF2PTR(off) (((char*) NULL) + off) + +void config_attr_info(GLAttrInfo *ai, unsigned count, int vidx, unsigned stride) { + unsigned n; + for (n = 0; n < count; n++, ai++) { + if (ai->vidx != vidx) continue; + if (ai->kind == KIND_ATTRIB_POINTER) { + printf("VTX ATR idx=%d sz=%d type=%d norm=%d stride=%d off=%d\n", + ai->index, ai->size, ai->type, ai->normalized, stride, ai->pointer); + glVertexAttribPointer(ai->index, ai->size, ai->type, + ai->normalized, stride, OFF2PTR(ai->pointer)); + CHECK(); + } else { + printf("VTX ATR idx=%d sz=%d type=%d stride=%d off=%d\n", + ai->index, ai->size, ai->type, stride, ai->pointer); + glVertexAttribIPointer(ai->index, ai->size, ai->type, + stride, OFF2PTR(ai->pointer)); + CHECK(); + } + if (ai->divisor) + glVertexAttribDivisor(ai->index, ai->divisor); + glEnableVertexAttribArray(ai->index); + } +} + +/* map attribute names to indices, and adapt layout parameters for GL */ +int setup_attr_info(GLAttrInfo **out, AttribInfo *in, unsigned count, unsigned pgm) { + GLAttrInfo *ai = (GLAttrInfo*) malloc(sizeof(GLAttrInfo) * count); + unsigned n; + if (!ai) + return error("Out of Memory"); + memset(ai, 0, sizeof(GLAttrInfo) * count); + for (n = 0; n < count; n++, in++) { + ai[n].vidx = in->slot; + ai[n].index = glGetAttribLocation(pgm, in->name); + if (ai[n].index < 0) + return error("cannot find attribute '%s'", in->name); + ai[n].pointer = in->offset; + ai[n].divisor = in->divisor; + switch (in->format) { + case FMT_32x4_FLOAT: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 4; + ai[n].type = GL_FLOAT; + ai[n].normalized = GL_FALSE; + break; + case FMT_32x3_FLOAT: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 3; + ai[n].type = GL_FLOAT; + ai[n].normalized = GL_FALSE; + break; + case FMT_32x2_FLOAT: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 2; + ai[n].type = GL_FLOAT; + ai[n].normalized = GL_FALSE; + break; + case FMT_32x1_FLOAT: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 1; + ai[n].type = GL_FLOAT; + ai[n].normalized = GL_FALSE; + break; + case FMT_8x4_SNORM: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 4; + ai[n].type = GL_BYTE; + ai[n].normalized = GL_TRUE; + break; + case FMT_8x4_UNORM: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 4; + ai[n].type = GL_UNSIGNED_BYTE; + ai[n].normalized = GL_TRUE; + break; + case FMT_8x2_UNORM: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 2; + ai[n].type = GL_UNSIGNED_BYTE; + ai[n].normalized = GL_TRUE; + break; + case FMT_8x1_UNORM: + ai[n].kind = KIND_ATTRIB_POINTER; + ai[n].size = 1; + ai[n].type = GL_UNSIGNED_BYTE; + ai[n].normalized = GL_TRUE; + break; + case FMT_8x4_UINT: + ai[n].kind = KIND_ATTRIB_IPOINTER; + ai[n].size = 4; + ai[n].type = GL_BYTE; + ai[n].normalized = GL_FALSE; + break; + case FMT_8x2_UINT: + ai[n].kind = KIND_ATTRIB_IPOINTER; + ai[n].size = 2; + ai[n].type = GL_BYTE; + ai[n].normalized = GL_FALSE; + break; + case FMT_8x1_UINT: + ai[n].kind = KIND_ATTRIB_IPOINTER; + ai[n].size = 1; + ai[n].type = GL_BYTE; + ai[n].normalized = GL_FALSE; + break; + default: + return error("unknown format %d\n", in->format); + } + } + *out = ai; + return 0; +}; + +void dump_compile_error(unsigned id) { + int len; + char *buf; + glGetShaderiv(id, GL_INFO_LOG_LENGTH, &len); + buf = (char*) malloc(len + 1); + if (buf != 0) { + memset(buf, 0, len); + glGetShaderInfoLog(id, len, &len, buf); + buf[len] = 0; + fprintf(stderr,"-- shader compiler error --\n%s\n", buf); + free(buf); + } +} + +void dump_link_error(unsigned id) { + int len; + char *buf; + glGetProgramiv(id, GL_INFO_LOG_LENGTH, &len); + buf = (char*) malloc(len + 1); + if (buf != 0) { + memset(buf, 0, len); + glGetProgramInfoLog(id, len, &len, buf); + buf[len] = 0; + fprintf(stderr,"-- shader link error --\n%s\n", buf); + free(buf); + } +} + +int App::compileShader(VertexShader *vs, const char *fn, + void *data, unsigned len, int raw, + AttribInfo *layout, unsigned lcount) { + + unsigned pgm, id; + int r; + + id = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(id, 1, (const char**) &data, NULL); + glCompileShader(id); + glGetShaderiv(id, GL_COMPILE_STATUS, &r); + if (!r) { + dump_compile_error(id); + glDeleteShader(id); + return error("vertex shader '%s' compile error" ,fn); + } + fprintf(stderr,"COMPILE %d\n", r); + + /* we need to compile a program to lookup the indices... */ + pgm = glCreateProgram(); + glAttachShader(pgm, id); + glLinkProgram(pgm); + glGetProgramiv(pgm, GL_LINK_STATUS, &r); + if (!r) { + dump_link_error(id); + glDeleteProgram(pgm); + glDeleteShader(id); + return error("vertex shader '%s' link error", fn); + } + + r = setup_attr_info(&vs->ai, layout, lcount, pgm); + glDeleteProgram(pgm); + if (r < 0) { + glDeleteShader(id); + return error("vertex shader '%s' bind error", fn); + } + vs->count = lcount; + vs->vs = id; + printx("VertexShader %d compiled\n", id); + return 0; +} + +int App::compileShader(PixelShader *ps, const char *fn, + void *data, unsigned len, int raw) { + unsigned id; + int r; + id = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(id, 1, (const char **) &data, NULL); + glCompileShader(id); + glGetShaderiv(id, GL_COMPILE_STATUS, &r); + if (!r) { + dump_compile_error(id); + glDeleteShader(id); + return error("pixel shader '%s' compile error", fn); + } + ps->ps = id; + printx("PixelShader %d compiled\n", id); + return 0; +} + +int App::loadShader(VertexShader *vs, const char *fn, + AttribInfo *layout, unsigned lcount) { + void *data; + unsigned sz; + int r; + if (!(data = load_file(fn, &sz))) + return error("cannot load file '%s'", fn); + r = compileShader(vs, fn, data, sz, 0, layout, lcount); + free(data); + return r; +} + +int App::loadShader(PixelShader *ps, const char *fn) { + void *data; + unsigned sz; + int r; + if (!(data = load_file(fn, &sz))) + return error("cannot load file '%s'", fn); + r = compileShader(ps, fn, data, sz, 0); + free(data); + return r; +} + +int App::initConfig(InputConfiguration *ic, VertexShader *vs, PixelShader *ps) { + unsigned pgm, vao; + int r; + // TODO program cache + pgm = glCreateProgram(); + glAttachShader(pgm, vs->vs); + glAttachShader(pgm, ps->ps); + glLinkProgram(pgm); + glGetProgramiv(pgm, GL_LINK_STATUS, &r); + if (!r) { + dump_link_error(pgm); + glDeleteProgram(pgm); + return error("program link error"); + } + glGenVertexArrays(1, &vao); + CHECK(); + glBindVertexArray(ic->vao); + CHECK(); + ic->vao = vao; + ic->pgm = pgm; + ic->vs = vs; + ic->ps = ps; + printx("Program %d linked\n", pgm); + return 0; +} + +void App::useConfig(InputConfiguration *ic) { + this->ic = ic; + glBindVertexArray(ic->vao); + CHECK(); + glUseProgram(ic->pgm); + CHECK(); +} + +int App::loadTextureRGBA(Texture2D *tex, const char *fn, int genmips) { + void *data; + unsigned dw, dh; + int r; + if (!(data = load_png_rgba(fn, &dw, &dh, 0))) + return error("cannot load '%s'", fn); + r = createTextureRGBA(tex, data, dw, dh, genmips); + free(data); + return r; +} +int App::createTextureRGBA(Texture2D *tex, void *data, unsigned w, unsigned h, int genmips) { + unsigned id; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + if (genmips) + glGenerateMipmap(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + tex->tex = id; + return 0; +} + +int App::initBuffer(VertexBuffer *vb, void *data, int sz) { + if (vb->buf) + glDeleteBuffers(1, &vb->buf); + glGenBuffers(1, &vb->buf); + vb->sz = sz; + if (data) { + glBindBuffer(GL_ARRAY_BUFFER, vb->buf); + CHECK(); + glBufferData(GL_ARRAY_BUFFER, sz, data, GL_STATIC_DRAW); + CHECK(); + } + return 0; +} +int App::initBuffer(IndexBuffer *ib, void *data, int sz) { + glGenBuffers(1, &ib->buf); + ib->sz = sz; + if (data) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->buf); + CHECK(); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sz, data, GL_STATIC_DRAW); + CHECK(); + } + return 0; +} +int App::initBuffer(UniformBuffer *ub, void *data, int sz) { + glGenBuffers(1, &ub->buf); + ub->sz = sz; + if (data) { + glBindBuffer(GL_UNIFORM_BUFFER, ub->buf); + CHECK(); + glBufferData(GL_UNIFORM_BUFFER, sz, data, GL_STATIC_DRAW); + CHECK(); + } + return 0; +} + +void App::updateBuffer(VertexBuffer *vb, void *data) { + glBindBuffer(GL_ARRAY_BUFFER, vb->buf); + CHECK(); + glBufferData(GL_ARRAY_BUFFER, vb->sz, data, GL_STATIC_DRAW); + CHECK(); +} +void App::updateBuffer(IndexBuffer *ib, void *data) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->buf); + CHECK(); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, ib->sz, data, GL_STATIC_DRAW); + CHECK(); +} +void App::updateBuffer(UniformBuffer *ub, void *data) { + glBindBuffer(GL_UNIFORM_BUFFER, ub->buf); + CHECK(); + glBufferData(GL_UNIFORM_BUFFER, ub->sz, data, GL_STATIC_DRAW); + CHECK(); +} + +void App::useBuffer(VertexBuffer *vb, int slot, unsigned stride, unsigned offset) { + glBindBuffer(GL_ARRAY_BUFFER, vb->buf); + CHECK(); + config_attr_info(ic->vs->ai, ic->vs->count, slot, stride); + ic->vbuf[slot] = vb; +} +void App::useBuffer(IndexBuffer *ib) { + ic->ibuf = ib; +} +void App::useBuffer(UniformBuffer *ub, int slot) { + if (ic->ubuf[slot] != ub) { + ic->ubuf[slot] = ub; + /* simple 1:1 binding */ + // TODO: smarting binding, avoid having to glBindBufferBase() on every draw + glUniformBlockBinding(ic->pgm, slot, slot); + } +} +void App::useTexture(Texture2D *tex, int slot) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex->tex); +} + +void _prepare(InputConfiguration *ic) { + int n; + if (ic->ibuf) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ic->ibuf->buf); + for (n = 0; n < 16; n++) + if (ic->ubuf[n]) + glBindBufferBase(GL_UNIFORM_BUFFER, n, ic->ubuf[n]->buf); +} +void App::drawIndexedInstanced(unsigned numindices, unsigned numinstances) { + _prepare(ic); + glDrawElementsInstanced(GL_TRIANGLES, numindices, GL_UNSIGNED_SHORT, NULL, numinstances); + CHECK(); +} +void App::drawInstanced(unsigned numvertices, unsigned numinstances) { + _prepare(ic); + glDrawArraysInstanced(GL_TRIANGLES, 0, numvertices, numinstances); + CHECK(); +} +void App::drawIndexed(unsigned numindices) { + _prepare(ic); + glDrawElements(GL_TRIANGLES, numindices, GL_UNSIGNED_SHORT, NULL); + CHECK(); +} diff --git a/common/glapp.h b/common/glapp.h @@ -0,0 +1,202 @@ +/* Copyright 2013 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _GL_APP_H_ +#define _GL_APP_H_ + +#define GL_GLEXT_PROTOTYPES 1 +#define NO_SDL_GLEXT 1 +#include <SDL.h> +#include <SDL_opengl.h> +#include <GL/glext.h> +#include <math.h> + +#define SL "glsl" + +enum { + FMT_32x4_FLOAT = 1, + FMT_32x3_FLOAT = 2, + FMT_32x2_FLOAT = 3, + FMT_32x1_FLOAT = 4, + + FMT_8x4_SNORM = 5, + + FMT_8x4_UNORM = 6, + FMT_8x2_UNORM = 7, + FMT_8x1_UNORM = 8, + + FMT_8x4_UINT = 9, + FMT_8x2_UINT = 10, + FMT_8x1_UINT = 11, +}; + +enum { + VERTEX_DATA = 1, + INSTANCE_DATA = 2, +}; + +struct AttribInfo { + const char *name; + unsigned nidx; + unsigned format; + unsigned slot; + unsigned offset; + unsigned type; + unsigned divisor; +}; + + +struct GLAttrInfo { +#define KIND_ATTRIB_POINTER 0 +#define KIND_ATTRIB_IPOINTER 1 + int kind; + +/* which VBO {0...} to associate with */ + int vidx; + +/* arguments for VertexAttrib[I]Pointer() */ + int index; + int size; + int type; + int normalized; + int pointer; + int divisor; +}; + +struct PixelShader { + unsigned ps; + PixelShader() : ps(0) {}; + ~PixelShader() { if (ps) glDeleteShader(ps); }; +}; + +struct VertexShader { + unsigned vs; + GLAttrInfo *ai; + AttribInfo *info; + unsigned count; + VertexShader() : vs(0), ai(NULL), info(NULL), count(0) {}; + ~VertexShader() { if (vs) { glDeleteShader(vs); free(ai); } }; +}; + +struct Texture2D { + unsigned tex; + Texture2D() : tex(0) {}; + ~Texture2D() { if (tex) { glDeleteTextures(1, &tex); } }; +}; + +struct UniformBuffer { + unsigned buf; + unsigned sz; + UniformBuffer() : buf(0) {}; + ~UniformBuffer() { if (buf) glDeleteBuffers(1, &buf); }; +}; + +struct VertexBuffer { + unsigned buf; + unsigned sz; + VertexBuffer() : buf(0) {}; + ~VertexBuffer() { if (buf) glDeleteBuffers(1, &buf); }; +}; + +struct IndexBuffer { + unsigned buf; + unsigned sz; + IndexBuffer() : buf(0) {}; + ~IndexBuffer() { if (buf) glDeleteBuffers(1, &buf); }; +}; + +struct InputConfiguration { + PixelShader *ps; + VertexShader *vs; + unsigned vao; + unsigned pgm; + VertexBuffer *vbuf[16]; + UniformBuffer *ubuf[16]; + IndexBuffer *ibuf; + InputConfiguration() : ps(NULL), vs(NULL), vao(0), pgm(0), ibuf(NULL) { + memset(vbuf, 0, sizeof(vbuf)); memset(ubuf, 0, sizeof(ubuf)); }; + ~InputConfiguration() { if (vao) glDeleteVertexArrays(1, &vao); if (pgm) glDeleteProgram(pgm); }; +}; + +class App { +public: + App(); + virtual ~App(); + + virtual int init(void) = 0; + virtual void render(void) = 0; + virtual void release(void) {}; + + /* glue - do not use */ + int start(void); + void handleEvents(void); + + // TODO: move away from D3D10_INPUT... + int compileShader(VertexShader *vs, const char *fn, + void *data, unsigned len, int raw, + AttribInfo *layout, unsigned lcount); + int compileShader(PixelShader *ps, const char *fn, + void *data, unsigned len, int raw); + int loadShader(VertexShader *vs, const char *fn, + AttribInfo *layout, unsigned lcount); + int loadShader(PixelShader *ps, const char *fn); + + int loadTextureRGBA(Texture2D *tex, const char *fn, int genmips); + int createTextureRGBA(Texture2D *tex, void *data, unsigned w, unsigned h, int genmips); + + int initConfig(InputConfiguration *ic, VertexShader *vs, PixelShader *ps); + int initBuffer(VertexBuffer *vb, void *data, int sz); + int initBuffer(IndexBuffer *ib, void *data, int sz); + int initBuffer(UniformBuffer *ub, void *data, int sz); + + void updateBuffer(VertexBuffer *vb, void *data); + void updateBuffer(IndexBuffer *ib, void *data); + void updateBuffer(UniformBuffer *ub, void *data); + + void useConfig(InputConfiguration *ic); + void useBuffer(VertexBuffer *vb, int slot, unsigned stride, unsigned offset); + void useBuffer(IndexBuffer *ib); + void useBuffer(UniformBuffer *ub, int slot); + void useTexture(Texture2D *tex, int slot); + void drawIndexedInstanced(unsigned numindices, unsigned numinstances); + void drawInstanced(unsigned numvertices, unsigned numinstances); + void drawIndexed(unsigned numindices); + +protected: + int width; + int height; + + /* mouse motion since last frame */ + int mouseDX, mouseDY, mouseDZ; + /* mouse button state */ + int mouseBTN; + /* mouse position in window coordinates */ + int mouseWX, mouseWY; + /* keys down */ + unsigned char keystate[256]; + + InputConfiguration *ic; + +private: + int _vsync; +}; + +App *createApp(void); + +void printx(const char *fmt, ...); +void printmtx(float *m, const char *name); +int error(const char *fmt, ...); + +#endif