b1fb7721e38043e348756d6693f126f003bd8f5b
[blender.git] / intern / gawain / src / vertex_format.c
1
2 // Gawain vertex format
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_format.h"
13 #include <stdlib.h>
14 #include <string.h>
15
16 #define PACK_DEBUG 0
17
18 #if PACK_DEBUG
19   #include <stdio.h>
20 #endif
21
22 void VertexFormat_clear(VertexFormat* format)
23         {
24 #if TRUST_NO_ONE
25         memset(format, 0, sizeof(VertexFormat));
26 #else
27         format->attrib_ct = 0;
28         format->packed = false;
29         format->name_offset = 0;
30 #endif
31         }
32
33 void VertexFormat_copy(VertexFormat* dest, const VertexFormat* src)
34         {
35         // copy regular struct fields
36         memcpy(dest, src, sizeof(VertexFormat));
37         }
38
39 static unsigned comp_sz(VertexCompType type)
40         {
41 #if TRUST_NO_ONE
42         assert(type >= GL_BYTE && type <= GL_FLOAT);
43 #endif
44
45         const GLubyte sizes[] = {1,1,2,2,4,4,4};
46         return sizes[type - GL_BYTE];
47         }
48
49 static unsigned attrib_sz(const Attrib *a)
50         {
51 #if USE_10_10_10
52         if (a->comp_type == COMP_I10)
53                 return 4; // always packed as 10_10_10_2
54 #endif
55
56         return a->comp_ct * comp_sz(a->comp_type);
57         }
58
59 static unsigned attrib_align(const Attrib *a)
60         {
61 #if USE_10_10_10
62         if (a->comp_type == COMP_I10)
63                 return 4; // always packed as 10_10_10_2
64 #endif
65
66         unsigned c = comp_sz(a->comp_type);
67         if (a->comp_ct == 3 && c <= 2)
68                 return 4 * c; // AMD HW can't fetch these well, so pad it out (other vendors too?)
69         else
70                 return c; // most fetches are ok if components are naturally aligned
71         }
72
73 unsigned vertex_buffer_size(const VertexFormat* format, unsigned vertex_ct)
74         {
75 #if TRUST_NO_ONE
76         assert(format->packed && format->stride > 0);
77 #endif
78
79         return format->stride * vertex_ct;
80         }
81
82 static const char* copy_attrib_name(VertexFormat* format, const char* name)
83         {
84         // strncpy does 110% of what we need; let's do exactly 100%
85         char* name_copy = format->names + format->name_offset;
86         unsigned available = VERTEX_ATTRIB_NAMES_BUFFER_LEN - format->name_offset;
87         bool terminated = false;
88
89         for (unsigned i = 0; i < available; ++i)
90                 {
91                 const char c = name[i];
92                 name_copy[i] = c;
93                 if (c == '\0')
94                         {
95                         terminated = true;
96                         format->name_offset += (i + 1);
97                         break;
98                         }
99                 }
100
101 #if TRUST_NO_ONE
102         assert(terminated);
103         assert(format->name_offset <= VERTEX_ATTRIB_NAMES_BUFFER_LEN);
104 #endif
105
106         return name_copy;
107         }
108
109 unsigned VertexFormat_add_attrib(VertexFormat* format, const char* name, VertexCompType comp_type, unsigned comp_ct, VertexFetchMode fetch_mode)
110         {
111 #if TRUST_NO_ONE
112         assert(format->attrib_ct < MAX_VERTEX_ATTRIBS); // there's room for more
113         assert(!format->packed); // packed means frozen/locked
114         assert(comp_ct >= 1 && comp_ct <= 4);
115         switch (comp_type)
116                 {
117                 case COMP_F32:
118                         // float type can only kept as float
119                         assert(fetch_mode == KEEP_FLOAT);
120                         break;
121  #if USE_10_10_10
122                 case COMP_I10:
123                         // 10_10_10 format intended for normals (xyz) or colors (rgb)
124                         // extra component packed.w can be manually set to { -2, -1, 0, 1 }
125                         assert(comp_ct == 3 || comp_ct == 4);
126                         assert(fetch_mode == NORMALIZE_INT_TO_FLOAT); // not strictly required, may relax later
127                         break;
128  #endif
129                 default:
130                         // integer types can be kept as int or converted/normalized to float
131                         assert(fetch_mode != KEEP_FLOAT);
132                 }
133 #endif
134
135         const unsigned attrib_id = format->attrib_ct++;
136         Attrib* attrib = format->attribs + attrib_id;
137
138         attrib->name = copy_attrib_name(format, name);
139         attrib->comp_type = comp_type;
140 #if USE_10_10_10
141         attrib->comp_ct = (comp_type == COMP_I10) ? 4 : comp_ct; // system needs 10_10_10_2 to be 4 or BGRA
142 #else
143         attrib->comp_ct = comp_ct;
144 #endif
145         attrib->sz = attrib_sz(attrib);
146         attrib->offset = 0; // offsets & stride are calculated later (during pack)
147         attrib->fetch_mode = fetch_mode;
148
149         return attrib_id;
150         }
151
152 unsigned padding(unsigned offset, unsigned alignment)
153         {
154         const unsigned mod = offset % alignment;
155         return (mod == 0) ? 0 : (alignment - mod);
156         }
157
158 #if PACK_DEBUG
159 static void show_pack(unsigned a_idx, unsigned sz, unsigned pad)
160         {
161         const char c = 'A' + a_idx;
162         for (unsigned i = 0; i < pad; ++i)
163                 putchar('-');
164         for (unsigned i = 0; i < sz; ++i)
165                 putchar(c);
166         }
167 #endif
168
169 void VertexFormat_pack(VertexFormat* format)
170         {
171         // for now, attributes are packed in the order they were added,
172         // making sure each attrib is naturally aligned (add padding where necessary)
173
174         // later we can implement more efficient packing w/ reordering
175         // (keep attrib ID order, adjust their offsets to reorder in buffer)
176
177         // TODO:
178         // realloc just enough to hold the final combo string. And just enough to
179         // hold used attribs, not all 16.
180
181         Attrib* a0 = format->attribs + 0;
182         a0->offset = 0;
183         unsigned offset = a0->sz;
184
185 #if PACK_DEBUG
186         show_pack(0, a0->sz, 0);
187 #endif
188
189         for (unsigned a_idx = 1; a_idx < format->attrib_ct; ++a_idx)
190                 {
191                 Attrib* a = format->attribs + a_idx;
192                 unsigned mid_padding = padding(offset, attrib_align(a));
193                 offset += mid_padding;
194                 a->offset = offset;
195                 offset += a->sz;
196
197 #if PACK_DEBUG
198                 show_pack(a_idx, a->sz, mid_padding);
199 #endif
200                 }
201
202         unsigned end_padding = padding(offset, attrib_align(a0));
203
204 #if PACK_DEBUG
205         show_pack(0, 0, end_padding);
206         putchar('\n');
207 #endif
208
209         format->stride = offset + end_padding;
210         format->packed = true;
211         }
212
213
214 #if USE_10_10_10
215
216 // OpenGL ES packs in a different order as desktop GL but component conversion is the same.
217 // Of the code here, only struct PackedNormal needs to change.
218
219 #define SIGNED_INT_10_MAX  511
220 #define SIGNED_INT_10_MIN -512
221
222 static int clampi(int x, int min_allowed, int max_allowed)
223         {
224 #if TRUST_NO_ONE
225         assert(min_allowed <= max_allowed);
226 #endif
227
228         if (x < min_allowed)
229                 return min_allowed;
230         else if (x > max_allowed)
231                 return max_allowed;
232         else
233                 return x;
234         }
235
236 static int quantize(float x)
237         {
238         int qx = x * 511.0f;
239         return clampi(qx, SIGNED_INT_10_MIN, SIGNED_INT_10_MAX);
240         }
241
242 PackedNormal convert_i10_v3(const float data[3])
243         {
244         PackedNormal n = { .x = quantize(data[0]), .y = quantize(data[1]), .z = quantize(data[2]) };
245         return n;
246         }
247
248 #endif // USE_10_10_10