diff options
author | Mike Erwin <significant.bit@gmail.com> | 2016-09-13 09:18:33 +0300 |
---|---|---|
committer | Mike Erwin <significant.bit@gmail.com> | 2016-09-13 09:18:33 +0300 |
commit | b6bd2993598712240bd354e4400d057732cda459 (patch) | |
tree | b6ebbad35ddb8513dd430b5c15eefd0cfcb3819f /source/blender/gpu/gawain/immediate.c | |
parent | 18d49a8283f2490b0751798685770533cc814d29 (diff) |
Gawain: reorganize source code
Put Gawain source code in a subfolder to make the boundary between the
library and the rest of Blender clear.
Changed Gawain’s license from Apache to Mozilla Public License. Has
more essence of copyleft — closer to GPL but not as restrictive.
Split immediate.c into several files so parts can be reused (adding
more files soon…)
Diffstat (limited to 'source/blender/gpu/gawain/immediate.c')
-rw-r--r-- | source/blender/gpu/gawain/immediate.c | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/source/blender/gpu/gawain/immediate.c b/source/blender/gpu/gawain/immediate.c new file mode 100644 index 00000000000..90a67dff014 --- /dev/null +++ b/source/blender/gpu/gawain/immediate.c @@ -0,0 +1,555 @@ + +// Gawain immediate mode work-alike +// +// This code is part of the Gawain library, with modifications +// specific to integration with Blender. +// +// Copyright 2016 Mike Erwin +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of +// the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "immediate.h" +#include "attrib_binding.h" +#include <string.h> + +typedef struct { + // TODO: organize this struct by frequency of change (run-time) + + // current draw call + GLubyte* buffer_data; + unsigned buffer_offset; + unsigned buffer_bytes_mapped; + unsigned vertex_ct; + bool strict_vertex_ct; + GLenum primitive; + + VertexFormat vertex_format; + + // current vertex + unsigned vertex_idx; + GLubyte* vertex_data; + unsigned short attrib_value_bits; // which attributes of current vertex have been given values? + + GLuint vbo_id; + GLuint vao_id; + + GLuint bound_program; + AttribBinding attrib_binding; + uint16_t prev_enabled_attrib_bits; // <-- only affects this VAO, so we're ok +} Immediate; + +// size of internal buffer -- make this adjustable? +#define IMM_BUFFER_SIZE (4 * 1024 * 1024) + +static PER_THREAD bool initialized = false; +static PER_THREAD Immediate imm; + +void immInit() + { +#if TRUST_NO_ONE + assert(!initialized); +#endif + + memset(&imm, 0, sizeof(Immediate)); + + glGenVertexArrays(1, &imm.vao_id); + glBindVertexArray(imm.vao_id); + glGenBuffers(1, &imm.vbo_id); + glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id); + glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW); + +#if APPLE_LEGACY + glBufferParameteriAPPLE(GL_ARRAY_BUFFER, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE); + glBufferParameteriAPPLE(GL_ARRAY_BUFFER, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE); +#endif + + imm.primitive = PRIM_NONE; + imm.strict_vertex_ct = true; + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + initialized = true; + } + +void immDestroy() + { +#if TRUST_NO_ONE + assert(initialized); + assert(imm.primitive == PRIM_NONE); // make sure we're not between a Begin/End pair +#endif + + clear_VertexFormat(&imm.vertex_format); + glDeleteVertexArrays(1, &imm.vao_id); + glDeleteBuffers(1, &imm.vbo_id); + initialized = false; + } + +VertexFormat* immVertexFormat() + { + clear_VertexFormat(&imm.vertex_format); + return &imm.vertex_format; + } + +void immBindProgram(GLuint program) + { +#if TRUST_NO_ONE + assert(imm.bound_program == 0); +#endif + + if (!imm.vertex_format.packed) + pack(&imm.vertex_format); + + glUseProgram(program); + get_attrib_locations(&imm.vertex_format, &imm.attrib_binding, program); + imm.bound_program = program; + } + +void immUnbindProgram() + { +#if TRUST_NO_ONE + assert(imm.bound_program != 0); +#endif + + glUseProgram(0); + imm.bound_program = 0; + } + +static bool vertex_count_makes_sense_for_primitive(unsigned vertex_ct, GLenum primitive) + { + // does vertex_ct make sense for this primitive type? + if (vertex_ct == 0) + return false; + + switch (primitive) + { + case GL_POINTS: + return true; + case GL_LINES: + return vertex_ct % 2 == 0; + case GL_LINE_STRIP: + case GL_LINE_LOOP: + return vertex_ct > 2; // otherwise why bother? + case GL_TRIANGLES: + return vertex_ct % 3 == 0; + #ifdef WITH_GL_PROFILE_COMPAT + case GL_QUADS: + return vertex_ct % 4 == 0; + #endif + default: + return false; + } + } + +void immBegin(GLenum primitive, unsigned vertex_ct) + { +#if TRUST_NO_ONE + assert(initialized); + assert(imm.primitive == PRIM_NONE); // make sure we haven't already begun + assert(vertex_count_makes_sense_for_primitive(vertex_ct, primitive)); +#endif + + imm.primitive = primitive; + imm.vertex_ct = vertex_ct; + imm.vertex_idx = 0; + imm.attrib_value_bits = 0; + + // how many bytes do we need for this draw call? + const unsigned bytes_needed = vertex_buffer_size(&imm.vertex_format, vertex_ct); + +#if TRUST_NO_ONE + assert(bytes_needed <= IMM_BUFFER_SIZE); +#endif + + glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id); + + // does the current buffer have enough room? + const unsigned available_bytes = IMM_BUFFER_SIZE - imm.buffer_offset; + // ensure vertex data is aligned + const unsigned pre_padding = padding(imm.buffer_offset, imm.vertex_format.stride); // might waste a little space, but it's safe + if ((bytes_needed + pre_padding) <= available_bytes) + imm.buffer_offset += pre_padding; + else + { + // orphan this buffer & start with a fresh one +#if APPLE_LEGACY + glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW); +#else + if (GLEW_VERSION_4_3 || GLEW_ARB_invalidate_subdata) + glInvalidateBufferData(imm.vbo_id); + else + glMapBufferRange(GL_ARRAY_BUFFER, 0, IMM_BUFFER_SIZE, GL_MAP_INVALIDATE_BUFFER_BIT); +#endif + + imm.buffer_offset = 0; + } + +// printf("mapping %u to %u\n", imm.buffer_offset, imm.buffer_offset + bytes_needed - 1); + +#if APPLE_LEGACY + imm.buffer_data = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY) + imm.buffer_offset; +#else + imm.buffer_data = glMapBufferRange(GL_ARRAY_BUFFER, imm.buffer_offset, bytes_needed, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); +#endif + +#if TRUST_NO_ONE + assert(imm.buffer_data != NULL); +#endif + + imm.buffer_bytes_mapped = bytes_needed; + imm.vertex_data = imm.buffer_data; + } + +void immBeginAtMost(GLenum primitive, unsigned vertex_ct) + { + imm.strict_vertex_ct = false; + immBegin(primitive, vertex_ct); + } + +void immEnd() + { +#if TRUST_NO_ONE + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair +#endif + + unsigned buffer_bytes_used; + if (imm.strict_vertex_ct) + { +#if TRUST_NO_ONE + assert(imm.vertex_idx == imm.vertex_ct); // with all vertices defined +#endif + buffer_bytes_used = imm.buffer_bytes_mapped; + } + else + { +#if TRUST_NO_ONE + assert(imm.vertex_idx <= imm.vertex_ct); + assert(vertex_count_makes_sense_for_primitive(imm.vertex_idx, imm.primitive)); +#endif + // printf("used %u of %u verts,", imm.vertex_idx, imm.vertex_ct); + imm.vertex_ct = imm.vertex_idx; + buffer_bytes_used = vertex_buffer_size(&imm.vertex_format, imm.vertex_ct); + // unused buffer bytes are available to the next immBegin + // printf(" %u of %u bytes\n", buffer_bytes_used, imm.buffer_bytes_mapped); + } + +#if APPLE_LEGACY + // tell OpenGL what range was modified so it doesn't copy the whole buffer + glFlushMappedBufferRangeAPPLE(GL_ARRAY_BUFFER, imm.buffer_offset, buffer_bytes_used); +// printf("flushing %u to %u\n", imm.buffer_offset, imm.buffer_offset + buffer_bytes_used - 1); +#endif + glUnmapBuffer(GL_ARRAY_BUFFER); + + // set up VAO -- can be done during Begin or End really + glBindVertexArray(imm.vao_id); + + // enable/disable vertex attribs as needed + if (imm.attrib_binding.enabled_bits != imm.prev_enabled_attrib_bits) + { + for (unsigned loc = 0; loc < MAX_VERTEX_ATTRIBS; ++loc) + { + bool is_enabled = imm.attrib_binding.enabled_bits & (1 << loc); + bool was_enabled = imm.prev_enabled_attrib_bits & (1 << loc); + + if (is_enabled && !was_enabled) + { +// printf("enabling attrib %u\n", loc); + glEnableVertexAttribArray(loc); + } + else if (was_enabled && !is_enabled) + { +// printf("disabling attrib %u\n", loc); + glDisableVertexAttribArray(loc); + } + } + + imm.prev_enabled_attrib_bits = imm.attrib_binding.enabled_bits; + } + + const unsigned stride = imm.vertex_format.stride; + + for (unsigned a_idx = 0; a_idx < imm.vertex_format.attrib_ct; ++a_idx) + { + const Attrib* a = imm.vertex_format.attribs + a_idx; + + const unsigned offset = imm.buffer_offset + a->offset; + const GLvoid* pointer = (const GLubyte*)0 + offset; + + const unsigned loc = read_attrib_location(&imm.attrib_binding, a_idx); + +// printf("specifying attrib %u '%s' with offset %u, stride %u\n", loc, a->name, offset, stride); + + switch (a->fetch_mode) + { + case KEEP_FLOAT: + case CONVERT_INT_TO_FLOAT: + glVertexAttribPointer(loc, a->comp_ct, a->comp_type, GL_FALSE, stride, pointer); + break; + case NORMALIZE_INT_TO_FLOAT: + glVertexAttribPointer(loc, a->comp_ct, a->comp_type, GL_TRUE, stride, pointer); + break; + case KEEP_INT: + glVertexAttribIPointer(loc, a->comp_ct, a->comp_type, stride, pointer); + } + } + + glDrawArrays(imm.primitive, 0, imm.vertex_ct); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // prep for next immBegin + imm.buffer_offset += buffer_bytes_used; + imm.primitive = PRIM_NONE; + imm.strict_vertex_ct = true; + + // further optional cleanup + imm.buffer_bytes_mapped = 0; + imm.buffer_data = NULL; + imm.vertex_data = NULL; + } + +static void setAttribValueBit(unsigned attrib_id) + { + unsigned short mask = 1 << attrib_id; + +#if TRUST_NO_ONE + assert((imm.attrib_value_bits & mask) == 0); // not already set +#endif + + imm.attrib_value_bits |= mask; + } + +void immAttrib1f(unsigned attrib_id, float x) + { + Attrib* attrib = imm.vertex_format.attribs + attrib_id; + +#if TRUST_NO_ONE + assert(attrib_id < imm.vertex_format.attrib_ct); + assert(attrib->comp_type == GL_FLOAT); + assert(attrib->comp_ct == 1); + assert(imm.vertex_idx < imm.vertex_ct); + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair +#endif + + setAttribValueBit(attrib_id); + + float* data = (float*)(imm.vertex_data + attrib->offset); +// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); + + data[0] = x; + } + +void immAttrib2f(unsigned attrib_id, float x, float y) + { + Attrib* attrib = imm.vertex_format.attribs + attrib_id; + +#if TRUST_NO_ONE + assert(attrib_id < imm.vertex_format.attrib_ct); + assert(attrib->comp_type == GL_FLOAT); + assert(attrib->comp_ct == 2); + assert(imm.vertex_idx < imm.vertex_ct); + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair +#endif + + setAttribValueBit(attrib_id); + + float* data = (float*)(imm.vertex_data + attrib->offset); +// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); + + data[0] = x; + data[1] = y; + } + +void immAttrib3f(unsigned attrib_id, float x, float y, float z) + { + Attrib* attrib = imm.vertex_format.attribs + attrib_id; + +#if TRUST_NO_ONE + assert(attrib_id < imm.vertex_format.attrib_ct); + assert(attrib->comp_type == GL_FLOAT); + assert(attrib->comp_ct == 3); + assert(imm.vertex_idx < imm.vertex_ct); + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair +#endif + + setAttribValueBit(attrib_id); + + float* data = (float*)(imm.vertex_data + attrib->offset); +// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); + + data[0] = x; + data[1] = y; + data[2] = z; + } + +void immAttrib4f(unsigned attrib_id, float x, float y, float z, float w) + { + Attrib* attrib = imm.vertex_format.attribs + attrib_id; + +#if TRUST_NO_ONE + assert(attrib_id < imm.vertex_format.attrib_ct); + assert(attrib->comp_type == GL_FLOAT); + assert(attrib->comp_ct == 4); + assert(imm.vertex_idx < imm.vertex_ct); + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair +#endif + + setAttribValueBit(attrib_id); + + float* data = (float*)(imm.vertex_data + attrib->offset); +// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); + + data[0] = x; + data[1] = y; + data[2] = z; + data[3] = w; + } + +void immAttrib3fv(unsigned attrib_id, const float data[3]) + { + immAttrib3f(attrib_id, data[0], data[1], data[2]); + } + +void immAttrib4fv(unsigned attrib_id, const float data[4]) + { + immAttrib4f(attrib_id, data[0], data[1], data[2], data[3]); + } + +void immAttrib3ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b) + { + Attrib* attrib = imm.vertex_format.attribs + attrib_id; + +#if TRUST_NO_ONE + assert(attrib_id < imm.vertex_format.attrib_ct); + assert(attrib->comp_type == GL_UNSIGNED_BYTE); + assert(attrib->comp_ct == 3); + assert(imm.vertex_idx < imm.vertex_ct); + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair +#endif + + setAttribValueBit(attrib_id); + + GLubyte* data = imm.vertex_data + attrib->offset; +// printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); + + data[0] = r; + data[1] = g; + data[2] = b; + } + +void immAttrib4ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b, unsigned char a) + { + Attrib* attrib = imm.vertex_format.attribs + attrib_id; + +#if TRUST_NO_ONE + assert(attrib_id < imm.vertex_format.attrib_ct); + assert(attrib->comp_type == GL_UNSIGNED_BYTE); + assert(attrib->comp_ct == 4); + assert(imm.vertex_idx < imm.vertex_ct); + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair +#endif + + setAttribValueBit(attrib_id); + + GLubyte* data = imm.vertex_data + attrib->offset; +// printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); + + data[0] = r; + data[1] = g; + data[2] = b; + data[3] = a; + } + +void immAttrib3ubv(unsigned attrib_id, const unsigned char data[3]) + { + immAttrib3ub(attrib_id, data[0], data[1], data[2]); + } + +void immAttrib4ubv(unsigned attrib_id, const unsigned char data[4]) + { + immAttrib4ub(attrib_id, data[0], data[1], data[2], data[3]); + } + +void immEndVertex() + { +#if TRUST_NO_ONE + assert(imm.primitive != PRIM_NONE); // make sure we're between a Begin/End pair + assert(imm.vertex_idx < imm.vertex_ct); +#endif + + // have all attribs been assigned values? + // if not, copy value from previous vertex + const unsigned short all_bits = ~(0xFFFFU << imm.vertex_format.attrib_ct); + if (imm.attrib_value_bits != all_bits) + { +#if TRUST_NO_ONE + assert(imm.vertex_idx > 0); // first vertex must have all attribs specified +#endif + + for (unsigned a_idx = 0; a_idx < imm.vertex_format.attrib_ct; ++a_idx) + { + const uint16_t mask = 1 << a_idx; + if ((imm.attrib_value_bits & mask) == 0) + { + const Attrib* a = imm.vertex_format.attribs + a_idx; + +// printf("copying %s from vertex %u to %u\n", a->name, imm.vertex_idx - 1, imm.vertex_idx); + + GLubyte* data = imm.vertex_data + a->offset; + memcpy(data, data - imm.vertex_format.stride, a->sz); + } + } + } + + imm.vertex_idx++; + imm.vertex_data += imm.vertex_format.stride; + imm.attrib_value_bits = 0; + } + +void immVertex2f(unsigned attrib_id, float x, float y) + { + immAttrib2f(attrib_id, x, y); + immEndVertex(); + } + +void immVertex3f(unsigned attrib_id, float x, float y, float z) + { + immAttrib3f(attrib_id, x, y, z); + immEndVertex(); + } + +void immVertex2fv(unsigned attrib_id, const float data[2]) + { + immAttrib2f(attrib_id, data[0], data[1]); + immEndVertex(); + } + +void immVertex3fv(unsigned attrib_id, const float data[3]) + { + immAttrib3f(attrib_id, data[0], data[1], data[2]); + immEndVertex(); + } + +void immUniform4f(const char* name, float x, float y, float z, float w) + { + int loc = glGetUniformLocation(imm.bound_program, name); + +#if TRUST_NO_ONE + assert(loc != -1); +#endif + + glUniform4f(loc, x, y, z, w); + } + +void immUniformColor3ubv(const unsigned char rgb[3]) + { + const float scale = 1.0f / 255.0f; + immUniform4f("color", scale * rgb[0], scale * rgb[1], scale * rgb[2], 1.0f); + } + +void immUniformColor4ubv(const unsigned char rgba[4]) + { + const float scale = 1.0f / 255.0f; + immUniform4f("color", scale * rgba[0], scale * rgba[1], scale * rgba[2], rgba[3]); + } |