77#define BUFFER_INDEX(buff_name) ((offsetof(MeshBufferList, buff_name) - offsetof(MeshBufferList, vbo)) / sizeof(void *))
78#define BUFFER_LEN (sizeof(MeshBufferList) / sizeof(void *))
80#define _BATCH_MAP1(a) batches_that_use_buffer(BUFFER_INDEX(a))
81#define _BATCH_MAP2(a, b) _BATCH_MAP1(a) | _BATCH_MAP1(b)
82#define _BATCH_MAP3(a, b, c) _BATCH_MAP2(a, b) | _BATCH_MAP1(c)
83#define _BATCH_MAP4(a, b, c, d) _BATCH_MAP3(a, b, c) | _BATCH_MAP1(d)
84#define _BATCH_MAP5(a, b, c, d, e) _BATCH_MAP4(a, b, c, d) | _BATCH_MAP1(e)
85#define _BATCH_MAP6(a, b, c, d, e, f) _BATCH_MAP5(a, b, c, d, e) | _BATCH_MAP1(f)
86#define _BATCH_MAP7(a, b, c, d, e, f, g) _BATCH_MAP6(a, b, c, d, e, f) | _BATCH_MAP1(g)
87#define _BATCH_MAP8(a, b, c, d, e, f, g, h) _BATCH_MAP7(a, b, c, d, e, f, g) | _BATCH_MAP1(h)
88#define _BATCH_MAP9(a, b, c, d, e, f, g, h, i) _BATCH_MAP8(a, b, c, d, e, f, g, h) | _BATCH_MAP1(i)
89#define _BATCH_MAP10(a, b, c, d, e, f, g, h, i, j) _BATCH_MAP9(a, b, c, d, e, f, g, h, i) | _BATCH_MAP1(j)
91#define BATCH_MAP(...) VA_NARGS_CALL_OVERLOAD(_BATCH_MAP, __VA_ARGS__)
95#define TRIS_PER_MAT_INDEX BUFFER_LEN
99 switch (buffer_index) {
210 if (batch_map & batch_requested) {
257 cd_used.
uv |= (1 << layer);
269 cd_used.
uv |= (1 << layer);
276 int gpumat_array_len,
293 for (
int i = 0; i < gpumat_array_len; i++) {
295 if (gpumat ==
nullptr) {
300 const char *name = gpu_attr->name;
303 std::optional<bke::AttrDomain> domain;
305 if (gpu_attr->is_default_color) {
306 name = default_color_name.
c_str();
314 if (name[0] !=
'\0') {
357 layer = (name[0] !=
'\0') ?
362 cd_used.
uv |= (1 << layer);
368 layer = (name[0] !=
'\0') ?
373 if (layer == -1 && name[0] !=
'\0') {
378 cd_used.
tan |= (1 << layer);
402 if (layer != -1 && domain.has_value()) {
428 memset(wstate, 0,
sizeof(*wstate));
441 memcpy(wstate_dst, wstate_src,
sizeof(*wstate_dst));
457 return ((!array1 && !array2) ||
458 (array1 && array2 && memcmp(array1, array2, size *
sizeof(
bool)) == 0));
465 return a->defgroup_active ==
b->defgroup_active && a->defgroup_len ==
b->defgroup_len &&
466 a->flags ==
b->flags && a->alert_mode ==
b->alert_mode &&
467 a->defgroup_sel_count ==
b->defgroup_sel_count &&
477 memset(wstate, 0,
sizeof(*wstate));
555 if (cache ==
nullptr) {
561 if (cache->
is_editmode != (mesh.runtime->edit_mesh !=
nullptr)) {
578 if (!mesh.runtime->batch_cache) {
579 mesh.runtime->batch_cache = MEM_new<MeshBatchCache>(__func__);
586 cache->
is_editmode = mesh.runtime->edit_mesh !=
nullptr;
609 if (mesh.runtime->batch_cache) {
640 for (
int i = 0; i < cache.
mat_len; i++) {
651 for (
int i = 0; i < cache.
mat_len; i++) {
684 vbo.edituv_stretch_area,
688 vbo.fdots_edituv_data,
716 vbo.fdots_edituv_data,
726 if (!mesh->runtime->batch_cache) {
737 batch_map =
BATCH_MAP(vbo.edit_data, vbo.fdots_nor);
751 batch_map =
BATCH_MAP(ibo.lines_paint_mask, vbo.pos, vbo.nor);
769 batch_map =
BATCH_MAP(vbo.edituv_data, vbo.fdots_edituv_data);
781 for (
int i = 0; i <
sizeof(mbuflist->
vbo) /
sizeof(
void *); i++) {
784 for (
int i = 0; i <
sizeof(mbuflist->
ibo) /
sizeof(
void *); i++) {
812 for (
int i = 0; i < cache.
mat_len; i++) {
817 for (
int i = 0; i <
sizeof(cache.
batch) /
sizeof(
void *); i++) {
818 gpu::Batch **
batch = (gpu::Batch **)&cache.
batch;
853 "No uv layer available in texpaint, but batches requested anyway!");
867 auto request_color_attribute = [&](
const char *name) {
953 int gpumat_array_len,
960 object, mesh, gpumat_array, gpumat_array_len, &attrs_needed);
963 *r_attrs = attrs_needed;
967 *r_cd_needed = cd_needed;
974 uint gpumat_array_len)
980 object, mesh, gpumat_array, gpumat_array_len, &attrs_needed);
1177 "No uv layer available in edituv, but batches requested anyway!");
1186 float **tot_uv_area)
1192 if (tot_area !=
nullptr) {
1195 if (tot_uv_area !=
nullptr) {
1267 if (cache ==
nullptr) {
1279 if (ctime - cache->
lastmatch >
U.vbotimeout) {
1334 const bool is_paint_mode,
1335 const bool use_hide)
1340 bool cd_uv_update =
false;
1355 for (
const int buffer_index : used_buffer_indices) {
1373 const bool is_editmode = (mesh.runtime->edit_mesh !=
nullptr) &&
1395 if (batch_requested &
1416 if (cd_overlap ==
false || attr_overlap ==
false) {
1420 cd_uv_update =
true;
1441 for (
int i = 0; i < cache.
mat_len; i++) {
1461 if (cd_uv_update || (cache.
is_uvsyncsel != is_uvsyncsel)) {
1486 if ((batch_requested & ~cache.
batch_ready) == 0) {
1499 if (do_update_sculpt_normals) {
1505 bool do_cage =
false, do_uvcage =
false;
1506 if (is_editmode && edit_mode_active) {
1510 do_cage = editmesh_eval_final != editmesh_eval_cage;
1511 do_uvcage = !(editmesh_eval_final->
runtime->is_original_bmesh &&
1635 for (
int i = 0; i < cache.
mat_len; i++) {
1671 if (!do_subdivision || do_cage) {
1682 if (!do_subdivision || do_cage) {
1692 if (!do_subdivision) {
1821 auto assert_final_deps_valid = [&](
const int buffer_index) {
1823 batches_that_use_buffer_local.
lookup(buffer_index));
1828 assert_final_deps_valid(
BUFFER_INDEX(vbo.sculpt_data));
1831 assert_final_deps_valid(
BUFFER_INDEX(vbo.mesh_analysis));
1842 assert_final_deps_valid(
BUFFER_INDEX(vbo.edituv_data));
1843 assert_final_deps_valid(
BUFFER_INDEX(vbo.edituv_stretch_area));
1844 assert_final_deps_valid(
BUFFER_INDEX(vbo.edituv_stretch_angle));
1846 assert_final_deps_valid(
BUFFER_INDEX(vbo.fdots_edituv_data));
1850 assert_final_deps_valid(
BUFFER_INDEX(vbo.attr_viewer));
1855 assert_final_deps_valid(
BUFFER_INDEX(ibo.lines_loose));
1856 assert_final_deps_valid(
BUFFER_INDEX(ibo.lines_adjacency));
1857 assert_final_deps_valid(
BUFFER_INDEX(ibo.lines_paint_mask));
1860 assert_final_deps_valid(
BUFFER_INDEX(ibo.edituv_tris));
1861 assert_final_deps_valid(
BUFFER_INDEX(ibo.edituv_lines));
1862 assert_final_deps_valid(
BUFFER_INDEX(ibo.edituv_points));
1863 assert_final_deps_valid(
BUFFER_INDEX(ibo.edituv_fdots));
1877 ob.object_to_world(),
1894 ob.object_to_world(),
1902 if (do_subdivision) {
1910 ob.object_to_world(),
1931 ob.object_to_world(),
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_named_layer(const CustomData *data, eCustomDataType type, blender::StringRef name)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
bool CustomData_layer_is_anonymous(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_stencil_layer(const CustomData *data, eCustomDataType type)
int CustomData_get_active_layer(const CustomData *data, eCustomDataType type)
int CustomData_get_render_layer(const CustomData *data, eCustomDataType type)
@ BKE_MESH_BATCH_DIRTY_UVEDIT_ALL
@ BKE_MESH_BATCH_DIRTY_SELECT_PAINT
@ BKE_MESH_BATCH_DIRTY_SHADING
@ BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT
@ BKE_MESH_BATCH_DIRTY_ALL
@ BKE_MESH_BATCH_DIRTY_SELECT
General operations, lookup, etc. for blender objects.
const Mesh * BKE_object_get_editmesh_eval_cage(const Object *object)
const Mesh * BKE_object_get_editmesh_eval_final(const Object *object)
A BVH for high poly meshes.
bool BKE_subsurf_modifier_has_gpu_subdiv(const Mesh *mesh)
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_task_graph_work_and_wait(struct TaskGraph *task_graph)
#define ME_USING_MIRROR_X_VERTEX_GROUPS(_me)
Object is a sort of wrapper for general info.
#define GPU_BATCH_CLEAR_SAFE(batch)
#define GPU_BATCH_DISCARD_SAFE(batch)
#define GPU_INDEXBUF_DISCARD_SAFE(elem)
ListBase GPU_material_attributes(const GPUMaterial *material)
#define GPU_VERTBUF_DISCARD_SAFE(verts)
Read Guarded memory(de)allocation.
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
const Value & lookup(const Key &key) const
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
constexpr const char * c_str() const
local_group_size(16, 16) .push_constant(Type b
#define BUFFER_INDEX(buff_name)
#define TRIS_PER_MAT_INDEX
bool DRW_batch_requested(blender::gpu::Batch *batch, GPUPrimType prim_type)
blender::gpu::Batch * DRW_batch_request(blender::gpu::Batch **batch)
void DRW_vbo_request(blender::gpu::Batch *batch, blender::gpu::VertBuf **vbo)
bool DRW_vbo_requested(blender::gpu::VertBuf *vbo)
void DRW_ibo_request(blender::gpu::Batch *batch, blender::gpu::IndexBuf **ibo)
bool DRW_ibo_requested(blender::gpu::IndexBuf *ibo)
bool DRW_object_is_in_edit_mode(const Object *ob)
void *(* MEM_dupallocN)(const void *vmemh)
pbvh::Tree * pbvh_get(Object &object)
void update_normals_from_eval(Object &object_eval, Tree &pbvh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(Object &object, Mesh &mesh)
const CustomData & mesh_cd_ldata_get_from_mesh(const Mesh &mesh)
static void drw_mesh_weight_state_extract(Object &ob, Mesh &mesh, const ToolSettings &ts, bool paint_mode, DRW_MeshWeightState *wstate)
static void mesh_batch_cache_discard_batch(MeshBatchCache &cache, const DRWBatchFlag batch_map)
BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b)
blender::gpu::Batch * DRW_mesh_batch_cache_get_loose_edges(Mesh &mesh)
blender::gpu::Batch ** DRW_mesh_batch_cache_get_surface_shaded(Object &object, Mesh &mesh, GPUMaterial **gpumat_array, uint gpumat_array_len)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edituv_edges(Object &object, Mesh &mesh)
static void mesh_batch_cache_init(Object &object, Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface_texpaint_single(Object &object, Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_edges(Mesh &mesh)
static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache &cache)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edituv_facedots(Object &object, Mesh &mesh)
static bool drw_mesh_flags_equal(const bool *array1, const bool *array2, int size)
blender::gpu::Batch * DRW_mesh_batch_cache_get_facedots_with_select_id(Mesh &mesh)
static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object &object, const Mesh &mesh, const GPUMaterial *const *gpumat_array, int gpumat_array_len, DRW_Attributes *attributes)
static void mesh_cd_calc_active_uv_layer(const Object &object, const Mesh &mesh, DRW_MeshCDMask &cd_used)
int DRW_mesh_material_count_get(const Object &object, const Mesh &mesh)
static constexpr DRWBatchFlag batches_that_use_buffer(const int buffer_index)
void drw_attributes_clear(DRW_Attributes *attributes)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_facedots(Mesh &mesh)
void DRW_create_subdivision(Object &ob, Mesh &mesh, MeshBatchCache &batch_cache, MeshBufferCache &mbc, const bool is_editmode, const bool is_paint_mode, const bool edit_mode_active, const float4x4 &object_to_world, const bool do_final, const bool do_uvedit, const bool do_cage, const ToolSettings *ts, const bool use_hide)
void draw_subdiv_cache_free(DRWSubdivCache &cache)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edituv_verts(Object &object, Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_mesh_analysis(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface(Mesh &mesh)
BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b)
static void texpaint_request_active_uv(MeshBatchCache &cache, Object &object, Mesh &mesh)
static void mesh_cd_calc_edit_uv_layer(const Mesh &, DRW_MeshCDMask *cd_used)
static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache &cache)
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface_weights(Mesh &mesh)
bool drw_custom_data_match_attribute(const CustomData &custom_data, const char *name, int *r_layer_index, eCustomDataType *r_type)
blender::gpu::Batch ** DRW_mesh_batch_cache_get_surface_texpaint(Object &object, Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edituv_faces(Object &object, Mesh &mesh)
const CustomData & mesh_cd_edata_get_from_mesh(const Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface_vertpaint(Object &object, Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_wireframes_face(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface_sculpt(Object &object, Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_all_edges(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_loop_normals(Mesh &mesh)
static bool drw_mesh_weight_state_compare(const DRW_MeshWeightState *a, const DRW_MeshWeightState *b)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edge_detection(Mesh &mesh, bool *r_is_manifold)
static void mesh_batch_cache_discard_surface_batches(MeshBatchCache &cache)
void DRW_mesh_batch_cache_free_old(Mesh *mesh, int ctime)
void DRW_mesh_batch_cache_free(void *batch_cache)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_triangles(Mesh &mesh)
int mesh_render_mat_len_get(const Object &object, const Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_vertices(Mesh &mesh)
void mesh_buffer_cache_create_requested(TaskGraph &task_graph, MeshBatchCache &cache, MeshBufferCache &mbc, Object &object, Mesh &mesh, bool is_editmode, bool is_paint_mode, bool edit_mode_active, const float4x4 &object_to_world, bool do_final, bool do_uvedit, const Scene &scene, const ToolSettings *ts, bool use_hide)
blender::gpu::Batch * DRW_mesh_batch_cache_get_uv_edges(Object &object, Mesh &mesh)
static void mesh_batch_cache_clear(MeshBatchCache &cache)
blender::gpu::Batch * DRW_mesh_batch_cache_get_triangles_with_select_id(Mesh &mesh)
static void mesh_buffer_cache_clear(MeshBufferCache *mbc)
static void mesh_batch_cache_discard_uvedit(MeshBatchCache &cache)
BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a)
static bool mesh_batch_cache_valid(Object &object, Mesh &mesh)
gpu::VertBuf * DRW_mesh_batch_cache_pos_vertbuf_get(Mesh &mesh)
static void mesh_batch_cache_check_vertex_group(MeshBatchCache &cache, const DRW_MeshWeightState *wstate)
static void drw_mesh_weight_state_copy(DRW_MeshWeightState *wstate_dst, const DRW_MeshWeightState *wstate_src)
@ DRW_MESH_WEIGHT_STATE_MULTIPAINT
@ DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE
@ DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE
BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b)
const Mesh & editmesh_final_or_this(const Object &object, const Mesh &mesh)
static void mesh_cd_calc_active_mask_uv_layer(const Object &object, const Mesh &mesh, DRW_MeshCDMask &cd_used)
BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache &cache, DRWBatchFlag new_flag)
void DRW_mesh_batch_cache_validate(Object &object, Mesh &mesh)
static void mesh_buffer_list_clear(MeshBufferList *mbuflist)
void drw_attributes_merge(DRW_Attributes *dst, const DRW_Attributes *src, std::mutex &render_mutex)
blender::gpu::Batch * DRW_mesh_batch_cache_get_verts_with_select_id(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_sculpt_overlays(Mesh &mesh)
void DRW_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface_viewer_attribute(Mesh &mesh)
@ MBC_EDIT_SELECTION_FACES
@ MBC_EDITUV_FACES_STRETCH_ANGLE
@ MBC_EDIT_SELECTION_FACEDOTS
@ MBC_EDIT_SELECTION_VERTS
@ MBC_EDITUV_FACES_STRETCH_AREA
@ MBC_VIEWER_ATTRIBUTE_OVERLAY
@ MBC_EDIT_SELECTION_EDGES
const CustomData & mesh_cd_vdata_get_from_mesh(const Mesh &mesh)
void DRW_mesh_batch_cache_create_requested(TaskGraph &task_graph, Object &ob, Mesh &mesh, const Scene &scene, bool is_paint_mode, bool use_hide)
static void mesh_batch_cache_free_subdiv_cache(MeshBatchCache &cache)
static void drw_add_attributes_vbo(gpu::Batch *batch, MeshBufferList *mbuflist, DRW_Attributes *attr_used)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_vert_normals(Mesh &mesh)
static void mesh_batch_cache_request_surface_batches(MeshBatchCache &cache)
static MeshBatchCache * mesh_batch_cache_get(Mesh &mesh)
static void drw_mesh_weight_state_clear(DRW_MeshWeightState *wstate)
void DRW_mesh_get_attributes(const Object &object, const Mesh &mesh, const GPUMaterial *const *gpumat_array, int gpumat_array_len, DRW_Attributes *r_attrs, DRW_MeshCDMask *r_cd_needed)
void drw_attributes_add_request(DRW_Attributes *attrs, const char *name, const eCustomDataType type, const int layer_index, const blender::bke::AttrDomain domain)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edges_with_select_id(Mesh &mesh)
static void request_active_and_default_color_attributes(const Object &object, const Mesh &mesh, DRW_Attributes &attributes)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Object &object, Mesh &mesh, float **tot_area, float **tot_uv_area)
static void edituv_request_active_uv(MeshBatchCache &cache, Object &object, Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_all_verts(Mesh &mesh)
blender::gpu::Batch * DRW_mesh_batch_cache_get_surface_edges(Object &object, Mesh &mesh)
bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b)
blender::gpu::Batch * DRW_mesh_batch_cache_get_edit_skin_roots(Mesh &mesh)
static void drw_mesh_batch_cache_check_available(TaskGraph &task_graph, Mesh &mesh)
const CustomData & mesh_cd_pdata_get_from_mesh(const Mesh &mesh)
MeshRuntimeHandle * runtime
char * default_color_attribute
char * active_color_attribute
struct SculptSession * sculpt
DRW_Attributes attr_needed
DRW_MeshWeightState weight_state
Array< gpu::IndexBuf * > tris_per_mat
DRWBatchFlag batch_requested
DRW_Attributes attr_used_over_time
DRWSubdivCache * subdiv_cache
Array< gpu::Batch * > surface_per_mat
DRW_MeshCDMask cd_used_over_time
gpu::Batch * edit_selection_faces
gpu::Batch * edit_skin_roots
gpu::Batch * wire_loops_uvs
gpu::Batch * surface_weights
gpu::Batch * edituv_fdots
gpu::Batch * sculpt_overlays
gpu::Batch * edit_selection_edges
gpu::Batch * edituv_faces
gpu::Batch * surface_viewer_attribute
gpu::Batch * edituv_faces_stretch_angle
gpu::Batch * edit_mesh_analysis
gpu::Batch * edit_vertices
gpu::Batch * edituv_verts
gpu::Batch * edge_detection
gpu::Batch * edit_selection_verts
gpu::Batch * edit_triangles
gpu::Batch * edituv_edges
gpu::Batch * edituv_faces_stretch_area
gpu::Batch * edit_selection_fdots
MeshExtractLooseGeom loose_geom
SortedFaceData face_sorted
gpu::VertBuf * edituv_stretch_area
gpu::IndexBuf * edituv_fdots
struct blender::draw::MeshBufferList::@247 vbo
gpu::VertBuf * attr_viewer
gpu::VertBuf * mesh_analysis
gpu::VertBuf * sculpt_data
gpu::VertBuf * edituv_stretch_angle
gpu::IndexBuf * lines_loose
gpu::IndexBuf * lines_paint_mask
gpu::VertBuf * fdots_edituv_data
gpu::IndexBuf * edituv_points
gpu::IndexBuf * edituv_lines
gpu::IndexBuf * lines_adjacency
gpu::VertBuf * edituv_data
struct blender::draw::MeshBufferList::@248 ibo
gpu::VertBuf * skin_roots
gpu::IndexBuf * edituv_tris
gpu::VertBuf * attr[GPU_MAX_ATTR]