diff options
author | Mike Erwin <significant.bit@gmail.com> | 2016-09-15 17:51:10 +0300 |
---|---|---|
committer | Mike Erwin <significant.bit@gmail.com> | 2016-09-15 17:51:10 +0300 |
commit | 39f7a8117692da3a6645ccb7ae2080077f9e5980 (patch) | |
tree | 0062f4acb59e8afa5ebb8e9e811e107b9de9c04f /source | |
parent | 0d54d32dd608be5d87d3d57131ede33a8f19aa37 (diff) |
Gawain: batch rendering API
Follow-up to rBddb1d5648dbd
API is nearly complete but untested.
1) create batch with vertex buffer & optional index buffer
2) choose shader program
3) draw!
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/gpu/CMakeLists.txt | 3 | ||||
-rw-r--r-- | source/blender/gpu/gawain/batch.c | 108 | ||||
-rw-r--r-- | source/blender/gpu/gawain/batch.h | 130 | ||||
-rw-r--r-- | source/blender/gpu/gawain/element.c | 42 | ||||
-rw-r--r-- | source/blender/gpu/gawain/element.h | 7 | ||||
-rw-r--r-- | source/blender/gpu/gawain/vertex_buffer.c | 34 | ||||
-rw-r--r-- | source/blender/gpu/gawain/vertex_buffer.h | 8 |
7 files changed, 209 insertions, 123 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 50c2db2544c..63ae58dad59 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -62,6 +62,8 @@ set(SRC gawain/attrib_binding.c gawain/attrib_binding.h + gawain/batch.c + gawain/batch.h gawain/common.h gawain/element.c gawain/element.h @@ -95,6 +97,7 @@ set(SRC shaders/gpu_shader_smoke_vert.glsl GPU_basic_shader.h + GPU_batch.h GPU_buffers.h GPU_compositing.h GPU_debug.h diff --git a/source/blender/gpu/gawain/batch.c b/source/blender/gpu/gawain/batch.c index 3934d35df06..96bf6dd1b5a 100644 --- a/source/blender/gpu/gawain/batch.c +++ b/source/blender/gpu/gawain/batch.c @@ -10,9 +10,107 @@ // the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. #include "batch.h" +#include <stdlib.h> -// BasicBatches -// Vertex buffer with 3D pos only -// Index buffer for edges (lines) -// Index buffer for surface (triangles) -// glGenBuffers(3,xxx) +Batch* Batch_create(GLenum prim_type, VertexBuffer* verts, ElementList* elem) + { +#if TRUST_NO_ONE + assert(verts != NULL); + assert(prim_type == GL_POINTS || prim_type == GL_LINES || prim_type == GL_TRIANGLES); + // we will allow other primitive types in a future update +#endif + + Batch* batch = calloc(1, sizeof(Batch)); + + batch->verts = verts; + batch->elem = elem; + batch->prim_type = prim_type; + + return batch; + } + +void Batch_set_program(Batch* batch, GLuint program) + { + batch->program = program; + batch->program_dirty = true; + } + +static void Batch_update_program_bindings(Batch* batch) + { +#if TRUST_NO_ONE + assert(glIsProgram(program)); +#endif + + const VertexFormat* format = &batch->verts->format; + + const unsigned attrib_ct = format->attrib_ct; + const unsigned stride = format->stride; + + for (unsigned a_idx = 0; a_idx < attrib_ct; ++a_idx) + { + const Attrib* a = format->attribs + a_idx; + + const GLvoid* pointer = (const GLubyte*)0 + a->offset; + + const unsigned loc = glGetAttribLocation(batch->program, a->name); + + glEnableVertexAttribArray(loc); + + 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); + } + } + + batch->program_dirty = false; + } + +static void Batch_prime(Batch* batch) + { + glGenVertexArrays(1, &batch->vao_id); + glBindVertexArray(batch->vao_id); + + VertexBuffer_use(batch->verts); + + if (batch->elem) + ElementList_use(batch->elem); + + // vertex attribs and element list remain bound to this VAO + } + +void Batch_draw(Batch* batch) + { + if (batch->vao_id) + glBindVertexArray(batch->vao_id); + else + Batch_prime(batch); + + if (batch->program_dirty) + Batch_update_program_bindings(batch); + + if (batch->elem) + { + const ElementList* el = batch->elem; + +#if TRACK_INDEX_RANGE + if (el->base_index) + glDrawRangeElementsBaseVertex(batch->prim_type, el->min_index, el->max_index, el->index_ct, el->index_type, 0, el->base_index); + else + glDrawRangeElements(batch->prim_type, el->min_index, el->max_index, el->index_ct, el->index_type, 0); +#else + glDrawElements(batch->prim_type, el->index_ct, GL_UNSIGNED_INT, 0); +#endif + } + else + glDrawArrays(batch->prim_type, 0, batch->verts->vertex_ct); + + glBindVertexArray(0); + } diff --git a/source/blender/gpu/gawain/batch.h b/source/blender/gpu/gawain/batch.h index 9625d666a41..8387b0144a3 100644 --- a/source/blender/gpu/gawain/batch.h +++ b/source/blender/gpu/gawain/batch.h @@ -13,34 +13,53 @@ #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 { + // geometry VertexBuffer* verts; - ElementList* elem; // <-- NULL if element list not needed + ElementList* elem; // NULL if element list not needed GLenum prim_type; - GLuint vao_id; - GLuint bound_program; - AttribBinding attrib_binding; + + // book-keeping + GLuint vao_id; // remembers all geometry state (vertex attrib bindings & element buffer) + bool program_dirty; + + // state + GLuint program; } Batch; +Batch* Batch_create(GLenum prim_type, VertexBuffer*, ElementList*); + +void Batch_set_program(Batch*, GLuint program); +// Entire batch draws with one shader program, but can be redrawn later with another program. +// Vertex shader's inputs must be compatible with the batch's vertex format. + +void Batch_draw(Batch*); + + + + + + +#if 0 // future plans + +// Can multiple batches share a VertexBuffer? Use ref count? + + +// for multithreaded batch building: +typedef enum { + READY_TO_FORMAT, + READY_TO_BUILD, + BUILDING, BUILDING_IMM, // choose one + READY_TO_DRAW +} BatchPhase; + + +Batch* immBeginBatch(GLenum prim_type, unsigned v_ct); +// use standard immFunctions after this. immEnd will finalize the batch instead +// of drawing. + + // We often need a batch with its own data, to be created and discarded together. // WithOwn variants reduce number of system allocations. @@ -67,69 +86,4 @@ Batch* create_BatchWithOwnVertexBufferAndElementList(GLenum prim_type, VertexFor // 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; +#endif // future plans diff --git a/source/blender/gpu/gawain/element.c b/source/blender/gpu/gawain/element.c index 0ec31f6a45a..385705c71c6 100644 --- a/source/blender/gpu/gawain/element.c +++ b/source/blender/gpu/gawain/element.c @@ -16,43 +16,47 @@ unsigned ElementList_size(const ElementList* elem) { +#if TRACK_INDEX_RANGE 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); + default: + assert(false); + return 0; } - return 0; +#else + return elem->index_ct * sizeof(GLuint); +#endif } -void ElementList_use(ElementList* elem) +static void ElementList_prime(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); + 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; + // now that GL has a copy, discard original + free(elem->data); + elem->data = NULL; #endif - } } -void ElementList_done_using() +void ElementList_use(ElementList* elem) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + if (elem->vbo_id) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elem->vbo_id); + else + ElementList_prime(elem); } -void init_ElementListBuilder(ElementListBuilder* builder, GLenum prim_type, unsigned prim_ct, unsigned vertex_ct) +void ElementListBuilder_init(ElementListBuilder* builder, GLenum prim_type, unsigned prim_ct, unsigned vertex_ct) { - unsigned verts_per_prim; + unsigned verts_per_prim = 0; switch (prim_type) { case GL_POINTS: @@ -195,7 +199,7 @@ static void squeeze_indices_short(const unsigned values[], ElementList* elem) #endif // TRACK_INDEX_RANGE -void build_ElementList(ElementListBuilder* builder, ElementList* elem) +void ElementList_build(ElementListBuilder* builder, ElementList* elem) { #if TRUST_NO_ONE assert(builder->data != NULL); diff --git a/source/blender/gpu/gawain/element.h b/source/blender/gpu/gawain/element.h index c092dba721f..9ba2ed2a5b1 100644 --- a/source/blender/gpu/gawain/element.h +++ b/source/blender/gpu/gawain/element.h @@ -28,7 +28,6 @@ typedef struct { } ElementList; void ElementList_use(ElementList*); -void ElementList_done_using(void); unsigned ElementList_size(const ElementList*); typedef struct { @@ -44,8 +43,8 @@ typedef struct { // 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 ElementListBuilder_init(ElementListBuilder*, GLenum prim_type, unsigned prim_ct, unsigned vertex_ct); +//void ElementListBuilder_init_custom(ElementListBuilder*, GLenum prim_type, unsigned index_ct, unsigned vertex_ct); void add_generic_vertex(ElementListBuilder*, unsigned v); @@ -53,4 +52,4 @@ 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*); +void ElementList_build(ElementListBuilder*, ElementList*); diff --git a/source/blender/gpu/gawain/vertex_buffer.c b/source/blender/gpu/gawain/vertex_buffer.c index 054148403f3..9ceec5c0ca4 100644 --- a/source/blender/gpu/gawain/vertex_buffer.c +++ b/source/blender/gpu/gawain/vertex_buffer.c @@ -13,19 +13,21 @@ #include <stdlib.h> #include <string.h> -VertexBuffer* create_VertexBuffer() +#define KEEP_SINGLE_COPY 1 + +VertexBuffer* VertexBuffer_create() { VertexBuffer* verts = malloc(sizeof(VertexBuffer)); - init_VertexBuffer(verts); + VertexBuffer_init(verts); return verts; } -void init_VertexBuffer(VertexBuffer* verts) +void VertexBuffer_init(VertexBuffer* verts) { memset(verts, 0, sizeof(VertexBuffer)); } -void allocate_vertex_data(VertexBuffer* verts, unsigned v_ct) +void VertexBuffer_allocate_data(VertexBuffer* verts, unsigned v_ct) { VertexFormat* format = &verts->format; if (!format->packed) @@ -89,3 +91,27 @@ void fillAttribStride(VertexBuffer* verts, unsigned a_idx, unsigned stride, cons memcpy((GLubyte*)verts->data + a->offset + v * format->stride, (const GLubyte*)data + v * stride, a->sz); } } + +static void VertexBuffer_prime(VertexBuffer* verts) + { + const VertexFormat* format = &verts->format; + + glGenBuffers(1, &verts->vbo_id); + glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id); + // fill with delicious data & send to GPU the first time only + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size(format, verts->vertex_ct), verts->data, GL_STATIC_DRAW); + +#if KEEP_SINGLE_COPY + // now that GL has a copy, discard original + free(verts->data); + verts->data = NULL; +#endif + } + +void VertexBuffer_use(VertexBuffer* verts) + { + if (verts->vbo_id) + glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id); + else + VertexBuffer_prime(verts); + } diff --git a/source/blender/gpu/gawain/vertex_buffer.h b/source/blender/gpu/gawain/vertex_buffer.h index 631b6652d92..a26a7306656 100644 --- a/source/blender/gpu/gawain/vertex_buffer.h +++ b/source/blender/gpu/gawain/vertex_buffer.h @@ -29,13 +29,13 @@ typedef struct { GLuint vbo_id; // 0 indicates not yet sent to VRAM } VertexBuffer; -VertexBuffer* create_VertexBuffer(void); // create means allocate, then init -void init_VertexBuffer(VertexBuffer*); +VertexBuffer* VertexBuffer_create(void); // create means allocate, then init +void VertexBuffer_init(VertexBuffer*); // TODO: use copy of existing format // void init_VertexBuffer_with_format(VertexBuffer*, VertexFormat*); -void allocate_vertex_data(VertexBuffer*, unsigned v_ct); +void VertexBuffer_allocate_data(VertexBuffer*, 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 @@ -56,3 +56,5 @@ void fillAttribStride(VertexBuffer*, unsigned a_idx, unsigned stride, const void // // 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); + +void VertexBuffer_use(VertexBuffer*); |