Blender  V3.3
extract_mesh_vbo_attributes.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2021 Blender Foundation. All rights reserved. */
3 
8 #include "MEM_guardedalloc.h"
9 
10 #include <functional>
11 
12 #include "BLI_color.hh"
13 #include "BLI_math_vec_types.hh"
14 #include "BLI_string.h"
15 
16 #include "BKE_attribute.h"
17 
18 #include "draw_attributes.h"
19 #include "draw_subdivision.h"
20 #include "extract_mesh.hh"
21 
22 namespace blender::draw {
23 
24 /* ---------------------------------------------------------------------- */
29 {
30  switch (domain) {
31  case ATTR_DOMAIN_POINT:
32  return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
33  case ATTR_DOMAIN_CORNER:
34  return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
35  case ATTR_DOMAIN_FACE:
36  return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata;
37  case ATTR_DOMAIN_EDGE:
38  return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata;
39  default:
40  return nullptr;
41  }
42 }
43 
44 /* Utility to convert from the type used in the attributes to the types for the VBO.
45  * This is mostly used to promote integers and booleans to floats, as other types (float, float2,
46  * etc.) directly map to available GPU types. Booleans are still converted as attributes are vec4
47  * in the shader.
48  */
49 template<typename AttributeType, typename VBOType> struct AttributeTypeConverter {
50  static VBOType convert_value(AttributeType value)
51  {
52  if constexpr (std::is_same_v<AttributeType, VBOType>) {
53  return value;
54  }
55 
56  /* This should only concern bools which are converted to floats. */
57  return static_cast<VBOType>(value);
58  }
59 };
60 
61 /* Similar to the one in #extract_mesh_vcol_vbo.cc */
62 struct gpuMeshCol {
63  ushort r, g, b, a;
64 };
65 
68  {
74  return result;
75  }
76 };
77 
80  {
85  result.a = unit_float_to_ushort_clamp(value.a * (1.0f / 255.0f));
86  return result;
87  }
88 };
89 
90 /* Return the number of component for the attribute's value type, or 0 if is it unsupported. */
92 {
93  switch (type) {
94  case CD_PROP_BOOL:
95  case CD_PROP_INT8:
96  case CD_PROP_INT32:
97  case CD_PROP_FLOAT:
98  /* TODO(@kevindietrich): should be 1 when scalar attributes conversion is handled by us. See
99  * comment #extract_attr_init. */
100  return 3;
101  case CD_PROP_FLOAT2:
102  return 2;
103  case CD_PROP_FLOAT3:
104  return 3;
105  case CD_PROP_COLOR:
106  case CD_PROP_BYTE_COLOR:
107  return 4;
108  default:
109  return 0;
110  }
111 }
112 
114 {
115  switch (type) {
116  case CD_PROP_INT32:
117  return GPU_FETCH_INT_TO_FLOAT;
118  case CD_PROP_COLOR:
119  case CD_PROP_BYTE_COLOR:
121  default:
122  return GPU_FETCH_FLOAT;
123  }
124 }
125 
127 {
128  switch (type) {
129  case CD_PROP_INT32:
130  return GPU_COMP_I32;
131  case CD_PROP_COLOR:
132  case CD_PROP_BYTE_COLOR:
133  return GPU_COMP_U16;
134  default:
135  return GPU_COMP_F32;
136  }
137 }
138 
140  GPUVertBuf *vbo,
141  const DRW_AttributeRequest &request,
142  bool build_on_device,
143  uint32_t len)
144 {
145  GPUVertCompType comp_type = get_comp_type_for_type(request.cd_type);
146  GPUVertFetchMode fetch_mode = get_fetch_mode_for_type(request.cd_type);
148  /* We should not be here if the attribute type is not supported. */
149  BLI_assert(comp_size != 0);
150 
151  char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
153  /* Attributes use auto-name. */
154  BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
155 
156  GPUVertFormat format = {0};
158  GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode);
159 
160  if (mr.active_color_name && STREQ(request.attribute_name, mr.active_color_name)) {
162  }
165  }
166 
167  if (build_on_device) {
169  }
170  else {
173  }
174 }
175 
176 template<typename AttributeType, typename VBOType>
178  VBOType *vbo_data,
179  const DRW_AttributeRequest &request)
180 {
181  const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain);
182  BLI_assert(custom_data);
183  const int layer_index = request.layer_index;
184 
185  const MPoly *mpoly = mr->mpoly;
186  const MLoop *mloop = mr->mloop;
187 
188  const AttributeType *attr_data = static_cast<const AttributeType *>(
189  CustomData_get_layer_n(custom_data, request.cd_type, layer_index));
190 
192 
193  switch (request.domain) {
194  case ATTR_DOMAIN_POINT:
195  for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) {
196  *vbo_data = Converter::convert_value(attr_data[mloop->v]);
197  }
198  break;
199  case ATTR_DOMAIN_CORNER:
200  for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) {
201  *vbo_data = Converter::convert_value(attr_data[ml_index]);
202  }
203  break;
204  case ATTR_DOMAIN_EDGE:
205  for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) {
206  *vbo_data = Converter::convert_value(attr_data[mloop->e]);
207  }
208  break;
209  case ATTR_DOMAIN_FACE:
210  for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
211  const MPoly &poly = mpoly[mp_index];
212  const VBOType value = Converter::convert_value(attr_data[mp_index]);
213  for (int l = 0; l < poly.totloop; l++) {
214  *vbo_data++ = value;
215  }
216  }
217  break;
218  default:
220  break;
221  }
222 }
223 
224 template<typename AttributeType, typename VBOType>
226  VBOType *&vbo_data,
227  const DRW_AttributeRequest &request)
228 {
229  const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain);
230  BLI_assert(custom_data);
231  const int layer_index = request.layer_index;
232 
233  const int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index);
234 
236 
237  BMIter f_iter;
238  BMFace *efa;
239  BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
240  BMLoop *l_iter, *l_first;
241  l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
242  do {
243  const AttributeType *attr_data = nullptr;
244  if (request.domain == ATTR_DOMAIN_POINT) {
245  attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs));
246  }
247  else if (request.domain == ATTR_DOMAIN_CORNER) {
248  attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs));
249  }
250  else if (request.domain == ATTR_DOMAIN_FACE) {
251  attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(efa, cd_ofs));
252  }
253  else if (request.domain == ATTR_DOMAIN_EDGE) {
254  attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs));
255  }
256  else {
258  continue;
259  }
260  *vbo_data = Converter::convert_value(*attr_data);
261  vbo_data++;
262  } while ((l_iter = l_iter->next) != l_first);
263  }
264 }
265 
266 template<typename AttributeType, typename VBOType = AttributeType>
267 static void extract_attr_generic(const MeshRenderData *mr,
268  GPUVertBuf *vbo,
269  const DRW_AttributeRequest &request)
270 {
271  VBOType *vbo_data = static_cast<VBOType *>(GPU_vertbuf_get_data(vbo));
272 
273  if (mr->extract_type == MR_EXTRACT_BMESH) {
274  fill_vertbuf_with_attribute_bm<AttributeType>(mr, vbo_data, request);
275  }
276  else {
277  fill_vertbuf_with_attribute<AttributeType>(mr, vbo_data, request);
278  }
279 }
280 
281 static void extract_attr_init(
282  const MeshRenderData *mr, MeshBatchCache *cache, void *buf, void *UNUSED(tls_data), int index)
283 {
284  const DRW_Attributes *attrs_used = &cache->attr_used;
285  const DRW_AttributeRequest &request = attrs_used->requests[index];
286 
287  GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
288 
289  init_vbo_for_attribute(*mr, vbo, request, false, static_cast<uint32_t>(mr->loop_len));
290 
291  /* TODO(@kevindietrich): float3 is used for scalar attributes as the implicit conversion done by
292  * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the
293  * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar
294  * texture as for volume attribute, so we can control the conversion ourselves. */
295  switch (request.cd_type) {
296  case CD_PROP_BOOL:
297  extract_attr_generic<bool, float3>(mr, vbo, request);
298  break;
299  case CD_PROP_INT8:
300  extract_attr_generic<int8_t, float3>(mr, vbo, request);
301  break;
302  case CD_PROP_INT32:
303  extract_attr_generic<int32_t, float3>(mr, vbo, request);
304  break;
305  case CD_PROP_FLOAT:
306  extract_attr_generic<float, float3>(mr, vbo, request);
307  break;
308  case CD_PROP_FLOAT2:
309  extract_attr_generic<float2>(mr, vbo, request);
310  break;
311  case CD_PROP_FLOAT3:
312  extract_attr_generic<float3>(mr, vbo, request);
313  break;
314  case CD_PROP_COLOR:
315  extract_attr_generic<MPropCol, gpuMeshCol>(mr, vbo, request);
316  break;
317  case CD_PROP_BYTE_COLOR:
318  extract_attr_generic<ColorGeometry4b, gpuMeshCol>(mr, vbo, request);
319  break;
320  default:
322  }
323 }
324 
325 static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache,
326  const MeshRenderData *mr,
327  MeshBatchCache *cache,
328  void *buffer,
329  void *UNUSED(tls_data),
330  int index)
331 {
332  const DRW_Attributes *attrs_used = &cache->attr_used;
333  const DRW_AttributeRequest &request = attrs_used->requests[index];
334 
335  Mesh *coarse_mesh = subdiv_cache->mesh;
336 
337  const uint32_t dimensions = gpu_component_size_for_attribute_type(request.cd_type);
338 
339  /* Prepare VBO for coarse data. The compute shader only expects floats. */
340  GPUVertBuf *src_data = GPU_vertbuf_calloc();
341  GPUVertFormat coarse_format = {0};
342  GPU_vertformat_attr_add(&coarse_format, "data", GPU_COMP_F32, dimensions, GPU_FETCH_FLOAT);
343  GPU_vertbuf_init_with_format_ex(src_data, &coarse_format, GPU_USAGE_STATIC);
344  GPU_vertbuf_data_alloc(src_data, static_cast<uint32_t>(coarse_mesh->totloop));
345 
346  switch (request.cd_type) {
347  case CD_PROP_BOOL:
348  extract_attr_generic<bool, float3>(mr, src_data, request);
349  break;
350  case CD_PROP_INT8:
351  extract_attr_generic<int8_t, float3>(mr, src_data, request);
352  break;
353  case CD_PROP_INT32:
354  extract_attr_generic<int32_t, float3>(mr, src_data, request);
355  break;
356  case CD_PROP_FLOAT:
357  extract_attr_generic<float, float3>(mr, src_data, request);
358  break;
359  case CD_PROP_FLOAT2:
360  extract_attr_generic<float2>(mr, src_data, request);
361  break;
362  case CD_PROP_FLOAT3:
363  extract_attr_generic<float3>(mr, src_data, request);
364  break;
365  case CD_PROP_COLOR:
366  extract_attr_generic<MPropCol, gpuMeshCol>(mr, src_data, request);
367  break;
368  case CD_PROP_BYTE_COLOR:
369  extract_attr_generic<ColorGeometry4b, gpuMeshCol>(mr, src_data, request);
370  break;
371  default:
373  }
374 
375  GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer);
376  init_vbo_for_attribute(*mr, dst_buffer, request, true, subdiv_cache->num_subdiv_loops);
377 
378  /* Ensure data is uploaded properly. */
379  GPU_vertbuf_tag_dirty(src_data);
380  draw_subdiv_interp_custom_data(subdiv_cache,
381  src_data,
382  dst_buffer,
383  static_cast<int>(dimensions),
384  0,
386 
387  GPU_vertbuf_discard(src_data);
388 }
389 
390 /* Wrappers around extract_attr_init so we can pass the index of the attribute that we want to
391  * extract. The overall API does not allow us to pass this in a convenient way. */
392 #define EXTRACT_INIT_WRAPPER(index) \
393  static void extract_attr_init##index( \
394  const MeshRenderData *mr, MeshBatchCache *cache, void *buf, void *tls_data) \
395  { \
396  extract_attr_init(mr, cache, buf, tls_data, index); \
397  } \
398  static void extract_attr_init_subdiv##index(const DRWSubdivCache *subdiv_cache, \
399  const MeshRenderData *mr, \
400  MeshBatchCache *cache, \
401  void *buf, \
402  void *tls_data) \
403  { \
404  extract_attr_init_subdiv(subdiv_cache, mr, cache, buf, tls_data, index); \
405  }
406 
422 
423 template<int Index>
425 {
426  MeshExtract extractor = {nullptr};
427  extractor.init = fn;
428  extractor.init_subdiv = subdiv_fn;
429  extractor.data_type = MR_DATA_NONE;
430  extractor.data_size = 0;
431  extractor.use_threading = false;
432  extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[Index]);
433  return extractor;
434 }
435 
438 } // namespace blender::draw
439 
440 #define CREATE_EXTRACTOR_ATTR(index) \
441  blender::draw::create_extractor_attr<index>(blender::draw::extract_attr_init##index, \
442  blender::draw::extract_attr_init_subdiv##index)
443 
460 };
Generic geometry attributes built on CustomData.
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ ATTR_DOMAIN_FACE
Definition: BKE_attribute.h:29
@ ATTR_DOMAIN_CORNER
Definition: BKE_attribute.h:30
@ ATTR_DOMAIN_EDGE
Definition: BKE_attribute.h:28
void * CustomData_get_layer_n(const struct CustomData *data, int type, int n)
int CustomData_get_n_offset(const struct CustomData *data, int type, int n)
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned int uint
Definition: BLI_sys_types.h:67
unsigned short ushort
Definition: BLI_sys_types.h:68
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
eCustomDataType
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_COLOR
@ CD_PROP_INT8
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_PROP_BOOL
_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
#define GPU_MAX_ATTR
Definition: GPU_shader.h:388
void GPU_vertbuf_discard(GPUVertBuf *)
struct GPUVertBuf GPUVertBuf
GPUVertBuf * GPU_vertbuf_calloc(void)
void GPU_vertbuf_data_alloc(GPUVertBuf *, uint v_len)
void GPU_vertbuf_init_with_format_ex(GPUVertBuf *, const GPUVertFormat *, GPUUsageType)
#define GPU_vertbuf_init_with_format(verts, format)
void * GPU_vertbuf_get_data(const GPUVertBuf *verts)
void GPU_vertbuf_init_build_on_device(GPUVertBuf *verts, GPUVertFormat *format, uint v_len)
void GPU_vertbuf_tag_dirty(GPUVertBuf *verts)
@ GPU_USAGE_STATIC
void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint max_len)
GPUVertFetchMode
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
@ GPU_FETCH_INT_TO_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
#define GPU_MAX_SAFE_ATTR_NAME
void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias)
void GPU_vertformat_deinterleave(GPUVertFormat *format)
GPUVertCompType
@ GPU_COMP_U16
@ GPU_COMP_F32
@ GPU_COMP_I32
Read Guarded memory(de)allocation.
#define BM_FACE_FIRST_LOOP(p)
Definition: bmesh_class.h:622
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
Definition: bmesh_class.h:541
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ChannelStorageType r
Definition: BLI_color.hh:85
ChannelStorageType g
Definition: BLI_color.hh:85
ChannelStorageType b
Definition: BLI_color.hh:85
ChannelStorageType a
Definition: BLI_color.hh:85
Utilities for rendering attributes.
@ MR_DATA_NONE
void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, GPUVertBuf *src_data, GPUVertBuf *dst_data, int dimensions, int dst_offset, bool compress_to_u16)
int len
Definition: draw_manager.c:108
Extraction of Mesh data into VBO to feed to GPU.
void(const MeshRenderData *mr, MeshBatchCache *cache, void *buffer, void *r_data) ExtractInitFn
void(const DRWSubdivCache *subdiv_cache, const MeshRenderData *mr, MeshBatchCache *cache, void *buf, void *data) ExtractInitSubdivFn
@ MR_EXTRACT_BMESH
Definition: extract_mesh.hh:31
#define EXTRACT_INIT_WRAPPER(index)
#define CREATE_EXTRACTOR_ATTR(index)
const MeshExtract extract_attr[GPU_MAX_ATTR]
static uint comp_size(GPUVertCompType type)
ccl_global float * buffer
format
Definition: logImageCore.h:38
MINLINE unsigned short unit_float_to_ushort_clamp(float val)
float BLI_color_from_srgb_table[256]
Definition: math_color.c:517
static GPUVertCompType get_comp_type_for_type(eCustomDataType type)
constexpr MeshExtract create_extractor_attr(ExtractInitFn fn, ExtractInitSubdivFn subdiv_fn)
static void fill_vertbuf_with_attribute(const MeshRenderData *mr, VBOType *vbo_data, const DRW_AttributeRequest &request)
static uint gpu_component_size_for_attribute_type(eCustomDataType type)
static void extract_attr_generic(const MeshRenderData *mr, GPUVertBuf *vbo, const DRW_AttributeRequest &request)
static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, const MeshRenderData *mr, MeshBatchCache *cache, void *buffer, void *UNUSED(tls_data), int index)
static CustomData * get_custom_data_for_domain(const MeshRenderData *mr, eAttrDomain domain)
static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, VBOType *&vbo_data, const DRW_AttributeRequest &request)
static void init_vbo_for_attribute(const MeshRenderData &mr, GPUVertBuf *vbo, const DRW_AttributeRequest &request, bool build_on_device, uint32_t len)
static GPUVertFetchMode get_fetch_mode_for_type(eCustomDataType type)
static void extract_attr_init(const MeshRenderData *mr, MeshBatchCache *cache, void *buf, void *UNUSED(tls_data), int index)
unsigned int uint32_t
Definition: stdint.h:80
struct BMVert * v
Definition: bmesh_class.h:153
struct BMEdge * e
Definition: bmesh_class.h:164
struct BMLoop * next
Definition: bmesh_class.h:233
CustomData vdata
Definition: bmesh_class.h:337
CustomData edata
Definition: bmesh_class.h:337
CustomData pdata
Definition: bmesh_class.h:337
CustomData ldata
Definition: bmesh_class.h:337
struct Mesh * mesh
eCustomDataType cd_type
DRW_AttributeRequest requests[GPU_MAX_ATTR]
unsigned int e
unsigned int v
float color[4]
DRW_Attributes attr_used
GPUVertBuf * attr[GPU_MAX_ATTR]
size_t mesh_buffer_offset
eMRDataType data_type
ExtractInitSubdivFn * init_subdiv
size_t data_size
bool use_threading
ExtractInitFn * init
eMRExtractType extract_type
Definition: extract_mesh.hh:37
const MLoop * mloop
Definition: extract_mesh.hh:76
const char * default_color_name
Definition: extract_mesh.hh:90
const char * active_color_name
Definition: extract_mesh.hh:89
const MPoly * mpoly
Definition: extract_mesh.hh:77
CustomData vdata
CustomData pdata
CustomData edata
int totloop
CustomData ldata
static VBOType convert_value(AttributeType value)