Cleanup: replace attrib w/ attr
[blender.git] / source / blender / draw / intern / draw_cache_impl_curve.c
index 3133a1d06d224686f0e436b70e93648f3f3162eb..742ca20ba83c9ac7a999ab0f05ad31ba3e99aeae 100644 (file)
 #include "DNA_curve_types.h"
 
 #include "BKE_curve.h"
-
+#include "BKE_displist.h"
 #include "BKE_font.h"
 
 #include "GPU_batch.h"
 #include "GPU_texture.h"
+#include "GPU_material.h"
 
 #include "UI_resources.h"
 
@@ -99,23 +100,27 @@ static void curve_render_overlay_verts_edges_len_get(
 
 static void curve_render_wire_verts_edges_len_get(
         const CurveCache *ob_curve_cache,
-        int *r_vert_len, int *r_edge_len)
+        int *r_curve_len, int *r_vert_len, int *r_edge_len)
 {
        BLI_assert(r_vert_len || r_edge_len);
        int vert_len = 0;
        int edge_len = 0;
+       int curve_len = 0;
        for (const BevList *bl = ob_curve_cache->bev.first; bl; bl = bl->next) {
                if (bl->nr > 0) {
                        const bool is_cyclic = bl->poly != -1;
-
-                       /* verts */
+                       edge_len += (is_cyclic) ? bl->nr : bl->nr - 1;
                        vert_len += bl->nr;
-
-                       /* edges */
-                       edge_len += bl->nr;
-                       if (!is_cyclic) {
-                               edge_len -= 1;
-                       }
+                       curve_len += 1;
+               }
+       }
+       for (const DispList *dl = ob_curve_cache->disp.first; dl; dl = dl->next) {
+               if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
+                       BLI_assert(dl->parts == 1);
+                       const bool is_cyclic = dl->type == DL_POLY;
+                       edge_len += (is_cyclic) ? dl->nr : dl->nr - 1;
+                       vert_len += dl->nr;
+                       curve_len += 1;
                }
        }
        if (r_vert_len) {
@@ -124,6 +129,9 @@ static void curve_render_wire_verts_edges_len_get(
        if (r_edge_len) {
                *r_edge_len = edge_len;
        }
+       if (r_curve_len) {
+               *r_curve_len = curve_len;
+       }
 }
 
 static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_curve_cache)
@@ -159,6 +167,7 @@ typedef struct CurveRenderData {
        } overlay;
 
        struct {
+               int curve_len;
                int vert_len;
                int edge_len;
        } wire;
@@ -216,7 +225,7 @@ static CurveRenderData *curve_render_data_create(Curve *cu, CurveCache *ob_curve
        if (types & CU_DATATYPE_WIRE) {
                curve_render_wire_verts_edges_len_get(
                        rdata->ob_curve_cache,
-                       &rdata->wire.vert_len, &rdata->wire.edge_len);
+                       &rdata->wire.curve_len, &rdata->wire.vert_len, &rdata->wire.edge_len);
        }
 
        if (cu->editnurb) {
@@ -281,64 +290,112 @@ static int curve_render_data_wire_edges_len_get(const CurveRenderData *rdata)
        return rdata->wire.edge_len;
 }
 
+static int curve_render_data_wire_curve_len_get(const CurveRenderData *rdata)
+{
+       BLI_assert(rdata->types & CU_DATATYPE_WIRE);
+       return rdata->wire.curve_len;
+}
+
 static int curve_render_data_normal_len_get(const CurveRenderData *rdata)
 {
        BLI_assert(rdata->types & CU_DATATYPE_NORMAL);
        return rdata->normal.len;
 }
 
+static void curve_cd_calc_used_gpu_layers(int *cd_layers, struct GPUMaterial **gpumat_array, int gpumat_array_len)
+{
+       GPUVertAttrLayers gpu_attrs = {{{0}}};
+       for (int i = 0; i < gpumat_array_len; i++) {
+               struct GPUMaterial *gpumat = gpumat_array[i];
+               if (gpumat == NULL) {
+                       continue;
+               }
+               GPU_material_vertex_attrs(gpumat, &gpu_attrs);
+               for (int j = 0; j < gpu_attrs.totlayer; j++) {
+                       const char *name = gpu_attrs.layer[j].name;
+                       int type = gpu_attrs.layer[j].type;
+
+                       /* Curves cannot have named layers.
+                        * Note: We could relax this assumption later. */
+                       if (name[0] != '\0') {
+                               continue;
+                       }
+
+                       if (type == CD_AUTO_FROM_NAME) {
+                               type = CD_MTFACE;
+                       }
+
+                       switch (type) {
+                               case CD_MTFACE:
+                                       *cd_layers |= CD_MLOOPUV;
+                                       break;
+                               case CD_TANGENT:
+                                       /* Currently unsupported */
+                                       // *cd_layers |= CD_TANGENT;
+                                       break;
+                               case CD_MCOL:
+                                       /* Curve object don't have Color data. */
+                                       break;
+                               case CD_ORCO:
+                                       *cd_layers |= CD_ORCO;
+                                       break;
+                       }
+               }
+       }
+}
 
 /* ---------------------------------------------------------------------- */
 /* Curve GPUBatch Cache */
 
 typedef struct CurveBatchCache {
-       /* center-line */
        struct {
-               GPUVertBuf *verts;
-               GPUVertBuf *edges;
-               GPUBatch *batch;
-               GPUIndexBuf *elem;
-       } wire;
+               GPUVertBuf *pos_nor;
+               GPUVertBuf *curves_pos;
+       } ordered;
 
-       /* normals */
        struct {
-               GPUVertBuf *verts;
-               GPUVertBuf *edges;
-               GPUBatch *batch;
-               GPUIndexBuf *elem;
-       } normal;
+               GPUVertBuf *pos_nor;
+               GPUVertBuf *uv;
 
-       /* control handles and vertices */
-       struct {
-               GPUBatch *edges;
-               GPUBatch *verts;
-               GPUBatch *verts_no_handles;
-       } overlay;
+               GPUVertBuf *wireframe_data;
+       } tess;
 
        struct {
-               GPUVertBuf *verts;
-               GPUIndexBuf *triangles_in_order;
-               GPUBatch **shaded_triangles;
-               GPUBatch *batch;
-               int mat_len;
-       } surface;
-
-       /* Wireframes */
+               /* Curve points. Aligned with ordered.pos_nor */
+               GPUVertBuf *curves_nor;
+               GPUVertBuf *curves_weight; /* TODO. */
+               /* Edit points (beztriples and bpoints) */
+               GPUVertBuf *pos;
+               GPUVertBuf *data;
+       } edit;
+
        struct {
-               GPUBatch *batch;
-       } face_wire;
+               GPUIndexBuf *surfaces_tris;
+               GPUIndexBuf *curves_lines;
+               /* Edit mode */
+               GPUIndexBuf *edit_verts_points; /* Only control points. Not handles. */
+               GPUIndexBuf *edit_lines;
+       } ibo;
 
-       /* 3d text */
        struct {
-               GPUBatch *select;
-               GPUBatch *cursor;
-       } text;
+               GPUBatch *surfaces;
+               GPUBatch *curves;
+               /* control handles and vertices */
+               GPUBatch *edit_edges;
+               GPUBatch *edit_verts;
+               GPUBatch *edit_handles_verts;
+               GPUBatch *edit_normals;
+               /* Triangles for object mode wireframe. */
+               GPUBatch *wire_triangles;
+       } batch;
+
+       GPUIndexBuf **surf_per_mat_tris;
+       GPUBatch **surf_per_mat;
+       int mat_len;
+       int cd_used, cd_needed;
 
        /* settings to determine if cache is invalid */
        bool is_dirty;
