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 + +#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);