From b6bd2993598712240bd354e4400d057732cda459 Mon Sep 17 00:00:00 2001 From: Mike Erwin Date: Tue, 13 Sep 2016 02:18:33 -0400 Subject: Gawain: reorganize source code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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…) --- source/blender/gpu/CMakeLists.txt | 10 +- source/blender/gpu/GPU_immediate.h | 126 ++--- source/blender/gpu/gawain/attrib_binding.c | 79 +++ source/blender/gpu/gawain/attrib_binding.h | 27 + source/blender/gpu/gawain/common.h | 40 ++ source/blender/gpu/gawain/immediate.c | 555 +++++++++++++++++++++ source/blender/gpu/gawain/immediate.h | 57 +++ source/blender/gpu/gawain/vertex_format.c | 150 ++++++ source/blender/gpu/gawain/vertex_format.h | 47 ++ source/blender/gpu/intern/gpu_immediate.c | 776 ----------------------------- 10 files changed, 994 insertions(+), 873 deletions(-) create mode 100644 source/blender/gpu/gawain/attrib_binding.c create mode 100644 source/blender/gpu/gawain/attrib_binding.h create mode 100644 source/blender/gpu/gawain/common.h create mode 100644 source/blender/gpu/gawain/immediate.c create mode 100644 source/blender/gpu/gawain/immediate.h create mode 100644 source/blender/gpu/gawain/vertex_format.c create mode 100644 source/blender/gpu/gawain/vertex_format.h delete mode 100644 source/blender/gpu/intern/gpu_immediate.c (limited to 'source') diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 40710046cc2..33ebb92791c 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -54,13 +54,20 @@ set(SRC intern/gpu_draw.c intern/gpu_extensions.c intern/gpu_framebuffer.c - intern/gpu_immediate.c intern/gpu_init_exit.c intern/gpu_material.c intern/gpu_select.c intern/gpu_shader.c intern/gpu_texture.c + gawain/attrib_binding.c + gawain/attrib_binding.h + gawain/common.h + gawain/immediate.c + gawain/immediate.h + gawain/vertex_format.c + gawain/vertex_format.h + shaders/gpu_shader_fx_lib.glsl shaders/gpu_shader_fx_ssao_frag.glsl shaders/gpu_shader_fx_dof_frag.glsl @@ -97,6 +104,7 @@ set(SRC GPU_select.h GPU_shader.h GPU_texture.h + intern/gpu_codegen.h intern/gpu_private.h ) diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h index ffd0a6d5afc..8b882751aca 100644 --- a/source/blender/gpu/GPU_immediate.h +++ b/source/blender/gpu/GPU_immediate.h @@ -1,99 +1,33 @@ - -// Gawain immediate mode work-alike, take 2 -// -// This code is part of the Gawain library, with modifications -// specific to integration with Blender. -// -// Copyright 2016 Mike Erwin -// -// 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 +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Mike Erwin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* Immediate mode rendering is powered by the Gawain library. + * This file contains any additions or modifications specific to Blender. + */ #pragma once -#include "GPU_glew.h" -#include - -#define PER_THREAD -// #define PER_THREAD __thread -// MSVC uses __declspec(thread) for C code - -#define MAX_VERTEX_ATTRIBS 16 - -#define TRUST_NO_ONE 1 - -typedef enum { - KEEP_FLOAT, - KEEP_INT, - NORMALIZE_INT_TO_FLOAT, // 127 (ubyte) -> 0.5 (and so on for other int types) - CONVERT_INT_TO_FLOAT // 127 (any int type) -> 127.0 -} VertexFetchMode; - -typedef struct { - GLenum comp_type; - unsigned comp_ct; // 1 to 4 - unsigned sz; // size in bytes, 1 to 16 - unsigned offset; // from beginning of vertex, in bytes - VertexFetchMode fetch_mode; - char* name; // TODO: shared allocation of all names within a VertexFormat -} Attrib; - -typedef struct { - unsigned attrib_ct; // 0 to 16 (MAX_VERTEX_ATTRIBS) - unsigned stride; // stride in bytes, 1 to 256 - bool packed; - Attrib attribs[MAX_VERTEX_ATTRIBS]; // TODO: variable-size attribs array -} VertexFormat; - -void clear_VertexFormat(VertexFormat*); -unsigned add_attrib(VertexFormat*, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode); -void pack(VertexFormat*); -// unsigned attrib_idx(const VertexFormat*, const char* name); -void bind_attrib_locations(const VertexFormat*, GLuint program); - -// --- immediate mode work-alike -------------------------------- - -void immInit(void); -void immDestroy(void); - -VertexFormat* immVertexFormat(void); // returns a cleared vertex format, ready for add_attrib - -void immBindProgram(GLuint program); -void immUnbindProgram(void); - -void immBegin(GLenum primitive, unsigned vertex_ct); // must supply exactly vertex_ct vertices -void immBeginAtMost(GLenum primitive, unsigned max_vertex_ct); // can supply fewer vertices -void immEnd(void); - -void immAttrib1f(unsigned attrib_id, float x); -void immAttrib2f(unsigned attrib_id, float x, float y); -void immAttrib3f(unsigned attrib_id, float x, float y, float z); -void immAttrib4f(unsigned attrib_id, float x, float y, float z, float w); - -void immAttrib3fv(unsigned attrib_id, const float data[3]); -void immAttrib4fv(unsigned attrib_id, const float data[4]); - -void immAttrib3ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b); -void immAttrib4ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b, unsigned char a); - -void immAttrib3ubv(unsigned attrib_id, const unsigned char data[4]); -void immAttrib4ubv(unsigned attrib_id, const unsigned char data[4]); - -void immEndVertex(void); // and move on to the next vertex - -// provide 2D or 3D attribute value and end the current vertex, similar to glVertex: -void immVertex2f(unsigned attrib_id, float x, float y); -void immVertex3f(unsigned attrib_id, float x, float y, float z); - -void immVertex2fv(unsigned attrib_id, const float data[2]); -void immVertex3fv(unsigned attrib_id, const float data[3]); - -// provide values that don't change for the entire draw call -void immUniform4f(const char* name, float x, float y, float z, float w); - -// these set "uniform vec4 color" -// TODO: treat as sRGB? -void immUniformColor3ubv(const unsigned char data[3]); -void immUniformColor4ubv(const unsigned char data[4]); +#include "gawain/immediate.h" diff --git a/source/blender/gpu/gawain/attrib_binding.c b/source/blender/gpu/gawain/attrib_binding.c new file mode 100644 index 00000000000..902f33a133e --- /dev/null +++ b/source/blender/gpu/gawain/attrib_binding.c @@ -0,0 +1,79 @@ + +// Gawain vertex attribute binding +// +// 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 "attrib_binding.h" + +void bind_attrib_locations(const VertexFormat* format, GLuint program) + { +#if TRUST_NO_ONE + assert(glIsProgram(program)); +#endif + + for (unsigned a_idx = 0; a_idx < format->attrib_ct; ++a_idx) + { + const Attrib* a = format->attribs + a_idx; + glBindAttribLocation(program, a_idx, a->name); + } + } + +void clear_AttribBinding(AttribBinding* binding) + { + binding->loc_bits = 0; + binding->enabled_bits = 0; + } + +unsigned read_attrib_location(const AttribBinding* binding, unsigned a_idx) + { +#if TRUST_NO_ONE + assert(MAX_VERTEX_ATTRIBS == 16); + assert(a_idx < MAX_VERTEX_ATTRIBS); + assert(binding->enabled_bits & (1 << a_idx)); +#endif + + return (binding->loc_bits >> (4 * a_idx)) & 0xF; + } + +void write_attrib_location(AttribBinding* binding, unsigned a_idx, unsigned location) + { +#if TRUST_NO_ONE + assert(MAX_VERTEX_ATTRIBS == 16); + assert(a_idx < MAX_VERTEX_ATTRIBS); + assert(location < MAX_VERTEX_ATTRIBS); +#endif + + const unsigned shift = 4 * a_idx; + const uint64_t mask = ((uint64_t)0xF) << shift; + // overwrite this attrib's previous location + binding->loc_bits = (binding->loc_bits & ~mask) | (location << shift); + // mark this attrib as enabled + binding->enabled_bits |= 1 << a_idx; + } + +void get_attrib_locations(const VertexFormat* format, AttribBinding* binding, GLuint program) + { +#if TRUST_NO_ONE + assert(glIsProgram(program)); +#endif + + clear_AttribBinding(binding); + + for (unsigned a_idx = 0; a_idx < format->attrib_ct; ++a_idx) + { + const Attrib* a = format->attribs + a_idx; + GLint loc = glGetAttribLocation(program, a->name); + +#if TRUST_NO_ONE + assert(loc != -1); +#endif + + write_attrib_location(binding, a_idx, loc); + } + } diff --git a/source/blender/gpu/gawain/attrib_binding.h b/source/blender/gpu/gawain/attrib_binding.h new file mode 100644 index 00000000000..f22939cf1a0 --- /dev/null +++ b/source/blender/gpu/gawain/attrib_binding.h @@ -0,0 +1,27 @@ + +// Gawain vertex attribute binding +// +// 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/. + +#pragma once + +#include "vertex_format.h" + +typedef struct { + uint64_t loc_bits; // store 4 bits for each of the 16 attribs + uint16_t enabled_bits; // 1 bit for each attrib +} AttribBinding; + +void clear_AttribBinding(AttribBinding*); + +unsigned read_attrib_location(const AttribBinding*, unsigned a_idx); +void write_attrib_location(AttribBinding*, unsigned a_idx, unsigned location); + +void get_attrib_locations(const VertexFormat*, AttribBinding*, GLuint program); +void bind_attrib_locations(const VertexFormat*, GLuint program); diff --git a/source/blender/gpu/gawain/common.h b/source/blender/gpu/gawain/common.h new file mode 100644 index 00000000000..77b953bd84a --- /dev/null +++ b/source/blender/gpu/gawain/common.h @@ -0,0 +1,40 @@ + +// Gawain common #defines and #includes +// +// 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/. + +#pragma once + +#define TRUST_NO_ONE 1 + +#include +#include + +#if TRUST_NO_ONE + #include +#endif + +#define PER_THREAD +// #define PER_THREAD __thread +// MSVC uses __declspec(thread) for C code + +#define APPLE_LEGACY (defined(__APPLE__) && defined(WITH_GL_PROFILE_COMPAT)) + +#if APPLE_LEGACY + #undef glGenVertexArrays + #define glGenVertexArrays glGenVertexArraysAPPLE + + #undef glDeleteVertexArrays + #define glDeleteVertexArrays glDeleteVertexArraysAPPLE + + #undef glBindVertexArray + #define glBindVertexArray glBindVertexArrayAPPLE +#endif + +#define PRIM_NONE 0xF 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 + +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]); + } diff --git a/source/blender/gpu/gawain/immediate.h b/source/blender/gpu/gawain/immediate.h new file mode 100644 index 00000000000..9a013528aa8 --- /dev/null +++ b/source/blender/gpu/gawain/immediate.h @@ -0,0 +1,57 @@ + +// 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/. + +#pragma once + +#include "vertex_format.h" + +void immInit(void); +void immDestroy(void); + +VertexFormat* immVertexFormat(void); // returns a cleared vertex format, ready for add_attrib + +void immBindProgram(GLuint program); +void immUnbindProgram(void); + +void immBegin(GLenum primitive, unsigned vertex_ct); // must supply exactly vertex_ct vertices +void immBeginAtMost(GLenum primitive, unsigned max_vertex_ct); // can supply fewer vertices +void immEnd(void); + +void immAttrib1f(unsigned attrib_id, float x); +void immAttrib2f(unsigned attrib_id, float x, float y); +void immAttrib3f(unsigned attrib_id, float x, float y, float z); +void immAttrib4f(unsigned attrib_id, float x, float y, float z, float w); + +void immAttrib3fv(unsigned attrib_id, const float data[3]); +void immAttrib4fv(unsigned attrib_id, const float data[4]); + +void immAttrib3ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b); +void immAttrib4ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +void immAttrib3ubv(unsigned attrib_id, const unsigned char data[4]); +void immAttrib4ubv(unsigned attrib_id, const unsigned char data[4]); + +void immEndVertex(void); // and move on to the next vertex + +// provide 2D or 3D attribute value and end the current vertex, similar to glVertex: +void immVertex2f(unsigned attrib_id, float x, float y); +void immVertex3f(unsigned attrib_id, float x, float y, float z); + +void immVertex2fv(unsigned attrib_id, const float data[2]); +void immVertex3fv(unsigned attrib_id, const float data[3]); + +// provide values that don't change for the entire draw call +void immUniform4f(const char* name, float x, float y, float z, float w); + +// these set "uniform vec4 color" +// TODO: treat as sRGB? +void immUniformColor3ubv(const unsigned char data[3]); +void immUniformColor4ubv(const unsigned char data[4]); diff --git a/source/blender/gpu/gawain/vertex_format.c b/source/blender/gpu/gawain/vertex_format.c new file mode 100644 index 00000000000..3f373b2feae --- /dev/null +++ b/source/blender/gpu/gawain/vertex_format.c @@ -0,0 +1,150 @@ + +// Gawain vertex format +// +// 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 "vertex_format.h" +#include +#include + +#define PACK_DEBUG 0 + +#if PACK_DEBUG + #include +#endif + +void clear_VertexFormat(VertexFormat* format) + { + for (unsigned a = 0; a < format->attrib_ct; ++a) + free(format->attribs[a].name); + +#if TRUST_NO_ONE + memset(format, 0, sizeof(VertexFormat)); +#else + format->attrib_ct = 0; + format->packed = false; +#endif + } + +static unsigned comp_sz(GLenum type) + { +#if TRUST_NO_ONE + assert(type >= GL_BYTE && type <= GL_FLOAT); +#endif + + const GLubyte sizes[] = {1,1,2,2,4,4,4}; + return sizes[type - GL_BYTE]; + } + +static unsigned attrib_sz(const Attrib *a) + { + return a->comp_ct * comp_sz(a->comp_type); + } + +static unsigned attrib_align(const Attrib *a) + { + unsigned c = comp_sz(a->comp_type); + if (a->comp_ct == 3 && c <= 2) + return 4 * c; // AMD HW can't fetch these well, so pad it out (other vendors too?) + else + return c; // most fetches are ok if components are naturally aligned + } + +unsigned vertex_buffer_size(const VertexFormat* format, unsigned vertex_ct) + { +#if TRUST_NO_ONE + assert(format->packed && format->stride > 0); +#endif + + return format->stride * vertex_ct; + } + +unsigned add_attrib(VertexFormat* format, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode fetch_mode) + { +#if TRUST_NO_ONE + assert(format->attrib_ct < MAX_VERTEX_ATTRIBS); // there's room for more + assert(!format->packed); // packed means frozen/locked +#endif + + const unsigned attrib_id = format->attrib_ct++; + Attrib* attrib = format->attribs + attrib_id; + + attrib->name = strdup(name); + attrib->comp_type = comp_type; + attrib->comp_ct = comp_ct; + attrib->sz = attrib_sz(attrib); + attrib->offset = 0; // offsets & stride are calculated later (during pack) + attrib->fetch_mode = fetch_mode; + + return attrib_id; + } + +unsigned padding(unsigned offset, unsigned alignment) + { + const unsigned mod = offset % alignment; + return (mod == 0) ? 0 : (alignment - mod); + } + +#if PACK_DEBUG +static void show_pack(unsigned a_idx, unsigned sz, unsigned pad) + { + const char c = 'A' + a_idx; + for (unsigned i = 0; i < pad; ++i) + putchar('-'); + for (unsigned i = 0; i < sz; ++i) + putchar(c); + } +#endif + +void pack(VertexFormat* format) + { + // for now, attributes are packed in the order they were added, + // making sure each attrib is naturally aligned (add padding where necessary) + + // later we can implement more efficient packing w/ reordering + // (keep attrib ID order, adjust their offsets to reorder in buffer) + + // TODO: concatentate name strings into attribs[0].name, point attribs[i] to + // offset into the combined string. Free all other name strings. Could save more + // space by storing combined string in VertexFormat, with each attrib having an + // offset into it. Could also append each name string as it's added... pack() + // could alloc just enough to hold the final combo string. And just enough to + // hold used attribs, not all 16. + + Attrib* a0 = format->attribs + 0; + a0->offset = 0; + unsigned offset = a0->sz; + +#if PACK_DEBUG + show_pack(0, a0->sz, 0); +#endif + + for (unsigned a_idx = 1; a_idx < format->attrib_ct; ++a_idx) + { + Attrib* a = format->attribs + a_idx; + unsigned mid_padding = padding(offset, attrib_align(a)); + offset += mid_padding; + a->offset = offset; + offset += a->sz; + +#if PACK_DEBUG + show_pack(a_idx, a->sz, mid_padding); +#endif + } + + unsigned end_padding = padding(offset, attrib_align(a0)); + +#if PACK_DEBUG + show_pack(0, 0, end_padding); + putchar('\n'); +#endif + + format->stride = offset + end_padding; + format->packed = true; + } diff --git a/source/blender/gpu/gawain/vertex_format.h b/source/blender/gpu/gawain/vertex_format.h new file mode 100644 index 00000000000..7921a47079f --- /dev/null +++ b/source/blender/gpu/gawain/vertex_format.h @@ -0,0 +1,47 @@ + +// Gawain vertex format +// +// 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/. + +#pragma once + +#include "common.h" + +#define MAX_VERTEX_ATTRIBS 16 + +typedef enum { + KEEP_FLOAT, + KEEP_INT, + NORMALIZE_INT_TO_FLOAT, // 127 (ubyte) -> 0.5 (and so on for other int types) + CONVERT_INT_TO_FLOAT // 127 (any int type) -> 127.0 +} VertexFetchMode; + +typedef struct { + GLenum comp_type; + unsigned comp_ct; // 1 to 4 + unsigned sz; // size in bytes, 1 to 16 + unsigned offset; // from beginning of vertex, in bytes + VertexFetchMode fetch_mode; + char* name; // TODO: shared allocation of all names within a VertexFormat +} Attrib; + +typedef struct { + unsigned attrib_ct; // 0 to 16 (MAX_VERTEX_ATTRIBS) + unsigned stride; // stride in bytes, 1 to 256 + bool packed; + Attrib attribs[MAX_VERTEX_ATTRIBS]; // TODO: variable-size attribs array +} VertexFormat; + +void clear_VertexFormat(VertexFormat*); +unsigned add_attrib(VertexFormat*, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode); + +// for internal use +void pack(VertexFormat*); +unsigned padding(unsigned offset, unsigned alignment); +unsigned vertex_buffer_size(const VertexFormat*, unsigned vertex_ct); diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.c deleted file mode 100644 index 98568d14482..00000000000 --- a/source/blender/gpu/intern/gpu_immediate.c +++ /dev/null @@ -1,776 +0,0 @@ - -// Gawain immediate mode work-alike, take 2 -// -// This code is part of the Gawain library, with modifications -// specific to integration with Blender. -// -// Copyright 2016 Mike Erwin -// -// 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 - -#include "GPU_immediate.h" -#include -#include -#include -#include - -#define PACK_DEBUG 0 - -#if PACK_DEBUG - #include -#endif - -#define APPLE_LEGACY (defined(__APPLE__) && defined(WITH_GL_PROFILE_COMPAT)) - -#if APPLE_LEGACY - #undef glGenVertexArrays - #define glGenVertexArrays glGenVertexArraysAPPLE - - #undef glDeleteVertexArrays - #define glDeleteVertexArrays glDeleteVertexArraysAPPLE - - #undef glBindVertexArray - #define glBindVertexArray glBindVertexArrayAPPLE -#endif - -#define PRIM_NONE 0xF - -void clear_VertexFormat(VertexFormat* format) - { - for (unsigned a = 0; a < format->attrib_ct; ++a) - free(format->attribs[a].name); - -#if TRUST_NO_ONE - memset(format, 0, sizeof(VertexFormat)); -#else - format->attrib_ct = 0; - format->packed = false; -#endif - } - -static unsigned comp_sz(GLenum type) - { -#if TRUST_NO_ONE - assert(type >= GL_BYTE && type <= GL_FLOAT); -#endif - - const GLubyte sizes[] = {1,1,2,2,4,4,4}; - return sizes[type - GL_BYTE]; - } - -static unsigned attrib_sz(const Attrib *a) - { - return a->comp_ct * comp_sz(a->comp_type); - } - -static unsigned attrib_align(const Attrib *a) - { - unsigned c = comp_sz(a->comp_type); - if (a->comp_ct == 3 && c <= 2) - return 4 * c; // AMD HW can't fetch these well, so pad it out (other vendors too?) - else - return c; // most fetches are ok if components are naturally aligned - } - -static unsigned vertex_buffer_size(const VertexFormat* format, unsigned vertex_ct) - { -#if TRUST_NO_ONE - assert(format->packed && format->stride > 0); -#endif - - return format->stride * vertex_ct; - } - -unsigned add_attrib(VertexFormat* format, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode fetch_mode) - { -#if TRUST_NO_ONE - assert(format->attrib_ct < MAX_VERTEX_ATTRIBS); // there's room for more - assert(!format->packed); // packed means frozen/locked -#endif - - const unsigned attrib_id = format->attrib_ct++; - Attrib* attrib = format->attribs + attrib_id; - - attrib->name = strdup(name); - attrib->comp_type = comp_type; - attrib->comp_ct = comp_ct; - attrib->sz = attrib_sz(attrib); - attrib->offset = 0; // offsets & stride are calculated later (during pack) - attrib->fetch_mode = fetch_mode; - - return attrib_id; - } - -static unsigned padding(unsigned offset, unsigned alignment) - { - const unsigned mod = offset % alignment; - return (mod == 0) ? 0 : (alignment - mod); - } - -#if PACK_DEBUG -static void show_pack(unsigned a_idx, unsigned sz, unsigned pad) - { - const char c = 'A' + a_idx; - for (unsigned i = 0; i < pad; ++i) - putchar('-'); - for (unsigned i = 0; i < sz; ++i) - putchar(c); - } -#endif - -void pack(VertexFormat* format) - { - // for now, attributes are packed in the order they were added, - // making sure each attrib is naturally aligned (add padding where necessary) - - // later we can implement more efficient packing w/ reordering - - Attrib* a0 = format->attribs + 0; - a0->offset = 0; - unsigned offset = a0->sz; - -#if PACK_DEBUG - show_pack(0, a0->sz, 0); -#endif - - for (unsigned a_idx = 1; a_idx < format->attrib_ct; ++a_idx) - { - Attrib* a = format->attribs + a_idx; - unsigned mid_padding = padding(offset, attrib_align(a)); - offset += mid_padding; - a->offset = offset; - offset += a->sz; - -#if PACK_DEBUG - show_pack(a_idx, a->sz, mid_padding); -#endif - } - - unsigned end_padding = padding(offset, attrib_align(a0)); - -#if PACK_DEBUG - show_pack(0, 0, end_padding); - putchar('\n'); -#endif - - format->stride = offset + end_padding; - format->packed = true; - } - -void bind_attrib_locations(const VertexFormat* format, GLuint program) - { -#if TRUST_NO_ONE - assert(glIsProgram(program)); -#endif - - for (unsigned a_idx = 0; a_idx < format->attrib_ct; ++a_idx) - { - const Attrib* a = format->attribs + a_idx; - glBindAttribLocation(program, a_idx, a->name); - } - } - -typedef struct { - uint64_t loc_bits; // store 4 bits for each of the 16 attribs - uint16_t enabled_bits; // 1 bit for each attrib -} AttribBinding; - -void clear_AttribBinding(AttribBinding* binding) - { - binding->loc_bits = 0; - binding->enabled_bits = 0; - } - -unsigned read_attrib_location(const AttribBinding* binding, unsigned a_idx) - { -#if TRUST_NO_ONE - assert(MAX_VERTEX_ATTRIBS == 16); - assert(a_idx < MAX_VERTEX_ATTRIBS); - assert(binding->enabled_bits & (1 << a_idx)); -#endif - - return (binding->loc_bits >> (4 * a_idx)) & 0xF; - } - -void write_attrib_location(AttribBinding* binding, unsigned a_idx, unsigned location) - { -#if TRUST_NO_ONE - assert(MAX_VERTEX_ATTRIBS == 16); - assert(a_idx < MAX_VERTEX_ATTRIBS); - assert(location < MAX_VERTEX_ATTRIBS); -#endif - - const unsigned shift = 4 * a_idx; - const uint64_t mask = ((uint64_t)0xF) << shift; - // overwrite this attrib's previous location - binding->loc_bits = (binding->loc_bits & ~mask) | (location << shift); - // mark this attrib as enabled - binding->enabled_bits |= 1 << a_idx; - } - -void get_attrib_locations(const VertexFormat* format, AttribBinding* binding, GLuint program) - { -#if TRUST_NO_ONE - assert(glIsProgram(program)); -#endif - - clear_AttribBinding(binding); - - for (unsigned a_idx = 0; a_idx < format->attrib_ct; ++a_idx) - { - const Attrib* a = format->attribs + a_idx; - GLint loc = glGetAttribLocation(program, a->name); - -#if TRUST_NO_ONE - assert(loc != -1); -#endif - - write_attrib_location(binding, a_idx, loc); - } - } - -// --- immediate mode work-alike -------------------------------- - -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; -} 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]); - } -- cgit v1.2.3