-
-       float normal_size;
-
        bool is_editmode;
 } CurveBatchCache;
 
@@ -352,6 +409,10 @@ static bool curve_batch_cache_valid(Curve *cu)
                return false;
        }
 
+       if (cache->mat_len != max_ii(1, cu->totcol)) {
+               return false;
+       }
+
        if (cache->is_dirty) {
                return false;
        }
@@ -391,6 +452,17 @@ static void curve_batch_cache_init(Curve *cu)
        }
 #endif
 
+       cache->cd_used = 0;
+       cache->mat_len = max_ii(1, cu->totcol);
+       cache->surf_per_mat_tris = MEM_mallocN(sizeof(*cache->surf_per_mat_tris) * cache->mat_len, __func__);
+       cache->surf_per_mat = MEM_mallocN(sizeof(*cache->surf_per_mat) * cache->mat_len, __func__);
+
+       /* TODO Might be wiser to alloc in one chunck. */
+       for (int i = 0; i < cache->mat_len; ++i) {
+               cache->surf_per_mat_tris[i] = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
+               cache->surf_per_mat[i] = MEM_callocN(sizeof(GPUBatch), "GPUBatch");
+       }
+
        cache->is_editmode = (cu->editnurb != NULL) || (cu->editfont != NULL);
 
        cache->is_dirty = false;
@@ -416,14 +488,11 @@ void DRW_curve_batch_cache_dirty_tag(Curve *cu, int mode)
                        cache->is_dirty = true;
                        break;
                case BKE_CURVE_BATCH_DIRTY_SELECT:
-                       /* editnurb */
-                       GPU_BATCH_DISCARD_SAFE(cache->overlay.verts_no_handles);
-                       GPU_BATCH_DISCARD_SAFE(cache->overlay.verts);
-                       GPU_BATCH_DISCARD_SAFE(cache->overlay.edges);
-
-                       /* editfont */
-                       GPU_BATCH_DISCARD_SAFE(cache->text.select);
-                       GPU_BATCH_DISCARD_SAFE(cache->text.cursor);
+                       GPU_VERTBUF_DISCARD_SAFE(cache->edit.data);
+
+                       GPU_BATCH_DISCARD_SAFE(cache->batch.edit_edges);
+                       GPU_BATCH_DISCARD_SAFE(cache->batch.edit_verts);
+                       GPU_BATCH_DISCARD_SAFE(cache->batch.edit_handles_verts);
                        break;
                default:
                        BLI_assert(0);
@@ -437,33 +506,35 @@ static void curve_batch_cache_clear(Curve *cu)
                return;
        }
 
-       GPU_BATCH_DISCARD_SAFE(cache->overlay.verts_no_handles);
-       GPU_BATCH_DISCARD_SAFE(cache->overlay.verts);
-       GPU_BATCH_DISCARD_SAFE(cache->overlay.edges);
-
-       GPU_VERTBUF_DISCARD_SAFE(cache->surface.verts);
-       GPU_INDEXBUF_DISCARD_SAFE(cache->surface.triangles_in_order);
-
-       GPU_BATCH_DISCARD_ARRAY_SAFE(cache->surface.shaded_triangles, cache->surface.mat_len);
-       GPU_BATCH_DISCARD_SAFE(cache->surface.batch);
-
-       GPU_BATCH_DISCARD_SAFE(cache->face_wire.batch);
-
-       /* don't own vbo & elems */
-       GPU_BATCH_DISCARD_SAFE(cache->wire.batch);
-       GPU_VERTBUF_DISCARD_SAFE(cache->wire.verts);
-       GPU_VERTBUF_DISCARD_SAFE(cache->wire.edges);
-       GPU_INDEXBUF_DISCARD_SAFE(cache->wire.elem);
-
-       /* don't own vbo & elems */
-       GPU_BATCH_DISCARD_SAFE(cache->normal.batch);
-       GPU_VERTBUF_DISCARD_SAFE(cache->normal.verts);
-       GPU_VERTBUF_DISCARD_SAFE(cache->normal.edges);
-       GPU_INDEXBUF_DISCARD_SAFE(cache->normal.elem);
+       for (int i = 0; i < sizeof(cache->ordered) / sizeof(void *); ++i) {
+               GPUVertBuf **vbo = (GPUVertBuf **)&cache->ordered;
+               GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
+       }
+       for (int i = 0; i < sizeof(cache->tess) / sizeof(void *); ++i) {
+               GPUVertBuf **vbo = (GPUVertBuf **)&cache->tess;
+               GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
+       }
+       for (int i = 0; i < sizeof(cache->edit) / sizeof(void *); ++i) {
+               GPUVertBuf **vbo = (GPUVertBuf **)&cache->edit;
+               GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
+       }
+       for (int i = 0; i < sizeof(cache->ibo) / sizeof(void *); ++i) {
+               GPUIndexBuf **ibo = (GPUIndexBuf **)&cache->ibo;
+               GPU_INDEXBUF_DISCARD_SAFE(ibo[i]);
+       }
+       for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); ++i) {
+               GPUBatch **batch = (GPUBatch **)&cache->batch;
+               GPU_BATCH_DISCARD_SAFE(batch[i]);
+       }
 
-       /* 3d text */
-       GPU_BATCH_DISCARD_SAFE(cache->text.cursor);
-       GPU_BATCH_DISCARD_SAFE(cache->text.select);
+       for (int i = 0; i < cache->mat_len; ++i) {
+               GPU_INDEXBUF_DISCARD_SAFE(cache->surf_per_mat_tris[i]);
+               GPU_BATCH_DISCARD_SAFE(cache->surf_per_mat[i]);
+       }
+       MEM_SAFE_FREE(cache->surf_per_mat_tris);
+       MEM_SAFE_FREE(cache->surf_per_mat);
+       cache->mat_len = 0;
+       cache->cd_used = 0;
 }
 
 void DRW_curve_batch_cache_free(Curve *cu)
@@ -473,617 +544,492 @@ void DRW_curve_batch_cache_free(Curve *cu)
 }
 
 /* -------------------------------------------------------------------- */
-
 /** \name Private Curve Cache API
  * \{ */
 
 /* GPUBatch cache usage. */
-static GPUVertBuf *curve_batch_cache_get_wire_verts(CurveRenderData *rdata, CurveBatchCache *cache)
+static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos)
 {
-       BLI_assert(rdata->types & CU_DATATYPE_WIRE);
        BLI_assert(rdata->ob_curve_cache != NULL);
 
-       if (cache->wire.verts == NULL) {
-               static GPUVertFormat format = { 0 };
-               static struct { uint pos; } attr_id;
-               if (format.attr_len == 0) {
-                       /* initialize vertex format */
-                       attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-               }
+       static GPUVertFormat format = { 0 };
+       static struct { uint pos; } attr_id;
+       if (format.attr_len == 0) {
+               attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+       }
 
-               const int vert_len = curve_render_data_wire_verts_len_get(rdata);
+       const int vert_len = curve_render_data_wire_verts_len_get(rdata);
+       GPU_vertbuf_init_with_format(vbo_curves_pos, &format);
+       GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len);
 
-               GPUVertBuf *vbo = cache->wire.verts = GPU_vertbuf_create_with_format(&format);
-               GPU_vertbuf_data_alloc(vbo, vert_len);
-               int vbo_len_used = 0;
-               for (const BevList *bl = rdata->ob_curve_cache->bev.first; bl; bl = bl->next) {
-                       if (bl->nr > 0) {
-                               const int i_end = vbo_len_used + bl->nr;
-                               for (const BevPoint *bevp = bl->bevpoints; vbo_len_used < i_end; vbo_len_used++, bevp++) {
-                                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used, bevp->vec);
-                               }
+       int v_idx = 0;
+       for (const BevList *bl = rdata->ob_curve_cache->bev.first; bl; bl = bl->next) {
+               if (bl->nr <= 0) {
+                       continue;
+               }
+               const int i_end = v_idx + bl->nr;
+               for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) {
+                       GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec);
+               }
+       }
+       for (const DispList *dl = rdata->ob_curve_cache->disp.first; dl; dl = dl->next) {
+               if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
+                       for (int i = 0; i < dl->nr; v_idx++, i++) {
+                               GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]);
                        }
                }
