diff options
author | Mike Erwin <significant.bit@gmail.com> | 2016-09-13 09:41:43 +0300 |
---|---|---|
committer | Mike Erwin <significant.bit@gmail.com> | 2016-09-13 09:41:43 +0300 |
commit | ddb1d5648dbd6d5be8b7fd9df4d122e6df5b5ce3 (patch) | |
tree | ede288bb0a66c5188bffd5fc97ed492e0f4372bc /source/blender/gpu | |
parent | b6bd2993598712240bd354e4400d057732cda459 (diff) |
Gawain: geometry batches (unfinished)
Vertex Buffer to store vertex attribute data.
Element List (AKA Index Buffer) to select which vertices to use.
Batch combines these into an object that can be built once then drawn
many times.
Porting over from the C++ version… Most of this C code is compiled but
unused. Some of it is not even compiled. Committing now in case I’m
lost at sea.
Diffstat (limited to 'source/blender/gpu')
-rw-r--r-- | source/blender/gpu/CMakeLists.txt | 4 | ||||
-rw-r--r-- | source/blender/gpu/GPU_batch.h | 33 | ||||
-rw-r--r-- | source/blender/gpu/gawain/batch.c | 18 | ||||
-rw-r--r-- | source/blender/gpu/gawain/batch.h | 135 | ||||
-rw-r--r-- | source/blender/gpu/gawain/element.c | 253 | ||||
-rw-r--r-- | source/blender/gpu/gawain/element.h | 56 | ||||
-rw-r--r-- | source/blender/gpu/gawain/vertex_buffer.c | 14 | ||||
-rw-r--r-- | source/blender/gpu/gawain/vertex_buffer.h | 41 |
8 files changed, 554 insertions, 0 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 33ebb92791c..99ce3eb5bdf 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -63,8 +63,12 @@ set(SRC gawain/attrib_binding.c gawain/attrib_binding.h gawain/common.h + gawain/element.c + gawain/element.h gawain/immediate.c gawain/immediate.h + gawain/vertex_buffer.c + gawain/vertex_buffer.h gawain/vertex_format.c gawain/vertex_format.h diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h new file mode 100644 index 00000000000..10b00c3375f --- /dev/null +++ b/source/blender/gpu/GPU_batch.h @@ -0,0 +1,33 @@ +/* + * ***** 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 ***** + */ + +/* Batched geometry rendering is powered by the Gawain library. + * This file contains any additions or modifications specific to Blender. + */ + +#pragma once + +#include "gawain/batch.h" diff --git a/source/blender/gpu/gawain/batch.c b/source/blender/gpu/gawain/batch.c new file mode 100644 index 00000000000..3934d35df06 --- /dev/null +++ b/source/blender/gpu/gawain/batch.c @@ -0,0 +1,18 @@ + +// Gawain geometry batch +// +// 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 "batch.h" + +// BasicBatches +// Vertex buffer with 3D pos only +// Index buffer for edges (lines) +// Index buffer for surface (triangles) +// glGenBuffers(3,xxx) diff --git a/source/blender/gpu/gawain/batch.h b/source/blender/gpu/gawain/batch.h new file mode 100644 index 00000000000..9625d666a41 --- /dev/null +++ b/source/blender/gpu/gawain/batch.h @@ -0,0 +1,135 @@ + +// Gawain geometry batch +// +// 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_buffer.h" +#include "element.h" +#include "attrib_binding.h" + +// How will this API be used? +// create batch +// ... +// profit! + +// TODO: finalize Batch struct design & usage, pare down this file + +typedef struct { + VertexBuffer; // format is fixed at "vec3 pos" + ElementList line_elem; + ElementList triangle_elem; + GLuint vao_id; + GLenum prev_prim; // did most recent draw use GL_POINTS, GL_LINES or GL_TRIANGLES? +} BasicBatch; + +// How to do this without replicating code? + +typedef struct { + VertexBuffer* verts; + ElementList* elem; // <-- NULL if element list not needed + GLenum prim_type; + GLuint vao_id; + GLuint bound_program; + AttribBinding attrib_binding; +} Batch; + +// We often need a batch with its own data, to be created and discarded together. +// WithOwn variants reduce number of system allocations. + +typedef struct { + Batch batch; + VertexBuffer verts; // link batch.verts to this +} BatchWithOwnVertexBuffer; + +typedef struct { + Batch batch; + ElementList elem; // link batch.elem to this +} BatchWithOwnElementList; + +typedef struct { + Batch batch; + ElementList elem; // link batch.elem to this + VertexBuffer verts; // link batch.verts to this +} BatchWithOwnVertexBufferAndElementList; + +Batch* create_BatchWithOwnVertexBuffer(GLenum prim_type, VertexFormat*, unsigned v_ct, ElementList*); +Batch* create_BatchWithOwnElementList(GLenum prim_type, VertexBuffer*, unsigned prim_ct); +Batch* create_BatchWithOwnVertexBufferAndElementList(GLenum prim_type, VertexFormat*, unsigned v_ct, unsigned prim_ct); +// verts: shared, own +// elem: none, shared, own +Batch* create_BatchInGeneral(GLenum prim_type, VertexBufferStuff, ElementListStuff); + +typedef struct { + // geometry + GLenum prim_type; + VertexBuffer verts; + ElementList elem; // <-- elem.index_ct = 0 if element list not needed + + // book-keeping + GLuint vao_id; // <-- remembers all vertex state (array buffer, element buffer, attrib bindings) + // wait a sec... I thought VAO held attrib bindings but not currently bound array buffer. + // That's fine but verify that VAO holds *element* buffer binding. + // Verified: ELEMENT_ARRAY_BUFFER_BINDING is part of VAO state. + // VERTEX_ATTRIB_ARRAY_BUFFER_BINDING is too, per vertex attrib. Currently bound ARRAY_BUFFER is not. + // Does APPLE_vertex_array_object also include ELEMENT_ARRAY_BUFFER_BINDING? + // The extension spec refers only to APPLE_element_array, so.. maybe, maybe not? + // Will have to test during development, maybe alter behavior for APPLE_LEGACY. Can strip out this + // platform-specific cruft for Blender, keep it for legacy Gawain. + + // state + GLuint bound_program; + AttribBinding attrib_binding; +} Batch; + +typedef struct { + Batch* batch; +} BatchBuilder; + +// One batch can be drawn with multiple shaders, as long as those shaders' inputs +// are compatible with the batch's vertex format. + +// Can multiple batches share a VertexBuffer? Use ref count? + +// BasicBatch +// Create one VertexBuffer from an object's verts (3D position only) +// Shader must depend only on position + uniforms: uniform color, depth only, or object ID. +// - draw verts via DrawArrays +// - draw lines via DrawElements (can have 2 element lists: true face edges, triangulated edges) +// - draw faces via DrawElements (raw triangles, not polygon faces) +// This is very 3D-mesh-modeling specific. I'm investigating what Gawain needs to allow/expose +// to meet Blender's needs, possibly other programs' needs. + +Batch* BatchPlease(GLenum prim_type, unsigned prim_ct, unsigned v_ct); +// GL_TRIANGLES 12 triangles that share 8 vertices + +// Is there ever a reason to index GL_POINTS? nothing comes to mind... +// (later) ok now that I'm thinking straight, *of course* you can draw +// indexed POINTS. Only some verts from the buffer will be drawn. I was +// just limiting my thinking to immediate needs. Batched needs. + +Batch* batch = BatchPlease(GL_TRIANGLES, 12, 8); +unsigned pos = add_attrib(batch->verts.format, "pos", GL_FLOAT, 3, KEEP_FLOAT); +pack(batch->verts->format); // or ... +finalize(batch); // <-- packs vertex format, allocates vertex buffer + +Batch* create_Batch(GLenum prim_type, VertexBuffer*, ElementList*); + +// and don't forget +Batch* immBeginBatch(GLenum prim_type, unsigned v_ct); +// use standard immFunctions after this. immEnd will finalize the batch instead +// of drawing. + +typedef enum { + READY_TO_FORMAT, + READY_TO_BUILD, + BUILDING, BUILDING_IMM, // choose one + READY_TO_DRAW +} BatchPhase; diff --git a/source/blender/gpu/gawain/element.c b/source/blender/gpu/gawain/element.c new file mode 100644 index 00000000000..0ec31f6a45a --- /dev/null +++ b/source/blender/gpu/gawain/element.c @@ -0,0 +1,253 @@ + +// Gawain element list (AKA index buffer) +// +// 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 "element.h" +#include <stdlib.h> + +#define KEEP_SINGLE_COPY 1 + +unsigned ElementList_size(const ElementList* elem) + { + switch (elem->index_type) + { + case GL_UNSIGNED_BYTE: return elem->index_ct * sizeof(GLubyte); + case GL_UNSIGNED_SHORT: return elem->index_ct * sizeof(GLushort); + case GL_UNSIGNED_INT: return elem->index_ct * sizeof(GLuint); + } + + return 0; + } + +void ElementList_use(ElementList* elem) + { + if (elem->vbo_id) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elem->vbo_id); + else + { + glGenBuffers(1, &elem->vbo_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elem->vbo_id); + // fill with delicious data & send to GPU the first time only + glBufferData(GL_ELEMENT_ARRAY_BUFFER, ElementList_size(elem), elem->data, GL_STATIC_DRAW); + +#if KEEP_SINGLE_COPY + // now that GL has a copy, discard original + free(elem->data); + elem->data = NULL; +#endif + } + } + +void ElementList_done_using() + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + +void init_ElementListBuilder(ElementListBuilder* builder, GLenum prim_type, unsigned prim_ct, unsigned vertex_ct) + { + unsigned verts_per_prim; + switch (prim_type) + { + case GL_POINTS: + verts_per_prim = 1; + break; + case GL_LINES: + verts_per_prim = 2; + break; + case GL_TRIANGLES: + verts_per_prim = 3; + break; + default: + assert(false); + } + + builder->max_allowed_index = vertex_ct - 1; + builder->max_index_ct = prim_ct * verts_per_prim; + builder->index_ct = 0; // start empty + builder->prim_type = prim_type; + builder->data = calloc(builder->max_index_ct, sizeof(unsigned)); + } + +void add_generic_vertex(ElementListBuilder* builder, unsigned v) + { +#if TRUST_NO_ONE + assert(builder->data != NULL); + assert(builder->index_ct < builder->max_index_ct); + assert(v <= builder->max_allowed_index); +#endif + + builder->data[builder->index_ct++] = v; + } + +void add_point_vertex(ElementListBuilder* builder, unsigned v) + { +#if TRUST_NO_ONE + assert(builder->prim_type == GL_POINTS); +#endif + + add_generic_vertex(builder, v); + } + +void add_line_vertices(ElementListBuilder* builder, unsigned v1, unsigned v2) + { +#if TRUST_NO_ONE + assert(builder->prim_type == GL_LINES); + assert(v1 != v2); +#endif + + add_generic_vertex(builder, v1); + add_generic_vertex(builder, v2); + } + +void add_triangle_vertices(ElementListBuilder* builder, unsigned v1, unsigned v2, unsigned v3) + { +#if TRUST_NO_ONE + assert(builder->prim_type == GL_TRIANGLES); + assert(v1 != v2 && v2 != v3 && v3 != v1); +#endif + + add_generic_vertex(builder, v1); + add_generic_vertex(builder, v2); + add_generic_vertex(builder, v3); + } + +#if TRACK_INDEX_RANGE +// Everything remains 32 bit while building to keep things simple. +// Find min/max after, then convert to smallest index type possible. + +static unsigned index_range(const unsigned values[], unsigned value_ct, unsigned* min_out, unsigned* max_out) + { + unsigned min_value = values[0]; + unsigned max_value = values[0]; + for (unsigned i = 1; i < value_ct; ++i) + { + const unsigned value = values[i]; + if (value < min_value) + min_value = value; + else if (value > max_value) + max_value = value; + } + *min_out = min_value; + *max_out = max_value; + return max_value - min_value; + } + +static void squeeze_indices_byte(const unsigned values[], ElementList* elem) + { + const unsigned index_ct = elem->index_ct; + GLubyte* data = malloc(index_ct * sizeof(GLubyte)); + + if (elem->max_index > 0xFF) + { + const unsigned base = elem->min_index; + + elem->base_index = base; + elem->min_index = 0; + elem->max_index -= base; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (GLubyte)(values[i] - base); + } + else + { + elem->base_index = 0; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (GLubyte)(values[i]); + } + + elem->data = data; + } + +static void squeeze_indices_short(const unsigned values[], ElementList* elem) + { + const unsigned index_ct = elem->index_ct; + GLushort* data = malloc(index_ct * sizeof(GLushort)); + + if (elem->max_index > 0xFFFF) + { + const unsigned base = elem->min_index; + + elem->base_index = base; + elem->min_index = 0; + elem->max_index -= base; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (GLushort)(values[i] - base); + } + else + { + elem->base_index = 0; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (GLushort)(values[i]); + } + + elem->data = data; + } + +#endif // TRACK_INDEX_RANGE + +void build_ElementList(ElementListBuilder* builder, ElementList* elem) + { +#if TRUST_NO_ONE + assert(builder->data != NULL); +#endif + + elem->index_ct = builder->index_ct; + +#if TRACK_INDEX_RANGE + const unsigned range = index_range(builder->data, builder->index_ct, &elem->min_index, &elem->max_index); + + if (range <= 0xFF) + { + elem->index_type = GL_UNSIGNED_BYTE; + squeeze_indices_byte(builder->data, elem); + } + else if (range <= 0xFFFF) + { + elem->index_type = GL_UNSIGNED_SHORT; + squeeze_indices_short(builder->data, elem); + } + else + { + elem->index_type = GL_UNSIGNED_INT; + elem->base_index = 0; + + if (builder->index_ct < builder->max_index_ct) + { + builder->data = realloc(builder->data, builder->index_ct * sizeof(unsigned)); + // TODO: realloc only if index_ct is much smaller than max_index_ct + } + + elem->data = builder->data; + } +#else + if (builder->index_ct < builder->max_index_ct) + { + builder->data = realloc(builder->data, builder->index_ct * sizeof(unsigned)); + // TODO: realloc only if index_ct is much smaller than max_index_ct + } + + elem->data = builder->data; +#endif + + // elem->data will never be *larger* than builder->data... how about converting + // in place to avoid extra allocation? + + elem->vbo_id = 0; + // TODO: create GL buffer object directly, based on an input flag + + // discard builder (one-time use) + if (builder->data != elem->data) + free(builder->data); + builder->data = NULL; + // other fields are safe to leave + } diff --git a/source/blender/gpu/gawain/element.h b/source/blender/gpu/gawain/element.h new file mode 100644 index 00000000000..c092dba721f --- /dev/null +++ b/source/blender/gpu/gawain/element.h @@ -0,0 +1,56 @@ + +// Gawain element list (AKA index buffer) +// +// 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 TRACK_INDEX_RANGE 1 + +typedef struct { + unsigned index_ct; +#if TRACK_INDEX_RANGE + GLenum index_type; + unsigned min_index; + unsigned max_index; + unsigned base_index; +#endif + void* data; // NULL indicates data in VRAM (unmapped) or not yet allocated + GLuint vbo_id; // 0 indicates not yet sent to VRAM +} ElementList; + +void ElementList_use(ElementList*); +void ElementList_done_using(void); +unsigned ElementList_size(const ElementList*); + +typedef struct { + unsigned max_allowed_index; + unsigned max_index_ct; + unsigned index_ct; + GLenum prim_type; + unsigned* data; +} ElementListBuilder; + +// supported primitives: +// GL_POINTS +// GL_LINES +// GL_TRIANGLES + +void init_ElementListBuilder(ElementListBuilder*, GLenum prim_type, unsigned prim_ct, unsigned vertex_ct); +//void init_CustomElementListBuilder(ElementListBuilder*, GLenum prim_type, unsigned index_ct, unsigned vertex_ct); + +void add_generic_vertex(ElementListBuilder*, unsigned v); + +void add_point_vertex(ElementListBuilder*, unsigned v); +void add_line_vertices(ElementListBuilder*, unsigned v1, unsigned v2); +void add_triangle_vertices(ElementListBuilder*, unsigned v1, unsigned v2, unsigned v3); + +void build_ElementList(ElementListBuilder*, ElementList*); diff --git a/source/blender/gpu/gawain/vertex_buffer.c b/source/blender/gpu/gawain/vertex_buffer.c new file mode 100644 index 00000000000..1a9a9cdc081 --- /dev/null +++ b/source/blender/gpu/gawain/vertex_buffer.c @@ -0,0 +1,14 @@ + +// Gawain geometry batch +// +// 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_buffer.h" + +// TODO: implement C functions diff --git a/source/blender/gpu/gawain/vertex_buffer.h b/source/blender/gpu/gawain/vertex_buffer.h new file mode 100644 index 00000000000..da4f614c2bd --- /dev/null +++ b/source/blender/gpu/gawain/vertex_buffer.h @@ -0,0 +1,41 @@ + +// Gawain geometry batch +// +// 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 { + VertexFormat format; + unsigned vertex_ct; + GLubyte* data; // NULL indicates data in VRAM (unmapped) or not yet allocated + GLuint vbo_id; // 0 indicates not yet sent to VRAM +} VertexBuffer; + +VertexBuffer* create_VertexBuffer(VertexFormat*, unsigned v_ct); // create means allocate, then init +void init_VertexBuffer(VertexBuffer*, VertexFormat*, unsigned v_ct); + +// The most important setAttrib variant is the untyped one. Get it right first. +// It takes a void* so the app developer is responsible for matching their app data types +// to the vertex attribute's type and component count. They're in control of both, so this +// should not be a problem. + +void setAttrib(VertexBuffer*, unsigned a_idx, unsigned v_idx, const void* data); +void fillAttrib(VertexBuffer*, unsigned a_idx, const void* data); +void fillAttribStride(VertexBuffer*, unsigned a_idx, unsigned stride, const void* data); + +// void setAttrib1f(unsigned a_idx, unsigned v_idx, float x); +// void setAttrib2f(unsigned a_idx, unsigned v_idx, float x, float y); +// void setAttrib3f(unsigned a_idx, unsigned v_idx, float x, float y, float z); +// void setAttrib4f(unsigned a_idx, unsigned v_idx, float x, float y, float z, float w); +// +// void setAttrib3ub(unsigned a_idx, unsigned v_idx, unsigned char r, unsigned char g, unsigned char b); +// void setAttrib4ub(unsigned a_idx, unsigned v_idx, unsigned char r, unsigned char g, unsigned char b, unsigned char a); |