Merge branch 'master' into blender2.8
[blender.git] / intern / gawain / src / vertex_buffer.c
1
2 // Gawain vertex buffer
3 //
4 // This code is part of the Gawain library, with modifications
5 // specific to integration with Blender.
6 //
7 // Copyright 2016 Mike Erwin
8 //
9 // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
10 // the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
11
12 #include "vertex_buffer.h"
13 #include "buffer_id.h"
14 #include "vertex_format_private.h"
15 #include <stdlib.h>
16 #include <string.h>
17
18 #define KEEP_SINGLE_COPY 1
19
20 static unsigned vbo_memory_usage;
21
22 Gwn_VertBuf* GWN_vertbuf_create(void)
23         {
24         Gwn_VertBuf* verts = malloc(sizeof(Gwn_VertBuf));
25         GWN_vertbuf_init(verts);
26         return verts;
27         }
28
29 Gwn_VertBuf* GWN_vertbuf_create_with_format(const Gwn_VertFormat* format)
30         {
31         Gwn_VertBuf* verts = GWN_vertbuf_create();
32         GWN_vertformat_copy(&verts->format, format);
33         if (!format->packed)
34                 VertexFormat_pack(&verts->format);
35         return verts;
36
37         // this function might seem redundant, but there is potential for memory savings here...
38         // TODO: implement those memory savings
39         }
40
41 void GWN_vertbuf_init(Gwn_VertBuf* verts)
42         {
43         memset(verts, 0, sizeof(Gwn_VertBuf));
44         }
45
46 void GWN_vertbuf_init_with_format(Gwn_VertBuf* verts, const Gwn_VertFormat* format)
47         {
48         GWN_vertbuf_init(verts);
49         GWN_vertformat_copy(&verts->format, format);
50         if (!format->packed)
51                 VertexFormat_pack(&verts->format);
52         }
53
54 void GWN_vertbuf_discard(Gwn_VertBuf* verts)
55         {
56         if (verts->vbo_id) {
57                 GWN_buf_id_free(verts->vbo_id);
58                 vbo_memory_usage -= GWN_vertbuf_size_get(verts);
59         }
60 #if KEEP_SINGLE_COPY
61         else
62 #endif
63         if (verts->data)
64                 free(verts->data);
65
66
67         free(verts);
68         }
69
70 unsigned GWN_vertbuf_size_get(const Gwn_VertBuf* verts)
71         {
72         return vertex_buffer_size(&verts->format, verts->vertex_ct);
73         }
74
75 void GWN_vertbuf_data_alloc(Gwn_VertBuf* verts, unsigned v_ct)
76         {
77         Gwn_VertFormat* format = &verts->format;
78         if (!format->packed)
79                 VertexFormat_pack(format);
80
81         verts->vertex_ct = v_ct;
82
83         // Data initially lives in main memory. Will be transferred to VRAM when we "prime" it.
84         verts->data = malloc(GWN_vertbuf_size_get(verts));
85         }
86
87 void GWN_vertbuf_data_resize(Gwn_VertBuf* verts, unsigned v_ct)
88         {
89 #if TRUST_NO_ONE
90         assert(verts->vertex_ct != v_ct); // allow this?
91         assert(verts->data != NULL); // has already been allocated
92         assert(verts->vbo_id == 0); // has not been sent to VRAM
93 #endif
94
95         verts->vertex_ct = v_ct;
96         verts->data = realloc(verts->data, GWN_vertbuf_size_get(verts));
97         // TODO: skip realloc if v_ct < existing vertex count
98         // extra space will be reclaimed, and never sent to VRAM (see VertexBuffer_prime)
99         }
100
101 void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, const void* data)
102         {
103         const Gwn_VertFormat* format = &verts->format;
104         const Gwn_VertAttr* a = format->attribs + a_idx;
105
106 #if TRUST_NO_ONE
107         assert(a_idx < format->attrib_ct);
108         assert(v_idx < verts->vertex_ct);
109         assert(verts->data != NULL); // data must be in main mem
110 #endif
111
112         memcpy((GLubyte*)verts->data + a->offset + v_idx * format->stride, data, a->sz);
113         }
114
115 void GWN_vertbuf_attr_fill(Gwn_VertBuf* verts, unsigned a_idx, const void* data)
116         {
117         const Gwn_VertFormat* format = &verts->format;
118         const Gwn_VertAttr* a = format->attribs + a_idx;
119
120 #if TRUST_NO_ONE
121         assert(a_idx < format->attrib_ct);
122 #endif
123
124         const unsigned stride = a->sz; // tightly packed input data
125
126         GWN_vertbuf_attr_fill_stride(verts, a_idx, stride, data);
127         }
128
129 void GWN_vertbuf_attr_fill_stride(Gwn_VertBuf* verts, unsigned a_idx, unsigned stride, const void* data)
130         {
131         const Gwn_VertFormat* format = &verts->format;
132         const Gwn_VertAttr* a = format->attribs + a_idx;
133
134 #if TRUST_NO_ONE
135         assert(a_idx < format->attrib_ct);
136         assert(verts->data != NULL); // data must be in main mem
137 #endif
138
139         const unsigned vertex_ct = verts->vertex_ct;
140
141         if (format->attrib_ct == 1 && stride == format->stride)
142                 {
143                 // we can copy it all at once
144                 memcpy(verts->data, data, vertex_ct * a->sz);
145                 }
146         else
147                 {
148                 // we must copy it per vertex
149                 for (unsigned v = 0; v < vertex_ct; ++v)
150                         memcpy((GLubyte*)verts->data + a->offset + v * format->stride, (const GLubyte*)data + v * stride, a->sz);
151                 }
152         }
153
154 void GWN_vertbuf_attr_get_raw_data(Gwn_VertBuf* verts, unsigned a_idx, Gwn_VertBufRaw *access)
155         {
156         const Gwn_VertFormat* format = &verts->format;
157         const Gwn_VertAttr* a = format->attribs + a_idx;
158
159 #if TRUST_NO_ONE
160         assert(a_idx < format->attrib_ct);
161         assert(verts->data != NULL); // data must be in main mem
162 #endif
163
164         access->size = a->sz;
165         access->stride = format->stride;
166         access->data = (GLubyte*)verts->data + a->offset;
167         access->data_init = access->data;
168 #if TRUST_NO_ONE
169         access->_data_end = access->data_init + (size_t)(verts->vertex_ct * format->stride);
170 #endif
171         }
172
173
174 static void VertexBuffer_prime(Gwn_VertBuf* verts)
175         {
176         const unsigned buffer_sz = GWN_vertbuf_size_get(verts);
177
178         verts->vbo_id = GWN_buf_id_alloc();
179         glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
180         // fill with delicious data & send to GPU the first time only
181         glBufferData(GL_ARRAY_BUFFER, buffer_sz, verts->data, GL_STATIC_DRAW);
182
183         vbo_memory_usage += buffer_sz;
184
185 #if KEEP_SINGLE_COPY
186         // now that GL has a copy, discard original
187         free(verts->data);
188         verts->data = NULL;
189 #endif
190         }
191
192 void GWN_vertbuf_use(Gwn_VertBuf* verts)
193         {
194         if (verts->vbo_id)
195                 glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
196         else
197                 VertexBuffer_prime(verts);
198         }
199
200 unsigned GWN_vertbuf_get_memory_usage(void)
201         {
202         return vbo_memory_usage;
203         }