-               BLI_assert(vbo_len_used == vert_len);
        }
-
-       return cache->wire.verts;
+       BLI_assert(v_idx == vert_len);
 }
 
-static GPUIndexBuf *curve_batch_cache_get_wire_edges(CurveRenderData *rdata, CurveBatchCache *cache)
+static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines)
 {
-       BLI_assert(rdata->types & CU_DATATYPE_WIRE);
        BLI_assert(rdata->ob_curve_cache != NULL);
 
-       if (cache->wire.edges == NULL) {
-               const int vert_len = curve_render_data_wire_verts_len_get(rdata);
-               const int edge_len = curve_render_data_wire_edges_len_get(rdata);
-               int edge_len_used = 0;
-
-               GPUIndexBufBuilder elb;
-               GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edge_len, vert_len);
-
-               int i = 0;
-               for (const BevList *bl = rdata->ob_curve_cache->bev.first; bl; bl = bl->next) {
-                       if (bl->nr > 0) {
-                               const bool is_cyclic = bl->poly != -1;
-                               const int i_end = i + (bl->nr);
-                               int i_prev;
-                               if (is_cyclic) {
-                                       i_prev = i + (bl->nr - 1);
-                               }
-                               else {
-                                       i_prev = i;
-                                       i += 1;
-                               }
-                               for (; i < i_end; i_prev = i++) {
-                                       GPU_indexbuf_add_line_verts(&elb, i_prev, i);
-                                       edge_len_used += 1;
-                               }
+       const int vert_len = curve_render_data_wire_verts_len_get(rdata);
+       const int edge_len = curve_render_data_wire_edges_len_get(rdata);
+       const int curve_len = curve_render_data_wire_curve_len_get(rdata);
+       /* Count the last vertex or each strip and the primitive restart. */
+       const int index_len = edge_len + curve_len * 2;
+
+       GPUIndexBufBuilder elb;
+       GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len, true);
+
+       int v_idx = 0;
+       for (const BevList *bl = rdata->ob_curve_cache->bev.first; bl; bl = bl->next) {
+               if (bl->nr <= 0) {
+                       continue;
+               }
+               const bool is_cyclic = bl->poly != -1;
+               if (is_cyclic) {
+                       GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1));
+               }
+               for (int i = 0; i < bl->nr; i++) {
+                       GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
+               }
+               GPU_indexbuf_add_primitive_restart(&elb);
+               v_idx += bl->nr;
+       }
+       for (const DispList *dl = rdata->ob_curve_cache->disp.first; dl; dl = dl->next) {
+               if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
+                       const bool is_cyclic = dl->type == DL_POLY;
+                       if (is_cyclic) {
+                               GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1));
+                       }
+                       for (int i = 0; i < dl->nr; i++) {
+                               GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
                        }
+                       GPU_indexbuf_add_primitive_restart(&elb);
+                       v_idx += dl->nr;
                }
-               cache->wire.elem = GPU_indexbuf_build(&elb);
        }
-
-       return cache->wire.elem;
+       GPU_indexbuf_build_in_place(&elb, ibo_curve_lines);
 }
 
