Blender V4.3
gpu_vertex_format.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "GPU_vertex_format.hh"
12#include "GPU_capabilities.hh"
13
15#include "gpu_shader_private.hh"
17
18#include <cstddef>
19#include <cstring>
20
21#include "BLI_ghash.h"
22#include "BLI_string.h"
23#include "BLI_utildefines.h"
24
25#define PACK_DEBUG 0
26
27#if PACK_DEBUG
28# include <stdio.h>
29#endif
30
31using namespace blender::gpu;
32using namespace blender::gpu::shader;
33
35{
36#if TRUST_NO_ONE
37 memset(format, 0, sizeof(GPUVertFormat));
38#else
39 format->attr_len = 0;
40 format->packed = false;
41 format->name_offset = 0;
42 format->name_len = 0;
43 format->deinterleaved = false;
44
45 for (uint i = 0; i < GPU_VERT_ATTR_MAX_LEN; i++) {
46 format->attrs[i].name_len = 0;
47 }
48#endif
49}
50
52{
53 /* copy regular struct fields */
54 memcpy(dest, &src, sizeof(GPUVertFormat));
55}
56
58{
59#if TRUST_NO_ONE
60 assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */
61#endif
62 const uint sizes[] = {1, 1, 2, 2, 4, 4, 4};
63 return sizes[type];
64}
65
66static uint attr_size(const GPUVertAttr *a)
67{
68 if (a->comp_type == GPU_COMP_I10) {
69 return 4; /* always packed as 10_10_10_2 */
70 }
71 return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type));
72}
73
74static uint attr_align(const GPUVertAttr *a, uint minimum_stride)
75{
76 if (a->comp_type == GPU_COMP_I10) {
77 return 4; /* always packed as 10_10_10_2 */
78 }
79 uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type));
80 if (a->comp_len == 3 && c <= 2) {
81 return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */
82 }
83
84 /* Most fetches are ok if components are naturally aligned.
85 * However, in Metal,the minimum supported per-vertex stride is 4,
86 * so we must query the GPU and pad out the size accordingly. */
87 return max_ii(minimum_stride, c);
88}
89
91{
92#if TRUST_NO_ONE
93 assert(format->packed && format->stride > 0);
94#endif
95 return format->stride * vertex_len;
96}
97
98static uchar copy_attr_name(GPUVertFormat *format, const char *name)
99{
100 /* `strncpy` does 110% of what we need; let's do exactly 100% */
101 uchar name_offset = format->name_offset;
102 char *name_copy = format->names + name_offset;
103 uint available = GPU_VERT_ATTR_NAMES_BUF_LEN - name_offset;
104 bool terminated = false;
105
106 for (uint i = 0; i < available; i++) {
107 const char c = name[i];
108 name_copy[i] = c;
109 if (c == '\0') {
110 terminated = true;
111 format->name_offset += (i + 1);
112 break;
113 }
114 }
115#if TRUST_NO_ONE
116 assert(terminated);
117 assert(format->name_offset <= GPU_VERT_ATTR_NAMES_BUF_LEN);
118#else
119 (void)terminated;
120#endif
121 return name_offset;
122}
123
125 const char *name,
126 GPUVertCompType comp_type,
127 uint comp_len,
128 GPUVertFetchMode fetch_mode)
129{
130#if TRUST_NO_ONE
131 assert(format->name_len < GPU_VERT_FORMAT_MAX_NAMES); /* there's room for more */
132 assert(format->attr_len < GPU_VERT_ATTR_MAX_LEN); /* there's room for more */
133 assert(!format->packed); /* packed means frozen/locked */
134 assert((comp_len >= 1 && comp_len <= 4) || comp_len == 8 || comp_len == 12 || comp_len == 16);
135
136 switch (comp_type) {
137 case GPU_COMP_F32:
138 /* float type can only kept as float */
139 assert(fetch_mode == GPU_FETCH_FLOAT);
140 break;
141 case GPU_COMP_I10:
142 /* 10_10_10 format intended for normals (XYZ) or colors (RGB)
143 * extra component packed.w can be manually set to { -2, -1, 0, 1 } */
144 assert(ELEM(comp_len, 3, 4));
145
146 /* Not strictly required, may relax later. */
147 assert(fetch_mode == GPU_FETCH_INT_TO_FLOAT_UNIT);
148
149 break;
150 default:
151 /* integer types can be kept as int or converted/normalized to float */
152 assert(fetch_mode != GPU_FETCH_FLOAT);
153 /* only support float matrices (see Batch_update_program_bindings) */
154 assert(!ELEM(comp_len, 8, 12, 16));
155 }
156#endif
157 format->name_len++; /* Multi-name support. */
158
159 const uint attr_id = format->attr_len++;
160 GPUVertAttr *attr = &format->attrs[attr_id];
161
162 attr->names[attr->name_len++] = copy_attr_name(format, name);
163 attr->comp_type = comp_type;
164 attr->comp_len = (comp_type == GPU_COMP_I10) ?
165 4 :
166 comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */
167 attr->size = attr_size(attr);
168 attr->offset = 0; /* offsets & stride are calculated later (during pack) */
169 attr->fetch_mode = fetch_mode;
170
171 return attr_id;
172}
173
175{
176 GPUVertAttr *attr = &format->attrs[format->attr_len - 1];
177#if TRUST_NO_ONE
178 assert(format->name_len < GPU_VERT_FORMAT_MAX_NAMES); /* there's room for more */
179 assert(attr->name_len < GPU_VERT_ATTR_MAX_NAMES);
180#endif
181 format->name_len++; /* Multi-name support. */
182 attr->names[attr->name_len++] = copy_attr_name(format, alias);
183}
184
186{
187 /* Sanity check. Maximum can be upgraded if needed. */
188 BLI_assert(load_count > 1 && load_count < 5);
189 /* We need a packed format because of format->stride. */
190 if (!format->packed) {
192 }
193
194 BLI_assert((format->name_len + 1) * load_count < GPU_VERT_FORMAT_MAX_NAMES);
195 BLI_assert(format->attr_len * load_count <= GPU_VERT_ATTR_MAX_LEN);
196 BLI_assert(format->name_offset * load_count < GPU_VERT_ATTR_NAMES_BUF_LEN);
197
198 const GPUVertAttr *attr = format->attrs;
199 int attr_len = format->attr_len;
200 for (int i = 0; i < attr_len; i++, attr++) {
201 const char *attr_name = GPU_vertformat_attr_name_get(format, attr, 0);
202 for (int j = 1; j < load_count; j++) {
203 char load_name[68 /* MAX_CUSTOMDATA_LAYER_NAME */];
204 SNPRINTF(load_name, "%s%d", attr_name, j);
205 GPUVertAttr *dst_attr = &format->attrs[format->attr_len++];
206 *dst_attr = *attr;
207
208 dst_attr->names[0] = copy_attr_name(format, load_name);
209 dst_attr->name_len = 1;
210 dst_attr->offset += format->stride * j;
211 }
212 }
213}
214
216{
217 for (int i = 0; i < format->attr_len; i++) {
218 const GPUVertAttr *attr = &format->attrs[i];
219 for (int j = 0; j < attr->name_len; j++) {
220 const char *attr_name = GPU_vertformat_attr_name_get(format, attr, j);
221 if (STREQ(name, attr_name)) {
222 return i;
223 }
224 }
225 }
226 return -1;
227}
228
230{
231 BLI_assert(attr_id > -1 && attr_id < format->attr_len);
232 GPUVertAttr *attr = &format->attrs[attr_id];
233 char *attr_name = (char *)GPU_vertformat_attr_name_get(format, attr, 0);
234 BLI_assert(strlen(attr_name) == strlen(new_name));
235 int i = 0;
236 while (attr_name[i] != '\0') {
237 attr_name[i] = new_name[i];
238 i++;
239 }
240 attr->name_len = 1;
241}
242
243/* Encode 8 original bytes into 11 safe bytes. */
244static void safe_bytes(char out[11], const char data[8])
245{
246 const char safe_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
247
248 uint64_t in = *(uint64_t *)data;
249 for (int i = 0; i < 11; i++) {
250 out[i] = safe_chars[in % 62lu];
251 in /= 62lu;
252 }
253}
254
255void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint /*max_len*/)
256{
257 char data[8] = {0};
258 uint len = strlen(attr_name);
259
260 if (len > 8) {
261 /* Start with the first 4 chars of the name. */
262 for (int i = 0; i < 4; i++) {
263 data[i] = attr_name[i];
264 }
265 /* We use a hash to identify each data layer based on its name.
266 * NOTE: This is still prone to hash collision but the risks are very low. */
267 /* Start hashing after the first 2 chars. */
268 *(uint *)&data[4] = BLI_ghashutil_strhash_p_murmur(attr_name + 4);
269 }
270 else {
271 /* Copy the whole name. Collision is barely possible
272 * (hash would have to be equal to the last 4 bytes). */
273 for (int i = 0; i < 8 && attr_name[i] != '\0'; i++) {
274 data[i] = attr_name[i];
275 }
276 }
277 /* Convert to safe bytes characters. */
278 safe_bytes(r_safe_name, data);
279 /* End the string */
280 r_safe_name[11] = '\0';
281
283#if 0 /* For debugging */
284 printf("%s > %lx > %s\n", attr_name, *(uint64_t *)data, r_safe_name);
285#endif
286}
287
289{
290 /* Ideally we should change the stride and offset here. This would allow
291 * us to use GPU_vertbuf_attr_set / GPU_vertbuf_attr_fill. But since
292 * we use only 11 bits for attr->offset this limits the size of the
293 * buffer considerably. So instead we do the conversion when creating
294 * bindings in create_bindings(). */
295 format->deinterleaved = true;
296}
297
298uint padding(uint offset, uint alignment)
299{
300 const uint mod = offset % alignment;
301 return (mod == 0) ? 0 : (alignment - mod);
302}
303
304#if PACK_DEBUG
305static void show_pack(uint a_idx, uint size, uint pad)
306{
307 const char c = 'A' + a_idx;
308 for (uint i = 0; i < pad; i++) {
309 putchar('-');
310 }
311 for (uint i = 0; i < size; i++) {
312 putchar(c);
313 }
314}
315#endif
316
317static void VertexFormat_pack_impl(GPUVertFormat *format, uint minimum_stride)
318{
319 GPUVertAttr *a0 = &format->attrs[0];
320 a0->offset = 0;
321 uint offset = a0->size;
322
323#if PACK_DEBUG
324 show_pack(0, a0->size, 0);
325#endif
326
327 for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) {
328 GPUVertAttr *a = &format->attrs[a_idx];
329 uint mid_padding = padding(offset, attr_align(a, minimum_stride));
330 offset += mid_padding;
331 a->offset = offset;
332 offset += a->size;
333
334#if PACK_DEBUG
335 show_pack(a_idx, a->size, mid_padding);
336#endif
337 }
338
339 uint end_padding = padding(offset, attr_align(a0, minimum_stride));
340
341#if PACK_DEBUG
342 show_pack(0, 0, end_padding);
343 putchar('\n');
344#endif
345 format->stride = offset + end_padding;
346 format->packed = true;
347}
348
350{
351 /* Perform standard vertex packing, ensuring vertex format satisfies
352 * minimum stride requirements for vertex assembly. */
354}
355
357{
358 /* Validates packing for vertex formats used with texture buffers.
359 * In these cases, there must only be a single vertex attribute.
360 * This attribute should be tightly packed without padding, to ensure
361 * it aligns with the backing texture data format, skipping
362 * minimum per-vertex stride, which mandates 4-byte alignment in Metal.
363 * This additional alignment padding caused smaller data types, e.g. U16,
364 * to mis-align. */
365 for (int i = 0; i < format->attr_len; i++) {
366 /* The buffer texture setup uses the first attribute for type and size.
367 * Make sure all attributes use the same size. */
368 BLI_assert_msg(format->attrs[i].size == format->attrs[0].size,
369 "Texture buffer mode should only use a attributes with the same size.");
370 }
371
372 /* Pack vertex format without minimum stride, as this is not required by texture buffers. */
374}
375
376static uint component_size_get(const Type gpu_type)
377{
378 switch (gpu_type) {
379 case Type::VEC2:
380 case Type::IVEC2:
381 case Type::UVEC2:
382 return 2;
383 case Type::VEC3:
384 case Type::IVEC3:
385 case Type::UVEC3:
386 return 3;
387 case Type::VEC4:
388 case Type::IVEC4:
389 case Type::UVEC4:
390 return 4;
391 case Type::MAT3:
392 return 12;
393 case Type::MAT4:
394 return 16;
395 default:
396 return 1;
397 }
398}
399
401 GPUVertCompType *r_comp_type,
402 GPUVertFetchMode *r_fetch_mode)
403{
404 switch (gpu_type) {
405 case Type::FLOAT:
406 case Type::VEC2:
407 case Type::VEC3:
408 case Type::VEC4:
409 case Type::MAT3:
410 case Type::MAT4:
411 *r_comp_type = GPU_COMP_F32;
412 *r_fetch_mode = GPU_FETCH_FLOAT;
413 break;
414 case Type::INT:
415 case Type::IVEC2:
416 case Type::IVEC3:
417 case Type::IVEC4:
418 *r_comp_type = GPU_COMP_I32;
419 *r_fetch_mode = GPU_FETCH_INT;
420 break;
421 case Type::UINT:
422 case Type::UVEC2:
423 case Type::UVEC3:
424 case Type::UVEC4:
425 *r_comp_type = GPU_COMP_U32;
426 *r_fetch_mode = GPU_FETCH_INT;
427 break;
428 default:
429 BLI_assert(0);
430 }
431}
432
434{
436
437 uint attr_len = GPU_shader_get_attribute_len(shader);
438 int location_test = 0, attrs_added = 0;
439 while (attrs_added < attr_len) {
440 char name[256];
441 Type gpu_type;
442 if (!GPU_shader_get_attribute_info(shader, location_test++, name, (int *)&gpu_type)) {
443 continue;
444 }
445
446 GPUVertCompType comp_type;
447 GPUVertFetchMode fetch_mode;
448 recommended_fetch_mode_and_comp_type(gpu_type, &comp_type, &fetch_mode);
449
450 int comp_len = component_size_get(gpu_type);
451
452 GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode);
453 attrs_added++;
454 }
455}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
unsigned int BLI_ghashutil_strhash_p_murmur(const void *ptr)
MINLINE int max_ii(int a, int b)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
unsigned char uchar
unsigned int uint
#define ELEM(...)
#define STREQ(a, b)
int GPU_minimum_per_vertex_stride()
bool GPU_shader_get_attribute_info(const GPUShader *shader, int attr_location, char r_name[256], int *r_type)
unsigned int GPU_shader_get_attribute_len(const GPUShader *shader)
#define GPU_VERT_ATTR_MAX_NAMES
BLI_INLINE const char * GPU_vertformat_attr_name_get(const GPUVertFormat *format, const GPUVertAttr *attr, uint n_idx)
GPUVertFetchMode
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
@ GPU_FETCH_INT
#define GPU_VERT_FORMAT_MAX_NAMES
#define GPU_VERT_ATTR_NAMES_BUF_LEN
#define GPU_MAX_SAFE_ATTR_NAME
#define GPU_VERT_ATTR_MAX_LEN
GPUVertCompType
@ GPU_COMP_I10
@ GPU_COMP_F32
@ GPU_COMP_I32
@ GPU_COMP_U32
int pad[32 - sizeof(int)]
struct GPUShader GPUShader
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define printf
int len
struct @620::@623 attr_id
static void safe_bytes(char out[11], const char data[8])
static void recommended_fetch_mode_and_comp_type(Type gpu_type, GPUVertCompType *r_comp_type, GPUVertFetchMode *r_fetch_mode)
void GPU_vertformat_alias_add(GPUVertFormat *format, const char *alias)
static uint attr_align(const GPUVertAttr *a, uint minimum_stride)
static uint component_size_get(const Type gpu_type)
uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len)
void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr_id, const char *new_name)
void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint)
uint padding(uint offset, uint alignment)
void GPU_vertformat_multiload_enable(GPUVertFormat *format, int load_count)
void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat &src)
int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name)
uint GPU_vertformat_attr_add(GPUVertFormat *format, const char *name, GPUVertCompType comp_type, uint comp_len, GPUVertFetchMode fetch_mode)
void GPU_vertformat_clear(GPUVertFormat *format)
static uint attr_size(const GPUVertAttr *a)
static void VertexFormat_pack_impl(GPUVertFormat *format, uint minimum_stride)
static uchar copy_attr_name(GPUVertFormat *format, const char *name)
void VertexFormat_texture_buffer_pack(GPUVertFormat *format)
void GPU_vertformat_deinterleave(GPUVertFormat *format)
void VertexFormat_pack(GPUVertFormat *format)
static uint comp_size(GPUVertCompType type)
void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader)
format
unsigned __int64 uint64_t
Definition stdint.h:90
uchar names[GPU_VERT_ATTR_MAX_NAMES]
ccl_device_inline int mod(int x, int m)
Definition util/math.h:520