Blender  V3.3
gpu_vertex_format.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2016 by Mike Erwin. All rights reserved. */
3 
10 #include "GPU_vertex_format.h"
11 #include "gpu_shader_private.hh"
13 
14 #include <cstddef>
15 #include <cstring>
16 
17 #include "BLI_ghash.h"
18 #include "BLI_string.h"
19 #include "BLI_utildefines.h"
20 
21 #define PACK_DEBUG 0
22 
23 #if PACK_DEBUG
24 # include <stdio.h>
25 #endif
26 
27 using namespace blender::gpu;
28 
30 {
31 #if TRUST_NO_ONE
32  memset(format, 0, sizeof(GPUVertFormat));
33 #else
34  format->attr_len = 0;
35  format->packed = false;
36  format->name_offset = 0;
37  format->name_len = 0;
38  format->deinterleaved = false;
39 
40  for (uint i = 0; i < GPU_VERT_ATTR_MAX_LEN; i++) {
41  format->attrs[i].name_len = 0;
42  }
43 #endif
44 }
45 
47 {
48  /* copy regular struct fields */
49  memcpy(dest, src, sizeof(GPUVertFormat));
50 }
51 
53 {
54 #if TRUST_NO_ONE
55  assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */
56 #endif
57  const uint sizes[] = {1, 1, 2, 2, 4, 4, 4};
58  return sizes[type];
59 }
60 
61 static uint attr_size(const GPUVertAttr *a)
62 {
63  if (a->comp_type == GPU_COMP_I10) {
64  return 4; /* always packed as 10_10_10_2 */
65  }
66  return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type));
67 }
68 
69 static uint attr_align(const GPUVertAttr *a)
70 {
71  if (a->comp_type == GPU_COMP_I10) {
72  return 4; /* always packed as 10_10_10_2 */
73  }
74  uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type));
75  if (a->comp_len == 3 && c <= 2) {
76  return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */
77  }
78 
79  return c; /* most fetches are ok if components are naturally aligned */
80 }
81 
83 {
84 #if TRUST_NO_ONE
85  assert(format->packed && format->stride > 0);
86 #endif
87  return format->stride * vertex_len;
88 }
89 
90 static uchar copy_attr_name(GPUVertFormat *format, const char *name)
91 {
92  /* `strncpy` does 110% of what we need; let's do exactly 100% */
93  uchar name_offset = format->name_offset;
94  char *name_copy = format->names + name_offset;
95  uint available = GPU_VERT_ATTR_NAMES_BUF_LEN - name_offset;
96  bool terminated = false;
97 
98  for (uint i = 0; i < available; i++) {
99  const char c = name[i];
100  name_copy[i] = c;
101  if (c == '\0') {
102  terminated = true;
103  format->name_offset += (i + 1);
104  break;
105  }
106  }
107 #if TRUST_NO_ONE
108  assert(terminated);
109  assert(format->name_offset <= GPU_VERT_ATTR_NAMES_BUF_LEN);
110 #else
111  (void)terminated;
112 #endif
113  return name_offset;
114 }
115 
117  const char *name,
118  GPUVertCompType comp_type,
119  uint comp_len,
120  GPUVertFetchMode fetch_mode)
121 {
122 #if TRUST_NO_ONE
123  assert(format->name_len < GPU_VERT_FORMAT_MAX_NAMES); /* there's room for more */
124  assert(format->attr_len < GPU_VERT_ATTR_MAX_LEN); /* there's room for more */
125  assert(!format->packed); /* packed means frozen/locked */
126  assert((comp_len >= 1 && comp_len <= 4) || comp_len == 8 || comp_len == 12 || comp_len == 16);
127 
128  switch (comp_type) {
129  case GPU_COMP_F32:
130  /* float type can only kept as float */
131  assert(fetch_mode == GPU_FETCH_FLOAT);
132  break;
133  case GPU_COMP_I10:
134  /* 10_10_10 format intended for normals (xyz) or colors (rgb)
135  * extra component packed.w can be manually set to { -2, -1, 0, 1 } */
136  assert(ELEM(comp_len, 3, 4));
137 
138  /* Not strictly required, may relax later. */
139  assert(fetch_mode == GPU_FETCH_INT_TO_FLOAT_UNIT);
140 
141  break;
142  default:
143  /* integer types can be kept as int or converted/normalized to float */
144  assert(fetch_mode != GPU_FETCH_FLOAT);
145  /* only support float matrices (see Batch_update_program_bindings) */
146  assert(!ELEM(comp_len, 8, 12, 16));
147  }
148 #endif
149  format->name_len++; /* Multi-name support. */
150 
151  const uint attr_id = format->attr_len++;
152  GPUVertAttr *attr = &format->attrs[attr_id];
153 
154  attr->names[attr->name_len++] = copy_attr_name(format, name);
155  attr->comp_type = comp_type;
156  attr->comp_len = (comp_type == GPU_COMP_I10) ?
157  4 :
158  comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */
159  attr->size = attr_size(attr);
160  attr->offset = 0; /* offsets & stride are calculated later (during pack) */
161  attr->fetch_mode = fetch_mode;
162 
163  return attr_id;
164 }
165 
167 {
168  GPUVertAttr *attr = &format->attrs[format->attr_len - 1];
169 #if TRUST_NO_ONE
170  assert(format->name_len < GPU_VERT_FORMAT_MAX_NAMES); /* there's room for more */
171  assert(attr->name_len < GPU_VERT_ATTR_MAX_NAMES);
172 #endif
173  format->name_len++; /* Multi-name support. */
174  attr->names[attr->name_len++] = copy_attr_name(format, alias);
175 }
176 
178 {
179  /* Sanity check. Maximum can be upgraded if needed. */
180  BLI_assert(load_count > 1 && load_count < 5);
181  /* We need a packed format because of format->stride. */
182  if (!format->packed) {
184  }
185 
186  BLI_assert((format->name_len + 1) * load_count < GPU_VERT_FORMAT_MAX_NAMES);
187  BLI_assert(format->attr_len * load_count <= GPU_VERT_ATTR_MAX_LEN);
188  BLI_assert(format->name_offset * load_count < GPU_VERT_ATTR_NAMES_BUF_LEN);
189 
190  const GPUVertAttr *attr = format->attrs;
191  int attr_len = format->attr_len;
192  for (int i = 0; i < attr_len; i++, attr++) {
193  const char *attr_name = GPU_vertformat_attr_name_get(format, attr, 0);
194  for (int j = 1; j < load_count; j++) {
195  char load_name[64];
196  BLI_snprintf(load_name, sizeof(load_name), "%s%d", attr_name, j);
197  GPUVertAttr *dst_attr = &format->attrs[format->attr_len++];
198  *dst_attr = *attr;
199 
200  dst_attr->names[0] = copy_attr_name(format, load_name);
201  dst_attr->name_len = 1;
202  dst_attr->offset += format->stride * j;
203  }
204  }
205 }
206 
207 int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name)
208 {
209  for (int i = 0; i < format->attr_len; i++) {
210  const GPUVertAttr *attr = &format->attrs[i];
211  for (int j = 0; j < attr->name_len; j++) {
212  const char *attr_name = GPU_vertformat_attr_name_get(format, attr, j);
213  if (STREQ(name, attr_name)) {
214  return i;
215  }
216  }
217  }
218  return -1;
219 }
220 
221 void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr_id, const char *new_name)
222 {
223  BLI_assert(attr_id > -1 && attr_id < format->attr_len);
224  GPUVertAttr *attr = &format->attrs[attr_id];
225  char *attr_name = (char *)GPU_vertformat_attr_name_get(format, attr, 0);
226  BLI_assert(strlen(attr_name) == strlen(new_name));
227  int i = 0;
228  while (attr_name[i] != '\0') {
229  attr_name[i] = new_name[i];
230  i++;
231  }
232  attr->name_len = 1;
233 }
234 
235 /* Encode 8 original bytes into 11 safe bytes. */
236 static void safe_bytes(char out[11], const char data[8])
237 {
238  char safe_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
239 
240  uint64_t in = *(uint64_t *)data;
241  for (int i = 0; i < 11; i++) {
242  out[i] = safe_chars[in % 62lu];
243  in /= 62lu;
244  }
245 }
246 
247 void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint UNUSED(max_len))
248 {
249  char data[8] = {0};
250  uint len = strlen(attr_name);
251 
252  if (len > 8) {
253  /* Start with the first 4 chars of the name; */
254  for (int i = 0; i < 4; i++) {
255  data[i] = attr_name[i];
256  }
257  /* We use a hash to identify each data layer based on its name.
258  * NOTE: This is still prone to hash collision but the risks are very low. */
259  /* Start hashing after the first 2 chars. */
260  *(uint *)&data[4] = BLI_ghashutil_strhash_p_murmur(attr_name + 4);
261  }
262  else {
263  /* Copy the whole name. Collision is barely possible
264  * (hash would have to be equal to the last 4 bytes). */
265  for (int i = 0; i < 8 && attr_name[i] != '\0'; i++) {
266  data[i] = attr_name[i];
267  }
268  }
269  /* Convert to safe bytes characters. */
270  safe_bytes(r_safe_name, data);
271  /* End the string */
272  r_safe_name[11] = '\0';
273 
275 #if 0 /* For debugging */
276  printf("%s > %lx > %s\n", attr_name, *(uint64_t *)data, r_safe_name);
277 #endif
278 }
279 
281 {
282  /* Ideally we should change the stride and offset here. This would allow
283  * us to use GPU_vertbuf_attr_set / GPU_vertbuf_attr_fill. But since
284  * we use only 11 bits for attr->offset this limits the size of the
285  * buffer considerably. So instead we do the conversion when creating
286  * bindings in create_bindings(). */
287  format->deinterleaved = true;
288 }
289 
291 {
292  const uint mod = offset % alignment;
293  return (mod == 0) ? 0 : (alignment - mod);
294 }
295 
296 #if PACK_DEBUG
297 static void show_pack(uint a_idx, uint size, uint pad)
298 {
299  const char c = 'A' + a_idx;
300  for (uint i = 0; i < pad; i++) {
301  putchar('-');
302  }
303  for (uint i = 0; i < size; i++) {
304  putchar(c);
305  }
306 }
307 #endif
308 
310 {
311  GPUVertAttr *a0 = &format->attrs[0];
312  a0->offset = 0;
313  uint offset = a0->size;
314 
315 #if PACK_DEBUG
316  show_pack(0, a0->size, 0);
317 #endif
318 
319  for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) {
320  GPUVertAttr *a = &format->attrs[a_idx];
321  uint mid_padding = padding(offset, attr_align(a));
322  offset += mid_padding;
323  a->offset = offset;
324  offset += a->size;
325 
326 #if PACK_DEBUG
327  show_pack(a_idx, a->size, mid_padding);
328 #endif
329  }
330 
331  uint end_padding = padding(offset, attr_align(a0));
332 
333 #if PACK_DEBUG
334  show_pack(0, 0, end_padding);
335  putchar('\n');
336 #endif
337  format->stride = offset + end_padding;
338  format->packed = true;
339 }
340 
342 {
343  const Shader *shader = reinterpret_cast<const Shader *>(gpushader);
345 }
#define BLI_assert(a)
Definition: BLI_assert.h:46
unsigned int BLI_ghashutil_strhash_p_murmur(const void *ptr)
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned char uchar
Definition: BLI_sys_types.h:70
unsigned int uint
Definition: BLI_sys_types.h:67
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
struct GPUShader GPUShader
Definition: GPU_shader.h:20
#define GPU_VERT_ATTR_MAX_NAMES
GPUVertFetchMode
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
#define GPU_VERT_FORMAT_MAX_NAMES
#define GPU_VERT_ATTR_NAMES_BUF_LEN
BLI_INLINE const char * GPU_vertformat_attr_name_get(const GPUVertFormat *format, const GPUVertAttr *attr, uint n_idx)
#define GPU_MAX_SAFE_ATTR_NAME
#define GPU_VERT_ATTR_MAX_LEN
GPUVertCompType
@ GPU_COMP_I10
@ GPU_COMP_F32
int pad[32 - sizeof(int)]
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
virtual void vertformat_from_shader(GPUVertFormat *) const =0
SyclQueue void void * src
SyclQueue void void size_t num_bytes void
SyclQueue void * dest
int len
Definition: draw_manager.c:108
struct @653::@656 attr_id
static void safe_bytes(char out[11], const char data[8])
void GPU_vertformat_alias_add(GPUVertFormat *format, const char *alias)
static uint attr_align(const GPUVertAttr *a)
void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader)
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_copy(GPUVertFormat *dest, const GPUVertFormat *src)
uint padding(uint offset, uint alignment)
void GPU_vertformat_multiload_enable(GPUVertFormat *format, int load_count)
void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint UNUSED(max_len))
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 uchar copy_attr_name(GPUVertFormat *format, const char *name)
void GPU_vertformat_deinterleave(GPUVertFormat *format)
void VertexFormat_pack(GPUVertFormat *format)
static uint comp_size(GPUVertCompType type)
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
format
Definition: logImageCore.h:38
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
static const pxr::TfToken out("out", pxr::TfToken::Immortal)
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:490