-static GPUVertBuf *curve_batch_cache_get_normal_verts(CurveRenderData *rdata, CurveBatchCache *cache)
+static void curve_create_edit_curves_nor(CurveRenderData *rdata, GPUVertBuf *vbo_curves_nor)
 {
-       BLI_assert(rdata->types & CU_DATATYPE_NORMAL);
-       BLI_assert(rdata->ob_curve_cache != NULL);
-
-       if (cache->normal.verts == NULL) {
-               static GPUVertFormat format = { 0 };
-               static struct { uint pos; } attr_id;
-               if (format.attr_len == 0) {
-                       /* initialize vertex format */
-                       attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-               }
-
-               const int normal_len = curve_render_data_normal_len_get(rdata);
-               const int vert_len = normal_len * 3;
-
-               GPUVertBuf *vbo = cache->normal.verts = GPU_vertbuf_create_with_format(&format);
-               GPU_vertbuf_data_alloc(vbo, vert_len);
-               int vbo_len_used = 0;
+       static GPUVertFormat format = { 0 };
+       static struct { uint pos, nor, tan, rad; } attr_id;
+       if (format.attr_len == 0) {
+               /* initialize vertex formats */
+               attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+               attr_id.rad = GPU_vertformat_attr_add(&format, "rad", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+               attr_id.nor = GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+               attr_id.tan = GPU_vertformat_attr_add(&format, "tan", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+       }
 
-               const BevList *bl;
-               const Nurb *nu;
+       int verts_len_capacity = curve_render_data_normal_len_get(rdata) * 2;
+       int vbo_len_used = 0;
 
-               for (bl = rdata->ob_curve_cache->bev.first, nu = rdata->nurbs->first;
-                    nu && bl;
-                    bl = bl->next, nu = nu->next)
-               {
-                       const BevPoint *bevp = bl->bevpoints;
-                       int nr = bl->nr;
-                       int skip = nu->resolu / 16;
+       GPU_vertbuf_init_with_format(vbo_curves_nor, &format);
+       GPU_vertbuf_data_alloc(vbo_curves_nor, verts_len_capacity);
 
-                       while (nr-- > 0) { /* accounts for empty bevel lists */
-                               const float fac = bevp->radius * cache->normal_size;
-                               float vec_a[3]; /* Offset perpendicular to the curve */
-                               float vec_b[3]; /* Delta along the curve */
+       const BevList *bl;
+       const Nurb *nu;
 
-                               vec_a[0] = fac;
-                               vec_a[1] = 0.0f;
-                               vec_a[2] = 0.0f;
+       for (bl = rdata->ob_curve_cache->bev.first, nu = rdata->nurbs->first;
+            nu && bl;
+            bl = bl->next, nu = nu->next)
+       {
+               const BevPoint *bevp = bl->bevpoints;
+               int nr = bl->nr;
+               int skip = nu->resolu / 16;
 
-                               mul_qt_v3(bevp->quat, vec_a);
-                               madd_v3_v3fl(vec_a, bevp->dir, -fac);
+               while (nr-- > 0) { /* accounts for empty bevel lists */
+                       float nor[3] = {1.0f, 0.0f, 0.0f};
+                       mul_qt_v3(bevp->quat, nor);
 
-                               reflect_v3_v3v3(vec_b, vec_a, bevp->dir);
-                               negate_v3(vec_b);
+                       GPUPackedNormal pnor = GPU_normal_convert_i10_v3(nor);
+                       GPUPackedNormal ptan = GPU_normal_convert_i10_v3(bevp->dir);
 
-                               add_v3_v3(vec_a, bevp->vec);
-                               add_v3_v3(vec_b, bevp->vec);
+                       /* Only set attributes for one vertex. */
+                       GPU_vertbuf_attr_set(vbo_curves_nor, attr_id.pos, vbo_len_used, bevp->vec);
+                       GPU_vertbuf_attr_set(vbo_curves_nor, attr_id.rad, vbo_len_used, &bevp->radius);
+                       GPU_vertbuf_attr_set(vbo_curves_nor, attr_id.nor, vbo_len_used, &pnor);
+                       GPU_vertbuf_attr_set(vbo_curves_nor, attr_id.tan, vbo_len_used, &ptan);
+                       vbo_len_used++;
 
-                               GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, vec_a);
-                               GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, bevp->vec);
-                               GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, vec_b);
+                       /* Skip the other vertex (it does not need to be offseted). */
+                       GPU_vertbuf_attr_set(vbo_curves_nor, attr_id.pos, vbo_len_used, bevp->vec);
+                       vbo_len_used++;
 
-                               bevp += skip + 1;
-                               nr -= skip;
-                       }
+                       bevp += skip + 1;
+                       nr -= skip;
                }
-               BLI_assert(vbo_len_used == vert_len);
        }
-
-       return cache->normal.verts;
+       BLI_assert(vbo_len_used == verts_len_capacity);
 }
 
-static GPUIndexBuf *curve_batch_cache_get_normal_edges(CurveRenderData *rdata, CurveBatchCache *cache)
+static char beztriple_vflag_get(CurveRenderData *rdata, char flag, char col_id, int v_idx, int nu_id)
 {
-       BLI_assert(rdata->types & CU_DATATYPE_NORMAL);
-       BLI_assert(rdata->ob_curve_cache != NULL);
-
-       if (cache->normal.edges == NULL) {
-               const int normal_len = curve_render_data_normal_len_get(rdata);
-               const int vert_len = normal_len * 3;
-               const int edge_len = normal_len * 2;
-
-               GPUIndexBufBuilder elb;
-               GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edge_len, vert_len);
-
-               int vbo_len_used = 0;
-               for (int i = 0; i < normal_len; i++) {
-                       GPU_indexbuf_add_line_verts(&elb, vbo_len_used + 0, vbo_len_used + 1);
-                       GPU_indexbuf_add_line_verts(&elb, vbo_len_used + 1, vbo_len_used + 2);
-                       vbo_len_used += 3;
-               }
+       char vflag = 0;
+       SET_FLAG_FROM_TEST(vflag, (flag & SELECT), VFLAG_VERTEX_SELECTED);
+       SET_FLAG_FROM_TEST(vflag, (v_idx == rdata->actvert), VFLAG_VERTEX_ACTIVE);
+       SET_FLAG_FROM_TEST(vflag, (nu_id == rdata->actnu), ACTIVE_NURB);
+       /* handle color id */
+       vflag |= col_id << 4; /* << 4 because of EVEN_U_BIT */
+       return vflag;
+}
 
-               BLI_assert(vbo_len_used == vert_len);
+static char bpoint_vflag_get(CurveRenderData *rdata, char flag, int v_idx, int nu_id, int u)
+{
+       char vflag = 0;
+       SET_FLAG_FROM_TEST(vflag, (flag & SELECT), VFLAG_VERTEX_SELECTED);
+       SET_FLAG_FROM_TEST(vflag, (v_idx == rdata->actvert), VFLAG_VERTEX_ACTIVE);
+       SET_FLAG_FROM_TEST(vflag, (nu_id == rdata->actnu), ACTIVE_NURB);
+       SET_FLAG_FROM_TEST(vflag, ((u % 2) == 0), EVEN_U_BIT);
+       vflag |= COLOR_NURB_ULINE_ID << 4; /* << 4 because of EVEN_U_BIT */
+       return vflag;
+}
 
-               cache->normal.elem = GPU_indexbuf_build(&elb);
+static void curve_create_edit_data_and_handles(
+        CurveRenderData *rdata,
+        GPUVertBuf *vbo_pos, GPUVertBuf *vbo_data, GPUIndexBuf *ibo_edit_verts_points, GPUIndexBuf *ibo_edit_lines)
+{
+       static GPUVertFormat format_pos = { 0 };
+       static GPUVertFormat format_data = { 0 };
+       static struct { uint pos, data; } attr_id;
+       if (format_pos.attr_len == 0) {
+               /* initialize vertex formats */
+               attr_id.pos = GPU_vertformat_attr_add(&format_pos, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+               attr_id.data = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U8, 1, GPU_FETCH_INT);
        }
 
-       return cache->normal.elem;
-}
+       int verts_len_capacity = curve_render_data_overlay_verts_len_get(rdata);
+       int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2;
+       int vbo_len_used = 0;
 
-static void curve_batch_cache_create_overlay_batches(Curve *cu)
-{
-       /* Since CU_DATATYPE_OVERLAY is slow to generate, generate them all at once */
-       int options = CU_DATATYPE_OVERLAY;
+       if (DRW_TEST_ASSIGN_VBO(vbo_pos)) {
+               GPU_vertbuf_init_with_format(vbo_pos, &format_pos);
+               GPU_vertbuf_data_alloc(vbo_pos, verts_len_capacity);
+       }
+       if (DRW_TEST_ASSIGN_VBO(vbo_data)) {
+               GPU_vertbuf_init_with_format(vbo_data, &format_data);
+               GPU_vertbuf_data_alloc(vbo_data, verts_len_capacity);
+       }
 
-       CurveBatchCache *cache = curve_batch_cache_get(cu);
-       CurveRenderData *rdata = curve_render_data_create(cu, NULL, options);
-
-       if (cache->overlay.verts == NULL) {
-               static GPUVertFormat format = { 0 };
-               static struct { uint pos, data; } attr_id;
-               if (format.attr_len == 0) {
-                       /* initialize vertex format */
-                       attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-                       attr_id.data = GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 1, GPU_FETCH_INT);
-               }
+       GPUIndexBufBuilder elb_verts, *elbp_verts = NULL;
+       GPUIndexBufBuilder elb_lines, *elbp_lines = NULL;
+       if (DRW_TEST_ASSIGN_IBO(ibo_edit_verts_points)) {
+               elbp_verts = &elb_verts;
+               GPU_indexbuf_init(elbp_verts, GPU_PRIM_POINTS, verts_len_capacity, verts_len_capacity);
+       }
+       if (DRW_TEST_ASSIGN_IBO(ibo_edit_lines)) {
+               elbp_lines = &elb_lines;
+               GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity);
+       }
 
-               GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
-               const int vbo_len_capacity = curve_render_data_overlay_verts_len_get(rdata);
-               GPUIndexBufBuilder elb;
-               GPU_indexbuf_init(&elb, GPU_PRIM_POINTS, vbo_len_capacity, vbo_len_capacity);
-               int vbo_len_used = 0;
-               GPU_vertbuf_data_alloc(vbo, vbo_len_capacity);
-               int i = 0, nu_id = 0;
-               for (Nurb *nu = rdata->nurbs->first; nu; nu = nu->next, nu_id++) {
-                       const bool is_active_nurb = (nu_id == cu->actnu);
-                       if (nu->bezt) {
-                               int a = 0;
-                               for (const BezTriple *bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
-                                       if (bezt->hide == false) {
-                                               const bool is_active = (i == rdata->actvert);
-                                               GPU_indexbuf_add_point_vert(&elb, vbo_len_used + 1);
-                                               for (int j = 0; j < 3; j++) {
-                                                       char vflag = ((&bezt->f1)[j] & SELECT) ? VFLAG_VERTEX_SELECTED : 0;
-                                                       vflag |= (is_active) ? VFLAG_VERTEX_ACTIVE : 0;
-                                                       vflag |= (is_active_nurb) ? ACTIVE_NURB : 0;
-                                                       /* handle color id */
-                                                       char col_id = (&bezt->h1)[j / 2];
-                                                       vflag |= col_id << 4; /* << 4 because of EVEN_U_BIT */
-                                                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used, bezt->vec[j]);
-                                                       GPU_vertbuf_attr_set(vbo, attr_id.data, vbo_len_used, &vflag);
-                                                       vbo_len_used += 1;
-                                               }
-                                       }
-                                       i += 1;
+       int v_idx = 0, nu_id = 0;
+       for (Nurb *nu = rdata->nurbs->first; nu; nu = nu->next, nu_id++) {
+               const BezTriple *bezt = nu->bezt;
+               const BPoint *bp = nu->bp;
+               if (bezt && bezt->hide == false) {
+                       for (int a = 0; a < nu->pntsu; a++, bezt++) {
+                               if (elbp_verts) {
+                                       GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used + 1);
                                }
-                       }
-                       else if (nu->bp) {
-                               int a = 0;
-                               int pt_len = nu->pntsu * nu->pntsv;
-                               for (const BPoint *bp = nu->bp; a < pt_len; a++, bp++) {
-                                       if (bp->hide == false) {
-                                               const bool is_active = (i == rdata->actvert);
-                                               char vflag = (bp->f1 & SELECT) ? VFLAG_VERTEX_SELECTED : 0;
-                                               vflag |= (is_active) ? VFLAG_VERTEX_ACTIVE : 0;
-                                               vflag |= (is_active_nurb) ? ACTIVE_NURB : 0;
-                                               vflag |= (((a % nu->pntsu) % 2) == 0) ? EVEN_U_BIT : 0;
-                                               vflag |= COLOR_NURB_ULINE_ID << 4; /* << 4 because of EVEN_U_BIT */
-                                               GPU_indexbuf_add_point_vert(&elb, vbo_len_used);
-                                               GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used, bp->vec);
-                                               GPU_vertbuf_attr_set(vbo, attr_id.data, vbo_len_used, &vflag);
-                                               vbo_len_used += 1;
+                               if (elbp_lines) {
+                                       GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used + 1, vbo_len_used + 0);
+                                       GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used + 1, vbo_len_used + 2);
+                               }
+                               if (vbo_data) {
+                                       char vflag[3] = {
+                                               beztriple_vflag_get(rdata, bezt->f1, bezt->h1, v_idx, nu_id),
+                                               beztriple_vflag_get(rdata, bezt->f2, bezt->h1, v_idx, nu_id),
+                                               beztriple_vflag_get(rdata, bezt->f3, bezt->h2, v_idx, nu_id)
+                                       };
+                                       for (int j = 0; j < 3; j++) {
+                                               GPU_vertbuf_attr_set(vbo_data, attr_id.data, vbo_len_used + j, &vflag[j]);
                                        }
-                                       i += 1;
                                }
-                       }
-                       i += nu->pntsu;
-               }
-               if (vbo_len_capacity != vbo_len_used) {
-                       GPU_vertbuf_data_resize(vbo, vbo_len_used);
-               }
-
-               GPUIndexBuf *ibo = GPU_indexbuf_build(&elb);
-
-               cache->overlay.verts = GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
-               cache->overlay.verts_no_handles = GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, ibo, GPU_BATCH_OWNS_INDEX);
-       }
-
-       if (cache->overlay.edges == NULL) {
-               GPUVertBuf *vbo = cache->overlay.verts->verts[0];
-
-               const int edge_len =  curve_render_data_overlay_edges_len_get(rdata);
-               const int vbo_len_capacity = edge_len * 2;
-
-               GPUIndexBufBuilder elb;
-               GPU_indexbuf_init(&elb, GPU_PRIM_LINES, vbo_len_capacity, vbo->vertex_len);
-
-               int curr_index = 0;
-               int i = 0;
-               for (Nurb *nu = rdata->nurbs->first; nu; nu = nu->next, i++) {
-                       if (nu->bezt) {
-                               int a = 0;
-                               for (const BezTriple *bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
-                                       if (bezt->hide == false) {
-                                               GPU_indexbuf_add_line_verts(&elb, curr_index + 1, curr_index + 0);
-                                               GPU_indexbuf_add_line_verts(&elb, curr_index + 1, curr_index + 2);
-                                               curr_index += 3;
+                               if (vbo_pos) {
+                                       for (int j = 0; j < 3; j++) {
+                                               GPU_vertbuf_attr_set(vbo_pos, attr_id.pos, vbo_len_used + j, bezt->vec[j]);
                                        }
                                }
+                               vbo_len_used += 3;
+                               v_idx += 1;
                        }
-                       else if (nu->bp) {
-                               int a = 0;
-                               int next_v_index = curr_index;
-                               for (const BPoint *bp = nu->bp; a < nu->pntsu; a++, bp++) {
-                                       if (bp->hide == false) {
-                                               next_v_index += 1;
-                                       }
+               }
+               else if (bp) {
+                       int pt_len = nu->pntsu * nu->pntsv;
+                       for (int a = 0; a < pt_len; a++, bp++) {
+                               int u = (a % nu->pntsu);
+                               int v = (a / nu->pntsu);
+                               /* Use indexed rendering for bezier.
+                                * Specify all points and use indices to hide/show. */
+                               if (elbp_verts && bp->hide == false) {
+                                       GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used);
                                }
-
-                               int pt_len = nu->pntsu * nu->pntsv;
-                               for (a = 0; a < pt_len; a++) {
-                                       const BPoint *bp_curr = &nu->bp[a];
-                                       const BPoint *bp_next_u = ((a % nu->pntsu) < (nu->pntsu - 1)) ? &nu->bp[a + 1] : NULL;
-                                       const BPoint *bp_next_v = (a < (pt_len - nu->pntsu)) ? &nu->bp[a + nu->pntsu] : NULL;
-                                       if (bp_curr->hide == false) {
-                                               if (bp_next_u && (bp_next_u->hide == false)) {
-                                                       GPU_indexbuf_add_line_verts(&elb, curr_index, curr_index + 1);
-                                               }
-                                               if (bp_next_v && (bp_next_v->hide == false)) {
-                                                       GPU_indexbuf_add_line_verts(&elb, curr_index, next_v_index);
-                                               }
-                                               curr_index += 1;
+                               if (elbp_lines && bp->hide == false) {
+                                       const BPoint *bp_next_u = (u < (nu->pntsu - 1)) ? &nu->bp[a + 1] : NULL;
+                                       const BPoint *bp_next_v = (v < (nu->pntsv - 1)) ? &nu->bp[a + nu->pntsu] : NULL;
+                                       if (bp_next_u && (bp_next_u->hide == false)) {
+                                               GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used, vbo_len_used + 1);
                                        }
                                        if (bp_next_v && (bp_next_v->hide == false)) {
-                                               next_v_index += 1;
+                                               GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used, vbo_len_used + nu->pntsu);
                                        }
                                }
+                               if (vbo_data) {
+                                       char vflag = bpoint_vflag_get(rdata, bp->f1, v_idx, nu_id, u);
+                                       GPU_vertbuf_attr_set(vbo_data, attr_id.data, vbo_len_used, &vflag);
+                               }
+                               if (vbo_pos) {
+                                       GPU_vertbuf_attr_set(vbo_pos, attr_id.pos, vbo_len_used, bp->vec);
+                               }
+                               vbo_len_used += 1;
+                               v_idx += 1;
                        }
                }
-
-               GPUIndexBuf *ibo = GPU_indexbuf_build(&elb);
-               cache->overlay.edges = GPU_batch_create_ex(GPU_PRIM_LINES, vbo, ibo, GPU_BATCH_OWNS_INDEX);
        }
 
-       curve_render_data_free(rdata);
-}
-
-static GPUBatch *curve_batch_cache_get_pos_and_normals(CurveRenderData *rdata, CurveBatchCache *cache)
-{
-       BLI_assert(rdata->types & CU_DATATYPE_SURFACE);
-       if (cache->surface.batch == NULL) {
-               ListBase *lb = &rdata->ob_curve_cache->disp;
-
-               if (cache->surface.verts == NULL) {
-                       cache->surface.verts = DRW_displist_vertbuf_calc_pos_with_normals(lb);
-               }
-               if (cache->surface.triangles_in_order == NULL) {
-                       cache->surface.triangles_in_order = DRW_displist_indexbuf_calc_triangles_in_order(lb);
-               }
-               cache->surface.batch = GPU_batch_create(
-                       GPU_PRIM_TRIS, cache->surface.verts, cache->surface.triangles_in_order);
+       /* Resize & Finish */
+       if (elbp_verts != NULL) {
+               GPU_indexbuf_build_in_place(elbp_verts, ibo_edit_verts_points);
        }
-
-       return cache->surface.batch;
-}
-
-/** \} */
-
-
-/* -------------------------------------------------------------------- */
-
-/** \name Private Object/Font Cache API
- * \{ */
-
-
-static GPUBatch *curve_batch_cache_get_edit_select(CurveRenderData *rdata, CurveBatchCache *cache)
-{
-       BLI_assert(rdata->types & CU_DATATYPE_TEXT_SELECT);
-       if (cache->text.select == NULL) {
-               EditFont *ef = rdata->text.edit_font;
-               static GPUVertFormat format = { 0 };
-               static struct { uint pos; } attr_id;
-               if (format.attr_len == 0) {
-                       attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-               }
-
-               GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
-               const int vbo_len_capacity = ef->selboxes_len * 6;
-               int vbo_len_used = 0;
-               GPU_vertbuf_data_alloc(vbo, vbo_len_capacity);
-
-               float box[4][3];
-
-               /* fill in xy below */
-               box[0][2] = box[1][2] = box[2][2] = box[3][2] = 0.001;
-
-               for (int i = 0; i < ef->selboxes_len; i++) {
-                       EditFontSelBox *sb = &ef->selboxes[i];
-
-                       float selboxw;
-                       if (i + 1 != ef->selboxes_len) {
-                               if (ef->selboxes[i + 1].y == sb->y)
-                                       selboxw = ef->selboxes[i + 1].x - sb->x;
-                               else
-                                       selboxw = sb->w;
-                       }
-                       else {
-                               selboxw = sb->w;
-                       }
-
-                       if (sb->rot == 0.0f) {
-                               copy_v2_fl2(box[0], sb->x, sb->y);
-                               copy_v2_fl2(box[1], sb->x + selboxw, sb->y);
-                               copy_v2_fl2(box[2], sb->x + selboxw, sb->y + sb->h);
-                               copy_v2_fl2(box[3], sb->x, sb->y + sb->h);
-                       }
-                       else {
-                               float mat[2][2];
-
-                               angle_to_mat2(mat, sb->rot);
-
-                               copy_v2_fl2(box[0], sb->x, sb->y);
-
-                               copy_v2_fl2(box[1], selboxw, 0.0f);
-                               mul_m2v2(mat, box[1]);
-                               add_v2_v2(box[1], &sb->x);
-
-                               copy_v2_fl2(box[2], selboxw, sb->h);
-                               mul_m2v2(mat, box[2]);
-                               add_v2_v2(box[2], &sb->x);
-
-                               copy_v2_fl2(box[3], 0.0f, sb->h);
-                               mul_m2v2(mat, box[3]);
-                               add_v2_v2(box[3], &sb->x);
-                       }
-
-                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, box[0]);
-                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, box[1]);
-                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, box[2]);
-
-                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, box[0]);
-                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, box[2]);
-                       GPU_vertbuf_attr_set(vbo, attr_id.pos, vbo_len_used++, box[3]);
-               }
-               BLI_assert(vbo_len_used == vbo_len_capacity);
-               cache->text.select = GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+       if (elbp_lines != NULL) {
+               GPU_indexbuf_build_in_place(elbp_lines, ibo_edit_lines);
        }
