diff options
Diffstat (limited to 'src/shared/glemu.cpp')
| -rw-r--r-- | src/shared/glemu.cpp | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/src/shared/glemu.cpp b/src/shared/glemu.cpp new file mode 100644 index 0000000..5658eb8 --- /dev/null +++ b/src/shared/glemu.cpp @@ -0,0 +1,355 @@ +#include "cube.h" + +extern int glversion; +extern int intel_mapbufferrange_bug; + +namespace gle +{ + struct attribinfo + { + int type, size, formatsize, offset; + GLenum format; + + attribinfo() : type(0), size(0), formatsize(0), offset(0), format(GL_FALSE) {} + + bool operator==(const attribinfo &a) const + { + return type == a.type && size == a.size && format == a.format && offset == a.offset; + } + bool operator!=(const attribinfo &a) const + { + return type != a.type || size != a.size || format != a.format || offset != a.offset; + } + }; + + extern const char * const attribnames[MAXATTRIBS] = { "vvertex", "vcolor", "vtexcoord0", "vtexcoord1", "vnormal", "vtangent", "vboneweight", "vboneindex" }; + ucharbuf attribbuf; + static uchar *attribdata; + static attribinfo attribdefs[MAXATTRIBS], lastattribs[MAXATTRIBS]; + int enabled = 0; + static int numattribs = 0, attribmask = 0, numlastattribs = 0, lastattribmask = 0, vertexsize = 0, lastvertexsize = 0; + static GLenum primtype = GL_TRIANGLES; + static uchar *lastbuf = NULL; + static bool changedattribs = false; + static vector<GLint> multidrawstart; + static vector<GLsizei> multidrawcount; + + #define MAXQUADS (0x10000/4) + static GLuint quadindexes = 0; + static bool quadsenabled = false; + + #define MAXVBOSIZE (4*1024*1024) + static GLuint vbo = 0; + static int vbooffset = MAXVBOSIZE; + + static GLuint defaultvao = 0; + + void enablequads() + { + quadsenabled = true; + + if(glversion < 300) return; + + if(quadindexes) + { + glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, quadindexes); + return; + } + + glGenBuffers_(1, &quadindexes); + ushort *data = new ushort[MAXQUADS*6], *dst = data; + for(int idx = 0; idx < MAXQUADS*4; idx += 4, dst += 6) + { + dst[0] = idx; + dst[1] = idx + 1; + dst[2] = idx + 2; + dst[3] = idx + 0; + dst[4] = idx + 2; + dst[5] = idx + 3; + } + glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, quadindexes); + glBufferData_(GL_ELEMENT_ARRAY_BUFFER, MAXQUADS*6*sizeof(ushort), data, GL_STATIC_DRAW); + delete[] data; + } + + void disablequads() + { + quadsenabled = false; + + if(glversion < 300) return; + + glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + void drawquads(int offset, int count) + { + if(count <= 0) return; + if(glversion < 300) + { + glDrawArrays(GL_QUADS, offset*4, count*4); + return; + } + if(offset + count > MAXQUADS) + { + if(offset >= MAXQUADS) return; + count = MAXQUADS - offset; + } + glDrawRangeElements_(GL_TRIANGLES, offset*4, (offset + count)*4-1, count*6, GL_UNSIGNED_SHORT, (ushort *)0 + offset*6); + } + + void defattrib(int type, int size, int format) + { + if(type == ATTRIB_VERTEX) + { + numattribs = attribmask = 0; + vertexsize = 0; + } + changedattribs = true; + attribmask |= 1<<type; + attribinfo &a = attribdefs[numattribs++]; + a.type = type; + a.size = size; + a.format = format; + switch(format) + { + case 'B': case GL_UNSIGNED_BYTE: a.formatsize = 1; a.format = GL_UNSIGNED_BYTE; break; + case 'b': case GL_BYTE: a.formatsize = 1; a.format = GL_BYTE; break; + case 'S': case GL_UNSIGNED_SHORT: a.formatsize = 2; a.format = GL_UNSIGNED_SHORT; break; + case 's': case GL_SHORT: a.formatsize = 2; a.format = GL_SHORT; break; + case 'I': case GL_UNSIGNED_INT: a.formatsize = 4; a.format = GL_UNSIGNED_INT; break; + case 'i': case GL_INT: a.formatsize = 4; a.format = GL_INT; break; + case 'f': case GL_FLOAT: a.formatsize = 4; a.format = GL_FLOAT; break; + case 'd': case GL_DOUBLE: a.formatsize = 8; a.format = GL_DOUBLE; break; + default: a.formatsize = 0; a.format = GL_FALSE; break; + } + a.formatsize *= size; + a.offset = vertexsize; + vertexsize += a.formatsize; + } + + void defattribs(const char *fmt) + { + for(;; fmt += 3) + { + GLenum format; + switch(fmt[0]) + { + case 'v': format = ATTRIB_VERTEX; break; + case 'c': format = ATTRIB_COLOR; break; + case 't': format = ATTRIB_TEXCOORD0; break; + case 'T': format = ATTRIB_TEXCOORD1; break; + case 'n': format = ATTRIB_NORMAL; break; + case 'x': format = ATTRIB_TANGENT; break; + case 'w': format = ATTRIB_BONEWEIGHT; break; + case 'i': format = ATTRIB_BONEINDEX; break; + default: return; + } + defattrib(format, fmt[1]-'0', fmt[2]); + } + } + + static inline void setattrib(const attribinfo &a, uchar *buf) + { + switch(a.type) + { + case ATTRIB_VERTEX: + case ATTRIB_TEXCOORD0: + case ATTRIB_TEXCOORD1: + case ATTRIB_BONEINDEX: + glVertexAttribPointer_(a.type, a.size, a.format, GL_FALSE, vertexsize, buf); + break; + case ATTRIB_COLOR: + case ATTRIB_NORMAL: + case ATTRIB_TANGENT: + case ATTRIB_BONEWEIGHT: + glVertexAttribPointer_(a.type, a.size, a.format, GL_TRUE, vertexsize, buf); + break; + } + if(!(enabled&(1<<a.type))) + { + glEnableVertexAttribArray_(a.type); + enabled |= 1<<a.type; + } + } + + static inline void unsetattrib(const attribinfo &a) + { + glDisableVertexAttribArray_(a.type); + enabled &= ~(1<<a.type); + } + + static inline void setattribs(uchar *buf) + { + bool forceattribs = numattribs != numlastattribs || vertexsize != lastvertexsize || buf != lastbuf; + if(forceattribs || changedattribs) + { + int diffmask = enabled & lastattribmask & ~attribmask; + if(diffmask) loopi(numlastattribs) + { + const attribinfo &a = lastattribs[i]; + if(diffmask & (1<<a.type)) unsetattrib(a); + } + uchar *src = buf; + loopi(numattribs) + { + const attribinfo &a = attribdefs[i]; + if(forceattribs || a != lastattribs[i]) + { + setattrib(a, src); + lastattribs[i] = a; + } + src += a.formatsize; + } + lastbuf = buf; + numlastattribs = numattribs; + lastattribmask = attribmask; + lastvertexsize = vertexsize; + changedattribs = false; + } + } + + void begin(GLenum mode) + { + primtype = mode; + } + + void begin(GLenum mode, int numverts) + { + primtype = mode; + if(glversion >= 300 && !intel_mapbufferrange_bug) + { + int len = numverts * vertexsize; + if(vbooffset + len >= MAXVBOSIZE) + { + len = min(len, MAXVBOSIZE); + if(!vbo) glGenBuffers_(1, &vbo); + glBindBuffer_(GL_ARRAY_BUFFER, vbo); + glBufferData_(GL_ARRAY_BUFFER, MAXVBOSIZE, NULL, GL_STREAM_DRAW); + vbooffset = 0; + } + else if(!lastvertexsize) glBindBuffer_(GL_ARRAY_BUFFER, vbo); + void *buf = glMapBufferRange_(GL_ARRAY_BUFFER, vbooffset, len, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT|GL_MAP_UNSYNCHRONIZED_BIT); + if(buf) attribbuf.reset((uchar *)buf, len); + } + } + + void multidraw() + { + int start = multidrawstart.length() ? multidrawstart.last() + multidrawcount.last() : 0, + count = attribbuf.length()/vertexsize - start; + if(count > 0) + { + multidrawstart.add(start); + multidrawcount.add(count); + } + } + + int end() + { + uchar *buf = attribbuf.getbuf(); + if(attribbuf.empty()) + { + if(buf != attribdata) + { + glUnmapBuffer_(GL_ARRAY_BUFFER); + attribbuf.reset(attribdata, MAXVBOSIZE); + } + return 0; + } + int start = 0; + if(glversion >= 300) + { + if(buf == attribdata) + { + if(vbooffset + attribbuf.length() >= MAXVBOSIZE) + { + if(!vbo) glGenBuffers_(1, &vbo); + glBindBuffer_(GL_ARRAY_BUFFER, vbo); + glBufferData_(GL_ARRAY_BUFFER, MAXVBOSIZE, NULL, GL_STREAM_DRAW); + vbooffset = 0; + } + else if(!lastvertexsize) glBindBuffer_(GL_ARRAY_BUFFER, vbo); + void *dst = intel_mapbufferrange_bug ? NULL : + glMapBufferRange_(GL_ARRAY_BUFFER, vbooffset, attribbuf.length(), GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT|GL_MAP_UNSYNCHRONIZED_BIT); + if(dst) + { + memcpy(dst, attribbuf.getbuf(), attribbuf.length()); + glUnmapBuffer_(GL_ARRAY_BUFFER); + } + else glBufferSubData_(GL_ARRAY_BUFFER, vbooffset, attribbuf.length(), attribbuf.getbuf()); + } + else glUnmapBuffer_(GL_ARRAY_BUFFER); + buf = (uchar *)0 + vbooffset; + if(vertexsize == lastvertexsize && buf >= lastbuf) + { + start = int(buf - lastbuf)/vertexsize; + if(primtype == GL_QUADS && (start%4 || start + attribbuf.length()/vertexsize >= 4*MAXQUADS)) + start = 0; + else buf = lastbuf; + } + vbooffset += attribbuf.length(); + } + setattribs(buf); + int numvertexes = attribbuf.length()/vertexsize; + if(primtype == GL_QUADS) + { + if(!quadsenabled) enablequads(); + for(int quads = numvertexes/4;;) + { + int count = min(quads, MAXQUADS); + drawquads(start/4, count); + quads -= count; + if(quads <= 0) break; + setattribs(buf + 4*count*vertexsize); + start = 0; + } + } + else + { + if(multidrawstart.length()) + { + multidraw(); + if(start) loopv(multidrawstart) multidrawstart[i] += start; + glMultiDrawArrays_(primtype, multidrawstart.getbuf(), multidrawcount.getbuf(), multidrawstart.length()); + multidrawstart.setsize(0); + multidrawcount.setsize(0); + } + else glDrawArrays(primtype, start, numvertexes); + } + attribbuf.reset(attribdata, MAXVBOSIZE); + return numvertexes; + } + + void forcedisable() + { + for(int i = 0; enabled; i++) if(enabled&(1<<i)) { glDisableVertexAttribArray_(i); enabled &= ~(1<<i); } + numlastattribs = lastattribmask = lastvertexsize = 0; + lastbuf = NULL; + if(quadsenabled) disablequads(); + if(glversion >= 300) glBindBuffer_(GL_ARRAY_BUFFER, 0); + } + + void setup() + { + if(glversion >= 300) + { + if(!defaultvao) glGenVertexArrays_(1, &defaultvao); + glBindVertexArray_(defaultvao); + } + attribdata = new uchar[MAXVBOSIZE]; + attribbuf.reset(attribdata, MAXVBOSIZE); + } + + void cleanup() + { + disable(); + + if(quadindexes) { glDeleteBuffers_(1, &quadindexes); quadindexes = 0; } + + if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; } + vbooffset = MAXVBOSIZE; + + if(defaultvao) { glDeleteVertexArrays_(1, &defaultvao); defaultvao = 0; } + } +} + |
