Cleanup: replace attrib w/ attr
[blender.git] / source / blender / gpu / intern / gpu_vertex_buffer.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2016 by Mike Erwin.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation, ClĂ©ment Foucault
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/gpu/intern/gpu_vertex_buffer.c
27  *  \ingroup gpu
28  *
29  * GPU vertex buffer
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "GPU_vertex_buffer.h"
35
36 #include "gpu_context_private.h"
37 #include "gpu_vertex_format_private.h"
38
39 #include <stdlib.h>
40 #include <string.h>
41
42 #define KEEP_SINGLE_COPY 1
43
44 static uint vbo_memory_usage;
45
46 static GLenum convert_usage_type_to_gl(GPUUsageType type)
47 {
48         static const GLenum table[] = {
49                 [GPU_USAGE_STREAM] = GL_STREAM_DRAW,
50                 [GPU_USAGE_STATIC] = GL_STATIC_DRAW,
51                 [GPU_USAGE_DYNAMIC] = GL_DYNAMIC_DRAW
52         };
53         return table[type];
54 }
55
56 GPUVertBuf *GPU_vertbuf_create(GPUUsageType usage)
57 {
58         GPUVertBuf *verts = MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf");
59         GPU_vertbuf_init(verts, usage);
60         return verts;
61 }
62
63 GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *format, GPUUsageType usage)
64 {
65         GPUVertBuf *verts = GPU_vertbuf_create(usage);
66         GPU_vertformat_copy(&verts->format, format);
67         if (!format->packed) {
68                 VertexFormat_pack(&verts->format);
69         }
70         return verts;
71
72         /* this function might seem redundant, but there is potential for memory savings here... */
73         /* TODO: implement those memory savings */
74 }
75
76 void GPU_vertbuf_init(GPUVertBuf *verts, GPUUsageType usage)
77 {
78         memset(verts, 0, sizeof(GPUVertBuf));
79         verts->usage = usage;
80         verts->dirty = true;
81 }
82
83 void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts, const GPUVertFormat *format, GPUUsageType usage)
84 {
85         GPU_vertbuf_init(verts, usage);
86         GPU_vertformat_copy(&verts->format, format);
87         if (!format->packed) {
88                 VertexFormat_pack(&verts->format);
89         }
90 }
91
92 void GPU_vertbuf_discard(GPUVertBuf *verts)
93 {
94         if (verts->vbo_id) {
95                 GPU_buf_free(verts->vbo_id);
96 #if VRAM_USAGE
97                 vbo_memory_usage -= GPU_vertbuf_size_get(verts);
98 #endif
99         }
100         if (verts->data) {
101                 MEM_freeN(verts->data);
102         }
103         MEM_freeN(verts);
104 }
105
106 uint GPU_vertbuf_size_get(const GPUVertBuf *verts)
107 {
108         return vertex_buffer_size(&verts->format, verts->vertex_len);
109 }
110
111 /* create a new allocation, discarding any existing data */
112 void GPU_vertbuf_data_alloc(GPUVertBuf *verts, uint v_len)
113 {
114         GPUVertFormat *format = &verts->format;
115         if (!format->packed) {
116                 VertexFormat_pack(format);
117         }
118 #if TRUST_NO_ONE
119         /* catch any unnecessary use */
120         assert(verts->vertex_alloc != v_len || verts->data == NULL);
121 #endif
122         /* discard previous data if any */
123         if (verts->data) {
124                 MEM_freeN(verts->data);
125         }
126 #if VRAM_USAGE
127         uint new_size = vertex_buffer_size(&verts->format, v_len);
128         vbo_memory_usage += new_size - GPU_vertbuf_size_get(verts);
129 #endif
130         verts->dirty = true;
131         verts->vertex_len = verts->vertex_alloc = v_len;
132         verts->data = MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), "GPUVertBuf data");
133 }
134
135 /* resize buffer keeping existing data */
136 void GPU_vertbuf_data_resize(GPUVertBuf *verts, uint v_len)
137 {
138 #if TRUST_NO_ONE
139         assert(verts->data != NULL);
140         assert(verts->vertex_alloc != v_len);
141 #endif
142
143 #if VRAM_USAGE
144         uint new_size = vertex_buffer_size(&verts->format, v_len);
145         vbo_memory_usage += new_size - GPU_vertbuf_size_get(verts);
146 #endif
147         verts->dirty = true;
148         verts->vertex_len = verts->vertex_alloc = v_len;
149         verts->data = MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts));
150 }
151
152 /* Set vertex count but does not change allocation.
153  * Only this many verts will be uploaded to the GPU and rendered.
154  * This is useful for streaming data. */
155 void GPU_vertbuf_data_len_set(GPUVertBuf *verts, uint v_len)
156 {
157 #if TRUST_NO_ONE
158         assert(verts->data != NULL); /* only for dynamic data */
159         assert(v_len <= verts->vertex_alloc);
160 #endif
161
162 #if VRAM_USAGE
163         uint new_size = vertex_buffer_size(&verts->format, v_len);
164         vbo_memory_usage += new_size - GPU_vertbuf_size_get(verts);
165 #endif
166         verts->vertex_len = v_len;
167 }
168
169 void GPU_vertbuf_attr_set(GPUVertBuf *verts, uint a_idx, uint v_idx, const void *data)
170 {
171         const GPUVertFormat *format = &verts->format;
172         const GPUVertAttr *a = &format->attrs[a_idx];
173
174 #if TRUST_NO_ONE
175         assert(a_idx < format->attr_len);
176         assert(v_idx < verts->vertex_alloc);
177         assert(verts->data != NULL);
178 #endif
179         verts->dirty = true;
180         memcpy((GLubyte *)verts->data + a->offset + v_idx * format->stride, data, a->sz);
181 }
182
183 void GPU_vertbuf_attr_fill(GPUVertBuf *verts, uint a_idx, const void *data)
184 {
185         const GPUVertFormat *format = &verts->format;
186         const GPUVertAttr *a = &format->attrs[a_idx];
187
188 #if TRUST_NO_ONE
189         assert(a_idx < format->attr_len);
190 #endif
191         const uint stride = a->sz; /* tightly packed input data */
192
193         GPU_vertbuf_attr_fill_stride(verts, a_idx, stride, data);
194 }
195
196 void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts, uint a_idx, uint stride, const void *data)
197 {
198         const GPUVertFormat *format = &verts->format;
199         const GPUVertAttr *a = &format->attrs[a_idx];
200
201 #if TRUST_NO_ONE
202         assert(a_idx < format->attr_len);
203         assert(verts->data != NULL);
204 #endif
205         verts->dirty = true;
206         const uint vertex_len = verts->vertex_len;
207
208         if (format->attr_len == 1 && stride == format->stride) {
209                 /* we can copy it all at once */
210                 memcpy(verts->data, data, vertex_len * a->sz);
211         }
212         else {
213                 /* we must copy it per vertex */
214                 for (uint v = 0; v < vertex_len; ++v) {
215                         memcpy((GLubyte *)verts->data + a->offset + v * format->stride, (const GLubyte *)data + v * stride, a->sz);
216                 }
217         }
218 }
219
220 void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts, uint a_idx, GPUVertBufRaw *access)
221 {
222         const GPUVertFormat *format = &verts->format;
223         const GPUVertAttr *a = &format->attrs[a_idx];
224
225 #if TRUST_NO_ONE
226         assert(a_idx < format->attr_len);
227         assert(verts->data != NULL);
228 #endif
229
230         verts->dirty = true;
231
232         access->size = a->sz;
233         access->stride = format->stride;
234         access->data = (GLubyte *)verts->data + a->offset;
235         access->data_init = access->data;
236 #if TRUST_NO_ONE
237         access->_data_end = access->data_init + (size_t)(verts->vertex_alloc * format->stride);
238 #endif
239 }
240
241 static void VertBuffer_upload_data(GPUVertBuf *verts)
242 {
243         uint buffer_sz = GPU_vertbuf_size_get(verts);
244
245         /* orphan the vbo to avoid sync */
246         glBufferData(GL_ARRAY_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage));
247         /* upload data */
248         glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_sz, verts->data);
249
250         if (verts->usage == GPU_USAGE_STATIC) {
251                 MEM_freeN(verts->data);
252                 verts->data = NULL;
253         }
254         verts->dirty = false;
255 }
256
257 void GPU_vertbuf_use(GPUVertBuf *verts)
258 {
259         /* only create the buffer the 1st time */
260         if (verts->vbo_id == 0) {
261                 verts->vbo_id = GPU_buf_alloc();
262         }
263         glBindBuffer(GL_ARRAY_BUFFER, verts->vbo_id);
264         if (verts->dirty) {
265                 VertBuffer_upload_data(verts);
266         }
267 }
268
269 uint GPU_vertbuf_get_memory_usage(void)
270 {
271         return vbo_memory_usage;
272 }