-       return cache->text.select;
-}
-
-static GPUBatch *curve_batch_cache_get_edit_cursor(CurveRenderData *rdata, CurveBatchCache *cache)
-{
-       BLI_assert(rdata->types & CU_DATATYPE_TEXT_SELECT);
-       if (cache->text.cursor == NULL) {
-               static GPUVertFormat format = { 0 };
-               static struct { uint pos; } attr_id;
-               if (format.attr_len == 0) {
-                       attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       if (vbo_len_used != verts_len_capacity) {
+               if (vbo_pos != NULL) {
+                       GPU_vertbuf_data_resize(vbo_pos, vbo_len_used);
                }
-
-               GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
-               const int vbo_len_capacity = 4;
-               GPU_vertbuf_data_alloc(vbo, vbo_len_capacity);
-               for (int i = 0; i < 4; i++) {
-                       GPU_vertbuf_attr_set(vbo, attr_id.pos, i, rdata->text.edit_font->textcurs[i]);
+               if (vbo_data != NULL) {
+                       GPU_vertbuf_data_resize(vbo_data, vbo_len_used);
                }
-               cache->text.cursor = GPU_batch_create_ex(GPU_PRIM_TRI_FAN, vbo, NULL, GPU_BATCH_OWNS_VBO);
        }
-       return cache->text.cursor;
 }
 
 /** \} */
 
 /* -------------------------------------------------------------------- */
-
 /** \name Public Object/Curve API
  * \{ */
 
-GPUBatch *DRW_curve_batch_cache_get_wire_edge(Curve *cu, CurveCache *ob_curve_cache)
+GPUBatch *DRW_curve_batch_cache_get_wire_edge(Curve *cu)
 {
        CurveBatchCache *cache = curve_batch_cache_get(cu);
-
-       if (cache->wire.batch == NULL) {
-               /* create batch from Curve */
-               CurveRenderData *rdata = curve_render_data_create(cu, ob_curve_cache, CU_DATATYPE_WIRE);
-
-               cache->wire.batch = GPU_batch_create(
-                       GPU_PRIM_LINES,
-                       curve_batch_cache_get_wire_verts(rdata, cache),
-                       curve_batch_cache_get_wire_edges(rdata, cache));
-
-               curve_render_data_free(rdata);
-       }
-       return cache->wire.batch;
+       return DRW_batch_request(&cache->batch.curves);
 }
 
-GPUBatch *DRW_curve_batch_cache_get_normal_edge(Curve *cu, CurveCache *ob_curve_cache, float normal_size)
+GPUBatch *DRW_curve_batch_cache_get_normal_edge(Curve *cu)
 {
        CurveBatchCache *cache = curve_batch_cache_get(cu);
-
-       if (cache->normal.batch != NULL) {
-               cache->normal_size = normal_size;
-               if (cache->normal_size != normal_size) {
-                       GPU_BATCH_DISCARD_SAFE(cache->normal.batch);
-                       GPU_VERTBUF_DISCARD_SAFE(cache->normal.edges);
-               }
-       }
-       cache->normal_size = normal_size;
-
-       if (cache->normal.batch == NULL) {
-               /* create batch from Curve */
-               CurveRenderData *rdata = curve_render_data_create(cu, ob_curve_cache, CU_DATATYPE_NORMAL);
-
-               cache->normal.batch = GPU_batch_create(
-                       GPU_PRIM_LINES,
-                       curve_batch_cache_get_normal_verts(rdata, cache),
-                       curve_batch_cache_get_normal_edges(rdata, cache));
-
-               curve_render_data_free(rdata);
-               cache->normal_size = normal_size;
-       }
-       return cache->normal.batch;
+       return DRW_batch_request(&cache->batch.edit_normals);
 }
 
 GPUBatch *DRW_curve_batch_cache_get_edit_edges(Curve *cu)
 {
        CurveBatchCache *cache = curve_batch_cache_get(cu);
-
-       if (cache->overlay.edges == NULL) {
-               curve_batch_cache_create_overlay_batches(cu);
-       }
-
-       return cache->overlay.edges;
+       return DRW_batch_request(&cache->batch.edit_edges);
 }
 
 GPUBatch *DRW_curve_batch_cache_get_edit_verts(Curve *cu, bool handles)
 {
        CurveBatchCache *cache = curve_batch_cache_get(cu);
-
-       if (cache->overlay.verts == NULL || cache->overlay.verts_no_handles == NULL) {
-               curve_batch_cache_create_overlay_batches(cu);
+       if (handles) {
+               return DRW_batch_request(&cache->batch.edit_handles_verts);
+       }
+       else {
+               return DRW_batch_request(&cache->batch.edit_verts);
        }
-
-       return (handles) ? cache->overlay.verts : cache->overlay.verts_no_handles;
 }
 
-GPUBatch *DRW_curve_batch_cache_get_triangles_with_normals(
-        struct Curve *cu, struct CurveCache *ob_curve_cache)
+GPUBatch *DRW_curve_batch_cache_get_triangles_with_normals(struct Curve *cu)
 {
        CurveBatchCache *cache = curve_batch_cache_get(cu);
-
-       if (cache->surface.batch == NULL) {
-               CurveRenderData *rdata = curve_render_data_create(cu, ob_curve_cache, CU_DATATYPE_SURFACE);
-
-               curve_batch_cache_get_pos_and_normals(rdata, cache);
-
-               curve_render_data_free(rdata);
-       }
-
-       return cache->surface.batch;
+       return DRW_batch_request(&cache->batch.surfaces);
 }
 
 GPUBatch **DRW_curve_batch_cache_get_surface_shaded(
-        struct Curve *cu, struct CurveCache *ob_curve_cache,
-        struct GPUMaterial **UNUSED(gpumat_array), uint gpumat_array_len)
+        struct Curve *cu,
+        struct GPUMaterial **gpumat_array, uint gpumat_array_len)
 {
        CurveBatchCache *cache = curve_batch_cache_get(cu);
 
-       if (cache->surface.mat_len != gpumat_array_len) {
-               GPU_BATCH_DISCARD_ARRAY_SAFE(cache->surface.shaded_triangles, cache->surface.mat_len);
-       }
-
-       if (cache->surface.shaded_triangles == NULL) {
-               CurveRenderData *rdata = curve_render_data_create(cu, ob_curve_cache, CU_DATATYPE_SURFACE);
-               ListBase *lb = &rdata->ob_curve_cache->disp;
+       BLI_assert(gpumat_array_len == cache->mat_len);
 
-               cache->surface.mat_len = gpumat_array_len;
-               if (cu->flag & CU_UV_ORCO) {
-                       cache->surface.shaded_triangles = DRW_displist_batch_calc_tri_pos_normals_and_uv_split_by_material(
-                               lb, gpumat_array_len);
-               }
-               else {
-                       cache->surface.shaded_triangles = MEM_mallocN(
-                               sizeof(*cache->surface.shaded_triangles) * gpumat_array_len, __func__);
-                       GPUIndexBuf **el = DRW_displist_indexbuf_calc_triangles_in_order_split_by_material(
-                               lb, gpumat_array_len);
-
-                       if (cache->surface.verts == NULL) {
-                               cache->surface.verts = DRW_displist_vertbuf_calc_pos_with_normals(lb);
-                       }
-
-                       for (int i = 0; i < gpumat_array_len; ++i) {
-                               cache->surface.shaded_triangles[i] = GPU_batch_create_ex(
-                                       GPU_PRIM_TRIS, cache->surface.verts, el[i], GPU_BATCH_OWNS_INDEX);
-                       }
+       curve_cd_calc_used_gpu_layers(&cache->cd_needed, gpumat_array, gpumat_array_len);
 
-                       MEM_freeN(el); /* Save `el` in cache? */
-               }
-
-               curve_render_data_free(rdata);
+       for (int i = 0; i < cache->mat_len; ++i) {
+               DRW_batch_request(&cache->surf_per_mat[i]);
        }
-
-       return cache->surface.shaded_triangles;
+       return cache->surf_per_mat;
 }
 
-GPUBatch *DRW_curve_batch_cache_get_wireframes_face(Curve *cu, CurveCache *ob_curve_cache)
+GPUBatch *DRW_curve_batch_cache_get_wireframes_face(Curve *cu)
 {
        CurveBatchCache *cache = curve_batch_cache_get(cu);
+       return DRW_batch_request(&cache->batch.wire_triangles);
+}
 
-       if (cache->face_wire.batch == NULL) {
-               CurveRenderData *rdata = curve_render_data_create(cu, ob_curve_cache, CU_DATATYPE_SURFACE);
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Grouped batch generation
+ * \{ */
 
-               ListBase *lb = &rdata->ob_curve_cache->disp;
+void DRW_curve_batch_cache_create_requested(Object *ob)
+{
+       BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT));
 
-               cache->face_wire.batch = DRW_displist_create_edges_overlay_batch(lb);
+       Curve *cu = ob->data;
+       CurveBatchCache *cache = curve_batch_cache_get(cu);
 
-               curve_render_data_free(rdata);
+       /* Verify that all surface batches have needed attribute layers. */
+       /* TODO(fclem): We could be a bit smarter here and only do it per material. */
+       for (int i = 0; i < cache->mat_len; ++i) {
+               if ((cache->cd_used & cache->cd_needed) != cache->cd_needed) {
+                       /* We can't discard batches at this point as they have been
+                        * referenced for drawing. Just clear them in place. */
+                       GPU_batch_clear(cache->surf_per_mat[i]);
+                       memset(cache->surf_per_mat[i], 0, sizeof(*cache->surf_per_mat[i]));
+               }
+       }
+       if ((cache->cd_used & cache->cd_needed) != cache->cd_needed) {
+               cache->cd_used |= cache->cd_needed;
+               cache->cd_needed = 0;
        }
 
-       return cache->face_wire.batch;
-}
+       /* Init batches and request VBOs & IBOs */
+       if (DRW_batch_requested(cache->batch.surfaces, GPU_PRIM_TRIS)) {
+               DRW_ibo_request(cache->batch.surfaces, &cache->ibo.surfaces_tris);
+               DRW_vbo_request(cache->batch.surfaces, &cache->ordered.pos_nor);
+       }
+       if (DRW_batch_requested(cache->batch.curves, GPU_PRIM_LINE_STRIP)) {
+               DRW_ibo_request(cache->batch.curves, &cache->ibo.curves_lines);
+               DRW_vbo_request(cache->batch.curves, &cache->ordered.curves_pos);
+       }
+       if (DRW_batch_requested(cache->batch.wire_triangles, GPU_PRIM_TRIS)) {
+               DRW_vbo_request(cache->batch.wire_triangles, &cache->tess.pos_nor);
+               DRW_vbo_request(cache->batch.wire_triangles, &cache->tess.wireframe_data);
+       }
 
-/* -------------------------------------------------------------------- */
+       /* Edit mode */
+       if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) {
+               DRW_ibo_request(cache->batch.edit_edges, &cache->ibo.edit_lines);
+               DRW_vbo_request(cache->batch.edit_edges, &cache->edit.pos);
+               DRW_vbo_request(cache->batch.edit_edges, &cache->edit.data);
+       }
+       if (DRW_batch_requested(cache->batch.edit_verts, GPU_PRIM_POINTS)) {
+               DRW_ibo_request(cache->batch.edit_verts, &cache->ibo.edit_verts_points);
+               DRW_vbo_request(cache->batch.edit_verts, &cache->edit.pos);
+               DRW_vbo_request(cache->batch.edit_verts, &cache->edit.data);
+       }
+       if (DRW_batch_requested(cache->batch.edit_handles_verts, GPU_PRIM_POINTS)) {
+               DRW_vbo_request(cache->batch.edit_handles_verts, &cache->edit.pos);
+               DRW_vbo_request(cache->batch.edit_handles_verts, &cache->edit.data);
+       }
+       if (DRW_batch_requested(cache->batch.edit_normals, GPU_PRIM_LINES)) {
+               DRW_vbo_request(cache->batch.edit_normals, &cache->edit.curves_nor);
+       }
+       for (int i = 0; i < cache->mat_len; ++i) {
+               if (DRW_batch_requested(cache->surf_per_mat[i], GPU_PRIM_TRIS)) {
+                       if (cache->mat_len > 1) {
+                               DRW_ibo_request(cache->surf_per_mat[i], &cache->surf_per_mat_tris[i]);
+                       }
+                       if (cache->cd_used & CD_MLOOPUV) {
+                               DRW_vbo_request(cache->surf_per_mat[i], &cache->tess.uv);
+                       }
+                       DRW_vbo_request(cache->surf_per_mat[i], &cache->tess.pos_nor);
+               }
+       }
 
-/** \name Public Object/Font API
- * \{ */
+       /* Generate MeshRenderData flags */
+       int mr_flag = 0;
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.pos_nor, CU_DATATYPE_SURFACE);
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.curves_pos, CU_DATATYPE_WIRE);
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->tess.pos_nor, CU_DATATYPE_SURFACE);
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->tess.uv, CU_DATATYPE_SURFACE);
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->tess.wireframe_data, CU_DATATYPE_SURFACE);
+       DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.surfaces_tris, CU_DATATYPE_SURFACE);
+       DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.curves_lines, CU_DATATYPE_WIRE);
+
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.pos, CU_DATATYPE_OVERLAY);
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.data, CU_DATATYPE_OVERLAY);
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.curves_nor, CU_DATATYPE_NORMAL);
+       DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.curves_weight, CU_DATATYPE_OVERLAY);
+       DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.edit_verts_points, CU_DATATYPE_OVERLAY);
+       DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.edit_lines, CU_DATATYPE_OVERLAY);
+
+       for (int i = 0; i < cache->mat_len; ++i) {
+               DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->surf_per_mat_tris[i], CU_DATATYPE_SURFACE);
+       }
 
-GPUBatch *DRW_curve_batch_cache_get_edit_select(Curve *cu)
-{
-       CurveBatchCache *cache = curve_batch_cache_get(cu);
+       CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime.curve_cache, mr_flag);
 
-       if (cache->text.select == NULL) {
-               CurveRenderData *rdata = curve_render_data_create(cu, NULL, CU_DATATYPE_TEXT_SELECT);
+       /* DispLists */
+       ListBase *lb = &rdata->ob_curve_cache->disp;
 
-               curve_batch_cache_get_edit_select(rdata, cache);
+       /* Generate VBOs */
+       if (DRW_vbo_requested(cache->ordered.pos_nor)) {
+               DRW_displist_vertbuf_create_pos_and_nor(lb, cache->ordered.pos_nor);
+       }
+       if (DRW_vbo_requested(cache->ordered.curves_pos)) {
+               curve_create_curves_pos(rdata, cache->ordered.curves_pos);
+       }
 
-               curve_render_data_free(rdata);
+       if (DRW_vbo_requested(cache->tess.pos_nor) ||
+           DRW_vbo_requested(cache->tess.uv))
+       {
+               DRW_displist_vertbuf_create_pos_and_nor_and_uv_tess(lb, cache->tess.pos_nor, cache->tess.uv);
+       }
+       if (DRW_vbo_requested(cache->tess.wireframe_data)) {
+               DRW_displist_vertbuf_create_wireframe_data_tess(lb, cache->tess.wireframe_data);
        }
 
-       return cache->text.select;
-}
+       if (DRW_ibo_requested(cache->surf_per_mat_tris[0])) {
+               DRW_displist_indexbuf_create_triangles_tess_split_by_material(lb, cache->surf_per_mat_tris, cache->mat_len);
+       }
 
-GPUBatch *DRW_curve_batch_cache_get_edit_cursor(Curve *cu)
-{
-       CurveBatchCache *cache = curve_batch_cache_get(cu);
+       if (DRW_ibo_requested(cache->ibo.curves_lines)) {
+               curve_create_curves_lines(rdata, cache->ibo.curves_lines);
+       }
+       if (DRW_ibo_requested(cache->ibo.surfaces_tris)) {
+               DRW_displist_indexbuf_create_triangles_in_order(lb, cache->ibo.surfaces_tris);
+       }
 
-       if (cache->text.cursor == NULL) {
-               CurveRenderData *rdata = curve_render_data_create(cu, NULL, CU_DATATYPE_TEXT_SELECT);
+       if (DRW_vbo_requested(cache->edit.pos) ||
+           DRW_vbo_requested(cache->edit.data) ||
+           DRW_ibo_requested(cache->ibo.edit_verts_points) ||
+           DRW_ibo_requested(cache->ibo.edit_lines))
+       {
+               curve_create_edit_data_and_handles(rdata, cache->edit.pos, cache->edit.data,
+                                                         cache->ibo.edit_verts_points, cache->ibo.edit_lines);
+       }
+       if (DRW_vbo_requested(cache->edit.curves_nor)) {
+               curve_create_edit_curves_nor(rdata, cache->edit.curves_nor);
+       }
 
-               curve_batch_cache_get_edit_cursor(rdata, cache);
+       curve_render_data_free(rdata);
 
-               curve_render_data_free(rdata);
+#ifdef DEBUG
+       /* Make sure all requested batches have been setup. */
+       for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); ++i) {
+               BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
        }
-
-       return cache->text.cursor;
+#endif
 }
 
 /** \} */