Mesh Batch Cache: Refactor + Multithread
authorClément Foucault <foucault.clem@gmail.com>
Sun, 14 Jul 2019 14:49:44 +0000 (16:49 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Wed, 14 Aug 2019 17:05:26 +0000 (19:05 +0200)
For clarity sake, the batch cache now uses exclusively per Loop attributes.
While this is a bit of a waste of VRAM (for the few case where per vert
attribs are enough) it reduces the complexity and amount of overall VBO
to update in general situations.

This patch also makes the VertexBuffers filling multithreaded. This make
the update of dense meshes a bit faster. The main bottleneck is the
IndexBuffers update which cannot be multithreaded efficiently (have to
increment a counter and/or do a final sorting pass).

We introduce the concept of "extract" functions/step.
All extract functions are executed in one thread each and if possible,
using multiple thread for looping over all elements.

Reviewed By: brecht

Differential Revision: http://developer.blender.org/D5424

23 files changed:
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/mesh_evaluate.c
source/blender/blenlib/BLI_math_geom.h
source/blender/blenlib/intern/math_geom.c
source/blender/draw/CMakeLists.txt
source/blender/draw/intern/draw_cache.c
source/blender/draw/intern/draw_cache_extract.h [new file with mode: 0644]
source/blender/draw/intern/draw_cache_extract_mesh.c [new file with mode: 0644]
source/blender/draw/intern/draw_cache_impl.h
source/blender/draw/intern/draw_cache_impl_mesh.c
source/blender/draw/modes/edit_mesh_mode.c
source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_frag.glsl
source/blender/draw/modes/shaders/edit_mesh_overlay_mesh_analysis_vert.glsl
source/blender/editors/uvedit/uvedit_draw.c
source/blender/gpu/GPU_batch.h
source/blender/gpu/GPU_element.h
source/blender/gpu/GPU_vertex_format.h
source/blender/gpu/intern/gpu_batch.c
source/blender/gpu/intern/gpu_element.c
source/blender/gpu/intern/gpu_vertex_format.c
source/blender/gpu/intern/gpu_vertex_format_private.h
source/blender/gpu/shaders/gpu_shader_2D_edituvs_stretch_vert.glsl
source/blender/makesrna/intern/rna_scene.c

index 924cfad37d66a8421acf6913eba6e949ba78e971..94d118bde3630f9b8d6dffa50f458907efea0258 100644 (file)
@@ -486,6 +486,7 @@ void BKE_mesh_calc_poly_center(const struct MPoly *mpoly,
 float BKE_mesh_calc_poly_area(const struct MPoly *mpoly,
                               const struct MLoop *loopstart,
                               const struct MVert *mvarray);
+float BKE_mesh_calc_poly_uv_area(const struct MPoly *mpoly, const struct MLoopUV *uv_array);
 void BKE_mesh_calc_poly_angles(const struct MPoly *mpoly,
                                const struct MLoop *loopstart,
                                const struct MVert *mvarray,
index e28d50cbde46b702dabca31ce194875d85e5e291..2ea275cdfb0a80f6339ee1117e77315d130a2281 100644 (file)
@@ -2378,6 +2378,24 @@ float BKE_mesh_calc_poly_area(const MPoly *mpoly, const MLoop *loopstart, const
   }
 }
 
+float BKE_mesh_calc_poly_uv_area(const MPoly *mpoly, const MLoopUV *uv_array)
+{
+
+  int i, l_iter = mpoly->loopstart;
+  float area;
+  float(*vertexcos)[2] = BLI_array_alloca(vertexcos, (size_t)mpoly->totloop);
+
+  /* pack vertex cos into an array for area_poly_v2 */
+  for (i = 0; i < mpoly->totloop; i++, l_iter++) {
+    copy_v2_v2(vertexcos[i], uv_array[l_iter].uv);
+  }
+
+  /* finally calculate the area */
+  area = area_poly_v2((const float(*)[2])vertexcos, (unsigned int)mpoly->totloop);
+
+  return area;
+}
+
 /**
  * Calculate the volume and volume-weighted centroid of the volume
  * formed by the polygon and the origin.
index b1437dbe140f801047374988bf8a8b09cd258af5..39b1b96d0097c376e5975d987abb624611bd2832 100644 (file)
@@ -92,6 +92,7 @@ float volume_tetrahedron_signed_v3(const float v1[3],
                                    const float v3[3],
                                    const float v4[3]);
 
+bool is_edge_convex_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3]);
 bool is_quad_convex_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3]);
 bool is_quad_convex_v2(const float v1[2], const float v2[2], const float v3[2], const float v4[2]);
 bool is_poly_convex_v2(const float verts[][2], unsigned int nr);
index bb3d2786ca65d59456585516b3656c6c10c61ca9..06fd5c7077209eeebb4ee2e21035a101a7533f3e 100644 (file)
@@ -5729,6 +5729,27 @@ float form_factor_hemi_poly(
   return contrib;
 }
 
+/**
+ * Check if the edge is convex or concave
+ * (depends on face winding)
+ * Copied from BM_edge_is_convex().
+ */
+bool is_edge_convex_v3(const float v1[3],
+                       const float v2[3],
+                       const float f1_no[3],
+                       const float f2_no[3])
+{
+  if (!equals_v3v3(f1_no, f2_no)) {
+    float cross[3];
+    float l_dir[3];
+    cross_v3_v3v3(cross, f1_no, f2_no);
+    /* we assume contiguous normals, otherwise the result isn't meaningful */
+    sub_v3_v3v3(l_dir, v2, v1);
+    return (dot_v3v3(l_dir, cross) > 0.0f);
+  }
+  return false;
+}
+
 /**
  * Evaluate if entire quad is a proper convex quad
  */
index e34ad155f21c49197db2220edcf7e926e1be85db..1112a7a87dbcf56a393a9b10650d995df2dc199b 100644 (file)
@@ -52,6 +52,7 @@ set(SRC
   intern/draw_anim_viz.c
   intern/draw_armature.c
   intern/draw_cache.c
+  intern/draw_cache_extract_mesh.c
   intern/draw_cache_impl_curve.c
   intern/draw_cache_impl_displist.c
   intern/draw_cache_impl_lattice.c
@@ -135,6 +136,7 @@ set(SRC
   DRW_select_buffer.h
   intern/DRW_render.h
   intern/draw_cache.h
+  intern/draw_cache_extract.h
   intern/draw_cache_impl.h
   intern/draw_cache_inline.h
   intern/draw_common.h
index e2e98a2db5a26219e70aad1966740079d0b6fc1f..bad4b55eb1aeb3e3e7185ba7c822eb12b0e79c5e 100644 (file)
@@ -4033,7 +4033,7 @@ void drw_batch_cache_validate(Object *ob)
 void drw_batch_cache_generate_requested(Object *ob)
 {
   const DRWContextState *draw_ctx = DRW_context_state_get();
-  const ToolSettings *ts = draw_ctx->scene->toolsettings;
+  const Scene *scene = draw_ctx->scene;
   const enum eContextObjectMode mode = CTX_data_mode_enum_ex(
       draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode);
   const bool is_paint_mode = ELEM(
@@ -4047,13 +4047,13 @@ void drw_batch_cache_generate_requested(Object *ob)
   struct Mesh *mesh_eval = ob->runtime.mesh_eval;
   switch (ob->type) {
     case OB_MESH:
-      DRW_mesh_batch_cache_create_requested(ob, (Mesh *)ob->data, ts, is_paint_mode, use_hide);
+      DRW_mesh_batch_cache_create_requested(ob, (Mesh *)ob->data, scene, is_paint_mode, use_hide);
       break;
     case OB_CURVE:
     case OB_FONT:
     case OB_SURF:
       if (mesh_eval) {
-        DRW_mesh_batch_cache_create_requested(ob, mesh_eval, ts, is_paint_mode, use_hide);
+        DRW_mesh_batch_cache_create_requested(ob, mesh_eval, scene, is_paint_mode, use_hide);
       }
       DRW_curve_batch_cache_create_requested(ob);
       break;
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
new file mode 100644 (file)
index 0000000..5a06210
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2019, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#ifndef __DRAW_CACHE_EXTRACT_MESH_H__
+#define __DRAW_CACHE_EXTRACT_MESH_H__
+
+/* Vertex Group Selection and display options */
+typedef struct DRW_MeshWeightState {
+  int defgroup_active;
+  int defgroup_len;
+
+  short flags;
+  char alert_mode;
+
+  /* Set of all selected bones for Multipaint. */
+  bool *defgroup_sel; /* [defgroup_len] */
+  int defgroup_sel_count;
+} DRW_MeshWeightState;
+
+/* DRW_MeshWeightState.flags */
+enum {
+  DRW_MESH_WEIGHT_STATE_MULTIPAINT = (1 << 0),
+  DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE = (1 << 1),
+};
+
+typedef struct DRW_MeshCDMask {
+  uint32_t uv : 8;
+  uint32_t tan : 8;
+  uint32_t vcol : 8;
+  uint32_t orco : 1;
+  uint32_t tan_orco : 1;
+} DRW_MeshCDMask;
+
+typedef enum eMRIterType {
+  MR_ITER_LOOPTRI = 1 << 0,
+  MR_ITER_LOOP = 1 << 1,
+  MR_ITER_LEDGE = 1 << 2,
+  MR_ITER_LVERT = 1 << 3,
+} eMRIterType;
+
+typedef enum eMRDataType {
+  MR_DATA_POLY_NOR = 1 << 1,
+  MR_DATA_LOOP_NOR = 1 << 2,
+  MR_DATA_LOOPTRI = 1 << 3,
+  /** Force loop normals calculation.  */
+  MR_DATA_TAN_LOOP_NOR = 1 << 4,
+} eMRDataType;
+
+typedef enum eMRExtractType {
+  MR_EXTRACT_BMESH,
+  MR_EXTRACT_MAPPED,
+  MR_EXTRACT_MESH,
+} eMRExtractType;
+
+BLI_INLINE int mesh_render_mat_len_get(Mesh *me)
+{
+  return MAX2(1, me->totcol);
+}
+
+typedef struct MeshBufferCache {
+  /* Every VBO below contains at least enough
+   * data for every loops in the mesh (except fdots).
+   * For some VBOs, it extends to (in this exact order) :
+   * loops + loose_edges*2 + loose_verts */
+  struct {
+    GPUVertBuf *pos_nor;  /* extend */
+    GPUVertBuf *lnor;     /* extend */
+    GPUVertBuf *edge_fac; /* extend */
+    GPUVertBuf *weights;  /* extend */
+    GPUVertBuf *uv_tan;
+    GPUVertBuf *vcol;
+    GPUVertBuf *orco;
+    /* Only for edit mode. */
+    GPUVertBuf *edit_data; /* extend */
+    GPUVertBuf *edituv_data;
+    GPUVertBuf *stretch_area;
+    GPUVertBuf *stretch_angle;
+    GPUVertBuf *mesh_analysis;
+    GPUVertBuf *fdots_pos;
+    GPUVertBuf *fdots_nor;
+    GPUVertBuf *fdots_uv;
+    // GPUVertBuf *fdots_edit_data; /* inside fdots_nor for now. */
+    GPUVertBuf *fdots_edituv_data;
+    /* Selection */
+    GPUVertBuf *vert_idx; /* extend */
+    GPUVertBuf *edge_idx; /* extend */
+    GPUVertBuf *poly_idx;
+    GPUVertBuf *fdot_idx;
+  } vbo;
+  /* Index Buffers:
+   * Only need to be updated when topology changes. */
+  struct {
+    /* Indices to vloops. */
+    GPUIndexBuf *tris;  /* Ordered per material. */
+    GPUIndexBuf *lines; /* Loose edges last. */
+    GPUIndexBuf *points;
+    GPUIndexBuf *fdots;
+    /* 3D overlays. */
+    GPUIndexBuf *lines_paint_mask; /* no loose edges. */
+    GPUIndexBuf *lines_adjacency;
+    /* Uv overlays. (visibility can differ from 3D view) */
+    GPUIndexBuf *edituv_tris;
+    GPUIndexBuf *edituv_lines;
+    GPUIndexBuf *edituv_points;
+    GPUIndexBuf *edituv_fdots;
+  } ibo;
+} MeshBufferCache;
+
+typedef enum DRWBatchFlag {
+  MBC_SURFACE = (1 << 0),
+  MBC_SURFACE_WEIGHTS = (1 << 1),
+  MBC_EDIT_TRIANGLES = (1 << 2),
+  MBC_EDIT_VERTICES = (1 << 3),
+  MBC_EDIT_EDGES = (1 << 4),
+  MBC_EDIT_VNOR = (1 << 5),
+  MBC_EDIT_LNOR = (1 << 6),
+  MBC_EDIT_FACEDOTS = (1 << 7),
+  MBC_EDIT_MESH_ANALYSIS = (1 << 8),
+  MBC_EDITUV_FACES_STRECH_AREA = (1 << 9),
+  MBC_EDITUV_FACES_STRECH_ANGLE = (1 << 10),
+  MBC_EDITUV_FACES = (1 << 11),
+  MBC_EDITUV_EDGES = (1 << 12),
+  MBC_EDITUV_VERTS = (1 << 13),
+  MBC_EDITUV_FACEDOTS = (1 << 14),
+  MBC_EDIT_SELECTION_VERTS = (1 << 15),
+  MBC_EDIT_SELECTION_EDGES = (1 << 16),
+  MBC_EDIT_SELECTION_FACES = (1 << 17),
+  MBC_EDIT_SELECTION_FACEDOTS = (1 << 18),
+  MBC_ALL_VERTS = (1 << 19),
+  MBC_ALL_EDGES = (1 << 20),
+  MBC_LOOSE_EDGES = (1 << 21),
+  MBC_EDGE_DETECTION = (1 << 22),
+  MBC_WIRE_EDGES = (1 << 23),
+  MBC_WIRE_LOOPS = (1 << 24),
+  MBC_WIRE_LOOPS_UVS = (1 << 25),
+  MBC_SURF_PER_MAT = (1 << 26),
+} DRWBatchFlag;
+
+#define MBC_EDITUV \
+  (MBC_EDITUV_FACES_STRECH_AREA | MBC_EDITUV_FACES_STRECH_ANGLE | MBC_EDITUV_FACES | \
+   MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_WIRE_LOOPS_UVS)
+
+#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \
+  for (MeshBufferCache *mbc = &batch_cache->final; \
+       mbc == &batch_cache->final || mbc == &batch_cache->cage || mbc == &batch_cache->uv_cage; \
+       mbc = (mbc == &batch_cache->final) ? \
+                 &batch_cache->cage : \
+                 ((mbc == &batch_cache->cage) ? &batch_cache->uv_cage : NULL))
+
+typedef struct MeshBatchCache {
+  MeshBufferCache final, cage, uv_cage;
+
+  struct {
+    /* Surfaces / Render */
+    GPUBatch *surface;
+    GPUBatch *surface_weights;
+    /* Edit mode */
+    GPUBatch *edit_triangles;
+    GPUBatch *edit_vertices;
+    GPUBatch *edit_edges;
+    GPUBatch *edit_vnor;
+    GPUBatch *edit_lnor;
+    GPUBatch *edit_fdots;
+    GPUBatch *edit_mesh_analysis;
+    /* Edit UVs */
+    GPUBatch *edituv_faces_strech_area;
+    GPUBatch *edituv_faces_strech_angle;
+    GPUBatch *edituv_faces;
+    GPUBatch *edituv_edges;
+    GPUBatch *edituv_verts;
+    GPUBatch *edituv_fdots;
+    /* Edit selection */
+    GPUBatch *edit_selection_verts;
+    GPUBatch *edit_selection_edges;
+    GPUBatch *edit_selection_faces;
+    GPUBatch *edit_selection_fdots;
+    /* Common display / Other */
+    GPUBatch *all_verts;
+    GPUBatch *all_edges;
+    GPUBatch *loose_edges;
+    GPUBatch *edge_detection;
+    GPUBatch *wire_edges;     /* Individual edges with face normals. */
+    GPUBatch *wire_loops;     /* Loops around faces. no edges between selected faces */
+    GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */
+  } batch;
+
+  GPUBatch **surface_per_mat;
+
+  /* arrays of bool uniform names (and value) that will be use to
+   * set srgb conversion for auto attributes.*/
+  char *auto_layer_names;
+  int *auto_layer_is_srgb;
+  int auto_layer_len;
+
+  DRWBatchFlag batch_requested;
+  DRWBatchFlag batch_ready;
+
+  /* settings to determine if cache is invalid */
+  int edge_len;
+  int tri_len;
+  int poly_len;
+  int vert_len;
+  int mat_len;
+  bool is_dirty; /* Instantly invalidates cache, skipping mesh check */
+  bool is_editmode;
+  bool is_uvsyncsel;
+
+  struct DRW_MeshWeightState weight_state;
+
+  DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time;
+
+  int lastmatch;
+
+  /* Valid only if edge_detection is up to date. */
+  bool is_manifold;
+
+  bool no_loose_wire;
+} MeshBatchCache;
+
+void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
+                                        MeshBufferCache mbc,
+                                        Mesh *me,
+                                        const bool do_final,
+                                        const bool do_uvedit,
+                                        const bool use_subsurf_fdots,
+                                        const DRW_MeshCDMask *cd_layer_used,
+                                        const ToolSettings *ts,
+                                        const bool use_hide);
+
+#endif /* __DRAW_CACHE_EXTRACT_MESH_H__ */
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
new file mode 100644 (file)
index 0000000..f9d6c9e
--- /dev/null
@@ -0,0 +1,4295 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_buffer.h"
+#include "BLI_utildefines.h"
+#include "BLI_math_vector.h"
+#include "BLI_math_bits.h"
+#include "BLI_string.h"
+#include "BLI_alloca.h"
+#include "BLI_edgehash.h"
+#include "BLI_task.h"
+#include "BLI_jitter_2d.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_customdata.h"
+#include "BKE_deform.h"
+#include "BKE_editmesh.h"
+#include "BKE_editmesh_cache.h"
+#include "BKE_editmesh_tangent.h"
+#include "BKE_editmesh_bvh.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_tangent.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_modifier.h"
+#include "BKE_object_deform.h"
+
+#include "atomic_ops.h"
+
+#include "bmesh.h"
+
+#include "GPU_batch.h"
+#include "GPU_extensions.h"
+#include "GPU_material.h"
+
+#include "DRW_render.h"
+
+#include "ED_mesh.h"
+#include "ED_uvedit.h"
+
+#include "draw_cache_inline.h"
+#include "draw_cache_impl.h"
+
+#include "draw_cache_extract.h"
+
+// #define DEBUG_TIME
+
+#ifdef DEBUG_TIME
+#  include "PIL_time_utildefines.h"
+#endif
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
+ * \{ */
+
+typedef struct MeshRenderData {
+  eMRExtractType extract_type;
+
+  int poly_len, edge_len, vert_len, loop_len;
+  int edge_loose_len;
+  int vert_loose_len;
+  int loop_loose_len;
+  int tri_len;
+  int mat_len;
+
+  bool use_hide;
+  bool use_subsurf_fdots;
+  bool use_final_mesh;
+
+  const ToolSettings *toolsettings;
+  /* HACK not supposed to be there but it's needed. */
+  struct MeshBatchCache *cache;
+  /** Edit Mesh */
+  BMEditMesh *edit_bmesh;
+  BMesh *bm;
+  EditMeshData *edit_data;
+  int *v_origindex, *e_origindex, *p_origindex;
+  int crease_ofs;
+  int bweight_ofs;
+  int freestyle_edge_ofs;
+  int freestyle_face_ofs;
+  /** Mesh */
+  Mesh *me;
+  const MVert *mvert;
+  const MEdge *medge;
+  const MLoop *mloop;
+  const MPoly *mpoly;
+  BMVert *eve_act;
+  BMEdge *eed_act;
+  BMFace *efa_act;
+  BMFace *efa_act_uv;
+  /* Data created on-demand (usually not for bmesh-based data). */
+  MLoopTri *mlooptri;
+  float (*loop_normals)[3];
+  float (*poly_normals)[3];
+  int *lverts, *ledges;
+} MeshRenderData;
+
+static MeshRenderData *mesh_render_data_create(Mesh *me,
+                                               const bool do_final,
+                                               const bool do_uvedit,
+                                               const eMRIterType iter_type,
+                                               const eMRDataType data_flag,
+                                               const DRW_MeshCDMask *UNUSED(cd_used),
+                                               const ToolSettings *ts)
+{
+  MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
+  mr->toolsettings = ts;
+  mr->mat_len = mesh_render_mat_len_get(me);
+
+  const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
+  const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
+
+  if (me->edit_mesh) {
+    BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final);
+    mr->bm = me->edit_mesh->bm;
+    mr->edit_bmesh = me->edit_mesh;
+    mr->edit_data = me->runtime.edit_data;
+    mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage;
+    bool use_mapped = !do_uvedit && mr->me && !mr->me->runtime.is_original;
+
+    int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
+
+    BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types);
+    BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP);
+
+    mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false);
+    mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true);
+    mr->eed_act = BM_mesh_active_edge_get(mr->bm);
+    mr->eve_act = BM_mesh_active_vert_get(mr->bm);
+
+    mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE);
+    mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT);
+#ifdef WITH_FREESTYLE
+    mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE);
+    mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE);
+#endif
+
+    if (use_mapped) {
+      mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+      mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+      mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+
+      use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
+    }
+
+    mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH;
+
+    /* Seems like the mesh_eval_final do not have the right origin indices.
+     * Force not mapped in this case. */
+    if (do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) {
+      // mr->edit_bmesh = NULL;
+      mr->extract_type = MR_EXTRACT_MESH;
+    }
+  }
+  else {
+    mr->me = me;
+    mr->edit_bmesh = NULL;
+    mr->extract_type = MR_EXTRACT_MESH;
+  }
+
+  if (mr->extract_type != MR_EXTRACT_BMESH) {
+    /* Mesh */
+    mr->vert_len = mr->me->totvert;
+    mr->edge_len = mr->me->totedge;
+    mr->loop_len = mr->me->totloop;
+    mr->poly_len = mr->me->totpoly;
+    mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
+
+    mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT);
+    mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE);
+    mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP);
+    mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY);
+
+    mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+    mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+    mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+
+    if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
+      mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
+      BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
+                                 NULL,
+                                 mr->vert_len,
+                                 mr->mloop,
+                                 mr->mpoly,
+                                 mr->loop_len,
+                                 mr->poly_len,
+                                 mr->poly_normals,
+                                 true);
+    }
+    if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+      mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+      short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL);
+      BKE_mesh_normals_loop_split(mr->me->mvert,
+                                  mr->vert_len,
+                                  mr->me->medge,
+                                  mr->edge_len,
+                                  mr->me->mloop,
+                                  mr->loop_normals,
+                                  mr->loop_len,
+                                  mr->me->mpoly,
+                                  mr->poly_normals,
+                                  mr->poly_len,
+                                  is_auto_smooth,
+                                  split_angle,
+                                  NULL,
+                                  clnors,
+                                  NULL);
+    }
+    if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
+      mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI");
+      BKE_mesh_recalc_looptri(mr->me->mloop,
+                              mr->me->mpoly,
+                              mr->me->mvert,
+                              mr->me->totloop,
+                              mr->me->totpoly,
+                              mr->mlooptri);
+    }
+    if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
+      mr->vert_loose_len = 0;
+      mr->edge_loose_len = 0;
+
+      BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, "lvert map");
+
+      mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__);
+      const MEdge *medge = mr->medge;
+      for (int e = 0; e < mr->edge_len; e++, medge++) {
+        if (medge->flag & ME_LOOSEEDGE) {
+          mr->ledges[mr->edge_loose_len++] = e;
+        }
+        /* Tag verts as not loose. */
+        BLI_BITMAP_ENABLE(lvert_map, medge->v1);
+        BLI_BITMAP_ENABLE(lvert_map, medge->v2);
+      }
+      if (mr->edge_loose_len < mr->edge_len) {
+        mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
+      }
+
+      mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
+      for (int v = 0; v < mr->vert_len; v++) {
+        if (!BLI_BITMAP_TEST(lvert_map, v)) {
+          mr->lverts[mr->vert_loose_len++] = v;
+        }
+      }
+      if (mr->vert_loose_len < mr->vert_len) {
+        mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
+      }
+
+      MEM_freeN(lvert_map);
+
+      mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
+    }
+  }
+  else {
+    /* BMesh */
+    BMesh *bm = mr->bm;
+
+    mr->vert_len = bm->totvert;
+    mr->edge_len = bm->totedge;
+    mr->loop_len = bm->totloop;
+    mr->poly_len = bm->totface;
+    mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
+
+    if (data_flag & MR_DATA_POLY_NOR) {
+      /* Use bmface->no instead. */
+    }
+    if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+      mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+      int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
+      BM_loops_calc_normal_vcos(mr->bm,
+                                NULL,
+                                NULL,
+                                NULL,
+                                is_auto_smooth,
+                                split_angle,
+                                mr->loop_normals,
+                                NULL,
+                                NULL,
+                                clnors_offset,
+                                false);
+    }
+    if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
+      /* Edit mode ensures this is valid, no need to calculate. */
+      BLI_assert((bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL));
+    }
+    if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
+      int elem_id;
+      BMIter iter;
+      BMVert *eve;
+      BMEdge *ede;
+      mr->vert_loose_len = 0;
+      mr->edge_loose_len = 0;
+
+      mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
+      BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) {
+        if (eve->e == NULL) {
+          mr->lverts[mr->vert_loose_len++] = elem_id;
+        }
+      }
+      if (mr->vert_loose_len < mr->vert_len) {
+        mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
+      }
+
+      mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__);
+      BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) {
+        if (ede->l == NULL) {
+          mr->ledges[mr->edge_loose_len++] = elem_id;
+        }
+      }
+      if (mr->edge_loose_len < mr->edge_len) {
+        mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
+      }
+
+      mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
+    }
+  }
+  return mr;
+}
+
+static void mesh_render_data_free(MeshRenderData *mr)
+{
+  MEM_SAFE_FREE(mr->mlooptri);
+  MEM_SAFE_FREE(mr->poly_normals);
+  MEM_SAFE_FREE(mr->loop_normals);
+
+  MEM_SAFE_FREE(mr->lverts);
+  MEM_SAFE_FREE(mr->ledges);
+
+  MEM_freeN(mr);
+}
+
+BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
+{
+  return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+             BM_face_at_index(mr->bm, mr->p_origindex[idx]) :
+             NULL;
+}
+
+BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx)
+{
+  return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+             BM_edge_at_index(mr->bm, mr->e_origindex[idx]) :
+             NULL;
+}
+
+BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx)
+{
+  return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+             BM_vert_at_index(mr->bm, mr->v_origindex[idx]) :
+             NULL;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract Iter
+ * \{ */
+
+typedef void *(ExtractInitFn)(const MeshRenderData *mr, void *buffer);
+typedef void(ExtractEditTriFn)(const MeshRenderData *mr, int t, BMLoop **e, void *data);
+typedef void(ExtractEditLoopFn)(const MeshRenderData *mr, int l, BMLoop *el, void *data);
+typedef void(ExtractEditLedgeFn)(const MeshRenderData *mr, int e, BMEdge *ed, void *data);
+typedef void(ExtractEditLvertFn)(const MeshRenderData *mr, int v, BMVert *ev, void *data);
+typedef void(ExtractTriFn)(const MeshRenderData *mr, int t, const MLoopTri *mlt, void *data);
+typedef void(ExtractLoopFn)(
+    const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data);
+typedef void(ExtractLedgeFn)(const MeshRenderData *mr, int e, const MEdge *medge, void *data);
+typedef void(ExtractLvertFn)(const MeshRenderData *mr, int v, const MVert *mvert, void *data);
+typedef void(ExtractFinishFn)(const MeshRenderData *mr, void *buffer, void *data);
+
+typedef struct MeshExtract {
+  /** Executed on main thread and return user data for iter functions. */
+  ExtractInitFn *init;
+  /** Executed on one (or more if use_threading) worker thread(s). */
+  ExtractEditTriFn *iter_looptri_bm;
+  ExtractTriFn *iter_looptri;
+  ExtractEditLoopFn *iter_loop_bm;
+  ExtractLoopFn *iter_loop;
+  ExtractEditLedgeFn *iter_ledge_bm;
+  ExtractLedgeFn *iter_ledge;
+  ExtractEditLvertFn *iter_lvert_bm;
+  ExtractLvertFn *iter_lvert;
+  /** Executed on one worker thread after all elements iterations. */
+  ExtractFinishFn *finish;
+  /** Used to request common data. */
+  const eMRDataType data_flag;
+  /** Used to know if the element callbacks are threadsafe and can be parallelized. */
+  const bool use_threading;
+} MeshExtract;
+
+BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext)
+{
+  eMRIterType type = 0;
+  SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri), MR_ITER_LOOPTRI);
+  SET_FLAG_FROM_TEST(type, (ext->iter_loop_bm || ext->iter_loop), MR_ITER_LOOP);
+  SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge), MR_ITER_LEDGE);
+  SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert), MR_ITER_LVERT);
+  return type;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Triangles Indices
+ * \{ */
+
+typedef struct MeshExtract_Tri_Data {
+  GPUIndexBufBuilder elb;
+  int *tri_mat_start;
+  int *tri_mat_end;
+} MeshExtract_Tri_Data;
+
+static void *extract_tris_init(const MeshRenderData *mr, void *UNUSED(ibo))
+{
+  MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__);
+
+  size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
+  data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__);
+  data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__);
+
+  int *mat_tri_len = data->tri_mat_start;
+  /* Count how many triangle for each material. */
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    BMIter iter;
+    BMFace *efa;
+    BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) {
+      if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+        mat_tri_len[efa->mat_nr] += efa->len - 2;
+      }
+    }
+  }
+  else {
+    const MPoly *mpoly = mr->mpoly;
+    for (int p = 0; p < mr->poly_len; p++, mpoly++) {
+      if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) {
+        mat_tri_len[mpoly->mat_nr] += mpoly->totloop - 2;
+      }
+    }
+  }
+  /* Accumulate tri len per mat to have correct offsets. */
+  int ofs = mat_tri_len[0];
+  mat_tri_len[0] = 0;
+  for (int i = 1; i < mr->mat_len; i++) {
+    int tmp = mat_tri_len[i];
+    mat_tri_len[i] = ofs;
+    ofs += tmp;
+  }
+
+  memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size);
+
+  int visible_tri_tot = ofs;
+  GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len);
+
+  return data;
+}
+
+static void extract_tris_looptri_bmesh(const MeshRenderData *UNUSED(mr),
+                                       int UNUSED(t),
+                                       BMLoop **elt,
+                                       void *_data)
+{
+  if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+    MeshExtract_Tri_Data *data = _data;
+    int *mat_tri_ofs = data->tri_mat_end;
+    GPU_indexbuf_set_tri_verts(&data->elb,
+                               mat_tri_ofs[elt[0]->f->mat_nr]++,
+                               BM_elem_index_get(elt[0]),
+                               BM_elem_index_get(elt[1]),
+                               BM_elem_index_get(elt[2]));
+  }
+}
+
+static void extract_tris_looptri_mesh(const MeshRenderData *mr,
+                                      int UNUSED(t),
+                                      const MLoopTri *mlt,
+                                      void *_data)
+{
+  const MPoly *mpoly = &mr->mpoly[mlt->poly];
+  if (!(mpoly->flag & ME_HIDE)) {
+    MeshExtract_Tri_Data *data = _data;
+    int *mat_tri_ofs = data->tri_mat_end;
+    GPU_indexbuf_set_tri_verts(
+        &data->elb, mat_tri_ofs[mpoly->mat_nr]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+  }
+}
+
+static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data)
+{
+  MeshExtract_Tri_Data *data = _data;
+  GPU_indexbuf_build_in_place(&data->elb, ibo);
+  /* HACK Create ibo subranges and assign them to each GPUBatch. */
+  if (mr->use_final_mesh && mr->cache->surface_per_mat && mr->cache->surface_per_mat[0]) {
+    BLI_assert(mr->cache->surface_per_mat[0]->elem == ibo);
+    for (int i = 0; i < mr->mat_len; ++i) {
+      /* Multiply by 3 because these are triangle indices. */
+      int start = data->tri_mat_start[i] * 3;
+      int len = data->tri_mat_end[i] * 3 - data->tri_mat_start[i] * 3;
+      GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len);
+      /* WARNING: We modify the GPUBatch here! */
+      GPU_batch_elembuf_set(mr->cache->surface_per_mat[i], sub_ibo, true);
+    }
+  }
+  MEM_freeN(data->tri_mat_start);
+  MEM_freeN(data->tri_mat_end);
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_tris = {extract_tris_init,
+                                  extract_tris_looptri_bmesh,
+                                  extract_tris_looptri_mesh,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  extract_tris_finish,
+                                  0,
+                                  false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edges Indices
+ * \{ */
+
+static void *extract_lines_init(const MeshRenderData *mr, void *UNUSED(buf))
+{
+  GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
+  /* Put loose edges at the end. */
+  GPU_indexbuf_init(
+      elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len);
+  return elb;
+}
+
+static void extract_lines_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                     int l,
+                                     BMLoop *loop,
+                                     void *elb)
+{
+  if (!BM_elem_flag_test(loop->e, BM_ELEM_HIDDEN)) {
+    GPU_indexbuf_set_line_verts(elb, BM_elem_index_get(loop->e), l, BM_elem_index_get(loop->next));
+  }
+  else {
+    GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(loop->e));
+  }
+}
+
+static void extract_lines_loop_mesh(const MeshRenderData *mr,
+                                    int l,
+                                    const MLoop *mloop,
+                                    int UNUSED(p),
+                                    const MPoly *mpoly,
+                                    void *elb)
+{
+  const MEdge *medge = &mr->medge[mloop->e];
+  if (!((mr->use_hide && (medge->flag & ME_HIDE)) ||
+        ((mr->extract_type == MR_EXTRACT_MAPPED) &&
+         (mr->e_origindex[mloop->e] == ORIGINDEX_NONE)))) {
+    int loopend = mpoly->totloop + mpoly->loopstart - 1;
+    int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1);
+    GPU_indexbuf_set_line_verts(elb, mloop->e, l, other_loop);
+  }
+  else {
+    GPU_indexbuf_set_line_restart(elb, mloop->e);
+  }
+}
+
+static void extract_lines_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *elb)
+{
+  int ledge_idx = mr->edge_len + e;
+  if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+    int l = mr->loop_len + e * 2;
+    GPU_indexbuf_set_line_verts(elb, ledge_idx, l, l + 1);
+  }
+  else {
+    GPU_indexbuf_set_line_restart(elb, ledge_idx);
+  }
+  /* Don't render the edge twice. */
+  GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed));
+}
+
+static void extract_lines_ledge_mesh(const MeshRenderData *mr,
+                                     int e,
+                                     const MEdge *medge,
+                                     void *elb)
+{
+  int ledge_idx = mr->edge_len + e;
+  int edge_idx = mr->ledges[e];
+  if (!((mr->use_hide && (medge->flag & ME_HIDE)) ||
+        ((mr->extract_type == MR_EXTRACT_MAPPED) &&
+         (mr->e_origindex[edge_idx] == ORIGINDEX_NONE)))) {
+    int l = mr->loop_len + e * 2;
+    GPU_indexbuf_set_line_verts(elb, ledge_idx, l, l + 1);
+  }
+  else {
+    GPU_indexbuf_set_line_restart(elb, ledge_idx);
+  }
+  /* Don't render the edge twice. */
+  GPU_indexbuf_set_line_restart(elb, edge_idx);
+}
+
+static void extract_lines_finish(const MeshRenderData *mr, void *ibo, void *elb)
+{
+  GPU_indexbuf_build_in_place(elb, ibo);
+  MEM_freeN(elb);
+  /* HACK Create ibo subranges and assign them to GPUBatch. */
+  if (mr->use_final_mesh && mr->cache->batch.loose_edges) {
+    BLI_assert(mr->cache->batch.loose_edges->elem == ibo);
+    /* Multiply by 2 because these are edges indices. */
+    int start = mr->edge_len * 2;
+    int len = mr->edge_loose_len * 2;
+    GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len);
+    /* WARNING: We modify the GPUBatch here! */
+    GPU_batch_elembuf_set(mr->cache->batch.loose_edges, sub_ibo, true);
+  }
+}
+
+const MeshExtract extract_lines = {extract_lines_init,
+                                   NULL,
+                                   NULL,
+                                   extract_lines_loop_bmesh,
+                                   extract_lines_loop_mesh,
+                                   extract_lines_ledge_bmesh,
+                                   extract_lines_ledge_mesh,
+                                   NULL,
+                                   NULL,
+                                   extract_lines_finish,
+                                   0,
+                                   false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Point Indices
+ * \{ */
+
+static void *extract_points_init(const MeshRenderData *mr, void *UNUSED(buf))
+{
+  GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
+  GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len);
+  return elb;
+}
+
+BLI_INLINE void vert_set_bmesh(GPUIndexBufBuilder *elb, BMVert *eve, int loop)
+{
+  int vert_idx = BM_elem_index_get(eve);
+  if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+    GPU_indexbuf_set_point_vert(elb, vert_idx, loop);
+  }
+  else {
+    GPU_indexbuf_set_point_restart(elb, vert_idx);
+  }
+}
+
+BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
+                              const MeshRenderData *mr,
+                              int vert_idx,
+                              int loop)
+{
+  const MVert *mvert = &mr->mvert[vert_idx];
+  if (!((mr->use_hide && (mvert->flag & ME_HIDE)) ||
+        ((mr->extract_type == MR_EXTRACT_MAPPED) &&
+         (mr->v_origindex[vert_idx] == ORIGINDEX_NONE)))) {
+    GPU_indexbuf_set_point_vert(elb, vert_idx, loop);
+  }
+  else {
+    GPU_indexbuf_set_point_restart(elb, vert_idx);
+  }
+}
+
+static void extract_points_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                      int l,
+                                      BMLoop *loop,
+                                      void *elb)
+{
+  vert_set_bmesh(elb, loop->v, l);
+}
+
+static void extract_points_loop_mesh(const MeshRenderData *mr,
+                                     int l,
+                                     const MLoop *mloop,
+                                     int UNUSED(p),
+                                     const MPoly *UNUSED(mpoly),
+                                     void *elb)
+{
+  vert_set_mesh(elb, mr, mloop->v, l);
+}
+
+static void extract_points_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *elb)
+{
+  vert_set_bmesh(elb, eed->v1, mr->loop_len + e * 2);
+  vert_set_bmesh(elb, eed->v2, mr->loop_len + e * 2 + 1);
+}
+
+static void extract_points_ledge_mesh(const MeshRenderData *mr,
+                                      int e,
+                                      const MEdge *medge,
+                                      void *elb)
+{
+  vert_set_mesh(elb, mr, medge->v1, mr->loop_len + e * 2);
+  vert_set_mesh(elb, mr, medge->v2, mr->loop_len + e * 2 + 1);
+}
+
+static void extract_points_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *elb)
+{
+  vert_set_bmesh(elb, eve, mr->loop_len + mr->edge_loose_len * 2 + v);
+}
+
+static void extract_points_lvert_mesh(const MeshRenderData *mr,
+                                      int v,
+                                      const MVert *UNUSED(mvert),
+                                      void *elb)
+{
+  vert_set_mesh(elb, mr, mr->lverts[v], mr->loop_len + mr->edge_loose_len * 2 + v);
+}
+
+static void extract_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb)
+{
+  GPU_indexbuf_build_in_place(elb, ibo);
+  MEM_freeN(elb);
+}
+
+const MeshExtract extract_points = {extract_points_init,
+                                    NULL,
+                                    NULL,
+                                    extract_points_loop_bmesh,
+                                    extract_points_loop_mesh,
+                                    extract_points_ledge_bmesh,
+                                    extract_points_ledge_mesh,
+                                    extract_points_lvert_bmesh,
+                                    extract_points_lvert_mesh,
+                                    extract_points_finish,
+                                    0,
+                                    false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Facedots Indices
+ * \{ */
+
+static void *extract_fdots_init(const MeshRenderData *mr, void *UNUSED(buf))
+{
+  GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
+  GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
+  return elb;
+}
+
+static void extract_fdots_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                     int UNUSED(l),
+                                     BMLoop *loop,
+                                     void *elb)
+{
+  int face_idx = BM_elem_index_get(loop->f);
+  if (!BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN)) {
+    GPU_indexbuf_set_point_vert(elb, face_idx, face_idx);
+  }
+  else {
+    GPU_indexbuf_set_point_restart(elb, face_idx);
+  }
+}
+
+static void extract_fdots_loop_mesh(const MeshRenderData *mr,
+                                    int UNUSED(l),
+                                    const MLoop *mloop,
+                                    int p,
+                                    const MPoly *mpoly,
+                                    void *elb)
+{
+  const MVert *mvert = &mr->mvert[mloop->v];
+  if ((!mr->use_subsurf_fdots || (mvert->flag & ME_VERT_FACEDOT)) &&
+      !(mr->use_hide && (mpoly->flag & ME_HIDE))) {
+    GPU_indexbuf_set_point_vert(elb, p, p);
+  }
+  else {
+    GPU_indexbuf_set_point_restart(elb, p);
+  }
+}
+
+static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb)
+{
+  GPU_indexbuf_build_in_place(elb, ibo);
+  MEM_freeN(elb);
+}
+
+const MeshExtract extract_fdots = {extract_fdots_init,
+                                   NULL,
+                                   NULL,
+                                   extract_fdots_loop_bmesh,
+                                   extract_fdots_loop_mesh,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   extract_fdots_finish,
+                                   0,
+                                   false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Paint Mask Line Indices
+ * \{ */
+
+typedef struct MeshExtract_LinePaintMask_Data {
+  GPUIndexBufBuilder elb;
+  /** One bit per edge set if face is selected. */
+  BLI_bitmap select_map[0];
+} MeshExtract_LinePaintMask_Data;
+
+static void *extract_lines_paint_mask_init(const MeshRenderData *mr, void *UNUSED(buf))
+{
+  size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len);
+  MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__);
+  GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len);
+  return data;
+}
+
+static void extract_lines_paint_mask_loop_mesh(const MeshRenderData *mr,
+                                               int l,
+                                               const MLoop *mloop,
+                                               int UNUSED(p),
+                                               const MPoly *mpoly,
+                                               void *_data)
+{
+  MeshExtract_LinePaintMask_Data *data = (MeshExtract_LinePaintMask_Data *)_data;
+  if (!(mr->use_hide && (mpoly->flag & ME_HIDE))) {
+    int loopend = mpoly->totloop + mpoly->loopstart - 1;
+    int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1);
+    int edge_idx = mloop->e;
+    if (mpoly->flag & ME_FACE_SEL) {
+      if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, edge_idx)) {
+        /* Hide edge as it has more than 2 selected loop. */
+        GPU_indexbuf_set_line_restart(&data->elb, edge_idx);
+      }
+      else {
+        /* First selected loop. Set edge visible, overwritting any unsel loop. */
+        GPU_indexbuf_set_line_verts(&data->elb, edge_idx, l, other_loop);
+      }
+    }
+    else {
+      /* Set theses unselected loop only if this edge has no other selected loop. */
+      if (!BLI_BITMAP_TEST(data->select_map, edge_idx)) {
+        GPU_indexbuf_set_line_verts(&data->elb, edge_idx, l, other_loop);
+      }
+    }
+  }
+}
+static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr),
+                                            void *ibo,
+                                            void *_data)
+{
+  MeshExtract_LinePaintMask_Data *data = (MeshExtract_LinePaintMask_Data *)_data;
+
+  GPU_indexbuf_build_in_place(&data->elb, ibo);
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_lines_paint_mask = {extract_lines_paint_mask_init,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              extract_lines_paint_mask_loop_mesh,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              extract_lines_paint_mask_finish,
+                                              0,
+                                              false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Line Adjacency Indices
+ * \{ */
+
+#define NO_EDGE INT_MAX
+
+typedef struct MeshExtract_LineAdjacency_Data {
+  GPUIndexBufBuilder elb;
+  EdgeHash *eh;
+  bool is_manifold;
+  /* Array to convert vert index to any loop index of this vert. */
+  uint vert_to_loop[0];
+} MeshExtract_LineAdjacency_Data;
+
+static void *extract_lines_adjacency_init(const MeshRenderData *mr, void *UNUSED(buf))
+{
+  /* Similar to poly_to_tri_count().
+   * There is always loop + tri - 1 edges inside a polygon.
+   * Cummulate for all polys and you get : */
+  uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len;
+
+  size_t vert_to_loop_size = sizeof(uint) * mr->vert_len;
+
+  MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__);
+  GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len);
+  data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len);
+  data->is_manifold = true;
+  return data;
+}
+
+BLI_INLINE void lines_adjacency_triangle(
+    uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data)
+{
+  GPUIndexBufBuilder *elb = &data->elb;
+  /* Iter around the triangle's edges. */
+  for (int e = 0; e < 3; e++) {
+    uint tmp = v1;
+    v1 = v2, v2 = v3, v3 = tmp;
+    tmp = l1;
+    l1 = l2, l2 = l3, l3 = tmp;
+
+    bool inv_indices = (v2 > v3);
+    void **pval;
+    bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval);
+    int v_data = POINTER_AS_INT(*pval);
+    if (!value_is_init || v_data == NO_EDGE) {
+      /* Save the winding order inside the sign bit. Because the
+       * edgehash sort the keys and we need to compare winding later. */
+      int value = (int)l1 + 1; /* 0 cannot be signed so add one. */
+      *pval = POINTER_FROM_INT((inv_indices) ? -value : value);
+      /* Store loop indices for remaining non-manifold edges. */
+      data->vert_to_loop[v2] = l2;
+      data->vert_to_loop[v3] = l3;
+    }
+    else {
+      /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */
+      *pval = POINTER_FROM_INT(NO_EDGE);
+      bool inv_opposite = (v_data < 0);
+      uint l_opposite = (uint)abs(v_data) - 1;
+      /* TODO Make this part threadsafe. */
+      if (inv_opposite == inv_indices) {
+        /* Don't share edge if triangles have non matching winding. */
+        GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1);
+        GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite);
+        data->is_manifold = false;
+      }
+      else {
+        GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite);
+      }
+    }
+  }
+}
+
+static void extract_lines_adjacency_looptri_bmesh(const MeshRenderData *UNUSED(mr),
+                                                  int UNUSED(t),
+                                                  BMLoop **elt,
+                                                  void *data)
+{
+  if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+    lines_adjacency_triangle(BM_elem_index_get(elt[0]->v),
+                             BM_elem_index_get(elt[1]->v),
+                             BM_elem_index_get(elt[2]->v),
+                             BM_elem_index_get(elt[0]),
+                             BM_elem_index_get(elt[1]),
+                             BM_elem_index_get(elt[2]),
+                             data);
+  }
+}
+
+static void extract_lines_adjacency_looptri_mesh(const MeshRenderData *mr,
+                                                 int UNUSED(t),
+                                                 const MLoopTri *mlt,
+                                                 void *data)
+{
+  const MPoly *mpoly = &mr->mpoly[mlt->poly];
+  if (!(mpoly->flag & ME_HIDE)) {
+    lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
+                             mr->mloop[mlt->tri[1]].v,
+                             mr->mloop[mlt->tri[2]].v,
+                             mlt->tri[0],
+                             mlt->tri[1],
+                             mlt->tri[2],
+                             data);
+  }
+}
+
+static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, void *_data)
+{
+  MeshExtract_LineAdjacency_Data *data = (MeshExtract_LineAdjacency_Data *)_data;
+  /* Create edges for remaining non manifold edges. */
+  EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh);
+  for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+    uint v2, v3, l1, l2, l3;
+    int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
+    if (v_data != NO_EDGE) {
+      BLI_edgehashIterator_getKey(ehi, &v2, &v3);
+      l1 = (uint)abs(v_data) - 1;
+      if (v_data < 0) { /* inv_opposite  */
+        SWAP(uint, v2, v3);
+      }
+      l2 = data->vert_to_loop[v2];
+      l3 = data->vert_to_loop[v3];
+      GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1);
+      data->is_manifold = false;
+    }
+  }
+  BLI_edgehashIterator_free(ehi);
+  BLI_edgehash_free(data->eh, NULL);
+
+  mr->cache->is_manifold = data->is_manifold;
+
+  GPU_indexbuf_build_in_place(&data->elb, ibo);
+  MEM_freeN(data);
+}
+
+#undef NO_EDGE
+
+const MeshExtract extract_lines_adjacency = {extract_lines_adjacency_init,
+                                             extract_lines_adjacency_looptri_bmesh,
+                                             extract_lines_adjacency_looptri_mesh,
+                                             NULL,
+                                             NULL,
+                                             NULL,
+                                             NULL,
+                                             NULL,
+                                             NULL,
+                                             extract_lines_adjacency_finish,
+                                             0,
+                                             false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Triangles Indices
+ * \{ */
+
+typedef struct MeshExtract_EditUvElem_Data {
+  GPUIndexBufBuilder elb;
+  bool sync_selection;
+} MeshExtract_EditUvElem_Data;
+
+static void *extract_edituv_tris_init(const MeshRenderData *mr, void *UNUSED(ibo))
+{
+  MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
+  GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
+  data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+  return data;
+}
+
+BLI_INLINE void edituv_tri_add(
+    MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3)
+{
+  if (!hidden && (data->sync_selection || selected)) {
+    GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3);
+  }
+}
+
+static void extract_edituv_tris_looptri_bmesh(const MeshRenderData *UNUSED(mr),
+                                              int UNUSED(t),
+                                              BMLoop **elt,
+                                              void *data)
+{
+  edituv_tri_add(data,
+                 BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN),
+                 BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT),
+                 BM_elem_index_get(elt[0]),
+                 BM_elem_index_get(elt[1]),
+                 BM_elem_index_get(elt[2]));
+}
+
+static void extract_edituv_tris_looptri_mesh(const MeshRenderData *mr,
+                                             int UNUSED(t),
+                                             const MLoopTri *mlt,
+                                             void *data)
+{
+  const MPoly *mpoly = &mr->mpoly[mlt->poly];
+  edituv_tri_add(data,
+                 (mpoly->flag & ME_HIDE) != 0,
+                 (mpoly->flag & ME_FACE_SEL) != 0,
+                 mlt->tri[0],
+                 mlt->tri[1],
+                 mlt->tri[2]);
+}
+
+static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
+{
+  MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data;
+  GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
+  MEM_freeN(extract_data);
+}
+
+const MeshExtract extract_edituv_tris = {extract_edituv_tris_init,
+                                         extract_edituv_tris_looptri_bmesh,
+                                         extract_edituv_tris_looptri_mesh,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         extract_edituv_tris_finish,
+                                         0,
+                                         false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Line Indices around faces
+ * \{ */
+
+static void *extract_edituv_lines_init(const MeshRenderData *mr, void *UNUSED(ibo))
+{
+  MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
+  GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len);
+
+  data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+  return data;
+}
+
+BLI_INLINE void edituv_edge_add(
+    MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2)
+{
+  if (!hidden && (data->sync_selection || selected)) {
+    GPU_indexbuf_add_line_verts(&data->elb, v1, v2);
+  }
+}
+
+static void extract_edituv_lines_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                            int l,
+                                            BMLoop *loop,
+                                            void *data)
+{
+  edituv_edge_add(data,
+                  BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
+                  BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
+                  l,
+                  BM_elem_index_get(loop->next));
+}
+
+static void extract_edituv_lines_loop_mesh(const MeshRenderData *mr,
+                                           int loop_idx,
+                                           const MLoop *mloop,
+                                           int UNUSED(p),
+                                           const MPoly *mpoly,
+                                           void *data)
+{
+  int loopend = mpoly->totloop + mpoly->loopstart - 1;
+  int loop_next_idx = (loop_idx == loopend) ? mpoly->loopstart : (loop_idx + 1);
+  const bool real_edge = (mr->extract_type == MR_EXTRACT_MAPPED &&
+                          mr->e_origindex[mloop->e] != ORIGINDEX_NONE);
+  edituv_edge_add(data,
+                  (mpoly->flag & ME_HIDE) != 0 || !real_edge,
+                  (mpoly->flag & ME_FACE_SEL) != 0,
+                  loop_idx,
+                  loop_next_idx);
+}
+
+static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
+{
+  MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data;
+  GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
+  MEM_freeN(extract_data);
+}
+
+const MeshExtract extract_edituv_lines = {extract_edituv_lines_init,
+                                          NULL,
+                                          NULL,
+                                          extract_edituv_lines_loop_bmesh,
+                                          extract_edituv_lines_loop_mesh,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          extract_edituv_lines_finish,
+                                          0,
+                                          false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Points Indices
+ * \{ */
+
+static void *extract_edituv_points_init(const MeshRenderData *mr, void *UNUSED(ibo))
+{
+  MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
+  GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len);
+
+  data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+  return data;
+}
+
+BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data,
+                                 bool hidden,
+                                 bool selected,
+                                 int v1)
+{
+  if (!hidden && (data->sync_selection || selected)) {
+    GPU_indexbuf_add_point_vert(&data->elb, v1);
+  }
+}
+
+static void extract_edituv_points_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                             int l,
+                                             BMLoop *loop,
+                                             void *data)
+{
+  edituv_point_add(data,
+                   BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
+                   BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
+                   l);
+}
+
+static void extract_edituv_points_loop_mesh(const MeshRenderData *mr,
+                                            int l,
+                                            const MLoop *mloop,
+                                            int UNUSED(p),
+                                            const MPoly *mpoly,
+                                            void *data)
+{
+  const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED &&
+                          mr->v_origindex[mloop->v] != ORIGINDEX_NONE);
+  edituv_point_add(
+      data, ((mpoly->flag & ME_HIDE) != 0) || !real_vert, (mpoly->flag & ME_FACE_SEL) != 0, l);
+}
+
+static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data)
+{
+  MeshExtract_EditUvElem_Data *extract_data = (MeshExtract_EditUvElem_Data *)data;
+  GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
+  MEM_freeN(extract_data);
+}
+
+const MeshExtract extract_edituv_points = {extract_edituv_points_init,
+                                           NULL,
+                                           NULL,
+                                           extract_edituv_points_loop_bmesh,
+                                           extract_edituv_points_loop_mesh,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           extract_edituv_points_finish,
+                                           0,
+                                           false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Facedots Indices
+ * \{ */
+
+static void *extract_edituv_fdots_init(const MeshRenderData *mr, void *UNUSED(ibo))
+{
+  MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
+  GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
+
+  data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+  return data;
+}
+
+BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data,
+                                   bool hidden,
+                                   bool selected,
+                                   int face_idx)
+{
+  if (!hidden && (data->sync_selection || selected)) {
+    GPU_indexbuf_set_point_vert(&data->elb, face_idx, face_idx);
+  }
+  else {
+    GPU_indexbuf_set_point_restart(&data->elb, face_idx);
+  }
+}
+
+static void extract_edituv_fdots_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                            int UNUSED(l),
+                                            BMLoop *loop,
+                                            void *data)
+{
+  edituv_facedot_add(data,
+                     BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
+                     BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
+                     BM_elem_index_get(loop->f));
+}
+
+static void extract_edituv_fdots_loop_mesh(const MeshRenderData *mr,
+                                           int UNUSED(l),
+                                           const MLoop *mloop,
+                                           int p,
+                                           const MPoly *mpoly,
+                                           void *data)
+{
+  const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED &&
+                          mr->p_origindex[p] != ORIGINDEX_NONE);
+  const bool subd_fdot = (!mr->use_subsurf_fdots ||
+                          (mr->mvert[mloop->v].flag & ME_VERT_FACEDOT) != 0);
+  edituv_facedot_add(data,
+                     ((mpoly->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
+                     (mpoly->flag & ME_FACE_SEL) != 0,
+                     p);
+}
+
+static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *_data)
+{
+  MeshExtract_EditUvElem_Data *data = (MeshExtract_EditUvElem_Data *)_data;
+  GPU_indexbuf_build_in_place(&data->elb, ibo);
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_edituv_fdots = {extract_edituv_fdots_init,
+                                          NULL,
+                                          NULL,
+                                          extract_edituv_fdots_loop_bmesh,
+                                          extract_edituv_fdots_loop_mesh,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          extract_edituv_fdots_finish,
+                                          0,
+                                          false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Position and Vertex Normal
+ * \{ */
+
+typedef struct PosNorLoop {
+  float pos[3];
+  GPUPackedNormal nor;
+} PosNorLoop;
+
+typedef struct MeshExtract_PosNor_Data {
+  PosNorLoop *vbo_data;
+  GPUPackedNormal packed_nor[];
+} MeshExtract_PosNor_Data;
+
+static void *extract_pos_nor_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    /* WARNING Adjust PosNorLoop struct accordingly. */
+    GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+    GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+    GPU_vertformat_alias_add(&format, "vnor");
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+  /* Pack normals per vert, reduce amount of computation. */
+  size_t packed_nor_len = sizeof(GPUPackedNormal) * mr->vert_len;
+  MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__);
+  data->vbo_data = (PosNorLoop *)vbo->data;
+
+  /* Quicker than doing it for each loop. */
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    BMIter iter;
+    BMVert *eve;
+    int v;
+    BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
+      data->packed_nor[v] = GPU_normal_convert_i10_v3(eve->no);
+    }
+  }
+  else {
+    const MVert *mvert = mr->mvert;
+    for (int v = 0; v < mr->vert_len; v++, mvert++) {
+      data->packed_nor[v] = GPU_normal_convert_i10_s3(mvert->no);
+    }
+  }
+  return data;
+}
+
+static void extract_pos_nor_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                       int l,
+                                       BMLoop *loop,
+                                       void *_data)
+{
+  MeshExtract_PosNor_Data *data = _data;
+  PosNorLoop *vert = data->vbo_data + l;
+  copy_v3_v3(vert->pos, loop->v->co);
+  vert->nor = data->packed_nor[BM_elem_index_get(loop->v)];
+}
+
+static void extract_pos_nor_loop_mesh(const MeshRenderData *mr,
+                                      int l,
+                                      const MLoop *mloop,
+                                      int UNUSED(p),
+                                      const MPoly *mpoly,
+                                      void *_data)
+{
+  MeshExtract_PosNor_Data *data = _data;
+  PosNorLoop *vert = data->vbo_data + l;
+  const MVert *mvert = &mr->mvert[mloop->v];
+  copy_v3_v3(vert->pos, mvert->co);
+  vert->nor = data->packed_nor[mloop->v];
+  /* Flag for paint mode overlay. */
+  if (mpoly->flag & ME_HIDE)
+    vert->nor.w = -1;
+  else if (mpoly->flag & ME_FACE_SEL)
+    vert->nor.w = 1;
+  else
+    vert->nor.w = 0;
+}
+
+static void extract_pos_nor_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *_data)
+{
+  int l = mr->loop_len + e * 2;
+  MeshExtract_PosNor_Data *data = _data;
+  PosNorLoop *vert = data->vbo_data + l;
+  copy_v3_v3(vert[0].pos, eed->v1->co);
+  copy_v3_v3(vert[1].pos, eed->v2->co);
+  vert[0].nor = data->packed_nor[BM_elem_index_get(eed->v1)];
+  vert[1].nor = data->packed_nor[BM_elem_index_get(eed->v2)];
+}
+
+static void extract_pos_nor_ledge_mesh(const MeshRenderData *mr,
+                                       int e,
+                                       const MEdge *medge,
+                                       void *_data)
+{
+  int l = mr->loop_len + e * 2;
+  MeshExtract_PosNor_Data *data = _data;
+  PosNorLoop *vert = data->vbo_data + l;
+  copy_v3_v3(vert[0].pos, mr->mvert[medge->v1].co);
+  copy_v3_v3(vert[1].pos, mr->mvert[medge->v2].co);
+  vert[0].nor = data->packed_nor[medge->v1];
+  vert[1].nor = data->packed_nor[medge->v2];
+}
+
+static void extract_pos_nor_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *_data)
+{
+  int l = mr->loop_len + mr->edge_loose_len * 2 + v;
+  MeshExtract_PosNor_Data *data = _data;
+  PosNorLoop *vert = data->vbo_data + l;
+  copy_v3_v3(vert->pos, eve->co);
+  vert->nor = data->packed_nor[BM_elem_index_get(eve)];
+}
+
+static void extract_pos_nor_lvert_mesh(const MeshRenderData *mr,
+                                       int v,
+                                       const MVert *mvert,
+                                       void *_data)
+{
+  int l = mr->loop_len + mr->edge_loose_len * 2 + v;
+  int v_idx = mr->lverts[v];
+  MeshExtract_PosNor_Data *data = _data;
+  PosNorLoop *vert = data->vbo_data + l;
+  copy_v3_v3(vert->pos, mvert->co);
+  vert->nor = data->packed_nor[v_idx];
+}
+
+static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(vbo), void *data)
+{
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_pos_nor = {extract_pos_nor_init,
+                                     NULL,
+                                     NULL,
+                                     extract_pos_nor_loop_bmesh,
+                                     extract_pos_nor_loop_mesh,
+                                     extract_pos_nor_ledge_bmesh,
+                                     extract_pos_nor_ledge_mesh,
+                                     extract_pos_nor_lvert_bmesh,
+                                     extract_pos_nor_lvert_mesh,
+                                     extract_pos_nor_finish,
+                                     0,
+                                     true};
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loop Normal
+ * \{ */
+
+static void *extract_lnor_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+    GPU_vertformat_alias_add(&format, "lnor");
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+  return vbo->data;
+}
+
+static void extract_lnor_loop_bmesh(const MeshRenderData *mr, int l, BMLoop *loop, void *data)
+{
+  if (mr->loop_normals) {
+    ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->loop_normals[l]);
+  }
+  else if (BM_elem_flag_test(loop->f, BM_ELEM_SMOOTH)) {
+    ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(loop->v->no);
+  }
+  else {
+    ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(loop->f->no);
+  }
+}
+
+static void extract_lnor_loop_mesh(
+    const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *data)
+{
+  if (mr->loop_normals) {
+    ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->loop_normals[l]);
+  }
+  else if (mpoly->flag & ME_SMOOTH) {
+    ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_s3(mr->mvert[mloop->v].no);
+  }
+  else {
+    ((GPUPackedNormal *)data)[l] = GPU_normal_convert_i10_v3(mr->poly_normals[p]);
+  }
+}
+
+const MeshExtract extract_lnor = {extract_lnor_init,
+                                  NULL,
+                                  NULL,
+                                  extract_lnor_loop_bmesh,
+                                  extract_lnor_loop_mesh,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  MR_DATA_LOOP_NOR,
+                                  true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract UV / Tangent layers
+ * \{ */
+
+static void *extract_uv_tan_init(const MeshRenderData *mr, void *buf)
+{
+  GPUVertFormat format = {0};
+  GPU_vertformat_deinterleave(&format);
+
+  CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+  CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+  uint32_t uv_layers = mr->cache->cd_used.uv;
+  uint32_t tan_layers = mr->cache->cd_used.tan;
+  float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO);
+  bool orco_allocated = false;
+  const bool use_orco_tan = mr->cache->cd_used.tan_orco != 0;
+
+  /* XXX FIXME XXX */
+  /* We use a hash to identify each data layer based on its name.
+   * Gawain then search for this name in the current shader and bind if it exists.
+   * NOTE : This is prone to hash collision.
+   * One solution to hash collision would be to format the cd layer name
+   * to a safe glsl var name, but without name clash.
+   * NOTE 2 : Replicate changes to code_generate_vertex_new() in gpu_codegen.c */
+  for (int i = 0; i < MAX_MTFACE; i++) {
+    if (uv_layers & (1 << i)) {
+      char attr_name[32];
+      const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
+      uint hash = BLI_ghashutil_strhash_p(layer_name);
+      /* UV layer name. */
+      BLI_snprintf(attr_name, sizeof(attr_name), "u%u", hash);
+      GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+      /* Auto layer name. */
+      BLI_snprintf(attr_name, sizeof(attr_name), "a%u", hash);
+      GPU_vertformat_alias_add(&format, attr_name);
+      /* Active render layer name. */
+      if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
+        GPU_vertformat_alias_add(&format, "u");
+      }
+      /* Active display layer name. */
+      if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
+        GPU_vertformat_alias_add(&format, "au");
+        /* Alias to pos for edit uvs. */
+        GPU_vertformat_alias_add(&format, "pos");
+      }
+      /* Stencil mask uv layer name. */
+      if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) {
+        GPU_vertformat_alias_add(&format, "mu");
+      }
+    }
+  }
+
+  int tan_len = 0;
+  char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
+
+  for (int i = 0; i < MAX_MTFACE; i++) {
+    if (tan_layers & (1 << i)) {
+      char attr_name[32];
+      const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
+      uint hash = BLI_ghashutil_strhash_p(layer_name);
+      /* Tangent layer name. */
+      BLI_snprintf(attr_name, sizeof(attr_name), "t%u", hash);
+      GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+      /* Active render layer name. */
+      if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
+        GPU_vertformat_alias_add(&format, "t");
+      }
+      /* Active display layer name. */
+      if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
+        GPU_vertformat_alias_add(&format, "at");
+      }
+
+      BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME);
+    }
+  }
+  if (use_orco_tan && orco == NULL) {
+    /* If orco is not available compute it ourselves */
+    orco_allocated = true;
+    orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__);
+
+    if (mr->extract_type == MR_EXTRACT_BMESH) {
+      BMesh *bm = mr->bm;
+      for (int v = 0; v < mr->vert_len; v++) {
+        copy_v3_v3(orco[v], BM_vert_at_index(bm, v)->co);
+      }
+    }
+    else {
+      const MVert *mvert = mr->mvert;
+      for (int v = 0; v < mr->vert_len; v++, mvert++) {
+        copy_v3_v3(orco[v], mvert->co);
+      }
+    }
+    BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0);
+  }
+
+  /* Start Fresh */
+  CustomData_free_layers(cd_ldata, CD_TANGENT, mr->loop_len);
+
+  if (tan_len != 0 || use_orco_tan) {
+    short tangent_mask = 0;
+    bool calc_active_tangent = false;
+    if (mr->extract_type == MR_EXTRACT_BMESH) {
+      BKE_editmesh_loop_tangent_calc(mr->edit_bmesh,
+                                     calc_active_tangent,
+                                     tangent_names,
+                                     tan_len,
+                                     mr->poly_normals,
+                                     mr->loop_normals,
+                                     orco,
+                                     cd_ldata,
+                                     mr->loop_len,
+                                     &tangent_mask);
+    }
+    else {
+      BKE_mesh_calc_loop_tangent_ex(mr->mvert,
+                                    mr->mpoly,
+                                    mr->poly_len,
+                                    mr->mloop,
+                                    mr->mlooptri,
+                                    mr->tri_len,
+                                    cd_ldata,
+                                    calc_active_tangent,
+                                    tangent_names,
+                                    tan_len,
+                                    mr->poly_normals,
+                                    mr->loop_normals,
+                                    orco,
+                                    cd_ldata,
+                                    mr->loop_len,
+                                    &tangent_mask);
+    }
+  }
+
+  if (use_orco_tan) {
+    char attr_name[32];
+    const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_TANGENT, 0);
+    uint hash = BLI_ghashutil_strhash_p(layer_name);
+    BLI_snprintf(attr_name, sizeof(*attr_name), "t%u", hash);
+    GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+    GPU_vertformat_alias_add(&format, "t");
+    GPU_vertformat_alias_add(&format, "at");
+  }
+
+  if (orco_allocated) {
+    MEM_SAFE_FREE(orco);
+  }
+
+  int v_len = mr->loop_len;
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+    /* VBO will not be used, only allocate minimum of memory. */
+    v_len = 1;
+  }
+
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, v_len);
+
+  float(*uv_data)[2] = (float(*)[2])vbo->data;
+  for (int i = 0; i < MAX_MTFACE; i++) {
+    if (uv_layers & (1 << i)) {
+      if (mr->extract_type == MR_EXTRACT_BMESH) {
+        int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i);
+        BMIter f_iter, l_iter;
+        BMFace *efa;
+        BMLoop *loop;
+        BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+          BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
+            MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs);
+            memcpy(uv_data, luv->uv, sizeof(*uv_data));
+            uv_data++;
+          }
+        }
+      }
+      else {
+        MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i);
+        for (int l = 0; l < mr->loop_len; l++, uv_data++, layer_data++) {
+          memcpy(uv_data, layer_data->uv, sizeof(*uv_data));
+        }
+      }
+    }
+  }
+  /* Start tan_data after uv_data. */
+  float(*tan_data)[4] = (float(*)[4])uv_data;
+  for (int i = 0; i < tan_len; i++) {
+    void *layer_data = CustomData_get_layer_named(cd_ldata, CD_TANGENT, tangent_names[i]);
+    memcpy(tan_data, layer_data, sizeof(*tan_data) * mr->loop_len);
+    tan_data += mr->loop_len;
+  }
+  if (use_orco_tan) {
+    void *layer_data = CustomData_get_layer_n(cd_ldata, CD_TANGENT, 0);
+    memcpy(tan_data, layer_data, sizeof(*tan_data) * mr->loop_len);
+  }
+
+  CustomData_free_layers(cd_ldata, CD_TANGENT, mr->loop_len);
+
+  return NULL;
+}
+
+const MeshExtract extract_uv_tan = {extract_uv_tan_init,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+                                    false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract VCol
+ * \{ */
+
+static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
+{
+  GPUVertFormat format = {0};
+  GPU_vertformat_deinterleave(&format);
+
+  CustomData *cd_ldata = &mr->me->ldata;
+  uint32_t vcol_layers = mr->cache->cd_used.vcol;
+
+  /* XXX FIXME XXX */
+  /* We use a hash to identify each data layer based on its name.
+   * Gawain then search for this name in the current shader and bind if it exists.
+   * NOTE : This is prone to hash collision.
+   * One solution to hash collision would be to format the cd layer name
+   * to a safe glsl var name, but without name clash.
+   * NOTE 2 : Replicate changes to code_generate_vertex_new() in gpu_codegen.c */
+  for (int i = 0; i < 8; i++) {
+    if (vcol_layers & (1 << i)) {
+      char attr_name[32];
+      const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i);
+      uint hash = BLI_ghashutil_strhash_p(layer_name);
+
+      BLI_snprintf(attr_name, sizeof(attr_name), "c%u", hash);
+      GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+      if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) {
+        GPU_vertformat_alias_add(&format, "c");
+      }
+      if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
+        GPU_vertformat_alias_add(&format, "ac");
+      }
+      /* Gather number of auto layers. */
+      /* We only do vcols that are not overridden by uvs */
+      if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
+        BLI_snprintf(attr_name, sizeof(attr_name), "a%u", hash);
+        GPU_vertformat_alias_add(&format, attr_name);
+      }
+    }
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+  MLoopCol *vcol_data = (MLoopCol *)vbo->data;
+  for (int i = 0; i < 8; i++) {
+    if (vcol_layers & (1 << i)) {
+      void *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i);
+      memcpy(vcol_data, layer_data, sizeof(*vcol_data) * mr->loop_len);
+      vcol_data += mr->loop_len;
+    }
+  }
+  return NULL;
+}
+
+const MeshExtract extract_vcol = {
+    extract_vcol_init, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, false};
+
+/** \} */ /** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Orco
+ * \{ */
+
+typedef struct MeshExtract_Orco_Data {
+  float (*vbo_data)[4];
+  float (*orco)[3];
+} MeshExtract_Orco_Data;
+
+static void *extract_orco_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
+     * attribs. This is a substential waste of Vram and should be done another way.
+     * Unfortunately, at the time of writting, I did not found any other "non disruptive"
+     * alternative. */
+    GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+  }
+
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+  CustomData *cd_vdata = &mr->me->vdata;
+
+  MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__);
+  data->vbo_data = (float(*)[4])vbo->data;
+  data->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
+  /* Make sure orco layer was requested only if needed! */
+  BLI_assert(data->orco);
+  return data;
+}
+
+static void extract_orco_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                    int l,
+                                    BMLoop *loop,
+                                    void *data)
+{
+  MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
+  float *loop_orco = orco_data->vbo_data[l];
+  copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]);
+  loop_orco[3] = 0.0; /* Tag as not a generic attrib */
+}
+
+static void extract_orco_loop_mesh(const MeshRenderData *UNUSED(mr),
+                                   int l,
+                                   const MLoop *mloop,
+                                   int UNUSED(p),
+                                   const MPoly *UNUSED(mpoly),
+                                   void *data)
+{
+  MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
+  float *loop_orco = orco_data->vbo_data[l];
+  copy_v3_v3(loop_orco, orco_data->orco[mloop->v]);
+  loop_orco[3] = 0.0; /* Tag as not a generic attrib */
+}
+
+static void extract_orco_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data)
+{
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_orco = {extract_orco_init,
+                                  NULL,
+                                  NULL,
+                                  extract_orco_loop_bmesh,
+                                  extract_orco_loop_mesh,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  extract_orco_finish,
+                                  0,
+                                  true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edge Factor
+ * Defines how much an edge is visible.
+ * \{ */
+
+typedef struct MeshExtract_EdgeFac_Data {
+  uchar *vbo_data;
+  bool use_edge_render;
+  /* Number of loop per edge. */
+  uchar edge_loop_count[0];
+} MeshExtract_EdgeFac_Data;
+
+static float loop_edge_factor_get(const float f_no[3],
+                                  const float v_co[3],
+                                  const float v_no[3],
+                                  const float v_next_co[3])
+{
+  float enor[3], evec[3];
+  sub_v3_v3v3(evec, v_next_co, v_co);
+  cross_v3_v3v3(enor, v_no, evec);
+  normalize_v3(enor);
+  float d = fabsf(dot_v3v3(enor, f_no));
+  /* Rescale to the slider range. */
+  d *= (1.0f / 0.065f);
+  CLAMP(d, 0.0f, 1.0f);
+  return d;
+}
+
+static void *extract_edge_fac_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+  MeshExtract_EdgeFac_Data *data;
+
+  if (mr->extract_type == MR_EXTRACT_MESH) {
+    size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len;
+    data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__);
+
+    /* HACK(fclem) Detecting the need for edge render.
+     * We could have a flag in the mesh instead or check the modifier stack. */
+    const MEdge *medge = mr->medge;
+    for (int e = 0; e < mr->edge_len; e++, medge++) {
+      if ((medge->flag & ME_EDGERENDER) == 0) {
+        data->use_edge_render = true;
+        break;
+      }
+    }
+  }
+  else {
+    data = MEM_callocN(sizeof(*data), __func__);
+    /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */
+    data->use_edge_render = true;
+  }
+
+  data->vbo_data = vbo->data;
+  return data;
+}
+
+static void extract_edge_fac_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                        int l,
+                                        BMLoop *loop,
+                                        void *_data)
+{
+  MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+  if (BM_edge_is_manifold(loop->e)) {
+    float ratio = loop_edge_factor_get(loop->f->no, loop->v->co, loop->v->no, loop->next->v->co);
+    data->vbo_data[l] = ratio * 253 + 1;
+  }
+  else {
+    data->vbo_data[l] = 255;
+  }
+}
+
+static void extract_edge_fac_loop_mesh(
+    const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data)
+{
+  MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+  if (data->use_edge_render) {
+    const MEdge *medge = &mr->medge[mloop->e];
+    data->vbo_data[l] = (medge->flag & ME_EDGERENDER) ? 255 : 0;
+  }
+  else {
+    /* Count loop per edge to detect non-manifold. */
+    if (data->edge_loop_count[mloop->e] < 3) {
+      data->edge_loop_count[mloop->e]++;
+    }
+    if (data->edge_loop_count[mloop->e] == 2) {
+      /* Manifold */
+      int loopend = mpoly->totloop + mpoly->loopstart - 1;
+      int other_loop = (l == loopend) ? mpoly->loopstart : (l + 1);
+      const MLoop *mloop_next = &mr->mloop[other_loop];
+      const MVert *v1 = &mr->mvert[mloop->v];
+      const MVert *v2 = &mr->mvert[mloop_next->v];
+      float vnor_f[3];
+      normal_short_to_float_v3(vnor_f, v1->no);
+      float ratio = loop_edge_factor_get(mr->poly_normals[p], v1->co, vnor_f, v2->co);
+      data->vbo_data[l] = ratio * 253 + 1;
+    }
+    else {
+      /* Non-manifold */
+      data->vbo_data[l] = 255;
+    }
+  }
+}
+
+static void extract_edge_fac_ledge_bmesh(const MeshRenderData *mr,
+                                         int e,
+                                         BMEdge *UNUSED(eed),
+                                         void *_data)
+{
+  MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+  data->vbo_data[mr->loop_len + e * 2 + 0] = 255;
+  data->vbo_data[mr->loop_len + e * 2 + 1] = 255;
+}
+
+static void extract_edge_fac_ledge_mesh(const MeshRenderData *mr,
+                                        int e,
+                                        const MEdge *UNUSED(edge),
+                                        void *_data)
+{
+  MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+  data->vbo_data[mr->loop_len + e * 2 + 0] = 255;
+  data->vbo_data[mr->loop_len + e * 2 + 1] = 255;
+}
+
+static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data)
+{
+  MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+
+  if (GPU_crappy_amd_driver()) {
+    GPUVertBuf *vbo = (GPUVertBuf *)buf;
+    /* Some AMD drivers strangely crash with VBOs with a one byte format.
+     * To workaround we reinit the vbo with another format and convert
+     * all bytes to floats. */
+    static GPUVertFormat format = {0};
+    if (format.attr_len == 0) {
+      GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+    }
+    /* We keep the data reference in data->vbo_data. */
+    vbo->data = NULL;
+    GPU_vertbuf_clear(vbo);
+
+    int buf_len = mr->loop_len + mr->loop_loose_len;
+    GPU_vertbuf_init_with_format(vbo, &format);
+    GPU_vertbuf_data_alloc(vbo, buf_len);
+
+    float *fdata = (float *)vbo->data;
+    for (int l = 0; l < buf_len; l++, fdata++) {
+      *fdata = data->vbo_data[l] / 255.0f;
+    }
+    /* Free old byte data. */
+    MEM_freeN(data->vbo_data);
+  }
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_edge_fac = {extract_edge_fac_init,
+                                      NULL,
+                                      NULL,
+                                      extract_edge_fac_loop_bmesh,
+                                      extract_edge_fac_loop_mesh,
+                                      extract_edge_fac_ledge_bmesh,
+                                      extract_edge_fac_ledge_mesh,
+                                      NULL,
+                                      NULL,
+                                      extract_edge_fac_finish,
+                                      MR_DATA_POLY_NOR,
+                                      false};
+
+/** \} */
+/* ---------------------------------------------------------------------- */
+/** \name Extract Vertex Weight
+ * \{ */
+
+typedef struct MeshExtract_Weight_Data {
+  float *vbo_data;
+  const DRW_MeshWeightState *wstate;
+  const MDeformVert *dvert; /* For Mesh. */
+  int cd_ofs;               /* For BMesh. */
+} MeshExtract_Weight_Data;
+
+static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
+{
+  /* Error state. */
+  if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
+    return -2.0f;
+  }
+  else if (dvert == NULL) {
+    return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f;
+  }
+
+  float input = 0.0f;
+  if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
+    /* Multi-Paint feature */
+    input = BKE_defvert_multipaint_collective_weight(
+        dvert,
+        wstate->defgroup_len,
+        wstate->defgroup_sel,
+        wstate->defgroup_sel_count,
+        (wstate->flags & DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE) != 0);
+    /* make it black if the selected groups have no weight on a vertex */
+    if (input == 0.0f) {
+      return -1.0f;
+    }
+  }
+  else {
+    /* default, non tricky behavior */
+    input = defvert_find_weight(dvert, wstate->defgroup_active);
+
+    if (input == 0.0f) {
+      switch (wstate->alert_mode) {
+        case OB_DRAW_GROUPUSER_ACTIVE:
+          return -1.0f;
+          break;
+        case OB_DRAW_GROUPUSER_ALL:
+          if (defvert_is_weight_zero(dvert, wstate->defgroup_len)) {
+            return -1.0f;
+          }
+          break;
+      }
+    }
+  }
+  CLAMP(input, 0.0f, 1.0f);
+  return input;
+}
+
+static void *extract_weights_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+  MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__);
+  data->vbo_data = (float *)vbo->data;
+  data->wstate = &mr->cache->weight_state;
+
+  if (data->wstate->defgroup_active == -1) {
+    /* Nothing to show. */
+    data->dvert = NULL;
+    data->cd_ofs = -1;
+  }
+  else if (mr->extract_type == MR_EXTRACT_BMESH) {
+    data->dvert = NULL;
+    data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT);
+  }
+  else {
+    data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT);
+    data->cd_ofs = -1;
+  }
+  return data;
+}
+
+static void extract_weights_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                       int l,
+                                       BMLoop *loop,
+                                       void *_data)
+{
+  MeshExtract_Weight_Data *data = (MeshExtract_Weight_Data *)_data;
+  const MDeformVert *dvert = (data->cd_ofs != -1) ? BM_ELEM_CD_GET_VOID_P(loop->v, data->cd_ofs) :
+                                                    NULL;
+  data->vbo_data[l] = evaluate_vertex_weight(dvert, data->wstate);
+}
+
+static void extract_weights_loop_mesh(const MeshRenderData *UNUSED(mr),
+                                      int l,
+                                      const MLoop *mloop,
+                                      int UNUSED(p),
+                                      const MPoly *UNUSED(mpoly),
+                                      void *_data)
+{
+  MeshExtract_Weight_Data *data = (MeshExtract_Weight_Data *)_data;
+  const MDeformVert *dvert = data->dvert ? &data->dvert[mloop->v] : NULL;
+  data->vbo_data[l] = evaluate_vertex_weight(dvert, data->wstate);
+}
+
+static void extract_weights_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data)
+{
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_weights = {extract_weights_init,
+                                     NULL,
+                                     NULL,
+                                     extract_weights_loop_bmesh,
+                                     extract_weights_loop_mesh,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     extract_weights_finish,
+                                     0,
+                                     true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit Mode Data / Flags
+ * \{ */
+
+typedef struct EditLoopData {
+  uchar v_flag;
+  uchar e_flag;
+  uchar crease;
+  uchar bweight;
+} EditLoopData;
+
+static void mesh_render_data_face_flag(const MeshRenderData *mr,
+                                       BMFace *efa,
+                                       const int cd_ofs,
+                                       EditLoopData *eattr)
+{
+  if (efa == mr->efa_act) {
+    eattr->v_flag |= VFLAG_FACE_ACTIVE;
+  }
+  if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+    eattr->v_flag |= VFLAG_FACE_SELECTED;
+  }
+
+  if (efa == mr->efa_act_uv) {
+    eattr->v_flag |= VFLAG_FACE_UV_ACTIVE;
+  }
+  if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) {
+    eattr->v_flag |= VFLAG_FACE_UV_SELECT;
+  }
+
+#ifdef WITH_FREESTYLE
+  if (mr->freestyle_face_ofs != -1) {
+    const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs);
+    if (ffa->flag & FREESTYLE_FACE_MARK) {
+      eattr->v_flag |= VFLAG_FACE_FREESTYLE;
+    }
+  }
+#endif
+}
+
+static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr)
+{
+  const ToolSettings *ts = mr->toolsettings;
+  const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0;
+  const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE);
+
+  if (eed == mr->eed_act) {
+    eattr->e_flag |= VFLAG_EDGE_ACTIVE;
+  }
+  if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+    eattr->e_flag |= VFLAG_EDGE_SELECTED;
+  }
+  if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
+      BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) {
+    eattr->e_flag |= VFLAG_EDGE_SELECTED;
+    eattr->e_flag |= VFLAG_VERT_SELECTED;
+  }
+  if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
+    eattr->e_flag |= VFLAG_EDGE_SEAM;
+  }
+  if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) {
+    eattr->e_flag |= VFLAG_EDGE_SHARP;
+  }
+
+  /* Use active edge color for active face edges because
+   * specular highlights make it hard to see T55456#510873.
+   *
+   * This isn't ideal since it can't be used when mixing edge/face modes
+   * but it's still better then not being able to see the active face. */
+  if (is_face_only_select_mode) {
+    if (mr->efa_act != NULL) {
+      if (BM_edge_in_face(eed, mr->efa_act)) {
+        eattr->e_flag |= VFLAG_EDGE_ACTIVE;
+      }
+    }
+  }
+
+  /* Use a byte for value range */
+  if (mr->crease_ofs != -1) {
+    float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs);
+    if (crease > 0) {
+      eattr->crease = (uchar)(crease * 255.0f);
+    }
+  }
+  /* Use a byte for value range */
+  if (mr->bweight_ofs != -1) {
+    float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs);
+    if (bweight > 0) {
+      eattr->bweight = (uchar)(bweight * 255.0f);
+    }
+  }
+#ifdef WITH_FREESTYLE
+  if (mr->freestyle_edge_ofs != -1) {
+    const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs);
+    if (fed->flag & FREESTYLE_EDGE_MARK) {
+      eattr->e_flag |= VFLAG_EDGE_FREESTYLE;
+    }
+  }
+#endif
+}
+
+static void mesh_render_data_loop_flag(const MeshRenderData *mr,
+                                       BMLoop *loop,
+                                       const int cd_ofs,
+                                       EditLoopData *eattr)
+{
+  if (cd_ofs == -1) {
+    return;
+  }
+  MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs);
+  if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) {
+    eattr->v_flag |= VFLAG_VERT_UV_PINNED;
+  }
+  if (uvedit_uv_select_test_ex(mr->toolsettings, loop, cd_ofs)) {
+    eattr->v_flag |= VFLAG_VERT_UV_SELECT;
+  }
+}
+
+static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr,
+                                            BMLoop *loop,
+                                            const int cd_ofs,
+                                            EditLoopData *eattr)
+{
+  if (cd_ofs == -1) {
+    return;
+  }
+  if (uvedit_edge_select_test_ex(mr->toolsettings, loop, cd_ofs)) {
+    eattr->v_flag |= VFLAG_EDGE_UV_SELECT;
+    eattr->v_flag |= VFLAG_VERT_UV_SELECT;
+  }
+}
+
+static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr)
+{
+  if (eve == mr->eve_act) {
+    eattr->e_flag |= VFLAG_VERT_ACTIVE;
+  }
+  if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+    eattr->e_flag |= VFLAG_VERT_SELECTED;
+  }
+}
+
+static void *extract_edit_data_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    /* WARNING Adjust EditLoopData struct accordingly. */
+    GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
+    GPU_vertformat_alias_add(&format, "flag");
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+  return vbo->data;
+}
+
+static void extract_edit_data_loop_bmesh(const MeshRenderData *mr,
+                                         int l,
+                                         BMLoop *loop,
+                                         void *_data)
+{
+  EditLoopData *data = (EditLoopData *)_data + l;
+  memset(data, 0x0, sizeof(*data));
+  mesh_render_data_face_flag(mr, loop->f, -1, data);
+  mesh_render_data_edge_flag(mr, loop->e, data);
+  mesh_render_data_vert_flag(mr, loop->v, data);
+}
+
+static void extract_edit_data_loop_mesh(const MeshRenderData *mr,
+                                        int l,
+                                        const MLoop *mloop,
+                                        int p,
+                                        const MPoly *UNUSED(mpoly),
+                                        void *_data)
+{
+  EditLoopData *data = (EditLoopData *)_data + l;
+  memset(data, 0x0, sizeof(*data));
+  BMFace *efa = bm_original_face_get(mr, p);
+  BMEdge *eed = bm_original_edge_get(mr, mloop->e);
+  BMVert *eve = bm_original_vert_get(mr, mloop->v);
+  if (efa) {
+    mesh_render_data_face_flag(mr, efa, -1, data);
+  }
+  if (eed) {
+    mesh_render_data_edge_flag(mr, eed, data);
+  }
+  if (eve) {
+    mesh_render_data_vert_flag(mr, eve, data);
+  }
+}
+
+static void extract_edit_data_ledge_bmesh(const MeshRenderData *mr,
+                                          int e,
+                                          BMEdge *eed,
+                                          void *_data)
+{
+  EditLoopData *data = (EditLoopData *)_data + mr->loop_len + e * 2;
+  memset(data, 0x0, sizeof(*data) * 2);
+  mesh_render_data_edge_flag(mr, eed, &data[0]);
+  data[1] = data[0];
+  mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
+  mesh_render_data_vert_flag(mr, eed->v2, &data[1]);
+}
+
+static void extract_edit_data_ledge_mesh(const MeshRenderData *mr,
+                                         int e,
+                                         const MEdge *edge,
+                                         void *_data)
+{
+  EditLoopData *data = (EditLoopData *)_data + mr->loop_len + e * 2;
+  memset(data, 0x0, sizeof(*data) * 2);
+  int e_idx = mr->ledges[e];
+  BMEdge *eed = bm_original_edge_get(mr, e_idx);
+  BMVert *eve1 = bm_original_vert_get(mr, edge->v1);
+  BMVert *eve2 = bm_original_vert_get(mr, edge->v2);
+  if (eed) {
+    mesh_render_data_edge_flag(mr, eed, &data[0]);
+    data[1] = data[0];
+  }
+  if (eve1) {
+    mesh_render_data_vert_flag(mr, eve1, &data[0]);
+  }
+  if (eve2) {
+    mesh_render_data_vert_flag(mr, eve2, &data[1]);
+  }
+}
+
+static void extract_edit_data_lvert_bmesh(const MeshRenderData *mr,
+                                          int v,
+                                          BMVert *eve,
+                                          void *_data)
+{
+  EditLoopData *data = (EditLoopData *)_data + mr->loop_len + mr->edge_loose_len * 2 + v;
+  memset(data, 0x0, sizeof(*data));
+  mesh_render_data_vert_flag(mr, eve, data);
+}
+
+static void extract_edit_data_lvert_mesh(const MeshRenderData *mr,
+                                         int v,
+                                         const MVert *UNUSED(mvert),
+                                         void *_data)
+{
+  EditLoopData *data = (EditLoopData *)_data + mr->loop_len + mr->edge_loose_len * 2 + v;
+  memset(data, 0x0, sizeof(*data));
+  int v_idx = mr->lverts[v];
+  BMVert *eve = bm_original_vert_get(mr, v_idx);
+  if (eve) {
+    mesh_render_data_vert_flag(mr, eve, data);
+  }
+}
+
+const MeshExtract extract_edit_data = {extract_edit_data_init,
+                                       NULL,
+                                       NULL,
+                                       extract_edit_data_loop_bmesh,
+                                       extract_edit_data_loop_mesh,
+                                       extract_edit_data_ledge_bmesh,
+                                       extract_edit_data_ledge_mesh,
+                                       extract_edit_data_lvert_bmesh,
+                                       extract_edit_data_lvert_mesh,
+                                       NULL,
+                                       0,
+                                       true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Data / Flags
+ * \{ */
+
+typedef struct MeshExtract_EditUVData_Data {
+  EditLoopData *vbo_data;
+  int cd_ofs;
+} MeshExtract_EditUVData_Data;
+
+static void *extract_edituv_data_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    /* WARNING Adjust EditLoopData struct accordingly. */
+    GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
+    GPU_vertformat_alias_add(&format, "flag");
+  }
+
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+  CustomData *cd_ldata = &mr->me->ldata;
+
+  MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__);
+  data->vbo_data = (EditLoopData *)vbo->data;
+  data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
+  return data;
+}
+
+static void extract_edituv_data_loop_bmesh(const MeshRenderData *mr,
+                                           int l,
+                                           BMLoop *loop,
+                                           void *_data)
+{
+  MeshExtract_EditUVData_Data *data = (MeshExtract_EditUVData_Data *)_data;
+  EditLoopData *eldata = data->vbo_data + l;
+  memset(eldata, 0x0, sizeof(*eldata));
+  mesh_render_data_loop_flag(mr, loop, data->cd_ofs, eldata);
+  mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata);
+}
+
+static void extract_edituv_data_loop_mesh(
+    const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data)
+{
+  MeshExtract_EditUVData_Data *data = (MeshExtract_EditUVData_Data *)_data;
+  EditLoopData *eldata = data->vbo_data + l;
+  memset(eldata, 0x0, sizeof(*eldata));
+  BMFace *efa = bm_original_face_get(mr, p);
+  if (efa) {
+    BMEdge *eed = bm_original_edge_get(mr, mloop->e);
+    BMVert *eve = bm_original_vert_get(mr, mloop->v);
+    if (eed && eve) {
+      /* Loop on an edge endpoint. */
+      BMLoop *loop = BM_face_edge_share_loop(efa, eed);
+      mesh_render_data_loop_flag(mr, loop, data->cd_ofs, eldata);
+      mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata);
+    }
+    else {
+      if (eed == NULL) {
+        /* Find if the loop's vert is not part of an edit edge.
+         * For this, we check if the previous loop was on an edge. */
+        int loopend = mpoly->loopstart + mpoly->totloop - 1;
+        int l_prev = (l == mpoly->loopstart) ? loopend : (l - 1);
+        const MLoop *mloop_prev = &mr->mloop[l_prev];
+        eed = bm_original_edge_get(mr, mloop_prev->e);
+      }
+      if (eed) {
+        /* Mapped points on an edge between two edit verts. */
+        BMLoop *loop = BM_face_edge_share_loop(efa, eed);
+        mesh_render_data_loop_edge_flag(mr, loop, data->cd_ofs, eldata);
+      }
+    }
+  }
+}
+
+static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
+                                       void *UNUSED(buf),
+                                       void *data)
+{
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_edituv_data = {extract_edituv_data_init,
+                                         NULL,
+                                         NULL,
+                                         extract_edituv_data_loop_bmesh,
+                                         extract_edituv_data_loop_mesh,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         extract_edituv_data_finish,
+                                         0,
+                                         true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV area stretch
+ * \{ */
+
+static void *extract_stretch_area_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "stretch", GPU_COMP_U16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+  }
+
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+  return NULL;
+}
+
+BLI_INLINE float area_ratio_get(float area, float uvarea)
+{
+  if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) {
+    /* Tag inversion by using the sign. */
+    return (area > uvarea) ? (uvarea / area) : -(area / uvarea);
+  }
+  return 0.0f;
+}
+
+BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio)
+{
+  ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio;
+  return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
+}
+
+static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data))
+{
+  float totarea = 0, totuvarea = 0;
+  float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__);
+
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    CustomData *cd_ldata = &mr->bm->ldata;
+    int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
+
+    BMFace *efa;
+    BMIter f_iter;
+    int f;
+    BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
+      float area = BM_face_calc_area(efa);
+      float uvarea = BM_face_calc_area_uv(efa, uv_ofs);
+      totarea += area;
+      totuvarea += uvarea;
+      area_ratio[f] = area_ratio_get(area, uvarea);
+    }
+  }
+  else if (mr->extract_type == MR_EXTRACT_MAPPED) {
+    const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+    const MPoly *mpoly = mr->mpoly;
+    for (int p = 0; p < mr->poly_len; p++, mpoly++) {
+      float area = BKE_mesh_calc_poly_area(mpoly, &mr->mloop[mpoly->loopstart], mr->mvert);
+      float uvarea = BKE_mesh_calc_poly_uv_area(mpoly, uv_data);
+      totarea += area;
+      totuvarea += uvarea;
+      area_ratio[p] = area_ratio_get(area, uvarea);
+    }
+  }
+  else {
+    /* Should not happen. */
+    BLI_assert(0);
+  }
+
+  float tot_ratio, inv_tot_ratio;
+  if (totarea < FLT_EPSILON || totuvarea < FLT_EPSILON) {
+    tot_ratio = 0.0f;
+    inv_tot_ratio = 0.0f;
+  }
+  else {
+    tot_ratio = totarea / totuvarea;
+    inv_tot_ratio = totuvarea / totarea;
+  }
+
+  /* Convert in place to avoid an extra allocation */
+  uint16_t *poly_stretch = (uint16_t *)area_ratio;
+  for (int p = 0; p < mr->poly_len; p++) {
+    float stretch = area_ratio_to_stretch(area_ratio[p], tot_ratio, inv_tot_ratio);
+    poly_stretch[p] = (1.0f - stretch) * 65534.0f;
+  }
+
+  /* Copy face data for each loop. */
+  GPUVertBuf *vbo = buf;
+  uint16_t *loop_stretch = (uint16_t *)vbo->data;
+
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    BMFace *efa;
+    BMIter f_iter;
+    int f, l = 0;
+    BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
+      for (int i = 0; i < efa->len; i++, l++) {
+        loop_stretch[l] = poly_stretch[f];
+      }
+    }
+  }
+  else if (mr->extract_type == MR_EXTRACT_MAPPED) {
+    const MPoly *mpoly = mr->mpoly;
+    for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
+      for (int i = 0; i < mpoly->totloop; i++, l++) {
+        loop_stretch[l] = poly_stretch[p];
+      }
+    }
+  }
+  else {
+    /* Should not happen. */
+    BLI_assert(0);
+  }
+
+  MEM_freeN(area_ratio);
+}
+
+const MeshExtract extract_stretch_area = {extract_stretch_area_init,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          mesh_stretch_area_finish,
+                                          0,
+                                          false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV angle stretch
+ * \{ */
+
+typedef struct UVStretchAngle {
+  int16_t angle;
+  int16_t uv_angles[2];
+} UVStretchAngle;
+
+typedef struct MeshExtract_StretchAngle_Data {
+  UVStretchAngle *vbo_data;
+  MLoopUV *luv;
+  float auv[2][2], last_auv[2];
+  float av[2][3], last_av[3];
+  int cd_ofs;
+} MeshExtract_StretchAngle_Data;
+
+static void compute_normalize_edge_vectors(float auv[2][2],
+                                           float av[2][3],
+                                           const float uv[2],
+                                           const float uv_prev[2],
+                                           const float co[2],
+                                           const float co_prev[2])
+{
+  /* Move previous edge. */
+  copy_v2_v2(auv[0], auv[1]);
+  copy_v3_v3(av[0], av[1]);
+  /* 2d edge */
+  sub_v2_v2v2(auv[1], uv_prev, uv);
+  normalize_v2(auv[1]);
+  /* 3d edge */
+  sub_v3_v3v3(av[1], co_prev, co);
+  normalize_v3(av[1]);
+}
+
+static short v2_to_short_angle(float v[2])
+{
+  return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX;
+}
+
+static void edituv_get_stretch_angle(float auv[2][2], float av[2][3], UVStretchAngle *r_stretch)
+{
+  /* Send uvs to the shader and let it compute the aspect corrected angle. */
+  r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]);
+  r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]);
+  /* Compute 3D angle here. */
+  r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX;
+
+#if 0 /* here for reference, this is done in shader now. */
+  float uvang = angle_normalized_v2v2(auv0, auv1);
+  float ang = angle_normalized_v3v3(av0, av1);
+  float stretch = fabsf(uvang - ang) / (float)M_PI;
+  return 1.0f - pow2f(1.0f - stretch);
+#endif
+}
+
+static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    /* WARNING Adjust UVStretchAngle struct accordingly. */
+    GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+    GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
+  }
+
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+  MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__);
+  data->vbo_data = (UVStretchAngle *)vbo->data;
+
+  /* Special iter nneded to save about half of the computing cost. */
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+  }
+  else if (mr->extract_type == MR_EXTRACT_MAPPED) {
+    data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+  }
+  else {
+    BLI_assert(0);
+  }
+  return data;
+}
+
+static void extract_stretch_angle_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                             int l,
+                                             BMLoop *loop,
+                                             void *_data)
+{
+  MeshExtract_StretchAngle_Data *data = (MeshExtract_StretchAngle_Data *)_data;
+  float(*auv)[2] = data->auv, *last_auv = data->last_auv;
+  float(*av)[3] = data->av, *last_av = data->last_av;
+  const MLoopUV *luv, *luv_next;
+  BMLoop *l_next = loop->next;
+  BMFace *efa = loop->f;
+  if (loop == efa->l_first) {
+    /* First loop in face. */
+    BMLoop *l_tmp = loop->prev;
+    BMLoop *l_next_tmp = loop;
+    luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs);
+    luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs);
+    compute_normalize_edge_vectors(
+        auv, av, luv->uv, luv_next->uv, l_tmp->v->co, l_next_tmp->v->co);
+    /* Save last edge. */
+    copy_v2_v2(last_auv, auv[1]);
+    copy_v3_v3(last_av, av[1]);
+  }
+  if (l_next == efa->l_first) {
+    /* Move previous edge. */
+    copy_v2_v2(auv[0], auv[1]);
+    copy_v3_v3(av[0], av[1]);
+    /* Copy already calculated last edge. */
+    copy_v2_v2(auv[1], last_auv);
+    copy_v3_v3(av[1], last_av);
+  }
+  else {
+    luv = BM_ELEM_CD_GET_VOID_P(loop, data->cd_ofs);
+    luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs);
+    compute_normalize_edge_vectors(auv, av, luv->uv, luv_next->uv, loop->v->co, l_next->v->co);
+  }
+  edituv_get_stretch_angle(auv, av, data->vbo_data + l);
+}
+
+static void extract_stretch_angle_loop_mesh(const MeshRenderData *mr,
+                                            int l,
+                                            const MLoop *UNUSED(mloop),
+                                            int UNUSED(p),
+                                            const MPoly *mpoly,
+                                            void *_data)
+{
+  MeshExtract_StretchAngle_Data *data = (MeshExtract_StretchAngle_Data *)_data;
+  float(*auv)[2] = data->auv, *last_auv = data->last_auv;
+  float(*av)[3] = data->av, *last_av = data->last_av;
+  int l_next = l + 1, loopend = mpoly->loopstart + mpoly->totloop;
+  const MVert *v, *v_next;
+  if (l == mpoly->loopstart) {
+    /* First loop in face. */
+    int l_tmp = loopend - 1;
+    int l_next_tmp = mpoly->loopstart;
+    v = &mr->mvert[mr->mloop[l_tmp].v];
+    v_next = &mr->mvert[mr->mloop[l_next_tmp].v];
+    compute_normalize_edge_vectors(
+        auv, av, data->luv[l_tmp].uv, data->luv[l_next_tmp].uv, v->co, v_next->co);
+    /* Save last edge. */
+    copy_v2_v2(last_auv, auv[1]);
+    copy_v3_v3(last_av, av[1]);
+  }
+  if (l_next == loopend) {
+    l_next = mpoly->loopstart;
+    /* Move previous edge. */
+    copy_v2_v2(auv[0], auv[1]);
+    copy_v3_v3(av[0], av[1]);
+    /* Copy already calculated last edge. */
+    copy_v2_v2(auv[1], last_auv);
+    copy_v3_v3(av[1], last_av);
+  }
+  else {
+    v = &mr->mvert[mr->mloop[l].v];
+    v_next = &mr->mvert[mr->mloop[l_next].v];
+    compute_normalize_edge_vectors(
+        auv, av, data->luv[l].uv, data->luv[l_next].uv, v->co, v_next->co);
+  }
+  edituv_get_stretch_angle(auv, av, data->vbo_data + l);
+}
+
+static void extract_stretch_angle_finish(const MeshRenderData *UNUSED(mr),
+                                         void *UNUSED(buf),
+                                         void *data)
+{
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_stretch_angle = {extract_stretch_angle_init,
+                                           NULL,
+                                           NULL,
+                                           extract_stretch_angle_loop_bmesh,
+                                           extract_stretch_angle_loop_mesh,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           extract_stretch_angle_finish,
+                                           0,
+                                           true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV angle stretch
+ * \{ */
+
+static void *extract_mesh_analysis_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+  }
+
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+  return NULL;
+}
+
+static void axis_from_enum_v3(float v[3], const char axis)
+{
+  zero_v3(v);
+  if (axis < 3) {
+    v[axis] = 1.0f;
+  }
+  else {
+    v[axis - 3] = -1.0f;
+  }
+}
+
+BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange)
+{
+  if (fac < min) {
+    fac = 1.0f;
+  }
+  else if (fac > max) {
+    fac = -1.0f;
+  }
+  else {
+    fac = (fac - min) * minmax_irange;
+    fac = 1.0f - fac;
+    CLAMP(fac, 0.0f, 1.0f);
+  }
+  return fac;
+}
+
+static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang)
+{
+  const MeshStatVis *statvis = &mr->toolsettings->statvis;
+  const float min = statvis->overhang_min / (float)M_PI;
+  const float max = statvis->overhang_max / (float)M_PI;
+  const char axis = statvis->overhang_axis;
+  BMEditMesh *em = mr->edit_bmesh;
+  BMIter iter;
+  BMesh *bm = em->bm;
+  BMFace *f;
+  float dir[3];
+  const float minmax_irange = 1.0f / (max - min);
+
+  BLI_assert(min <= max);
+
+  axis_from_enum_v3(dir, axis);
+
+  if (em && LIKELY(em->ob)) {
+    /* now convert into global space */
+    mul_transposed_mat3_m4_v3(em->ob->obmat, dir);
+    normalize_v3(dir);
+  }
+
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    int l = 0;
+    BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+      float fac = angle_normalized_v3v3(f->no, dir) / (float)M_PI;
+      fac = overhang_remap(fac, min, max, minmax_irange);
+      for (int i = 0; i < f->len; i++, l++) {
+        r_overhang[l] = fac;
+      }
+    }
+  }
+  else {
+    const MPoly *mpoly = mr->mpoly;
+    for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
+      float fac = angle_normalized_v3v3(mr->poly_normals[p], dir) / (float)M_PI;
+      fac = overhang_remap(fac, min, max, minmax_irange);
+      for (int i = 0; i < mpoly->totloop; i++, l++) {
+        r_overhang[l] = fac;
+      }
+    }
+  }
+}
+
+/* so we can use jitter values for face interpolation */
+static void uv_from_jitter_v2(float uv[2])
+{
+  uv[0] += 0.5f;
+  uv[1] += 0.5f;
+  if (uv[0] + uv[1] > 1.0f) {
+    uv[0] = 1.0f - uv[0];
+    uv[1] = 1.0f - uv[1];
+  }
+
+  CLAMP(uv[0], 0.0f, 1.0f);
+  CLAMP(uv[1], 0.0f, 1.0f);
+}
+
+BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange)
+{
+  /* important not '<=' */
+  if (fac < max) {
+    fac = (fac - min) * minmax_irange;
+    fac = 1.0f - fac;
+    CLAMP(fac, 0.0f, 1.0f);
+  }
+  else {
+    fac = -1.0f;
+  }
+  return fac;
+}
+
+static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
+{
+  const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */
+  /* cheating to avoid another allocation */
+  float *face_dists = r_thickness + (mr->loop_len - mr->poly_len);
+  BMEditMesh *em = mr->edit_bmesh;
+  const float scale = 1.0f / mat4_to_scale(em->ob->obmat);
+  const MeshStatVis *statvis = &mr->toolsettings->statvis;
+  const float min = statvis->thickness_min * scale;
+  const float max = statvis->thickness_max * scale;
+  const float minmax_irange = 1.0f / (max - min);
+  const int samples = statvis->thickness_samples;
+  float jit_ofs[32][2];
+  BLI_assert(samples <= 32);
+  BLI_assert(min <= max);
+
+  copy_vn_fl(face_dists, mr->poly_len, max);
+
+  BLI_jitter_init(jit_ofs, samples);
+  for (int j = 0; j < samples; j++) {
+    uv_from_jitter_v2(jit_ofs[j]);
+  }
+
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    BMesh *bm = em->bm;
+    BM_mesh_elem_index_ensure(bm, BM_FACE);
+
+    struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
+    struct BMLoop *(*looptris)[3] = em->looptris;
+    for (int i = 0; i < mr->tri_len; i++) {
+      BMLoop **ltri = looptris[i];
+      const int index = BM_elem_index_get(ltri[0]->f);
+      const float *cos[3] = {ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co};
+      float ray_co[3];
+      float ray_no[3];
+
+      normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
+
+      for (int j = 0; j < samples; j++) {
+        float dist = face_dists[index];
+        interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
+        madd_v3_v3fl(ray_co, ray_no, eps_offset);
+
+        BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL);
+        if (f_hit && dist < face_dists[index]) {
+          float angle_fac = fabsf(dot_v3v3(ltri[0]->f->no, f_hit->no));
+          angle_fac = 1.0f - angle_fac;
+          angle_fac = angle_fac * angle_fac * angle_fac;
+          angle_fac = 1.0f - angle_fac;
+          dist /= angle_fac;
+          if (dist < face_dists[index]) {
+            face_dists[index] = dist;
+          }
+        }
+      }
+    }
+    BKE_bmbvh_free(bmtree);
+
+    BMIter iter;
+    BMFace *f;
+    int l = 0;
+    BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+      float fac = face_dists[BM_elem_index_get(f)];
+      fac = thickness_remap(fac, min, max, minmax_irange);
+      for (int i = 0; i < f->len; i++, l++) {
+        r_thickness[l] = fac;
+      }
+    }
+  }
+  else {
+    BVHTreeFromMesh treeData = {NULL};
+
+    BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
+    const MLoopTri *mlooptri = mr->mlooptri;
+    for (int i = 0; i < mr->tri_len; i++, mlooptri++) {
+      const int index = mlooptri->poly;
+      const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co,
+                             mr->mvert[mr->mloop[mlooptri->tri[1]].v].co,
+                             mr->mvert[mr->mloop[mlooptri->tri[2]].v].co};
+      float ray_co[3];
+      float ray_no[3];
+
+      normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
+
+      for (int j = 0; j < samples; j++) {
+        interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
+        madd_v3_v3fl(ray_co, ray_no, eps_offset);
+
+        BVHTreeRayHit hit;
+        hit.index = -1;
+        hit.dist = face_dists[index];
+        if ((BLI_bvhtree_ray_cast(
+                 tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) &&
+            hit.dist < face_dists[index]) {
+          float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no));
+          angle_fac = 1.0f - angle_fac;
+          angle_fac = angle_fac * angle_fac * angle_fac;
+          angle_fac = 1.0f - angle_fac;
+          hit.dist /= angle_fac;
+          if (hit.dist < face_dists[index]) {
+            face_dists[index] = hit.dist;
+          }
+        }
+      }
+    }
+
+    const MPoly *mpoly = mr->mpoly;
+    for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
+      float fac = face_dists[p];
+      fac = thickness_remap(fac, min, max, minmax_irange);
+      for (int i = 0; i < mpoly->totloop; i++, l++) {
+        r_thickness[l] = fac;
+      }
+    }
+  }
+}
+
+struct BVHTree_OverlapData {
+  const Mesh *me;
+  const MLoopTri *mlooptri;
+  float epsilon;
+};
+
+static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
+{
+  struct BVHTree_OverlapData *data = userdata;
+  const Mesh *me = data->me;
+
+  const MLoopTri *tri_a = &data->mlooptri[index_a];
+  const MLoopTri *tri_b = &data->mlooptri[index_b];
+
+  if (UNLIKELY(tri_a->poly == tri_b->poly)) {
+    return false;
+  }
+
+  const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co,
+                              me->mvert[me->mloop[tri_a->tri[1]].v].co,
+                              me->mvert[me->mloop[tri_a->tri[2]].v].co};
+  const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co,
+                              me->mvert[me->mloop[tri_b->tri[1]].v].co,
+                              me->mvert[me->mloop[tri_b->tri[2]].v].co};
+  float ix_pair[2][3];
+  int verts_shared = 0;
+
+  verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) +
+                  ELEM(tri_a_co[2], UNPACK3(tri_b_co)));
+
+  /* if 2 points are shared, bail out */
+  if (verts_shared >= 2) {
+    return false;
+  }
+
+  return (isect_tri_tri_epsilon_v3(
+              UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1], data->epsilon) &&
+          /* if we share a vertex, check the intersection isn't a 'point' */
+          ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
+}
+
+static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
+{
+  BMEditMesh *em = mr->edit_bmesh;
+
+  for (int l = 0; l < mr->loop_len; l++) {
+    r_intersect[l] = -1.0f;
+  }
+
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    uint overlap_len;
+    BMesh *bm = em->bm;
+
+    BM_mesh_elem_index_ensure(bm, BM_FACE);
+
+    struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
+    BVHTreeOverlap *overlap = BKE_bmbvh_overlap(bmtree, bmtree, &overlap_len);
+
+    if (overlap) {
+      for (int i = 0; i < overlap_len; i++) {
+        BMFace *f_hit_pair[2] = {
+            em->looptris[overlap[i].indexA][0]->f,
+            em->looptris[overlap[i].indexB][0]->f,
+        };
+        for (int j = 0; j < 2; j++) {
+          BMFace *f_hit = f_hit_pair[j];
+          BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit);
+          int l = BM_elem_index_get(l_first);
+          for (int k = 0; k < f_hit->len; k++, l++) {
+            r_intersect[l] = 1.0f;
+          }
+        }
+      }
+      MEM_freeN(overlap);
+    }
+
+    BKE_bmbvh_free(bmtree);
+  }
+  else {
+    uint overlap_len;
+    BVHTreeFromMesh treeData = {NULL};
+
+    BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
+
+    struct BVHTree_OverlapData data = {
+        .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)};
+
+    BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data);
+    if (overlap) {
+      for (int i = 0; i < overlap_len; i++) {
+        const MPoly *f_hit_pair[2] = {
+            &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly],
+            &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly],
+        };
+        for (int j = 0; j < 2; j++) {
+          const MPoly *f_hit = f_hit_pair[j];
+          int l = f_hit->loopstart;
+          for (int k = 0; k < f_hit->totloop; k++, l++) {
+            r_intersect[l] = 1.0f;
+          }
+        }
+      }
+      MEM_freeN(overlap);
+    }
+  }
+}
+
+BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange)
+{
+  if (fac >= min) {
+    fac = (fac - min) * minmax_irange;
+    CLAMP(fac, 0.0f, 1.0f);
+  }
+  else {
+    /* fallback */
+    fac = -1.0f;
+  }
+  return fac;
+}
+
+static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
+{
+  BMEditMesh *em = mr->edit_bmesh;
+  const MeshStatVis *statvis = &mr->toolsettings->statvis;
+  const float min = statvis->distort_min;
+  const float max = statvis->distort_max;
+  const float minmax_irange = 1.0f / (max - min);
+
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    BMIter iter;
+    BMesh *bm = em->bm;
+    BMFace *f;
+
+    int l = 0;
+    BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+      float fac = -1.0f;
+
+      if (f->len > 3) {
+        BMLoop *l_iter, *l_first;
+
+        fac = 0.0f;
+        l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+        do {
+          float no_corner[3];
+          BM_loop_calc_face_normal_safe(l_iter, no_corner);
+          /* simple way to detect (what is most likely) concave */
+          if (dot_v3v3(f->no, no_corner) < 0.0f) {
+            negate_v3(no_corner);
+          }
+          fac = max_ff(fac, angle_normalized_v3v3(f->no, no_corner));
+        } while ((l_iter = l_iter->next) != l_first);
+        fac *= 2.0f;
+      }
+
+      fac = distort_remap(fac, min, max, minmax_irange);
+      for (int i = 0; i < f->len; i++, l++) {
+        r_distort[l] = fac;
+      }
+    }
+  }
+  else {
+    const MPoly *mpoly = mr->mpoly;
+    for (int p = 0, l = 0; p < mr->poly_len; p++, mpoly++) {
+      float fac = -1.0f;
+
+      if (mpoly->totloop > 3) {
+        float *f_no = mr->poly_normals[p];
+        fac = 0.0f;
+
+        for (int i = 1; i <= mpoly->totloop; i++) {
+          const MLoop *l_prev = &mr->mloop[mpoly->loopstart + (i - 1) % mpoly->totloop];
+          const MLoop *l_curr = &mr->mloop[mpoly->loopstart + (i + 0) % mpoly->totloop];
+          const MLoop *l_next = &mr->mloop[mpoly->loopstart + (i + 1) % mpoly->totloop];
+          float no_corner[3];
+          normal_tri_v3(no_corner,
+                        mr->mvert[l_prev->v].co,
+                        mr->mvert[l_curr->v].co,
+                        mr->mvert[l_next->v].co);
+          /* simple way to detect (what is most likely) concave */
+          if (dot_v3v3(f_no, no_corner) < 0.0f) {
+            negate_v3(no_corner);
+          }
+          fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner));
+        }
+        fac *= 2.0f;
+      }
+
+      fac = distort_remap(fac, min, max, minmax_irange);
+      for (int i = 0; i < mpoly->totloop; i++, l++) {
+        r_distort[l] = fac;
+      }
+    }
+  }
+}
+
+BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange)
+{
+  /* important not '>=' */
+  if (fac > min) {
+    fac = (fac - min) * minmax_irange;
+    CLAMP(fac, 0.0f, 1.0f);
+  }
+  else {
+    /* fallback */
+    fac = -1.0f;
+  }
+  return fac;
+}
+
+static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
+{
+  BMEditMesh *em = mr->edit_bmesh;
+  const MeshStatVis *statvis = &mr->toolsettings->statvis;
+  const float min = statvis->sharp_min;
+  const float max = statvis->sharp_max;
+  const float minmax_irange = 1.0f / (max - min);
+
+  /* Can we avoid this extra allocation? */
+  float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__);
+  copy_vn_fl(vert_angles, mr->vert_len, -M_PI);
+
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    BMIter iter, l_iter;
+    BMesh *bm = em->bm;
+    BMFace *efa;
+    BMEdge *e;
+    BMLoop *loop;
+    /* first assign float values to verts */
+    BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+      float angle = BM_edge_calc_face_angle_signed(e);
+      float *col1 = &vert_angles[BM_elem_index_get(e->v1)];
+      float *col2 = &vert_angles[BM_elem_index_get(e->v2)];
+      *col1 = max_ff(*col1, angle);
+      *col2 = max_ff(*col2, angle);
+    }
+    /* Copy vert value to loops. */
+    BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+      BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
+        int l = BM_elem_index_get(loop);
+        int v = BM_elem_index_get(loop->v);
+        r_sharp[l] = sharp_remap(vert_angles[v], min, max, minmax_irange);
+      }
+    }
+  }
+  else {
+    /* first assign float values to verts */
+    const MPoly *mpoly = mr->mpoly;
+
+    EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len);
+
+    for (int p = 0; p < mr->poly_len; p++, mpoly++) {
+      for (int i = 0; i < mpoly->totloop; i++) {
+        const MLoop *l_curr = &mr->mloop[mpoly->loopstart + (i + 0) % mpoly->totloop];
+        const MLoop *l_next = &mr->mloop[mpoly->loopstart + (i + 1) % mpoly->totloop];
+        const MVert *v_curr = &mr->mvert[l_curr->v];
+        const MVert *v_next = &mr->mvert[l_next->v];
+        float angle;
+        void **pval;
+        bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval);
+        if (!value_is_init) {
+          *pval = mr->poly_normals[p];
+          /* non-manifold edge, yet... */
+          continue;
+        }
+        else if (*pval != NULL) {
+          const float *f1_no = mr->poly_normals[p];
+          const float *f2_no = *pval;
+          angle = angle_normalized_v3v3(f1_no, f2_no);
+          angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle;
+          /* Tag as manifold. */
+          *pval = NULL;
+        }
+        else {
+          /* non-manifold edge */
+          angle = DEG2RADF(90.0f);
+        }
+        float *col1 = &vert_angles[l_curr->v];
+        float *col2 = &vert_angles[l_next->v];
+        *col1 = max_ff(*col1, angle);
+        *col2 = max_ff(*col2, angle);
+      }
+    }
+    /* Remaining non manifold edges. */
+    EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh);
+    for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+      if (BLI_edgehashIterator_getValue(ehi) != NULL) {
+        uint v1, v2;
+        const float angle = DEG2RADF(90.0f);
+        BLI_edgehashIterator_getKey(ehi, &v1, &v2);
+        float *col1 = &vert_angles[v1];
+        float *col2 = &vert_angles[v2];
+        *col1 = max_ff(*col1, angle);
+        *col2 = max_ff(*col2, angle);
+      }
+    }
+    BLI_edgehashIterator_free(ehi);
+    BLI_edgehash_free(eh, NULL);
+
+    const MLoop *mloop = mr->mloop;
+    for (int l = 0; l < mr->loop_len; l++, mloop++) {
+      r_sharp[l] = sharp_remap(vert_angles[mloop->v], min, max, minmax_irange);
+    }
+  }
+
+  MEM_freeN(vert_angles);
+}
+
+static void extract_mesh_analysis_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data))
+{
+  BLI_assert(mr->edit_bmesh);
+
+  GPUVertBuf *vbo = buf;
+  float *l_weight = (float *)vbo->data;
+
+  switch (mr->toolsettings->statvis.type) {
+    case SCE_STATVIS_OVERHANG:
+      statvis_calc_overhang(mr, l_weight);
+      break;
+    case SCE_STATVIS_THICKNESS:
+      statvis_calc_thickness(mr, l_weight);
+      break;
+    case SCE_STATVIS_INTERSECT:
+      statvis_calc_intersect(mr, l_weight);
+      break;
+    case SCE_STATVIS_DISTORT:
+      statvis_calc_distort(mr, l_weight);
+      break;
+    case SCE_STATVIS_SHARP:
+      statvis_calc_sharp(mr, l_weight);
+      break;
+  }
+}
+
+const MeshExtract extract_mesh_analysis = {extract_mesh_analysis_init,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           extract_mesh_analysis_finish,
+                                           /* This is not needed for all vis type.
+                                            * Maybe split into different extract. */
+                                           MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
+                                           false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Facedots positions
+ * \{ */
+
+static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+  if (!mr->use_subsurf_fdots) {
+    /* Clear so we can accumulate on it. */
+    memset(vbo->data, 0x0, mr->poly_len * vbo->format.stride);
+  }
+  return vbo->data;
+}
+
+static void extract_fdots_pos_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                         int UNUSED(l),
+                                         BMLoop *loop,
+                                         void *data)
+{
+  float(*center)[3] = (float(*)[3])data;
+  float w = 1.0f / (float)loop->f->len;
+  madd_v3_v3fl(center[BM_elem_index_get(loop->f)], loop->v->co, w);
+}
+
+static void extract_fdots_pos_loop_mesh(const MeshRenderData *mr,
+                                        int UNUSED(l),
+                                        const MLoop *mloop,
+                                        int p,
+                                        const MPoly *mpoly,
+                                        void *data)
+{
+  float(*center)[3] = (float(*)[3])data;
+  const MVert *mvert = &mr->mvert[mloop->v];
+  if (mr->use_subsurf_fdots) {
+    if (mvert->flag & ME_VERT_FACEDOT) {
+      copy_v3_v3(center[p], mvert->co);
+    }
+  }
+  else {
+    float w = 1.0f / (float)mpoly->totloop;
+    madd_v3_v3fl(center[p], mvert->co, w);
+  }
+}
+
+const MeshExtract extract_fdots_pos = {extract_fdots_pos_init,
+                                       NULL,
+                                       NULL,
+                                       extract_fdots_pos_loop_bmesh,
+                                       extract_fdots_pos_loop_mesh,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       0,
+                                       true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Facedots Normal and edit flag
+ * \{ */
+
+static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+
+  return NULL;
+}
+
+static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data))
+{
+  GPUVertBuf *vbo = buf;
+  GPUPackedNormal *nor = (GPUPackedNormal *)vbo->data;
+  BMFace *efa;
+
+  /* Quicker than doing it for each loop. */
+  if (mr->extract_type == MR_EXTRACT_BMESH) {
+    for (int f = 0; f < mr->poly_len; f++) {
+      efa = BM_face_at_index(mr->bm, f);
+      nor[f] = GPU_normal_convert_i10_v3(efa->no);
+      /* Select / Active Flag. */
+      nor[f].w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == mr->efa_act) ? -1 : 1) : 0;
+    }
+  }
+  else {
+    for (int f = 0; f < mr->poly_len; f++) {
+      nor[f] = GPU_normal_convert_i10_v3(mr->poly_normals[f]);
+      if ((efa = bm_original_face_get(mr, f))) {
+        /* Select / Active Flag. */
+        nor[f].w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == mr->efa_act) ? -1 : 1) : 0;
+      }
+    }
+  }
+}
+
+const MeshExtract extract_fdots_nor = {extract_fdots_nor_init,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       extract_fdots_nor_finish,
+                                       MR_DATA_POLY_NOR,
+                                       false};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Facedots Normal and edit flag
+ * \{ */
+
+typedef struct MeshExtract_FdotUV_Data {
+  float (*vbo_data)[2];
+  MLoopUV *uv_data;
+} MeshExtract_FdotUV_Data;
+
+static void *extract_fdots_uv_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+    GPU_vertformat_alias_add(&format, "au");
+    GPU_vertformat_alias_add(&format, "pos");
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+
+  if (!mr->use_subsurf_fdots) {
+    /* Clear so we can accumulate on it. */
+    memset(vbo->data, 0x0, mr->poly_len * vbo->format.stride);
+  }
+
+  CustomData *cd_ldata = &mr->me->ldata;
+
+  MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__);
+  data->vbo_data = (float(*)[2])vbo->data;
+  data->uv_data = CustomData_get_layer(cd_ldata, CD_MLOOPUV);
+  return data;
+}
+
+static void extract_fdots_uv_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                        int l,
+                                        BMLoop *loop,
+                                        void *_data)
+{
+  MeshExtract_FdotUV_Data *data = (MeshExtract_FdotUV_Data *)_data;
+  float w = 1.0f / (float)loop->f->len;
+  madd_v2_v2fl(data->vbo_data[BM_elem_index_get(loop->f)], data->uv_data[l].uv, w);
+}
+
+static void extract_fdots_uv_loop_mesh(
+    const MeshRenderData *mr, int l, const MLoop *mloop, int p, const MPoly *mpoly, void *_data)
+{
+  MeshExtract_FdotUV_Data *data = (MeshExtract_FdotUV_Data *)_data;
+  if (mr->use_subsurf_fdots) {
+    const MVert *mvert = &mr->mvert[mloop->v];
+    if (mvert->flag & ME_VERT_FACEDOT) {
+      copy_v2_v2(data->vbo_data[p], data->uv_data[l].uv);
+    }
+  }
+  else {
+    float w = 1.0f / (float)mpoly->totloop;
+    madd_v2_v2fl(data->vbo_data[p], data->uv_data[l].uv, w);
+  }
+}
+
+static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr),
+                                    void *UNUSED(buf),
+                                    void *data)
+{
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_fdots_uv = {extract_fdots_uv_init,
+                                      NULL,
+                                      NULL,
+                                      extract_fdots_uv_loop_bmesh,
+                                      extract_fdots_uv_loop_mesh,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      extract_fdots_uv_finish,
+                                      0,
+                                      true};
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Facedots  Edit UV flag
+ * \{ */
+
+typedef struct MeshExtract_EditUVFdotData_Data {
+  EditLoopData *vbo_data;
+  int cd_ofs;
+} MeshExtract_EditUVFdotData_Data;
+
+static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT);
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+
+  MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__);
+  data->vbo_data = (EditLoopData *)vbo->data;
+  data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+  return data;
+}
+
+static void extract_fdots_edituv_data_loop_bmesh(const MeshRenderData *mr,
+                                                 int UNUSED(l),
+                                                 BMLoop *loop,
+                                                 void *_data)
+{
+  MeshExtract_EditUVFdotData_Data *data = (MeshExtract_EditUVFdotData_Data *)_data;
+  EditLoopData *eldata = data->vbo_data + BM_elem_index_get(loop->f);
+  memset(eldata, 0x0, sizeof(*eldata));
+  mesh_render_data_face_flag(mr, loop->f, data->cd_ofs, eldata);
+}
+
+static void extract_fdots_edituv_data_loop_mesh(const MeshRenderData *mr,
+                                                int UNUSED(l),
+                                                const MLoop *UNUSED(mloop),
+                                                int p,
+                                                const MPoly *UNUSED(mpoly),
+                                                void *_data)
+{
+  MeshExtract_EditUVFdotData_Data *data = (MeshExtract_EditUVFdotData_Data *)_data;
+  EditLoopData *eldata = data->vbo_data + p;
+  memset(eldata, 0x0, sizeof(*eldata));
+  BMFace *efa = bm_original_face_get(mr, p);
+  if (efa) {
+    mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata);
+  }
+}
+
+static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr),
+                                             void *UNUSED(buf),
+                                             void *data)
+{
+  MEM_freeN(data);
+}
+
+const MeshExtract extract_fdots_edituv_data = {extract_fdots_edituv_data_init,
+                                               NULL,
+                                               NULL,
+                                               extract_fdots_edituv_data_loop_bmesh,
+                                               extract_fdots_edituv_data_loop_mesh,
+                                               NULL,
+                                               NULL,
+                                               NULL,
+                                               NULL,
+                                               extract_fdots_edituv_data_finish,
+                                               0,
+                                               true};
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Selection Index
+ * \{ */
+
+static void *extract_select_idx_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    /* TODO rename "color" to something more descriptive. */
+    GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+  return vbo->data;
+}
+
+/* TODO Use glVertexID to get loop index and use the data structure on the CPU to retreive the
+ * select element associated with this loop ID. This would remove the need for this separate index
+ * VBOs. We could upload the p/e/v_origindex as a buffer texture and sample it inside the shader to
+ * output original index. */
+
+static void extract_poly_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                        int l,
+                                        BMLoop *loop,
+                                        void *data)
+{
+  ((uint32_t *)data)[l] = BM_elem_index_get(loop->f);
+}
+
+static void extract_edge_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                        int l,
+                                        BMLoop *loop,
+                                        void *data)
+{
+  ((uint32_t *)data)[l] = BM_elem_index_get(loop->e);
+}
+
+static void extract_vert_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                        int l,
+                                        BMLoop *loop,
+                                        void *data)
+{
+  ((uint32_t *)data)[l] = BM_elem_index_get(loop->v);
+}
+
+static void extract_edge_idx_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *data)
+{
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = BM_elem_index_get(eed);
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = BM_elem_index_get(eed);
+}
+
+static void extract_vert_idx_ledge_bmesh(const MeshRenderData *mr, int e, BMEdge *eed, void *data)
+{
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = BM_elem_index_get(eed->v1);
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = BM_elem_index_get(eed->v2);
+}
+
+static void extract_vert_idx_lvert_bmesh(const MeshRenderData *mr, int v, BMVert *eve, void *data)
+{
+  ((uint32_t *)data)[mr->loop_len + mr->edge_loose_len * 2 + v] = BM_elem_index_get(eve);
+}
+
+static void extract_poly_idx_loop_mesh(const MeshRenderData *mr,
+                                       int l,
+                                       const MLoop *UNUSED(mloop),
+                                       int p,
+                                       const MPoly *UNUSED(mpoly),
+                                       void *data)
+{
+  ((uint32_t *)data)[l] = (mr->p_origindex) ? mr->p_origindex[p] : p;
+}
+
+static void extract_edge_idx_loop_mesh(const MeshRenderData *mr,
+                                       int l,
+                                       const MLoop *mloop,
+                                       int UNUSED(p),
+                                       const MPoly *UNUSED(mpoly),
+                                       void *data)
+{
+  ((uint32_t *)data)[l] = (mr->e_origindex) ? mr->e_origindex[mloop->e] : mloop->e;
+}
+
+static void extract_vert_idx_loop_mesh(const MeshRenderData *mr,
+                                       int l,
+                                       const MLoop *mloop,
+                                       int UNUSED(p),
+                                       const MPoly *UNUSED(mpoly),
+                                       void *data)
+{
+  ((uint32_t *)data)[l] = (mr->v_origindex) ? mr->v_origindex[mloop->v] : mloop->v;
+}
+
+static void extract_edge_idx_ledge_mesh(const MeshRenderData *mr,
+                                        int e,
+                                        const MEdge *UNUSED(medge),
+                                        void *data)
+{
+  int e_idx = mr->ledges[e];
+  int e_orig = (mr->e_origindex) ? mr->e_origindex[e_idx] : e_idx;
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = e_orig;
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = e_orig;
+}
+
+static void extract_vert_idx_ledge_mesh(const MeshRenderData *mr,
+                                        int e,
+                                        const MEdge *medge,
+                                        void *data)
+{
+  int v1_orig = (mr->v_origindex) ? mr->v_origindex[medge->v1] : medge->v1;
+  int v2_orig = (mr->v_origindex) ? mr->v_origindex[medge->v2] : medge->v2;
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 0] = v1_orig;
+  ((uint32_t *)data)[mr->loop_len + e * 2 + 1] = v2_orig;
+}
+
+static void extract_vert_idx_lvert_mesh(const MeshRenderData *mr,
+                                        int v,
+                                        const MVert *UNUSED(mvert),
+                                        void *data)
+{
+  int v_idx = mr->lverts[v];
+  int v_orig = (mr->v_origindex) ? mr->v_origindex[v_idx] : v_idx;
+  ((uint32_t *)data)[mr->loop_len + mr->edge_loose_len * 2 + v] = v_orig;
+}
+
+const MeshExtract extract_poly_idx = {extract_select_idx_init,
+                                      NULL,
+                                      NULL,
+                                      extract_poly_idx_loop_bmesh,
+                                      extract_poly_idx_loop_mesh,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      0,
+                                      true};
+
+const MeshExtract extract_edge_idx = {extract_select_idx_init,
+                                      NULL,
+                                      NULL,
+                                      extract_edge_idx_loop_bmesh,
+                                      extract_edge_idx_loop_mesh,
+                                      extract_edge_idx_ledge_bmesh,
+                                      extract_edge_idx_ledge_mesh,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      0,
+                                      true};
+
+const MeshExtract extract_vert_idx = {extract_select_idx_init,
+                                      NULL,
+                                      NULL,
+                                      extract_vert_idx_loop_bmesh,
+                                      extract_vert_idx_loop_mesh,
+                                      extract_vert_idx_ledge_bmesh,
+                                      extract_vert_idx_ledge_mesh,
+                                      extract_vert_idx_lvert_bmesh,
+                                      extract_vert_idx_lvert_mesh,
+                                      NULL,
+                                      0,
+                                      true};
+
+static void *extract_select_fdot_idx_init(const MeshRenderData *mr, void *buf)
+{
+  static GPUVertFormat format = {0};
+  if (format.attr_len == 0) {
+    /* TODO rename "color" to something more descriptive. */
+    GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
+  }
+  GPUVertBuf *vbo = buf;
+  GPU_vertbuf_init_with_format(vbo, &format);
+  GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+  return vbo->data;
+}
+
+static void extract_fdot_idx_loop_bmesh(const MeshRenderData *UNUSED(mr),
+                                        int UNUSED(l),
+                                        BMLoop *loop,
+                                        void *data)
+{
+  ((uint32_t *)data)[BM_elem_index_get(loop->f)] = BM_elem_index_get(loop->f);
+}
+
+static void extract_fdot_idx_loop_mesh(const MeshRenderData *mr,
+                                       int UNUSED(l),
+                                       const MLoop *UNUSED(mloop),
+                                       int p,
+                                       const MPoly *UNUSED(mpoly),
+                                       void *data)
+{
+  ((uint32_t *)data)[p] = (mr->p_origindex) ? mr->p_origindex[p] : p;
+}
+
+const MeshExtract extract_fdot_idx = {extract_select_fdot_idx_init,
+                                      NULL,
+                                      NULL,
+                                      extract_fdot_idx_loop_bmesh,
+                                      extract_fdot_idx_loop_mesh,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      0,
+                                      true};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loop
+ * \{ */
+
+typedef struct ExtractTaskData {
+  const MeshRenderData *mr;
+  const MeshExtract *extract;
+  eMRIterType iter_type;
+  int start, end;
+  /** Decremented each time a task is finished. */
+  int32_t *task_counter;
+  void *buf;
+  void *user_data;
+} ExtractTaskData;
+
+BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
+                                  const eMRIterType iter_type,
+                                  int start,
+                                  int end,
+                                  const MeshExtract *extract,
+                                  void *user_data)
+{
+  switch (mr->extract_type) {
+    case MR_EXTRACT_BMESH:
+      if (iter_type & MR_ITER_LOOPTRI) {
+        int t_end = min_ii(mr->tri_len, end);
+        for (int t = start; t < t_end; t++) {
+          BMLoop **elt = &mr->edit_bmesh->looptris[t][0];
+          extract->iter_looptri_bm(mr, t, elt, user_data);
+        }
+      }
+      if (iter_type & MR_ITER_LOOP) {
+        int l_end = min_ii(mr->poly_len, end);
+        for (int f = start; f < l_end; f++) {
+          BMFace *efa = BM_face_at_index(mr->bm, f);
+          BMLoop *loop;
+          BMIter l_iter;
+          BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
+            extract->iter_loop_bm(mr, BM_elem_index_get(loop), loop, user_data);
+          }
+        }
+      }
+      if (iter_type & MR_ITER_LEDGE) {
+        int le_end = min_ii(mr->edge_loose_len, end);
+        for (int e = start; e < le_end; e++) {
+          BMEdge *eed = BM_edge_at_index(mr->bm, mr->ledges[e]);
+          extract->iter_ledge_bm(mr, e, eed, user_data);
+        }
+      }
+      if (iter_type & MR_ITER_LVERT) {
+        int lv_end = min_ii(mr->vert_loose_len, end);
+        for (int v = start; v < lv_end; v++) {
+          BMVert *eve = BM_vert_at_index(mr->bm, mr->lverts[v]);
+          extract->iter_lvert_bm(mr, v, eve, user_data);
+        }
+      }
+      break;
+    case MR_EXTRACT_MAPPED:
+    case MR_EXTRACT_MESH:
+      if (iter_type & MR_ITER_LOOPTRI) {
+        int t_end = min_ii(mr->tri_len, end);
+        for (int t = start; t < t_end; t++) {
+          extract->iter_looptri(mr, t, &mr->mlooptri[t], user_data);
+        }
+      }
+      if (iter_type & MR_ITER_LOOP) {
+        int l_end = min_ii(mr->poly_len, end);
+        for (int p = start; p < l_end; p++) {
+          const MPoly *mpoly = &mr->mpoly[p];
+          int l = mpoly->loopstart;
+          for (int i = 0; i < mpoly->totloop; i++, l++) {
+            extract->iter_loop(mr, l, &mr->mloop[l], p, mpoly, user_data);
+          }
+        }
+      }
+      if (iter_type & MR_ITER_LEDGE) {
+        int le_end = min_ii(mr->edge_loose_len, end);
+        for (int e = start; e < le_end; e++) {
+          extract->iter_ledge(mr, e, &mr->medge[mr->ledges[e]], user_data);
+        }
+      }
+      if (iter_type & MR_ITER_LVERT) {
+        int lv_end = min_ii(mr->vert_loose_len, end);
+        for (int v = start; v < lv_end; v++) {
+          extract->iter_lvert(mr, v, &mr->mvert[mr->lverts[v]], user_data);
+        }
+      }
+      break;
+  }
+}
+
+static void extract_run(TaskPool *__restrict UNUSED(pool), void *taskdata, int UNUSED(threadid))
+{
+  ExtractTaskData *data = taskdata;
+  mesh_extract_iter(
+      data->mr, data->iter_type, data->start, data->end, data->extract, data->user_data);
+
+  /* If this is the last task, we do the finish function. */
+  int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
+  if (remainin_tasks == 0 && data->extract->finish != NULL) {
+    data->extract->finish(data->mr, data->buf, data->user_data);
+  }
+}
+
+static void extract_range_task_create(
+    TaskPool *task_pool, ExtractTaskData *taskdata, const eMRIterType type, int start, int length)
+{
+  taskdata = MEM_dupallocN(taskdata);
+  atomic_add_and_fetch_int32(taskdata->task_counter, 1);
+  taskdata->iter_type = type;
+  taskdata->start = start;
+  taskdata->end = start + length;
+  BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH);
+}
+
+static void extract_task_create(TaskPool *task_pool,
+                                const MeshRenderData *mr,
+                                const MeshExtract *extract,
+                                void *buf,
+                                int32_t *task_counter)
+{
+  /* Divide extraction of the VBO/IBO into sensible chunks of works. */
+  ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), "ExtractTaskData");
+  taskdata->mr = mr;
+  taskdata->extract = extract;
+  taskdata->buf = buf;
+  taskdata->user_data = extract->init(mr, buf);
+  taskdata->iter_type = mesh_extract_iter_type(extract);
+  taskdata->task_counter = task_counter;
+  taskdata->start = 0;
+  taskdata->end = INT_MAX;
+
+  /* Simple heuristic. */
+  const bool use_thread = (mr->loop_len + mr->loop_loose_len) > 8192;
+  if (use_thread && extract->use_threading) {
+    /* Divide task into sensible chunks. */
+    const int chunk_size = 8192;
+    if (taskdata->iter_type & MR_ITER_LOOPTRI) {
+      for (int i = 0; i < mr->tri_len; i += chunk_size) {
+        extract_range_task_create(task_pool, taskdata, MR_ITER_LOOPTRI, i, chunk_size);
+      }
+    }
+    if (taskdata->iter_type & MR_ITER_LOOP) {
+      for (int i = 0; i < mr->poly_len; i += chunk_size) {
+        extract_range_task_create(task_pool, taskdata, MR_ITER_LOOP, i, chunk_size);
+      }
+    }
+    if (taskdata->iter_type & MR_ITER_LEDGE) {
+      for (int i = 0; i < mr->edge_loose_len; i += chunk_size) {
+        extract_range_task_create(task_pool, taskdata, MR_ITER_LEDGE, i, chunk_size);
+      }
+    }
+    if (taskdata->iter_type & MR_ITER_LVERT) {
+      for (int i = 0; i < mr->vert_loose_len; i += chunk_size) {
+        extract_range_task_create(task_pool, taskdata, MR_ITER_LVERT, i, chunk_size);
+      }
+    }
+    MEM_freeN(taskdata);
+  }
+  else if (use_thread) {
+    /* One task for the whole VBO. */
+    (*task_counter)++;
+    BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH);
+  }
+  else {
+    /* Single threaded extraction. */
+    (*task_counter)++;
+    extract_run(NULL, taskdata, -1);
+    MEM_freeN(taskdata);
+  }
+}
+
+void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
+                                        MeshBufferCache mbc,
+                                        Mesh *me,
+                                        const bool do_final,
+                                        const bool do_uvedit,
+                                        const bool use_subsurf_fdots,
+                                        const DRW_MeshCDMask *cd_layer_used,
+                                        const ToolSettings *ts,
+                                        const bool use_hide)
+{
+  eMRIterType iter_flag = 0;
+  eMRDataType data_flag = 0;
+
+#define TEST_ASSIGN(type, type_lowercase, name) \
+  do { \
+    if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \
+      iter_flag |= mesh_extract_iter_type(&extract_##name); \
+      data_flag |= extract_##name.data_flag; \
+    } \
+  } while (0)
+
+  TEST_ASSIGN(VBO, vbo, pos_nor);
+  TEST_ASSIGN(VBO, vbo, lnor);
+  TEST_ASSIGN(VBO, vbo, uv_tan);
+  TEST_ASSIGN(VBO, vbo, vcol);
+  TEST_ASSIGN(VBO, vbo, orco);
+  TEST_ASSIGN(VBO, vbo, edge_fac);
+  TEST_ASSIGN(VBO, vbo, weights);
+  TEST_ASSIGN(VBO, vbo, edit_data);
+  TEST_ASSIGN(VBO, vbo, edituv_data);
+  TEST_ASSIGN(VBO, vbo, stretch_area);
+  TEST_ASSIGN(VBO, vbo, stretch_angle);
+  TEST_ASSIGN(VBO, vbo, mesh_analysis);
+  TEST_ASSIGN(VBO, vbo, fdots_pos);
+  TEST_ASSIGN(VBO, vbo, fdots_nor);
+  TEST_ASSIGN(VBO, vbo, fdots_uv);
+  TEST_ASSIGN(VBO, vbo, fdots_edituv_data);
+  TEST_ASSIGN(VBO, vbo, poly_idx);
+  TEST_ASSIGN(VBO, vbo, edge_idx);
+  TEST_ASSIGN(VBO, vbo, vert_idx);
+  TEST_ASSIGN(VBO, vbo, fdot_idx);
+
+  TEST_ASSIGN(IBO, ibo, tris);
+  TEST_ASSIGN(IBO, ibo, lines);
+  TEST_ASSIGN(IBO, ibo, points);
+  TEST_ASSIGN(IBO, ibo, fdots);
+  TEST_ASSIGN(IBO, ibo, lines_paint_mask);
+  TEST_ASSIGN(IBO, ibo, lines_adjacency);
+  TEST_ASSIGN(IBO, ibo, edituv_tris);
+  TEST_ASSIGN(IBO, ibo, edituv_lines);
+  TEST_ASSIGN(IBO, ibo, edituv_points);
+  TEST_ASSIGN(IBO, ibo, edituv_fdots);
+
+#undef TEST_ASSIGN
+
+#ifdef DEBUG_TIME
+  double rdata_start = PIL_check_seconds_timer();
+#endif
+
+  MeshRenderData *mr = mesh_render_data_create(
+      me, do_final, do_uvedit, iter_flag, data_flag, cd_layer_used, ts);
+  mr->cache = cache; /* HACK */
+  mr->use_hide = use_hide;
+  mr->use_subsurf_fdots = use_subsurf_fdots;
+  mr->use_final_mesh = do_final;
+
+#ifdef DEBUG_TIME
+  double rdata_end = PIL_check_seconds_timer();
+#endif
+
+  TaskScheduler *task_scheduler;
+  TaskPool *task_pool;
+
+  task_scheduler = BLI_task_scheduler_get();
+  task_pool = BLI_task_pool_create(task_scheduler, NULL);
+
+  size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t);
+  int32_t *task_counters = MEM_callocN(counters_size, __func__);
+  int counter_used = 0;
+
+#define EXTRACT(buf, name) \
+  if (mbc.buf.name) { \
+    extract_task_create( \
+        task_pool, mr, &extract_##name, mbc.buf.name, &task_counters[counter_used++]); \
+  }
+
+  EXTRACT(vbo, pos_nor);
+  EXTRACT(vbo, lnor);
+  EXTRACT(vbo, uv_tan);
+  EXTRACT(vbo, vcol);
+  EXTRACT(vbo, orco);
+  EXTRACT(vbo, edge_fac);
+  EXTRACT(vbo, weights);
+  EXTRACT(vbo, edit_data);
+  EXTRACT(vbo, edituv_data);
+  EXTRACT(vbo, stretch_area);
+  EXTRACT(vbo, stretch_angle);
+  EXTRACT(vbo, mesh_analysis);
+  EXTRACT(vbo, fdots_pos);
+  EXTRACT(vbo, fdots_nor);
+  EXTRACT(vbo, fdots_uv);
+  EXTRACT(vbo, fdots_edituv_data);
+  EXTRACT(vbo, poly_idx);
+  EXTRACT(vbo, edge_idx);
+  EXTRACT(vbo, vert_idx);
+  EXTRACT(vbo, fdot_idx);
+
+  EXTRACT(ibo, tris);
+  EXTRACT(ibo, lines);
+  EXTRACT(ibo, points);
+  EXTRACT(ibo, fdots);
+  EXTRACT(ibo, lines_paint_mask);
+  EXTRACT(ibo, lines_adjacency);
+  EXTRACT(ibo, edituv_tris);
+  EXTRACT(ibo, edituv_lines);
+  EXTRACT(ibo, edituv_points);
+  EXTRACT(ibo, edituv_fdots);
+
+#undef EXTRACT
+
+  /* TODO(fclem) Ideally, we should have one global pool for all
+   * objects and wait for finish only before drawing when buffers
+   * need to be ready. */
+  BLI_task_pool_work_and_wait(task_pool);
+  BLI_task_pool_free(task_pool);
+
+  MEM_freeN(task_counters);
+
+  mesh_render_data_free(mr);
+
+#ifdef DEBUG_TIME
+  double end = PIL_check_seconds_timer();
+
+  static double avg = 0;
+  static double avg_fps = 0;
+  static double avg_rdata = 0;
+  static double end_prev = 0;
+
+  if (end_prev == 0) {
+    end_prev = end;
+  }
+
+  avg = avg * 0.95 + (end - rdata_end) * 0.05;
+  avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05;
+  avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05;
+
+  printf(
+      "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000);
+
+  end_prev = end;
+#endif
+}
+
+/** \} */
index 4dc58972ce6562962518717e52307ddb08c16e2f..d392db639386177c18791ec5406eecd1856bc1cd 100644 (file)
@@ -119,7 +119,7 @@ struct GPUBatch *DRW_lattice_batch_cache_get_edit_verts(struct Lattice *lt);
 /* Mesh */
 void DRW_mesh_batch_cache_create_requested(struct Object *ob,
                                            struct Mesh *me,
-                                           const struct ToolSettings *ts,
+                                           const struct Scene *scene,
                                            const bool is_paint_mode,
                                            const bool use_hide);
 
@@ -143,6 +143,7 @@ struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_edit_vertices(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_edit_edges(struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_edit_vnors(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_edit_lnors(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_edit_facedots(struct Mesh *me);
 /* edit-mesh selection */
index ba58dc3d9deaa9462fa2abed5d53745509b22070..f498771b596048a78e7d0bfdaefa76f8116f0121 100644 (file)
 #include "ED_uvedit.h"
 
 #include "draw_cache_inline.h"
+#include "draw_cache_extract.h"
 
 #include "draw_cache_impl.h" /* own include */
 
 static void mesh_batch_cache_clear(Mesh *me);
 
-/* Vertex Group Selection and display options */
-typedef struct DRW_MeshWeightState {
-  int defgroup_active;
-  int defgroup_len;
-
-  short flags;
-  char alert_mode;
-
-  /* Set of all selected bones for Multipaint. */
-  bool *defgroup_sel; /* [defgroup_len] */
-  int defgroup_sel_count;
-} DRW_MeshWeightState;
-
-typedef struct DRW_MeshCDMask {
-  uint32_t uv : 8;
-  uint32_t tan : 8;
-  uint32_t vcol : 8;
-  uint32_t orco : 1;
-  uint32_t tan_orco : 1;
-} DRW_MeshCDMask;
-
-/* DRW_MeshWeightState.flags */
-enum {
-  DRW_MESH_WEIGHT_STATE_MULTIPAINT = (1 << 0),
-  DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE = (1 << 1),
-};
-
-/* ---------------------------------------------------------------------- */
-/** \name BMesh Inline Wrappers
- * \{ */
-
-/**
- * Wrapper for #BM_vert_find_first_loop_visible
- * since most of the time this can be accessed directly without a function call.
- */
-BLI_INLINE BMLoop *bm_vert_find_first_loop_visible_inline(BMVert *v)
-{
-  if (v->e) {
-    BMLoop *l = v->e->l;
-    if (l && !BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
-      return l->v == v ? l : l->next;
-    }
-    return BM_vert_find_first_loop_visible(v);
-  }
-  return NULL;
-}
-
-BLI_INLINE BMLoop *bm_edge_find_first_loop_visible_inline(BMEdge *e)
-{
-  if (e->l) {
-    BMLoop *l = e->l;
-    if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
-      return l;
-    }
-    return BM_edge_find_first_loop_visible(e);
-  }
-  return NULL;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh/BMesh Interface (direct access to basic data).
- * \{ */
-
-static int mesh_render_verts_len_get(Mesh *me)
-{
-  return me->edit_mesh ? me->edit_mesh->bm->totvert : me->totvert;
-}
-
-static int mesh_render_edges_len_get(Mesh *me)
-{
-  return me->edit_mesh ? me->edit_mesh->bm->totedge : me->totedge;
-}
-
-static int mesh_render_looptri_len_get(Mesh *me)
-{
-  return me->edit_mesh ? me->edit_mesh->tottri : poly_to_tri_count(me->totpoly, me->totloop);
-}
-
-static int mesh_render_polys_len_get(Mesh *me)
-{
-  return me->edit_mesh ? me->edit_mesh->bm->totface : me->totpoly;
-}
-
-static int mesh_render_mat_len_get(Mesh *me)
-{
-  return MAX2(1, me->totcol);
-}
-
-static int UNUSED_FUNCTION(mesh_render_loops_len_get)(Mesh *me)
-{
-  return me->edit_mesh ? me->edit_mesh->bm->totloop : me->totloop;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
- * \{ */
-
-typedef struct EdgeAdjacentPolys {
-  int count;
-  int face_index[2];
-} EdgeAdjacentPolys;
-
-typedef struct EdgeAdjacentVerts {
-  int vert_index[2]; /* -1 if none */
-} EdgeAdjacentVerts;
-
-typedef struct EdgeDrawAttr {
-  uchar v_flag;
-  uchar e_flag;
-  uchar crease;
-  uchar bweight;
-} EdgeDrawAttr;
-
-typedef struct MeshRenderData {
-  int types;
-
-  int vert_len;
-  int edge_len;
-  int tri_len;
-  int loop_len;
-  int poly_len;
-  int mat_len;
-  int loose_vert_len;
-  int loose_edge_len;
-
-  /* Support for mapped mesh data. */
-  struct {
-    /* Must be set if we want to get mapped data. */
-    bool use;
-    bool supported;
-
-    Mesh *me_cage;
-
-    int vert_len;
-    int edge_len;
-    int tri_len;
-    int loop_len;
-    int poly_len;
-
-    int *loose_verts;
-    int loose_vert_len;
-
-    int *loose_edges;
-    int loose_edge_len;
-
-    /* origindex layers */
-    int *v_origindex;
-    int *e_origindex;
-    int *l_origindex;
-    int *p_origindex;
-  } mapped;
-
-  BMEditMesh *edit_bmesh;
-  struct EditMeshData *edit_data;
-  const ToolSettings *toolsettings;
-
-  Mesh *me;
-
-  MVert *mvert;
-  const MEdge *medge;
-  const MLoop *mloop;
-  const MPoly *mpoly;
-  float (*orco)[3]; /* vertex coordinates normalized to bounding box */
-  bool is_orco_allocated;
-  MDeformVert *dvert;
-  MLoopUV *mloopuv;
-  MLoopCol *mloopcol;
-  float (*loop_normals)[3];
-
-  /* CustomData 'cd' cache for efficient access. */
-  struct {
-    struct {
-      MLoopUV **uv;
-      MLoopCol **vcol;
-      float (**tangent)[4];
-
-      int uv_len;
-      int uv_active;
-      int uv_render;
-      int uv_mask_active;
-
-      int vcol_len;
-      int vcol_active;
-      int vcol_render;
-
-      int tangent_len;
-      int tangent_active;
-      int tangent_render;
-
-      bool *auto_vcol;
-    } layers;
-
-    /* Custom-data offsets (only needed for BMesh access) */
-    struct {
-      int crease;
-      int bweight;
-      int *uv;
-      int *vcol;
-#ifdef WITH_FREESTYLE
-      int freestyle_edge;
-      int freestyle_face;
-#endif
-    } offset;
-
-    struct {
-      char (*auto_mix)[32];
-      char (*uv)[32];
-      char (*vcol)[32];
-      char (*tangent)[32];
-    } uuid;
-
-    /* for certain cases we need an output loop-data storage (bmesh tangents) */
-    struct {
-      CustomData ldata;
-      /* grr, special case variable (use in place of 'dm->tangent_mask') */
-      short tangent_mask;
-    } output;
-  } cd;
-
-  BMVert *eve_act;
-  BMEdge *eed_act;
-  BMFace *efa_act;
-  BMFace *efa_act_uv;
-
-  /* Data created on-demand (usually not for bmesh-based data). */
-  EdgeAdjacentPolys *edges_adjacent_polys;
-  MLoopTri *mlooptri;
-  int *loose_edges;
-  int *loose_verts;
-
-  float (*poly_normals)[3];
-  float *vert_weight;
-  char (*vert_color)[3];
-  GPUPackedNormal *poly_normals_pack;
-  GPUPackedNormal *vert_normals_pack;
-  bool *edge_select_bool;
-  bool *edge_visible_bool;
-} MeshRenderData;
-
-typedef enum eMRDataType {
-  MR_DATATYPE_VERT = 1 << 0,
-  MR_DATATYPE_EDGE = 1 << 1,
-  MR_DATATYPE_LOOPTRI = 1 << 2,
-  MR_DATATYPE_LOOP = 1 << 3,
-  MR_DATATYPE_POLY = 1 << 4,
-  MR_DATATYPE_OVERLAY = 1 << 5,
-  MR_DATATYPE_SHADING = 1 << 6,
-  MR_DATATYPE_DVERT = 1 << 7,
-  MR_DATATYPE_LOOPCOL = 1 << 8,
-  MR_DATATYPE_LOOPUV = 1 << 9,
-  MR_DATATYPE_LOOSE_VERT = 1 << 10,
-  MR_DATATYPE_LOOSE_EDGE = 1 << 11,
-  MR_DATATYPE_LOOP_NORMALS = 1 << 12,
-} eMRDataType;
-
-#define MR_DATATYPE_VERT_LOOP_POLY (MR_DATATYPE_VERT | MR_DATATYPE_POLY | MR_DATATYPE_LOOP)
-#define MR_DATATYPE_VERT_LOOP_TRI_POLY (MR_DATATYPE_VERT_LOOP_POLY | MR_DATATYPE_LOOPTRI)
-#define MR_DATATYPE_LOOSE_VERT_EGDE (MR_DATATYPE_LOOSE_VERT | MR_DATATYPE_LOOSE_EDGE)
-
-/**
- * These functions look like they would be slow but they will typically return true on the first
- * iteration. Only false when all attached elements are hidden.
- */
-static bool bm_vert_has_visible_edge(const BMVert *v)
-{
-  const BMEdge *e_iter, *e_first;
-
-  e_iter = e_first = v->e;
-  do {
-    if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) {
-      return true;
-    }
-  } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
-  return false;
-}
-
-static bool bm_edge_has_visible_face(const BMEdge *e)
-{
-  const BMLoop *l_iter, *l_first;
-  l_iter = l_first = e->l;
-  do {
-    if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) {
-      return true;
-    }
-  } while ((l_iter = l_iter->radial_next) != l_first);
-  return false;
-}
-
-BLI_INLINE bool bm_vert_is_loose_and_visible(const BMVert *v)
-{
-  return (!BM_elem_flag_test(v, BM_ELEM_HIDDEN) && (v->e == NULL || !bm_vert_has_visible_edge(v)));
-}
-
-BLI_INLINE bool bm_edge_is_loose_and_visible(const BMEdge *e)
-{
-  return (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && (e->l == NULL || !bm_edge_has_visible_face(e)));
-}
-
 /* Return true is all layers in _b_ are inside _a_. */
 BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b)
 {
@@ -523,43 +222,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
   return cd_used;
 }
 
-static void mesh_render_calc_normals_loop_and_poly(const Mesh *me,
-                                                   const float split_angle,
-                                                   MeshRenderData *rdata)
-{
-  BLI_assert((me->flag & ME_AUTOSMOOTH) != 0);
-
-  int totloop = me->totloop;
-  int totpoly = me->totpoly;
-  float(*loop_normals)[3] = MEM_mallocN(sizeof(*loop_normals) * totloop, __func__);
-  float(*poly_normals)[3] = MEM_mallocN(sizeof(*poly_normals) * totpoly, __func__);
-  short(*clnors)[2] = CustomData_get_layer(&me->ldata, CD_CUSTOMLOOPNORMAL);
-
-  BKE_mesh_calc_normals_poly(
-      me->mvert, NULL, me->totvert, me->mloop, me->mpoly, totloop, totpoly, poly_normals, false);
-
-  BKE_mesh_normals_loop_split(me->mvert,
-                              me->totvert,
-                              me->medge,
-                              me->totedge,
-                              me->mloop,
-                              loop_normals,
-                              totloop,
-                              me->mpoly,
-                              poly_normals,
-                              totpoly,
-                              true,
-                              split_angle,
-                              NULL,
-                              clnors,
-                              NULL);
-
-  rdata->loop_len = totloop;
-  rdata->poly_len = totpoly;
-  rdata->loop_normals = loop_normals;
-  rdata->poly_normals = poly_normals;
-}
-
 static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me,
                                                        DRW_MeshCDMask cd_used,
                                                        char **r_auto_layers_names,
@@ -617,3823 +279,346 @@ static void mesh_cd_extract_auto_layers_names_and_srgb(Mesh *me,
   *r_auto_layers_len = auto_is_srgb_ofs;
 }
 
-/**
- * TODO(campbell): 'gpumat_array' may include materials linked to the object.
- * While not default, object materials should be supported.
- * Although this only impacts the data that's generated, not the materials that display.
- */
-static MeshRenderData *mesh_render_data_create_ex(Mesh *me,
-                                                  const int types,
-                                                  const DRW_MeshCDMask *cd_used,
-                                                  const ToolSettings *ts)
-{
-  MeshRenderData *rdata = MEM_callocN(sizeof(*rdata), __func__);
-  rdata->types = types;
-  rdata->toolsettings = ts;
-  rdata->mat_len = mesh_render_mat_len_get(me);
-
-  CustomData_reset(&rdata->cd.output.ldata);
-
-  const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
-  const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
+/** \} */
 
-  if (me->edit_mesh) {
-    BMEditMesh *embm = me->edit_mesh;
-    BMesh *bm = embm->bm;
+/* ---------------------------------------------------------------------- */
+/** \name Vertex Group Selection
+ * \{ */
 
-    rdata->edit_bmesh = embm;
-    rdata->edit_data = me->runtime.edit_data;
+/** Reset the selection structure, deallocating heap memory as appropriate. */
+static void drw_mesh_weight_state_clear(struct DRW_MeshWeightState *wstate)
+{
+  MEM_SAFE_FREE(wstate->defgroup_sel);
 
-    if (embm->mesh_eval_cage && (embm->mesh_eval_cage->runtime.is_original == false)) {
-      Mesh *me_cage = embm->mesh_eval_cage;
+  memset(wstate, 0, sizeof(*wstate));
 
-      rdata->mapped.me_cage = me_cage;
-      if (types & MR_DATATYPE_VERT) {
-        rdata->mapped.vert_len = me_cage->totvert;
-      }
-      if (types & MR_DATATYPE_EDGE) {
-        rdata->mapped.edge_len = me_cage->totedge;
-      }
-      if (types & MR_DATATYPE_LOOP) {
-        rdata->mapped.loop_len = me_cage->totloop;
-      }
-      if (types & MR_DATATYPE_POLY) {
-        rdata->mapped.poly_len = me_cage->totpoly;
-      }
-      if (types & MR_DATATYPE_LOOPTRI) {
-        rdata->mapped.tri_len = poly_to_tri_count(me_cage->totpoly, me_cage->totloop);
-      }
-      if (types & MR_DATATYPE_LOOPUV) {
-        rdata->mloopuv = CustomData_get_layer(&me_cage->ldata, CD_MLOOPUV);
-      }
+  wstate->defgroup_active = -1;
+}
 
-      rdata->mapped.v_origindex = CustomData_get_layer(&me_cage->vdata, CD_ORIGINDEX);
-      rdata->mapped.e_origindex = CustomData_get_layer(&me_cage->edata, CD_ORIGINDEX);
-      rdata->mapped.l_origindex = CustomData_get_layer(&me_cage->ldata, CD_ORIGINDEX);
-      rdata->mapped.p_origindex = CustomData_get_layer(&me_cage->pdata, CD_ORIGINDEX);
-      rdata->mapped.supported = (rdata->mapped.v_origindex || rdata->mapped.e_origindex ||
-                                 rdata->mapped.p_origindex);
-    }
+/** Copy selection data from one structure to another, including heap memory. */
+static void drw_mesh_weight_state_copy(struct DRW_MeshWeightState *wstate_dst,
+                                       const struct DRW_MeshWeightState *wstate_src)
+{
+  MEM_SAFE_FREE(wstate_dst->defgroup_sel);
 
-    int bm_ensure_types = 0;
-    if (types & MR_DATATYPE_VERT) {
-      rdata->vert_len = bm->totvert;
-      bm_ensure_types |= BM_VERT;
-    }
-    if (types & MR_DATATYPE_EDGE) {
-      rdata->edge_len = bm->totedge;
-      bm_ensure_types |= BM_EDGE;
-    }
-    if (types & MR_DATATYPE_LOOPTRI) {
-      bm_ensure_types |= BM_LOOP;
-    }
-    if (types & MR_DATATYPE_LOOP) {
-      rdata->loop_len = bm->totloop;
-      bm_ensure_types |= BM_LOOP;
-    }
-    if (types & MR_DATATYPE_POLY) {
-      rdata->poly_len = bm->totface;
-      bm_ensure_types |= BM_FACE;
-    }
-    if (types & MR_DATATYPE_LOOP_NORMALS) {
-      BLI_assert(types & MR_DATATYPE_LOOP);
-      if (is_auto_smooth) {
-        rdata->loop_normals = MEM_mallocN(sizeof(*rdata->loop_normals) * bm->totloop, __func__);
-        int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
-        BM_loops_calc_normal_vcos(bm,
-                                  NULL,
-                                  NULL,
-                                  NULL,
-                                  true,
-                                  split_angle,
-                                  rdata->loop_normals,
-                                  NULL,
-                                  NULL,
-                                  cd_loop_clnors_offset,
-                                  false);
-      }
-    }
-    if (types & MR_DATATYPE_OVERLAY) {
-      rdata->efa_act_uv = EDBM_uv_active_face_get(embm, false, false);
-      rdata->efa_act = BM_mesh_active_face_get(bm, false, true);
-      rdata->eed_act = BM_mesh_active_edge_get(bm);
-      rdata->eve_act = BM_mesh_active_vert_get(bm);
-      rdata->cd.offset.crease = CustomData_get_offset(&bm->edata, CD_CREASE);
-      rdata->cd.offset.bweight = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
-
-#ifdef WITH_FREESTYLE
-      rdata->cd.offset.freestyle_edge = CustomData_get_offset(&bm->edata, CD_FREESTYLE_EDGE);
-      rdata->cd.offset.freestyle_face = CustomData_get_offset(&bm->pdata, CD_FREESTYLE_FACE);
-#endif
-    }
-    if (types & (MR_DATATYPE_DVERT)) {
-      bm_ensure_types |= BM_VERT;
-    }
-    if (rdata->edit_data != NULL) {
-      bm_ensure_types |= BM_VERT;
-    }
+  memcpy(wstate_dst, wstate_src, sizeof(*wstate_dst));
 
-    BM_mesh_elem_index_ensure(bm, bm_ensure_types);
-    BM_mesh_elem_table_ensure(bm, bm_ensure_types & ~BM_LOOP);
-
-    if (types & MR_DATATYPE_LOOPTRI) {
-      /* Edit mode ensures this is valid, no need to calculate. */
-      BLI_assert((bm->totloop == 0) || (embm->looptris != NULL));
-      int tottri = embm->tottri;
-      MLoopTri *mlooptri = MEM_mallocN(sizeof(*rdata->mlooptri) * embm->tottri, __func__);
-      for (int index = 0; index < tottri; index++) {
-        BMLoop **bmtri = embm->looptris[index];
-        MLoopTri *mtri = &mlooptri[index];
-        mtri->tri[0] = BM_elem_index_get(bmtri[0]);
-        mtri->tri[1] = BM_elem_index_get(bmtri[1]);
-        mtri->tri[2] = BM_elem_index_get(bmtri[2]);
-      }
-      rdata->mlooptri = mlooptri;
-      rdata->tri_len = tottri;
-    }
+  if (wstate_src->defgroup_sel) {
+    wstate_dst->defgroup_sel = MEM_dupallocN(wstate_src->defgroup_sel);
+  }
+}
 
-    if (types & MR_DATATYPE_LOOSE_VERT) {
-      BLI_assert(types & MR_DATATYPE_VERT);
-      rdata->loose_vert_len = 0;
+/** Compare two selection structures. */
+static bool drw_mesh_weight_state_compare(const struct DRW_MeshWeightState *a,
+                                          const struct DRW_MeshWeightState *b)
+{
+  return a->defgroup_active == b->defgroup_active && a->defgroup_len == b->defgroup_len &&
+         a->flags == b->flags && a->alert_mode == b->alert_mode &&
+         a->defgroup_sel_count == b->defgroup_sel_count &&
+         ((!a->defgroup_sel && !b->defgroup_sel) ||
+          (a->defgroup_sel && b->defgroup_sel &&
+           memcmp(a->defgroup_sel, b->defgroup_sel, a->defgroup_len * sizeof(bool)) == 0));
+}
 
-      {
-        int *lverts = MEM_mallocN(rdata->vert_len * sizeof(int), __func__);
-        BLI_assert((bm->elem_table_dirty & BM_VERT) == 0);
-        for (int i = 0; i < bm->totvert; i++) {
-          const BMVert *eve = BM_vert_at_index(bm, i);
-          if (bm_vert_is_loose_and_visible(eve)) {
-            lverts[rdata->loose_vert_len++] = i;
-          }
-        }
-        rdata->loose_verts = MEM_reallocN(lverts, rdata->loose_vert_len * sizeof(int));
-      }
+static void drw_mesh_weight_state_extract(Object *ob,
+                                          Mesh *me,
+                                          const ToolSettings *ts,
+                                          bool paint_mode,
+                                          struct DRW_MeshWeightState *wstate)
+{
+  /* Extract complete vertex weight group selection state and mode flags. */
+  memset(wstate, 0, sizeof(*wstate));
 
-      if (rdata->mapped.supported) {
-        Mesh *me_cage = embm->mesh_eval_cage;
-        rdata->mapped.loose_vert_len = 0;
-
-        if (rdata->loose_vert_len) {
-          int *lverts = MEM_mallocN(me_cage->totvert * sizeof(int), __func__);
-          const int *v_origindex = rdata->mapped.v_origindex;
-          for (int i = 0; i < me_cage->totvert; i++) {
-            const int v_orig = v_origindex[i];
-            if (v_orig != ORIGINDEX_NONE) {
-              BMVert *eve = BM_vert_at_index(bm, v_orig);
-              if (bm_vert_is_loose_and_visible(eve)) {
-                lverts[rdata->mapped.loose_vert_len++] = i;
-              }
-            }
-          }
-          rdata->mapped.loose_verts = MEM_reallocN(lverts,
-                                                   rdata->mapped.loose_vert_len * sizeof(int));
-        }
-      }
-    }
+  wstate->defgroup_active = ob->actdef - 1;
+  wstate->defgroup_len = BLI_listbase_count(&ob->defbase);
 
-    if (types & MR_DATATYPE_LOOSE_EDGE) {
-      BLI_assert(types & MR_DATATYPE_EDGE);
-      rdata->loose_edge_len = 0;
+  wstate->alert_mode = ts->weightuser;
 
-      {
-        int *ledges = MEM_mallocN(rdata->edge_len * sizeof(int), __func__);
-        BLI_assert((bm->elem_table_dirty & BM_EDGE) == 0);
-        for (int i = 0; i < bm->totedge; i++) {
-          const BMEdge *eed = BM_edge_at_index(bm, i);
-          if (bm_edge_is_loose_and_visible(eed)) {
-            ledges[rdata->loose_edge_len++] = i;
-          }
-        }
-        rdata->loose_edges = MEM_reallocN(ledges, rdata->loose_edge_len * sizeof(int));
-      }
+  if (paint_mode && ts->multipaint) {
+    /* Multipaint needs to know all selected bones, not just the active group.
+     * This is actually a relatively expensive operation, but caching would be difficult. */
+    wstate->defgroup_sel = BKE_object_defgroup_selected_get(
+        ob, wstate->defgroup_len, &wstate->defgroup_sel_count);
 
-      if (rdata->mapped.supported) {
-        Mesh *me_cage = embm->mesh_eval_cage;
-        rdata->mapped.loose_edge_len = 0;
-
-        if (rdata->loose_edge_len) {
-          int *ledges = MEM_mallocN(me_cage->totedge * sizeof(int), __func__);
-          const int *e_origindex = rdata->mapped.e_origindex;
-          for (int i = 0; i < me_cage->totedge; i++) {
-            const int e_orig = e_origindex[i];
-            if (e_orig != ORIGINDEX_NONE) {
-              BMEdge *eed = BM_edge_at_index(bm, e_orig);
-              if (bm_edge_is_loose_and_visible(eed)) {
-                ledges[rdata->mapped.loose_edge_len++] = i;
-              }
-            }
-          }
-          rdata->mapped.loose_edges = MEM_reallocN(ledges,
-                                                   rdata->mapped.loose_edge_len * sizeof(int));
-        }
-      }
-    }
-  }
-  else {
-    rdata->me = me;
+    if (wstate->defgroup_sel_count > 1) {
+      wstate->flags |= DRW_MESH_WEIGHT_STATE_MULTIPAINT |
+                       (ts->auto_normalize ? DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE : 0);
 
-    if (types & (MR_DATATYPE_VERT)) {
-      rdata->vert_len = me->totvert;
-      rdata->mvert = CustomData_get_layer(&me->vdata, CD_MVERT);
-    }
-    if (types & (MR_DATATYPE_EDGE)) {
-      rdata->edge_len = me->totedge;
-      rdata->medge = CustomData_get_layer(&me->edata, CD_MEDGE);
-    }
-    if (types & MR_DATATYPE_LOOPTRI) {
-      const int tri_len = rdata->tri_len = poly_to_tri_count(me->totpoly, me->totloop);
-      MLoopTri *mlooptri = MEM_mallocN(sizeof(*mlooptri) * tri_len, __func__);
-      BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mlooptri);
-      rdata->mlooptri = mlooptri;
-    }
-    if (types & MR_DATATYPE_LOOP) {
-      rdata->loop_len = me->totloop;
-      rdata->mloop = CustomData_get_layer(&me->ldata, CD_MLOOP);
-    }
-    if (types & MR_DATATYPE_LOOP_NORMALS) {
-      BLI_assert(types & MR_DATATYPE_LOOP);
-      if (is_auto_smooth) {
-        mesh_render_calc_normals_loop_and_poly(me, split_angle, rdata);
+      if (me->editflag & ME_EDIT_MIRROR_X) {
+        BKE_object_defgroup_mirror_selection(ob,
+                                             wstate->defgroup_len,
+                                             wstate->defgroup_sel,
+                                             wstate->defgroup_sel,
+                                             &wstate->defgroup_sel_count);
       }
     }
-    if (types & MR_DATATYPE_POLY) {
-      rdata->poly_len = me->totpoly;
-      rdata->mpoly = CustomData_get_layer(&me->pdata, CD_MPOLY);
-    }
-    if (types & MR_DATATYPE_DVERT) {
-      rdata->vert_len = me->totvert;
-      rdata->dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT);
-    }
-    if (types & MR_DATATYPE_LOOPCOL) {
-      rdata->loop_len = me->totloop;
-      rdata->mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL);
-    }
-    if (types & MR_DATATYPE_LOOPUV) {
-      rdata->loop_len = me->totloop;
-      rdata->mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
+    /* With only one selected bone Multipaint reverts to regular mode. */
+    else {
+      wstate->defgroup_sel_count = 0;
+      MEM_SAFE_FREE(wstate->defgroup_sel);
     }
   }
+}
 
-  if (types & MR_DATATYPE_SHADING) {
-    CustomData *cd_vdata, *cd_ldata;
+/** \} */
 
-    BLI_assert(cd_used != NULL);
+/* ---------------------------------------------------------------------- */
+/** \name Mesh GPUBatch Cache
+ * \{ */
 
-    if (me->edit_mesh) {
-      BMesh *bm = me->edit_mesh->bm;
-      cd_vdata = &bm->vdata;
-      cd_ldata = &bm->ldata;
-    }
-    else {
-      cd_vdata = &me->vdata;
-      cd_ldata = &me->ldata;
-    }
+BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache *cache, DRWBatchFlag new_flag)
+{
+  atomic_fetch_and_or_uint32((uint32_t *)(&cache->batch_requested), *(uint32_t *)&new_flag);
+}
 
-    rdata->cd.layers.uv_active = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
-    rdata->cd.layers.uv_render = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV);
-    rdata->cd.layers.uv_mask_active = CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV);
-    rdata->cd.layers.vcol_active = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL);
-    rdata->cd.layers.vcol_render = CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL);
-    rdata->cd.layers.tangent_active = rdata->cd.layers.uv_active;
-    rdata->cd.layers.tangent_render = rdata->cd.layers.uv_render;
-
-#define CD_VALIDATE_ACTIVE_LAYER(active_index, used) \
-  if ((active_index != -1) && (used & (1 << active_index)) == 0) { \
-    active_index = -1; \
-  } \
-  ((void)0)
-
-    CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.uv_active, cd_used->uv);
-    CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.uv_render, cd_used->uv);
-    CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.uv_mask_active, cd_used->uv);
-    CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.tangent_active, cd_used->tan);
-    CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.tangent_render, cd_used->tan);
-    CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.vcol_active, cd_used->vcol);
-    CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.vcol_render, cd_used->vcol);
-
-#undef CD_VALIDATE_ACTIVE_LAYER
-
-    rdata->is_orco_allocated = false;
-    if (cd_used->orco != 0) {
-      rdata->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
-      /* If orco is not available compute it ourselves */
-      if (!rdata->orco) {
-        rdata->is_orco_allocated = true;
-        if (me->edit_mesh) {
-          BMesh *bm = me->edit_mesh->bm;
-          rdata->orco = MEM_mallocN(sizeof(*rdata->orco) * rdata->vert_len, "orco mesh");
-          BLI_assert((bm->elem_table_dirty & BM_VERT) == 0);
-          for (int i = 0; i < bm->totvert; i++) {
-            copy_v3_v3(rdata->orco[i], BM_vert_at_index(bm, i)->co);
-          }
-          BKE_mesh_orco_verts_transform(me, rdata->orco, rdata->vert_len, 0);
-        }
-        else {
-          rdata->orco = MEM_mallocN(sizeof(*rdata->orco) * rdata->vert_len, "orco mesh");
-          MVert *mvert = rdata->mvert;
-          for (int a = 0; a < rdata->vert_len; a++, mvert++) {
-            copy_v3_v3(rdata->orco[a], mvert->co);
-          }
-          BKE_mesh_orco_verts_transform(me, rdata->orco, rdata->vert_len, 0);
-        }
-      }
-    }
-    else {
-      rdata->orco = NULL;
-    }
+/* GPUBatch cache management. */
 
-    /* don't access mesh directly, instead use vars taken from BMesh or Mesh */
-#define me DONT_USE_THIS
-#ifdef me /* quiet warning */
-#endif
-    struct {
-      uint uv_len;
-      uint vcol_len;
-    } cd_layers_src = {
-        .uv_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPUV),
-        .vcol_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPCOL),
-    };
-
-    rdata->cd.layers.uv_len = min_ii(cd_layers_src.uv_len, count_bits_i(cd_used->uv));
-    rdata->cd.layers.tangent_len = count_bits_i(cd_used->tan) + cd_used->tan_orco;
-    rdata->cd.layers.vcol_len = min_ii(cd_layers_src.vcol_len, count_bits_i(cd_used->vcol));
-
-    rdata->cd.layers.uv = MEM_mallocN(sizeof(*rdata->cd.layers.uv) * rdata->cd.layers.uv_len,
-                                      __func__);
-    rdata->cd.layers.vcol = MEM_mallocN(sizeof(*rdata->cd.layers.vcol) * rdata->cd.layers.vcol_len,
-                                        __func__);
-    rdata->cd.layers.tangent = MEM_mallocN(
-        sizeof(*rdata->cd.layers.tangent) * rdata->cd.layers.tangent_len, __func__);
-
-    rdata->cd.uuid.uv = MEM_mallocN(sizeof(*rdata->cd.uuid.uv) * rdata->cd.layers.uv_len,
-                                    __func__);
-    rdata->cd.uuid.vcol = MEM_mallocN(sizeof(*rdata->cd.uuid.vcol) * rdata->cd.layers.vcol_len,
-                                      __func__);
-    rdata->cd.uuid.tangent = MEM_mallocN(
-        sizeof(*rdata->cd.uuid.tangent) * rdata->cd.layers.tangent_len, __func__);
-
-    rdata->cd.offset.uv = MEM_mallocN(sizeof(*rdata->cd.offset.uv) * rdata->cd.layers.uv_len,
-                                      __func__);
-    rdata->cd.offset.vcol = MEM_mallocN(sizeof(*rdata->cd.offset.vcol) * rdata->cd.layers.vcol_len,
-                                        __func__);
-
-    /* Allocate max */
-    rdata->cd.layers.auto_vcol = MEM_callocN(
-        sizeof(*rdata->cd.layers.auto_vcol) * rdata->cd.layers.vcol_len, __func__);
-    rdata->cd.uuid.auto_mix = MEM_mallocN(
-        sizeof(*rdata->cd.uuid.auto_mix) * (rdata->cd.layers.vcol_len + rdata->cd.layers.uv_len),
-        __func__);
-
-    /* XXX FIXME XXX */
-    /* We use a hash to identify each data layer based on its name.
-     * Gawain then search for this name in the current shader and bind if it exists.
-     * NOTE : This is prone to hash collision.
-     * One solution to hash collision would be to format the cd layer name
-     * to a safe glsl var name, but without name clash.
-     * NOTE 2 : Replicate changes to code_generate_vertex_new() in gpu_codegen.c */
-    if (rdata->cd.layers.vcol_len != 0) {
-      int act_vcol = rdata->cd.layers.vcol_active;
-      int ren_vcol = rdata->cd.layers.vcol_render;
-      for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.vcol_len; i_src++, i_dst++) {
-        if ((cd_used->vcol & (1 << i_src)) == 0) {
-          /* This is a non-used VCol slot. Skip. */
-          i_dst--;
-          if (rdata->cd.layers.vcol_active >= i_src) {
-            act_vcol--;
-          }
-          if (rdata->cd.layers.vcol_render >= i_src) {
-            ren_vcol--;
-          }
-        }
-        else {
-          const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i_src);
-          uint hash = BLI_ghashutil_strhash_p(name);
-          BLI_snprintf(rdata->cd.uuid.vcol[i_dst], sizeof(*rdata->cd.uuid.vcol), "c%u", hash);
-          rdata->cd.layers.vcol[i_dst] = CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i_src);
-          if (rdata->edit_bmesh) {
-            rdata->cd.offset.vcol[i_dst] = CustomData_get_n_offset(
-                &rdata->edit_bmesh->bm->ldata, CD_MLOOPCOL, i_src);
-          }
+static bool mesh_batch_cache_valid(Mesh *me)
+{
+  MeshBatchCache *cache = me->runtime.batch_cache;
 
-          /* Gather number of auto layers. */
-          /* We only do vcols that are not overridden by uvs */
-          if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, name) == -1) {
-            BLI_snprintf(rdata->cd.uuid.auto_mix[rdata->cd.layers.uv_len + i_dst],
-                         sizeof(*rdata->cd.uuid.auto_mix),
-                         "a%u",
-                         hash);
-            rdata->cd.layers.auto_vcol[i_dst] = true;
-          }
-        }
-      }
-      /* Actual active Vcol slot inside vcol layers used for shading. */
-      if (rdata->cd.layers.vcol_active != -1) {
-        rdata->cd.layers.vcol_active = act_vcol;
-      }
-      if (rdata->cd.layers.vcol_render != -1) {
-        rdata->cd.layers.vcol_render = ren_vcol;
-      }
-    }
+  if (cache == NULL) {
+    return false;
+  }
 
-    /* Start Fresh */
-    CustomData_free_layers(cd_ldata, CD_TANGENT, rdata->loop_len);
-    CustomData_free_layers(cd_ldata, CD_MLOOPTANGENT, rdata->loop_len);
-
-    if (rdata->cd.layers.uv_len != 0) {
-      int ren_uv = rdata->cd.layers.uv_render;
-      int act_uv = rdata->cd.layers.uv_active;
-      for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) {
-        if ((cd_used->uv & (1 << i_src)) == 0) {
-          /* This is a non-used UV slot. Skip. */
-          i_dst--;
-          if (rdata->cd.layers.uv_render >= i_src) {
-            ren_uv--;
-          }
-          if (rdata->cd.layers.uv_active >= i_src) {
-            act_uv--;
-          }
-        }
-        else {
-          const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src);
-          uint hash = BLI_ghashutil_strhash_p(name);
-
-          BLI_snprintf(rdata->cd.uuid.uv[i_dst], sizeof(*rdata->cd.uuid.uv), "u%u", hash);
-          rdata->cd.layers.uv[i_dst] = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i_src);
-          if (rdata->edit_bmesh) {
-            rdata->cd.offset.uv[i_dst] = CustomData_get_n_offset(
-                &rdata->edit_bmesh->bm->ldata, CD_MLOOPUV, i_src);
-          }
-          BLI_snprintf(
-              rdata->cd.uuid.auto_mix[i_dst], sizeof(*rdata->cd.uuid.auto_mix), "a%u", hash);
-        }
-      }
-      /* Actual active / Render UV slot inside uv layers used for shading. */
-      if (rdata->cd.layers.uv_render != -1) {
-        rdata->cd.layers.uv_render = ren_uv;
-      }
-      if (rdata->cd.layers.uv_active != -1) {
-        rdata->cd.layers.uv_active = act_uv;
-      }
-    }
+  if (cache->is_editmode != (me->edit_mesh != NULL)) {
+    return false;
+  }
 
-    if (rdata->cd.layers.tangent_len != 0) {
+  if (cache->is_dirty) {
+    return false;
+  }
 
-      /* -------------------------------------------------------------------- */
-      /* Pre-calculate tangents into 'rdata->cd.output.ldata' */
+  if (cache->mat_len != mesh_render_mat_len_get(me)) {
+    return false;
+  }
 
-      BLI_assert(!CustomData_has_layer(&rdata->cd.output.ldata, CD_TANGENT));
+  return true;
+}
 
-      /* Tangent Names */
-      char tangent_names[MAX_MTFACE][MAX_NAME];
-      for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) {
-        if ((cd_used->tan & (1 << i_src)) == 0) {
-          i_dst--;
-        }
-        else {
-          BLI_strncpy(tangent_names[i_dst],
-                      CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src),
-                      MAX_NAME);
-        }
-      }
+static void mesh_batch_cache_init(Mesh *me)
+{
+  MeshBatchCache *cache = me->runtime.batch_cache;
 
-      /* If tangent from orco is requested, decrement tangent_len */
-      int actual_tangent_len = (cd_used->tan_orco != 0) ? rdata->cd.layers.tangent_len - 1 :
-                                                          rdata->cd.layers.tangent_len;
-      if (rdata->edit_bmesh) {
-        BMEditMesh *em = rdata->edit_bmesh;
-        BMesh *bm = em->bm;
-
-        if (is_auto_smooth && rdata->loop_normals == NULL) {
-          /* Should we store the previous array of `loop_normals` in somewhere? */
-          rdata->loop_len = bm->totloop;
-          rdata->loop_normals = MEM_mallocN(sizeof(*rdata->loop_normals) * rdata->loop_len,
-                                            __func__);
-          BM_loops_calc_normal_vcos(
-              bm, NULL, NULL, NULL, true, split_angle, rdata->loop_normals, NULL, NULL, -1, false);
-        }
+  if (!cache) {
+    cache = me->runtime.batch_cache = MEM_callocN(sizeof(*cache), __func__);
+  }
+  else {
+    memset(cache, 0, sizeof(*cache));
+  }
 
-        bool calc_active_tangent = false;
-
-        BKE_editmesh_loop_tangent_calc(em,
-                                       calc_active_tangent,
-                                       tangent_names,
-                                       actual_tangent_len,
-                                       rdata->poly_normals,
-                                       rdata->loop_normals,
-                                       rdata->orco,
-                                       &rdata->cd.output.ldata,
-                                       bm->totloop,
-                                       &rdata->cd.output.tangent_mask);
-      }
-      else {
-#undef me
+  cache->is_editmode = me->edit_mesh != NULL;
 
-        if (is_auto_smooth && rdata->loop_normals == NULL) {
-          /* Should we store the previous array of `loop_normals` in CustomData? */
-          mesh_render_calc_normals_loop_and_poly(me, split_angle, rdata);
-        }
+  if (cache->is_editmode == false) {
+    // cache->edge_len = mesh_render_edges_len_get(me);
+    // cache->tri_len = mesh_render_looptri_len_get(me);
+    // cache->poly_len = mesh_render_polys_len_get(me);
+    // cache->vert_len = mesh_render_verts_len_get(me);
+  }
 
-        bool calc_active_tangent = false;
-
-        BKE_mesh_calc_loop_tangent_ex(me->mvert,
-                                      me->mpoly,
-                                      me->totpoly,
-                                      me->mloop,
-                                      rdata->mlooptri,
-                                      rdata->tri_len,
-                                      cd_ldata,
-                                      calc_active_tangent,
-                                      tangent_names,
-                                      actual_tangent_len,
-                                      rdata->poly_normals,
-                                      rdata->loop_normals,
-                                      rdata->orco,
-                                      &rdata->cd.output.ldata,
-                                      me->totloop,
-                                      &rdata->cd.output.tangent_mask);
-
-        /* If we store tangents in the mesh, set temporary. */
-#if 0
-        CustomData_set_layer_flag(cd_ldata, CD_TANGENT, CD_FLAG_TEMPORARY);
-#endif
+  cache->mat_len = mesh_render_mat_len_get(me);
+  cache->surface_per_mat = MEM_callocN(sizeof(*cache->surface_per_mat) * cache->mat_len, __func__);
 
-#define me DONT_USE_THIS
-#ifdef me /* quiet warning */
-#endif
-      }
-
-      /* End tangent calculation */
-      /* -------------------------------------------------------------------- */
-
-      BLI_assert(CustomData_number_of_layers(&rdata->cd.output.ldata, CD_TANGENT) ==
-                 rdata->cd.layers.tangent_len);
-
-      int i_dst = 0;
-      int act_tan = rdata->cd.layers.tangent_active;
-      int ren_tan = rdata->cd.layers.tangent_render;
-      for (int i_src = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) {
-        if ((cd_used->tan & (1 << i_src)) == 0) {
-          i_dst--;
-          if (rdata->cd.layers.tangent_render >= i_src) {
-            ren_tan--;
-          }
-          if (rdata->cd.layers.tangent_active >= i_src) {
-            act_tan--;
-          }
-        }
-        else {
-          const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src);
-          uint hash = BLI_ghashutil_strhash_p(name);
-
-          BLI_snprintf(
-              rdata->cd.uuid.tangent[i_dst], sizeof(*rdata->cd.uuid.tangent), "t%u", hash);
-
-          /* Done adding tangents. */
-
-          /* note: BKE_editmesh_loop_tangent_calc calculates 'CD_TANGENT',
-           * not 'CD_MLOOPTANGENT' (as done below). It's OK, they're compatible. */
-
-          /* note: normally we'd use 'i_src' here, but 'i_dst' is in sync with 'rdata->cd.output'
-           */
-          rdata->cd.layers.tangent[i_dst] = CustomData_get_layer_n(
-              &rdata->cd.output.ldata, CD_TANGENT, i_dst);
-          if (rdata->tri_len != 0) {
-            BLI_assert(rdata->cd.layers.tangent[i_dst] != NULL);
-          }
-        }
-      }
-      /* Actual active rangent slot inside uv layers used for shading. */
-      if (rdata->cd.layers.tangent_active != -1) {
-        rdata->cd.layers.tangent_active = act_tan;
-      }
-      if (rdata->cd.layers.tangent_render != -1) {
-        rdata->cd.layers.tangent_render = ren_tan;
-      }
-
-      if (cd_used->tan_orco != 0) {
-        const char *name = CustomData_get_layer_name(&rdata->cd.output.ldata, CD_TANGENT, i_dst);
-        uint hash = BLI_ghashutil_strhash_p(name);
-        BLI_snprintf(rdata->cd.uuid.tangent[i_dst], sizeof(*rdata->cd.uuid.tangent), "t%u", hash);
-
-        rdata->cd.layers.tangent[i_dst] = CustomData_get_layer_n(
-            &rdata->cd.output.ldata, CD_TANGENT, i_dst);
-      }
-    }
-
-#undef me
-  }
-
-  return rdata;
-}
-
-/* Warning replace mesh pointer. */
-#define MBC_GET_FINAL_MESH(me) \
-  /* Hack to show the final result. */ \
-  const bool _use_em_final = ((me)->edit_mesh && (me)->edit_mesh->mesh_eval_final && \
-                              ((me)->edit_mesh->mesh_eval_final->runtime.is_original == false)); \
-  Mesh _me_fake; \
-  if (_use_em_final) { \
-    _me_fake = *(me)->edit_mesh->mesh_eval_final; \
-    _me_fake.mat = (me)->mat; \
-    _me_fake.totcol = (me)->totcol; \
-    (me) = &_me_fake; \
-  } \
-  ((void)0)
-
-static void mesh_render_data_free(MeshRenderData *rdata)
-{
-  if (rdata->is_orco_allocated) {
-    MEM_SAFE_FREE(rdata->orco);
-  }
-  MEM_SAFE_FREE(rdata->cd.offset.uv);
-  MEM_SAFE_FREE(rdata->cd.offset.vcol);
-  MEM_SAFE_FREE(rdata->cd.uuid.auto_mix);
-  MEM_SAFE_FREE(rdata->cd.uuid.uv);
-  MEM_SAFE_FREE(rdata->cd.uuid.vcol);
-  MEM_SAFE_FREE(rdata->cd.uuid.tangent);
-  MEM_SAFE_FREE(rdata->cd.layers.uv);
-  MEM_SAFE_FREE(rdata->cd.layers.vcol);
-  MEM_SAFE_FREE(rdata->cd.layers.tangent);
-  MEM_SAFE_FREE(rdata->cd.layers.auto_vcol);
-  MEM_SAFE_FREE(rdata->loose_verts);
-  MEM_SAFE_FREE(rdata->loose_edges);
-  MEM_SAFE_FREE(rdata->edges_adjacent_polys);
-  MEM_SAFE_FREE(rdata->mlooptri);
-  MEM_SAFE_FREE(rdata->loop_normals);
-  MEM_SAFE_FREE(rdata->poly_normals);
-  MEM_SAFE_FREE(rdata->poly_normals_pack);
-  MEM_SAFE_FREE(rdata->vert_normals_pack);
-  MEM_SAFE_FREE(rdata->vert_weight);
-  MEM_SAFE_FREE(rdata->edge_select_bool);
-  MEM_SAFE_FREE(rdata->edge_visible_bool);
-  MEM_SAFE_FREE(rdata->vert_color);
-
-  MEM_SAFE_FREE(rdata->mapped.loose_verts);
-  MEM_SAFE_FREE(rdata->mapped.loose_edges);
-
-  CustomData_free(&rdata->cd.output.ldata, rdata->loop_len);
-
-  MEM_freeN(rdata);
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Accessor Functions
- * \{ */
-
-static const char *mesh_render_data_uv_auto_layer_uuid_get(const MeshRenderData *rdata, int layer)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_SHADING);
-  return rdata->cd.uuid.auto_mix[layer];
-}
-
-static const char *mesh_render_data_vcol_auto_layer_uuid_get(const MeshRenderData *rdata,
-                                                             int layer)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_SHADING);
-  return rdata->cd.uuid.auto_mix[rdata->cd.layers.uv_len + layer];
-}
-
-static const char *mesh_render_data_uv_layer_uuid_get(const MeshRenderData *rdata, int layer)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_SHADING);
-  return rdata->cd.uuid.uv[layer];
-}
-
-static const char *mesh_render_data_vcol_layer_uuid_get(const MeshRenderData *rdata, int layer)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_SHADING);
-  return rdata->cd.uuid.vcol[layer];
-}
-
-static const char *mesh_render_data_tangent_layer_uuid_get(const MeshRenderData *rdata, int layer)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_SHADING);
-  return rdata->cd.uuid.tangent[layer];
-}
-
-static int UNUSED_FUNCTION(mesh_render_data_verts_len_get)(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_VERT);
-  return rdata->vert_len;
-}
-static int mesh_render_data_verts_len_get_maybe_mapped(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_VERT);
-  return ((rdata->mapped.use == false) ? rdata->vert_len : rdata->mapped.vert_len);
-}
-
-static int UNUSED_FUNCTION(mesh_render_data_loose_verts_len_get)(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOSE_VERT);
-  return rdata->loose_vert_len;
-}
-static int mesh_render_data_loose_verts_len_get_maybe_mapped(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOSE_VERT);
-  return ((rdata->mapped.use == false) ? rdata->loose_vert_len : rdata->mapped.loose_vert_len);
-}
-
-static int mesh_render_data_edges_len_get(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_EDGE);
-  return rdata->edge_len;
-}
-static int mesh_render_data_edges_len_get_maybe_mapped(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_EDGE);
-  return ((rdata->mapped.use == false) ? rdata->edge_len : rdata->mapped.edge_len);
-}
-
-static int UNUSED_FUNCTION(mesh_render_data_loose_edges_len_get)(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOSE_EDGE);
-  return rdata->loose_edge_len;
-}
-static int mesh_render_data_loose_edges_len_get_maybe_mapped(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOSE_EDGE);
-  return ((rdata->mapped.use == false) ? rdata->loose_edge_len : rdata->mapped.loose_edge_len);
-}
-
-static int mesh_render_data_looptri_len_get(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOPTRI);
-  return rdata->tri_len;
-}
-static int mesh_render_data_looptri_len_get_maybe_mapped(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOPTRI);
-  return ((rdata->mapped.use == false) ? rdata->tri_len : rdata->mapped.tri_len);
-}
-
-static int UNUSED_FUNCTION(mesh_render_data_mat_len_get)(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_POLY);
-  return rdata->mat_len;
-}
-
-static int mesh_render_data_loops_len_get(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOP);
-  return rdata->loop_len;
-}
-
-static int mesh_render_data_loops_len_get_maybe_mapped(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_LOOP);
-  return ((rdata->mapped.use == false) ? rdata->loop_len : rdata->mapped.loop_len);
-}
-
-static int mesh_render_data_polys_len_get(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_POLY);
-  return rdata->poly_len;
-}
-static int mesh_render_data_polys_len_get_maybe_mapped(const MeshRenderData *rdata)
-{
-  BLI_assert(rdata->types & MR_DATATYPE_POLY);
-  return ((rdata->mapped.use == false) ? rdata->poly_len : rdata->mapped.poly_len);
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-
-/** \name Internal Cache (Lazy Initialization)
- * \{ */
-
-/** Ensure #MeshRenderData.poly_normals_pack */
-static void mesh_render_data_ensure_poly_normals_pack(MeshRenderData *rdata)
-{
-  GPUPackedNormal *pnors_pack = rdata->poly_normals_pack;
-  if (pnors_pack == NULL) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter fiter;
-      BMFace *efa;
-      int i;
-
-      pnors_pack = rdata->poly_normals_pack = MEM_mallocN(sizeof(*pnors_pack) * rdata->poly_len,
-                                                          __func__);
-      if (rdata->edit_data && rdata->edit_data->vertexCos != NULL) {
-        BKE_editmesh_cache_ensure_poly_normals(rdata->edit_bmesh, rdata->edit_data);
-        const float(*pnors)[3] = rdata->edit_data->polyNos;
-        for (i = 0; i < bm->totface; i++) {
-          pnors_pack[i] = GPU_normal_convert_i10_v3(pnors[i]);
-        }
-      }
-      else {
-        BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
-          pnors_pack[i] = GPU_normal_convert_i10_v3(efa->no);
-        }
-      }
-    }
-    else {
-      float(*pnors)[3] = rdata->poly_normals;
-
-      if (!pnors) {
-        pnors = rdata->poly_normals = MEM_mallocN(sizeof(*pnors) * rdata->poly_len, __func__);
-        BKE_mesh_calc_normals_poly(rdata->mvert,
-                                   NULL,
-                                   rdata->vert_len,
-                                   rdata->mloop,
-                                   rdata->mpoly,
-                                   rdata->loop_len,
-                                   rdata->poly_len,
-                                   pnors,
-                                   true);
-      }
-
-      pnors_pack = rdata->poly_normals_pack = MEM_mallocN(sizeof(*pnors_pack) * rdata->poly_len,
-                                                          __func__);
-      for (int i = 0; i < rdata->poly_len; i++) {
-        pnors_pack[i] = GPU_normal_convert_i10_v3(pnors[i]);
-      }
-    }
-  }
-}
-
-/** Ensure #MeshRenderData.vert_normals_pack */
-static void mesh_render_data_ensure_vert_normals_pack(MeshRenderData *rdata)
-{
-  GPUPackedNormal *vnors_pack = rdata->vert_normals_pack;
-  if (vnors_pack == NULL) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter viter;
-      BMVert *eve;
-      int i;
-
-      vnors_pack = rdata->vert_normals_pack = MEM_mallocN(sizeof(*vnors_pack) * rdata->vert_len,
-                                                          __func__);
-      BM_ITER_MESH_INDEX (eve, &viter, bm, BM_VERT, i) {
-        vnors_pack[i] = GPU_normal_convert_i10_v3(eve->no);
-      }
-    }
-    else {
-      /* data from mesh used directly */
-      BLI_assert(0);
-    }
-  }
-}
-
-/** Ensure #MeshRenderData.vert_color */
-static void UNUSED_FUNCTION(mesh_render_data_ensure_vert_color)(MeshRenderData *rdata)
-{
-  char(*vcol)[3] = rdata->vert_color;
-  if (vcol == NULL) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      const int cd_loop_color_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPCOL);
-      if (cd_loop_color_offset == -1) {
-        goto fallback;
-      }
-
-      vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__);
-
-      BMIter fiter;
-      BMFace *efa;
-      int i = 0;
-
-      BM_ITER_MESH (efa, &fiter, bm, BM_FACES_OF_MESH) {
-        BMLoop *l_iter, *l_first;
-        l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
-        do {
-          const MLoopCol *lcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_color_offset);
-          vcol[i][0] = lcol->r;
-          vcol[i][1] = lcol->g;
-          vcol[i][2] = lcol->b;
-          i += 1;
-        } while ((l_iter = l_iter->next) != l_first);
-      }
-      BLI_assert(i == rdata->loop_len);
-    }
-    else {
-      if (rdata->mloopcol == NULL) {
-        goto fallback;
-      }
-
-      vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__);
-
-      for (int i = 0; i < rdata->loop_len; i++) {
-        vcol[i][0] = rdata->mloopcol[i].r;
-        vcol[i][1] = rdata->mloopcol[i].g;
-        vcol[i][2] = rdata->mloopcol[i].b;
-      }
-    }
-  }
-  return;
-
-fallback:
-  vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__);
-
-  for (int i = 0; i < rdata->loop_len; i++) {
-    vcol[i][0] = 255;
-    vcol[i][1] = 255;
-    vcol[i][2] = 255;
-  }
-}
-
-static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
-{
-  float input = 0.0f;
-  bool show_alert_color = false;
-
-  if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
-    /* Multi-Paint feature */
-    input = BKE_defvert_multipaint_collective_weight(
-        dvert,
-        wstate->defgroup_len,
-        wstate->defgroup_sel,
-        wstate->defgroup_sel_count,
-        (wstate->flags & DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE) != 0);
-
-    /* make it black if the selected groups have no weight on a vertex */
-    if (input == 0.0f) {
-      show_alert_color = true;
-    }
-  }
-  else {
-    /* default, non tricky behavior */
-    input = defvert_find_weight(dvert, wstate->defgroup_active);
-
-    if (input == 0.0f) {
-      switch (wstate->alert_mode) {
-        case OB_DRAW_GROUPUSER_ACTIVE:
-          show_alert_color = true;
-          break;
-
-        case OB_DRAW_GROUPUSER_ALL:
-          show_alert_color = defvert_is_weight_zero(dvert, wstate->defgroup_len);
-          break;
-      }
-    }
-  }
-
-  if (show_alert_color) {
-    return -1.0f;
-  }
-  else {
-    CLAMP(input, 0.0f, 1.0f);
-    return input;
-  }
-}
-
-/** Ensure #MeshRenderData.vert_weight */
-static void mesh_render_data_ensure_vert_weight(MeshRenderData *rdata,
-                                                const struct DRW_MeshWeightState *wstate)
-{
-  float *vweight = rdata->vert_weight;
-  if (vweight == NULL) {
-    if (wstate->defgroup_active == -1) {
-      goto fallback;
-    }
-
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
-      if (cd_dvert_offset == -1) {
-        goto fallback;
-      }
-
-      BMIter viter;
-      BMVert *eve;
-      int i;
-
-      vweight = rdata->vert_weight = MEM_mallocN(sizeof(*vweight) * rdata->vert_len, __func__);
-      BM_ITER_MESH_INDEX (eve, &viter, bm, BM_VERT, i) {
-        const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
-        vweight[i] = evaluate_vertex_weight(dvert, wstate);
-      }
-    }
-    else {
-      if (rdata->dvert == NULL) {
-        goto fallback;
-      }
-
-      vweight = rdata->vert_weight = MEM_mallocN(sizeof(*vweight) * rdata->vert_len, __func__);
-      for (int i = 0; i < rdata->vert_len; i++) {
-        vweight[i] = evaluate_vertex_weight(&rdata->dvert[i], wstate);
-      }
-    }
-  }
-  return;
-
-fallback:
-  vweight = rdata->vert_weight = MEM_callocN(sizeof(*vweight) * rdata->vert_len, __func__);
-
-  if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
-    copy_vn_fl(vweight, rdata->vert_len, -2.0f);
-  }
-  else if (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) {
-    copy_vn_fl(vweight, rdata->vert_len, -1.0f);
-  }
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Internal Cache Generation
- * \{ */
-
-static uchar mesh_render_data_face_flag(MeshRenderData *rdata, const BMFace *efa, const int cd_ofs)
-{
-  uchar fflag = 0;
-
-  if (efa == rdata->efa_act) {
-    fflag |= VFLAG_FACE_ACTIVE;
-  }
-  if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
-    fflag |= VFLAG_FACE_SELECTED;
-  }
-
-  if (efa == rdata->efa_act_uv) {
-    fflag |= VFLAG_FACE_UV_ACTIVE;
-  }
-  if ((cd_ofs != -1) && uvedit_face_select_test_ex(rdata->toolsettings, (BMFace *)efa, cd_ofs)) {
-    fflag |= VFLAG_FACE_UV_SELECT;
-  }
-
-#ifdef WITH_FREESTYLE
-  if (rdata->cd.offset.freestyle_face != -1) {
-    const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, rdata->cd.offset.freestyle_face);
-    if (ffa->flag & FREESTYLE_FACE_MARK) {
-      fflag |= VFLAG_FACE_FREESTYLE;
-    }
-  }
-#endif
-
-  return fflag;
-}
-
-static void mesh_render_data_edge_flag(const MeshRenderData *rdata,
-                                       const BMEdge *eed,
-                                       EdgeDrawAttr *eattr)
-{
-  const ToolSettings *ts = rdata->toolsettings;
-  const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0;
-  const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE);
-
-  if (eed == rdata->eed_act) {
-    eattr->e_flag |= VFLAG_EDGE_ACTIVE;
-  }
-  if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
-    eattr->e_flag |= VFLAG_EDGE_SELECTED;
-  }
-  if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
-      BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) {
-    eattr->e_flag |= VFLAG_EDGE_SELECTED;
-    eattr->e_flag |= VFLAG_VERT_SELECTED;
-  }
-  if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
-    eattr->e_flag |= VFLAG_EDGE_SEAM;
-  }
-  if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) {
-    eattr->e_flag |= VFLAG_EDGE_SHARP;
-  }
-
-  /* Use active edge color for active face edges because
-   * specular highlights make it hard to see T55456#510873.
-   *
-   * This isn't ideal since it can't be used when mixing edge/face modes
-   * but it's still better then not being able to see the active face. */
-  if (is_face_only_select_mode) {
-    if (rdata->efa_act != NULL) {
-      if (BM_edge_in_face(eed, rdata->efa_act)) {
-        eattr->e_flag |= VFLAG_EDGE_ACTIVE;
-      }
-    }
-  }
-
-  /* Use a byte for value range */
-  if (rdata->cd.offset.crease != -1) {
-    float crease = BM_ELEM_CD_GET_FLOAT(eed, rdata->cd.offset.crease);
-    if (crease > 0) {
-      eattr->crease = (uchar)(crease * 255.0f);
-    }
-  }
-  /* Use a byte for value range */
-  if (rdata->cd.offset.bweight != -1) {
-    float bweight = BM_ELEM_CD_GET_FLOAT(eed, rdata->cd.offset.bweight);
-    if (bweight > 0) {
-      eattr->bweight = (uchar)(bweight * 255.0f);
-    }
-  }
-#ifdef WITH_FREESTYLE
-  if (rdata->cd.offset.freestyle_edge != -1) {
-    const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, rdata->cd.offset.freestyle_edge);
-    if (fed->flag & FREESTYLE_EDGE_MARK) {
-      eattr->e_flag |= VFLAG_EDGE_FREESTYLE;
-    }
-  }
-#endif
-}
-
-static void mesh_render_data_loop_flag(MeshRenderData *rdata,
-                                       BMLoop *loop,
-                                       const int cd_ofs,
-                                       EdgeDrawAttr *eattr)
-{
-  if (cd_ofs == -1) {
-    return;
-  }
-  MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_ofs);
-  if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) {
-    eattr->v_flag |= VFLAG_VERT_UV_PINNED;
-  }
-  if (uvedit_uv_select_test_ex(rdata->toolsettings, loop, cd_ofs)) {
-    eattr->v_flag |= VFLAG_VERT_UV_SELECT;
-  }
-  if (uvedit_edge_select_test_ex(rdata->toolsettings, loop, cd_ofs)) {
-    eattr->v_flag |= VFLAG_EDGE_UV_SELECT;
-  }
-}
-
-static void mesh_render_data_vert_flag(MeshRenderData *rdata,
-                                       const BMVert *eve,
-                                       EdgeDrawAttr *eattr)
-{
-  if (eve == rdata->eve_act) {
-    eattr->e_flag |= VFLAG_VERT_ACTIVE;
-  }
-  if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
-    eattr->e_flag |= VFLAG_VERT_SELECTED;
-  }
-}
-
-static bool add_edit_facedot(MeshRenderData *rdata,
-                             GPUVertBuf *vbo,
-                             const uint fdot_pos_id,
-                             const uint fdot_nor_flag_id,
-                             const int poly,
-                             const int base_vert_idx)
-{
-  BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY));
-  float pnor[3], center[3];
-  int facedot_flag;
-  if (rdata->edit_bmesh) {
-    BMEditMesh *em = rdata->edit_bmesh;
-    const BMFace *efa = BM_face_at_index(em->bm, poly);
-    if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-      return false;
-    }
-    if (rdata->edit_data && rdata->edit_data->vertexCos) {
-      copy_v3_v3(center, rdata->edit_data->polyCos[poly]);
-      copy_v3_v3(pnor, rdata->edit_data->polyNos[poly]);
-    }
-    else {
-      BM_face_calc_center_median(efa, center);
-      copy_v3_v3(pnor, efa->no);
-    }
-    facedot_flag = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == em->bm->act_face) ? -1 : 1) :
-                                                            0;
-  }
-  else {
-    MVert *mvert = rdata->mvert;
-    const MPoly *mpoly = rdata->mpoly + poly;
-    const MLoop *mloop = rdata->mloop + mpoly->loopstart;
-
-    BKE_mesh_calc_poly_center(mpoly, mloop, mvert, center);
-    BKE_mesh_calc_poly_normal(mpoly, mloop, mvert, pnor);
-    /* No selection if not in edit mode. */
-    facedot_flag = 0;
-  }
-
-  GPUPackedNormal nor = GPU_normal_convert_i10_v3(pnor);
-  nor.w = facedot_flag;
-  GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor);
-  GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, center);
-
-  return true;
-}
-static bool add_edit_facedot_mapped(MeshRenderData *rdata,
-                                    GPUVertBuf *vbo,
-                                    const uint fdot_pos_id,
-                                    const uint fdot_nor_flag_id,
-                                    const int poly,
-                                    const int base_vert_idx)
-{
-  BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY));
-  float pnor[3], center[3];
-  const int *p_origindex = rdata->mapped.p_origindex;
-  const int p_orig = p_origindex[poly];
-  if (p_orig == ORIGINDEX_NONE) {
-    return false;
-  }
-  BMEditMesh *em = rdata->edit_bmesh;
-  const BMFace *efa = BM_face_at_index(em->bm, p_orig);
-  if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-    return false;
-  }
-
-  Mesh *me_cage = em->mesh_eval_cage;
-  const MVert *mvert = me_cage->mvert;
-  const MLoop *mloop = me_cage->mloop;
-  const MPoly *mpoly = me_cage->mpoly;
-
-  const MPoly *mp = mpoly + poly;
-  const MLoop *ml = mloop + mp->loopstart;
-
-  BKE_mesh_calc_poly_center(mp, ml, mvert, center);
-  BKE_mesh_calc_poly_normal(mp, ml, mvert, pnor);
-
-  GPUPackedNormal nor = GPU_normal_convert_i10_v3(pnor);
-  nor.w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == em->bm->act_face) ? -1 : 1) : 0;
-  GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor);
-  GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, center);
-
-  return true;
-}
-static bool add_edit_facedot_subdiv(MeshRenderData *rdata,
-                                    GPUVertBuf *vbo,
-                                    const uint fdot_pos_id,
-                                    const uint fdot_nor_flag_id,
-                                    const int vert,
-                                    const int poly,
-                                    const int base_vert_idx)
-{
-  BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY));
-  const int *p_origindex = rdata->mapped.p_origindex;
-  const int p_orig = p_origindex[poly];
-  if (p_orig == ORIGINDEX_NONE) {
-    return false;
-  }
-  BMEditMesh *em = rdata->edit_bmesh;
-  const BMFace *efa = BM_face_at_index(em->bm, p_orig);
-  if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-    return false;
-  }
-
-  Mesh *me_cage = em->mesh_eval_cage;
-  const MVert *mvert = &me_cage->mvert[vert];
-
-  GPUPackedNormal nor = GPU_normal_convert_i10_s3(mvert->no);
-  nor.w = BM_elem_flag_test(efa, BM_ELEM_SELECT) ? ((efa == em->bm->act_face) ? -1 : 1) : 0;
-  GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor);
-  GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, mvert->co);
-
-  return true;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Vertex Group Selection
- * \{ */
-
-/** Reset the selection structure, deallocating heap memory as appropriate. */
-static void drw_mesh_weight_state_clear(struct DRW_MeshWeightState *wstate)
-{
-  MEM_SAFE_FREE(wstate->defgroup_sel);
-
-  memset(wstate, 0, sizeof(*wstate));
-
-  wstate->defgroup_active = -1;
-}
-
-/** Copy selection data from one structure to another, including heap memory. */
-static void drw_mesh_weight_state_copy(struct DRW_MeshWeightState *wstate_dst,
-                                       const struct DRW_MeshWeightState *wstate_src)
-{
-  MEM_SAFE_FREE(wstate_dst->defgroup_sel);
-
-  memcpy(wstate_dst, wstate_src, sizeof(*wstate_dst));
-
-  if (wstate_src->defgroup_sel) {
-    wstate_dst->defgroup_sel = MEM_dupallocN(wstate_src->defgroup_sel);
-  }
-}
-
-/** Compare two selection structures. */
-static bool drw_mesh_weight_state_compare(const struct DRW_MeshWeightState *a,
-                                          const struct DRW_MeshWeightState *b)
-{
-  return a->defgroup_active == b->defgroup_active && a->defgroup_len == b->defgroup_len &&
-         a->flags == b->flags && a->alert_mode == b->alert_mode &&
-         a->defgroup_sel_count == b->defgroup_sel_count &&
-         ((!a->defgroup_sel && !b->defgroup_sel) ||
-          (a->defgroup_sel && b->defgroup_sel &&
-           memcmp(a->defgroup_sel, b->defgroup_sel, a->defgroup_len * sizeof(bool)) == 0));
-}
-
-static void drw_mesh_weight_state_extract(Object *ob,
-                                          Mesh *me,
-                                          const ToolSettings *ts,
-                                          bool paint_mode,
-                                          struct DRW_MeshWeightState *wstate)
-{
-  /* Extract complete vertex weight group selection state and mode flags. */
-  memset(wstate, 0, sizeof(*wstate));
-
-  wstate->defgroup_active = ob->actdef - 1;
-  wstate->defgroup_len = BLI_listbase_count(&ob->defbase);
-
-  wstate->alert_mode = ts->weightuser;
-
-  if (paint_mode && ts->multipaint) {
-    /* Multipaint needs to know all selected bones, not just the active group.
-     * This is actually a relatively expensive operation, but caching would be difficult. */
-    wstate->defgroup_sel = BKE_object_defgroup_selected_get(
-        ob, wstate->defgroup_len, &wstate->defgroup_sel_count);
-
-    if (wstate->defgroup_sel_count > 1) {
-      wstate->flags |= DRW_MESH_WEIGHT_STATE_MULTIPAINT |
-                       (ts->auto_normalize ? DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE : 0);
-
-      if (me->editflag & ME_EDIT_MIRROR_X) {
-        BKE_object_defgroup_mirror_selection(ob,
-                                             wstate->defgroup_len,
-                                             wstate->defgroup_sel,
-                                             wstate->defgroup_sel,
-                                             &wstate->defgroup_sel_count);
-      }
-    }
-    /* With only one selected bone Multipaint reverts to regular mode. */
-    else {
-      wstate->defgroup_sel_count = 0;
-      MEM_SAFE_FREE(wstate->defgroup_sel);
-    }
-  }
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh GPUBatch Cache
- * \{ */
-
-typedef enum DRWBatchFlag {
-  MBC_SURFACE = (1 << 0),
-  MBC_SURFACE_WEIGHTS = (1 << 1),
-  MBC_EDIT_TRIANGLES = (1 << 2),
-  MBC_EDIT_VERTICES = (1 << 3),
-  MBC_EDIT_EDGES = (1 << 4),
-  MBC_EDIT_LNOR = (1 << 5),
-  MBC_EDIT_FACEDOTS = (1 << 6),
-  MBC_EDIT_MESH_ANALYSIS = (1 << 7),
-  MBC_EDITUV_FACES_STRECH_AREA = (1 << 8),
-  MBC_EDITUV_FACES_STRECH_ANGLE = (1 << 9),
-  MBC_EDITUV_FACES = (1 << 10),
-  MBC_EDITUV_EDGES = (1 << 11),
-  MBC_EDITUV_VERTS = (1 << 12),
-  MBC_EDITUV_FACEDOTS = (1 << 13),
-  MBC_EDIT_SELECTION_VERTS = (1 << 14),
-  MBC_EDIT_SELECTION_EDGES = (1 << 15),
-  MBC_EDIT_SELECTION_FACES = (1 << 16),
-  MBC_EDIT_SELECTION_FACEDOTS = (1 << 17),
-  MBC_ALL_VERTS = (1 << 18),
-  MBC_ALL_EDGES = (1 << 19),
-  MBC_LOOSE_EDGES = (1 << 20),
-  MBC_EDGE_DETECTION = (1 << 21),
-  MBC_WIRE_EDGES = (1 << 22),
-  MBC_WIRE_LOOPS = (1 << 23),
-  MBC_WIRE_LOOPS_UVS = (1 << 24),
-  MBC_SURF_PER_MAT = (1 << 25),
-} DRWBatchFlag;
-
-#define MBC_EDITUV \
-  (MBC_EDITUV_FACES_STRECH_AREA | MBC_EDITUV_FACES_STRECH_ANGLE | MBC_EDITUV_FACES | \
-   MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS)
-
-typedef struct MeshBatchCache {
-  /* In order buffers: All verts only specified once
-   * or once per loop. To be used with a GPUIndexBuf. */
-  struct {
-    /* Vertex data. */
-    GPUVertBuf *pos_nor;
-    GPUVertBuf *weights;
-    /* Loop data. */
-    GPUVertBuf *loop_pos_nor;
-    GPUVertBuf *loop_uv_tan;
-    GPUVertBuf *loop_vcol;
-    GPUVertBuf *loop_edge_fac;
-    GPUVertBuf *loop_orco;
-  } ordered;
-
-  /* Edit Mesh Data:
-   * Edit cage can be different from final mesh so vertex count
-   * might differ. */
-  struct {
-    /* TODO(fclem): Reuse ordered.loop_pos_nor and maybe even
-     * ordered.loop_uv_tan when cage match final mesh. */
-    GPUVertBuf *loop_pos_nor;
-    GPUVertBuf *loop_data;
-    GPUVertBuf *loop_lnor;
-    GPUVertBuf *facedots_pos_nor_data;
-    GPUVertBuf *loop_mesh_analysis;
-    /* UV data without modifier applied.
-     * Vertex count is always the one of the cage. */
-    GPUVertBuf *loop_uv;
-    GPUVertBuf *loop_uv_data;
-    GPUVertBuf *loop_stretch_angle;
-    GPUVertBuf *loop_stretch_area;
-    GPUVertBuf *facedots_uv;
-    GPUVertBuf *facedots_uv_data;
-    /* Selection */
-    GPUVertBuf *loop_vert_idx;
-    GPUVertBuf *loop_edge_idx;
-    GPUVertBuf *loop_face_idx;
-    GPUVertBuf *facedots_idx;
-  } edit;
-
-  /* Index Buffers:
-   * Only need to be updated when topology changes. */
-  struct {
-    /* Indices to verts. */
-    GPUIndexBuf *surf_tris;
-    GPUIndexBuf *edges_lines;
-    GPUIndexBuf *edges_adj_lines;
-    GPUIndexBuf *loose_edges_lines;
-    /* Indices to vloops. */
-    GPUIndexBuf *loops_tris;
-    GPUIndexBuf *loops_lines;
-    GPUIndexBuf *loops_lines_paint_mask;
-    GPUIndexBuf *loops_line_strips;
-    /* Edit mode. */
-    GPUIndexBuf *edit_loops_points; /* verts */
-    GPUIndexBuf *edit_loops_lines;  /* edges */
-    GPUIndexBuf *edit_loops_tris;   /* faces */
-    /* Edit UVs */
-    GPUIndexBuf *edituv_loops_points;      /* verts */
-    GPUIndexBuf *edituv_loops_line_strips; /* edges */
-    GPUIndexBuf *edituv_loops_tri_fans;    /* faces */
-  } ibo;
-
-  struct {
-    /* Surfaces / Render */
-    GPUBatch *surface;
-    GPUBatch *surface_weights;
-    /* Edit mode */
-    GPUBatch *edit_triangles;
-    GPUBatch *edit_vertices;
-    GPUBatch *edit_edges;
-    GPUBatch *edit_lnor;
-    GPUBatch *edit_facedots;
-    GPUBatch *edit_mesh_analysis;
-    /* Edit UVs */
-    GPUBatch *edituv_faces_strech_area;
-    GPUBatch *edituv_faces_strech_angle;
-    GPUBatch *edituv_faces;
-    GPUBatch *edituv_edges;
-    GPUBatch *edituv_verts;
-    GPUBatch *edituv_facedots;
-    /* Edit selection */
-    GPUBatch *edit_selection_verts;
-    GPUBatch *edit_selection_edges;
-    GPUBatch *edit_selection_faces;
-    GPUBatch *edit_selection_facedots;
-    /* Common display / Other */
-    GPUBatch *all_verts;
-    GPUBatch *all_edges;
-    GPUBatch *loose_edges;
-    GPUBatch *edge_detection;
-    GPUBatch *wire_edges;     /* Individual edges with face normals. */
-    GPUBatch *wire_loops;     /* Loops around faces. no edges between selected faces */
-    GPUBatch *wire_loops_uvs; /* Same as wire_loops but only has uvs. */
-  } batch;
-
-  GPUIndexBuf **surf_per_mat_tris;
-  GPUBatch **surf_per_mat;
-
-  /* arrays of bool uniform names (and value) that will be use to
-   * set srgb conversion for auto attributes.*/
-  char *auto_layer_names;
-  int *auto_layer_is_srgb;
-  int auto_layer_len;
-
-  DRWBatchFlag batch_requested;
-  DRWBatchFlag batch_ready;
-
-  /* settings to determine if cache is invalid */
-  int edge_len;
-  int tri_len;
-  int poly_len;
-  int vert_len;
-  int mat_len;
-  bool is_dirty; /* Instantly invalidates cache, skipping mesh check */
-  bool is_editmode;
-  bool is_uvsyncsel;
-
-  struct DRW_MeshWeightState weight_state;
-
-  DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time;
-
-  int lastmatch;
-
-  /* Valid only if edge_detection is up to date. */
-  bool is_manifold;
-
-  bool no_loose_wire;
-} MeshBatchCache;
-
-BLI_INLINE void mesh_batch_cache_add_request(MeshBatchCache *cache, DRWBatchFlag new_flag)
-{
-  atomic_fetch_and_or_uint32((uint32_t *)(&cache->batch_requested), *(uint32_t *)&new_flag);
-}
-
-/* GPUBatch cache management. */
-
-static bool mesh_batch_cache_valid(Mesh *me)
-{
-  MeshBatchCache *cache = me->runtime.batch_cache;
-
-  if (cache == NULL) {
-    return false;
-  }
-
-  if (cache->is_editmode != (me->edit_mesh != NULL)) {
-    return false;
-  }
-
-  if (cache->is_dirty) {
-    return false;
-  }
-
-  if (cache->mat_len != mesh_render_mat_len_get(me)) {
-    return false;
-  }
-
-  return true;
-}
-
-static void mesh_batch_cache_init(Mesh *me)
-{
-  MeshBatchCache *cache = me->runtime.batch_cache;
-
-  if (!cache) {
-    cache = me->runtime.batch_cache = MEM_callocN(sizeof(*cache), __func__);
-  }
-  else {
-    memset(cache, 0, sizeof(*cache));
-  }
-
-  cache->is_editmode = me->edit_mesh != NULL;
-
-  if (cache->is_editmode == false) {
-    cache->edge_len = mesh_render_edges_len_get(me);
-    cache->tri_len = mesh_render_looptri_len_get(me);
-    cache->poly_len = mesh_render_polys_len_get(me);
-    cache->vert_len = mesh_render_verts_len_get(me);
-  }
-
-  cache->mat_len = mesh_render_mat_len_get(me);
-  cache->surf_per_mat_tris = MEM_callocN(sizeof(*cache->surf_per_mat_tris) * cache->mat_len,
-                                         __func__);
-  cache->surf_per_mat = MEM_callocN(sizeof(*cache->surf_per_mat) * cache->mat_len, __func__);
-
-  cache->is_dirty = false;
-  cache->batch_ready = 0;
-  cache->batch_requested = 0;
-
-  drw_mesh_weight_state_clear(&cache->weight_state);
-}
-
-void DRW_mesh_batch_cache_validate(Mesh *me)
-{
-  if (!mesh_batch_cache_valid(me)) {
-    mesh_batch_cache_clear(me);
-    mesh_batch_cache_init(me);
-  }
-}
-
-static MeshBatchCache *mesh_batch_cache_get(Mesh *me)
-{
-  return me->runtime.batch_cache;
-}
-
-static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache,
-                                                const struct DRW_MeshWeightState *wstate)
-{
-  if (!drw_mesh_weight_state_compare(&cache->weight_state, wstate)) {
-    GPU_BATCH_CLEAR_SAFE(cache->batch.surface_weights);
-    GPU_VERTBUF_DISCARD_SAFE(cache->ordered.weights);
-
-    cache->batch_ready &= ~MBC_SURFACE_WEIGHTS;
-
-    drw_mesh_weight_state_clear(&cache->weight_state);
-  }
-}
-
-static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
-{
-  GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_pos_nor);
-  GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_uv_tan);
-  GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_vcol);
-  GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_orco);
-
-  if (cache->surf_per_mat_tris) {
-    for (int i = 0; i < cache->mat_len; i++) {
-      GPU_INDEXBUF_DISCARD_SAFE(cache->surf_per_mat_tris[i]);
-    }
-  }
-  MEM_SAFE_FREE(cache->surf_per_mat_tris);
-  if (cache->surf_per_mat) {
-    for (int i = 0; i < cache->mat_len; i++) {
-      GPU_BATCH_DISCARD_SAFE(cache->surf_per_mat[i]);
-    }
-  }
-  MEM_SAFE_FREE(cache->surf_per_mat);
-
-  cache->batch_ready &= ~MBC_SURF_PER_MAT;
-
-  MEM_SAFE_FREE(cache->auto_layer_names);
-  MEM_SAFE_FREE(cache->auto_layer_is_srgb);
-
-  mesh_cd_layers_type_clear(&cache->cd_used);
-
-  cache->mat_len = 0;
-}
-
-static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache)
-{
-  GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_stretch_angle);
-  GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_stretch_area);
-  GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv);
-  GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv_data);
-  GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_uv);
-  GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_uv_data);
-  GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_tri_fans);
-  GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_line_strips);
-  GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.edituv_loops_points);
-  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_area);
-  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_angle);
-  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces);
-  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges);
-  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts);
-  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_facedots);
-
-  cache->batch_ready &= ~MBC_EDITUV;
-}
-
-void DRW_mesh_batch_cache_dirty_tag(Mesh *me, int mode)
-{
-  MeshBatchCache *cache = me->runtime.batch_cache;
-  if (cache == NULL) {
-    return;
-  }
-  switch (mode) {
-    case BKE_MESH_BATCH_DIRTY_SELECT:
-      GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_data);
-      GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_pos_nor_data);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_triangles);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_vertices);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_edges);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_facedots);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_facedots);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_mesh_analysis);
-      cache->batch_ready &= ~(MBC_EDIT_TRIANGLES | MBC_EDIT_VERTICES | MBC_EDIT_EDGES |
-                              MBC_EDIT_FACEDOTS | MBC_EDIT_SELECTION_FACEDOTS |
-                              MBC_EDIT_MESH_ANALYSIS);
-      /* Because visible UVs depends on edit mode selection, discard everything. */
-      mesh_batch_cache_discard_uvedit(cache);
-      break;
-    case BKE_MESH_BATCH_DIRTY_SELECT_PAINT:
-      /* Paint mode selection flag is packed inside the nor attrib.
-       * Note that it can be slow if auto smooth is enabled. (see T63946) */
-      GPU_INDEXBUF_DISCARD_SAFE(cache->ibo.loops_lines_paint_mask);
-      GPU_VERTBUF_DISCARD_SAFE(cache->ordered.loop_pos_nor);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.surface);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.wire_edges);
-      if (cache->surf_per_mat) {
-        for (int i = 0; i < cache->mat_len; i++) {
-          GPU_BATCH_DISCARD_SAFE(cache->surf_per_mat[i]);
-        }
-      }
-      cache->batch_ready &= ~(MBC_SURFACE | MBC_WIRE_EDGES | MBC_WIRE_LOOPS | MBC_SURF_PER_MAT);
-      break;
-    case BKE_MESH_BATCH_DIRTY_ALL:
-      cache->is_dirty = true;
-      break;
-    case BKE_MESH_BATCH_DIRTY_SHADING:
-      mesh_batch_cache_discard_shaded_tri(cache);
-      mesh_batch_cache_discard_uvedit(cache);
-      break;
-    case BKE_MESH_BATCH_DIRTY_UVEDIT_ALL:
-      mesh_batch_cache_discard_uvedit(cache);
-      break;
-    case BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT:
-      GPU_VERTBUF_DISCARD_SAFE(cache->edit.loop_uv_data);
-      GPU_VERTBUF_DISCARD_SAFE(cache->edit.facedots_uv_data);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_area);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_angle);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts);
-      GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_facedots);
-      cache->batch_ready &= ~MBC_EDITUV;
-      break;
-    default:
-      BLI_assert(0);
-  }
-}
-
-static void mesh_batch_cache_clear(Mesh *me)
-{
-  MeshBatchCache *cache = me->runtime.batch_cache;
-  if (!cache) {
-    return;
-  }
-
-  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->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]);
-  }
-
-  mesh_batch_cache_discard_shaded_tri(cache);
-
-  mesh_batch_cache_discard_uvedit(cache);
-
-  cache->batch_ready = 0;
-
-  drw_mesh_weight_state_clear(&cache->weight_state);
-}
-
-void DRW_mesh_batch_cache_free(Mesh *me)
-{
-  mesh_batch_cache_clear(me);
-  MEM_SAFE_FREE(me->runtime.batch_cache);
-}
-
-/* GPUBatch cache usage. */
-
-static void mesh_create_edit_vertex_loops(MeshRenderData *rdata,
-                                          GPUVertBuf *vbo_pos_nor,
-                                          GPUVertBuf *vbo_lnor,
-                                          GPUVertBuf *vbo_uv,
-                                          GPUVertBuf *vbo_data,
-                                          GPUVertBuf *vbo_verts,
-                                          GPUVertBuf *vbo_edges,
-                                          GPUVertBuf *vbo_faces)
-{
-#if 0
-  const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-  const int edge_len = mesh_render_data_edges_len_get_maybe_mapped(rdata);
-#endif
-  const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata);
-  const int lvert_len = mesh_render_data_loose_verts_len_get_maybe_mapped(rdata);
-  const int ledge_len = mesh_render_data_loose_edges_len_get_maybe_mapped(rdata);
-  const int loop_len = mesh_render_data_loops_len_get_maybe_mapped(rdata);
-  const int tot_loop_len = loop_len + ledge_len * 2 + lvert_len;
-  float(*lnors)[3] = rdata->loop_normals;
-  uchar fflag;
-
-  /* Static formats */
-  static struct {
-    GPUVertFormat sel_id, pos_nor, lnor, flag, uv;
-  } format = {{0}};
-  static struct {
-    uint sel_id, pos, nor, lnor, data, uvs;
-  } attr_id;
-  if (format.sel_id.attr_len == 0) {
-    attr_id.sel_id = GPU_vertformat_attr_add(
-        &format.sel_id, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
-    attr_id.pos = GPU_vertformat_attr_add(
-        &format.pos_nor, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-    attr_id.nor = GPU_vertformat_attr_add(
-        &format.pos_nor, "vnor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
-    attr_id.lnor = GPU_vertformat_attr_add(
-        &format.lnor, "lnor", GPU_COMP_I10, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
-    attr_id.data = GPU_vertformat_attr_add(&format.flag, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
-    attr_id.uvs = GPU_vertformat_attr_add(&format.uv, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
-    GPU_vertformat_alias_add(&format.uv, "pos");
-    GPU_vertformat_alias_add(&format.flag, "flag");
-  }
-
-  GPUVertBufRaw raw_verts, raw_edges, raw_faces, raw_pos, raw_nor, raw_lnor, raw_uv, raw_data;
-  if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) {
-    GPU_vertbuf_init_with_format(vbo_pos_nor, &format.pos_nor);
-    GPU_vertbuf_data_alloc(vbo_pos_nor, tot_loop_len);
-    GPU_vertbuf_attr_get_raw_data(vbo_pos_nor, attr_id.pos, &raw_pos);
-    GPU_vertbuf_attr_get_raw_data(vbo_pos_nor, attr_id.nor, &raw_nor);
-  }
-  if (DRW_TEST_ASSIGN_VBO(vbo_lnor)) {
-    GPU_vertbuf_init_with_format(vbo_lnor, &format.lnor);
-    GPU_vertbuf_data_alloc(vbo_lnor, tot_loop_len);
-    GPU_vertbuf_attr_get_raw_data(vbo_lnor, attr_id.lnor, &raw_lnor);
-  }
-  if (DRW_TEST_ASSIGN_VBO(vbo_data)) {
-    GPU_vertbuf_init_with_format(vbo_data, &format.flag);
-    GPU_vertbuf_data_alloc(vbo_data, tot_loop_len);
-    GPU_vertbuf_attr_get_raw_data(vbo_data, attr_id.data, &raw_data);
-  }
-  if (DRW_TEST_ASSIGN_VBO(vbo_uv)) {
-    GPU_vertbuf_init_with_format(vbo_uv, &format.uv);
-    GPU_vertbuf_data_alloc(vbo_uv, tot_loop_len);
-    GPU_vertbuf_attr_get_raw_data(vbo_uv, attr_id.uvs, &raw_uv);
-  }
-  /* Select Idx */
-  if (DRW_TEST_ASSIGN_VBO(vbo_verts)) {
-    GPU_vertbuf_init_with_format(vbo_verts, &format.sel_id);
-    GPU_vertbuf_data_alloc(vbo_verts, tot_loop_len);
-    GPU_vertbuf_attr_get_raw_data(vbo_verts, attr_id.sel_id, &raw_verts);
-  }
-  if (DRW_TEST_ASSIGN_VBO(vbo_edges)) {
-    GPU_vertbuf_init_with_format(vbo_edges, &format.sel_id);
-    GPU_vertbuf_data_alloc(vbo_edges, tot_loop_len);
-    GPU_vertbuf_attr_get_raw_data(vbo_edges, attr_id.sel_id, &raw_edges);
-  }
-  if (DRW_TEST_ASSIGN_VBO(vbo_faces)) {
-    GPU_vertbuf_init_with_format(vbo_faces, &format.sel_id);
-    GPU_vertbuf_data_alloc(vbo_faces, tot_loop_len);
-    GPU_vertbuf_attr_get_raw_data(vbo_faces, attr_id.sel_id, &raw_faces);
-  }
-
-  if (rdata->edit_bmesh && rdata->mapped.use == false) {
-    BMesh *bm = rdata->edit_bmesh->bm;
-    BMIter iter_efa, iter_loop, iter_vert;
-    BMFace *efa;
-    BMEdge *eed;
-    BMVert *eve;
-    BMLoop *loop;
-    const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
-
-    /* Face Loops */
-    BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) {
-      int fidx = BM_elem_index_get(efa);
-      if (vbo_data) {
-        fflag = mesh_render_data_face_flag(rdata, efa, cd_loop_uv_offset);
-      }
-      BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-        if (vbo_pos_nor) {
-          GPUPackedNormal *vnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor);
-          *vnor = GPU_normal_convert_i10_v3(loop->v->no);
-          copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), loop->v->co);
-        }
-        if (vbo_lnor) {
-          const float *nor = (lnors) ? lnors[BM_elem_index_get(loop)] : efa->no;
-          GPUPackedNormal *lnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_lnor);
-          *lnor = GPU_normal_convert_i10_v3(nor);
-        }
-        if (vbo_data) {
-          EdgeDrawAttr eattr = {.v_flag = fflag};
-          mesh_render_data_edge_flag(rdata, loop->e, &eattr);
-          mesh_render_data_vert_flag(rdata, loop->v, &eattr);
-          mesh_render_data_loop_flag(rdata, loop, cd_loop_uv_offset, &eattr);
-          memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr));
-        }
-        if (vbo_uv) {
-          MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(loop, cd_loop_uv_offset);
-          copy_v2_v2(GPU_vertbuf_raw_step(&raw_uv), luv->uv);
-        }
-        /* Select Idx */
-        if (vbo_verts) {
-          int vidx = BM_elem_index_get(loop->v);
-          *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-        }
-        if (vbo_edges) {
-          int eidx = BM_elem_index_get(loop->e);
-          *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx;
-        }
-        if (vbo_faces) {
-          *((uint *)GPU_vertbuf_raw_step(&raw_faces)) = fidx;
-        }
-      }
-    }
-    /* Loose edges */
-    for (int e = 0; e < ledge_len; e++) {
-      eed = BM_edge_at_index(bm, rdata->loose_edges[e]);
-      BM_ITER_ELEM (eve, &iter_vert, eed, BM_VERTS_OF_EDGE) {
-        if (vbo_pos_nor) {
-          GPUPackedNormal *vnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor);
-          *vnor = GPU_normal_convert_i10_v3(eve->no);
-          copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), eve->co);
-        }
-        if (vbo_data) {
-          EdgeDrawAttr eattr = {0};
-          mesh_render_data_edge_flag(rdata, eed, &eattr);
-          mesh_render_data_vert_flag(rdata, eve, &eattr);
-          memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr));
-        }
-        if (vbo_lnor) {
-          memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal));
-        }
-        /* Select Idx */
-        if (vbo_verts) {
-          int vidx = BM_elem_index_get(eve);
-          *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-        }
-        if (vbo_edges) {
-          int eidx = BM_elem_index_get(eed);
-          *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx;
-        }
-      }
-    }
-    /* Loose verts */
-    for (int e = 0; e < lvert_len; e++) {
-      eve = BM_vert_at_index(bm, rdata->loose_verts[e]);
-      if (vbo_pos_nor) {
-        GPUPackedNormal *vnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor);
-        *vnor = GPU_normal_convert_i10_v3(eve->no);
-        copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), eve->co);
-      }
-      if (vbo_lnor) {
-        memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal));
-      }
-      if (vbo_data) {
-        EdgeDrawAttr eattr = {0};
-        mesh_render_data_vert_flag(rdata, eve, &eattr);
-        memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr));
-      }
-      /* Select Idx */
-      if (vbo_verts) {
-        int vidx = BM_elem_index_get(eve);
-        *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-      }
-    }
-  }
-  else if (rdata->mapped.use == true) {
-    BMesh *bm = rdata->edit_bmesh->bm;
-    const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
-
-    const MPoly *mpoly = rdata->mapped.me_cage->mpoly;
-    const MEdge *medge = rdata->mapped.me_cage->medge;
-    const MVert *mvert = rdata->mapped.me_cage->mvert;
-    const MLoop *mloop = rdata->mapped.me_cage->mloop;
-
-    const int *v_origindex = rdata->mapped.v_origindex;
-    const int *e_origindex = rdata->mapped.e_origindex;
-    const int *p_origindex = rdata->mapped.p_origindex;
-
-    /* Face Loops */
-    for (int poly = 0; poly < poly_len; poly++, mpoly++) {
-      const MLoop *l = &mloop[mpoly->loopstart];
-      int fidx = p_origindex[poly];
-      BMFace *efa = NULL;
-      if (vbo_data) {
-        fflag = 0;
-        if (fidx != ORIGINDEX_NONE) {
-          efa = BM_face_at_index(bm, fidx);
-          fflag = mesh_render_data_face_flag(rdata, efa, cd_loop_uv_offset);
-        }
-      }
-      for (int i = 0; i < mpoly->totloop; i++, l++) {
-        if (vbo_pos_nor) {
-          copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[l->v].co);
-        }
-        if (vbo_lnor || vbo_pos_nor) {
-          GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[l->v].no);
-          if (vbo_pos_nor) {
-            *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor;
-          }
-          if (vbo_lnor) {
-            /* Mapped does not support lnors yet. */
-            *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_lnor) = vnor;
-          }
-        }
-        if (vbo_data) {
-          EdgeDrawAttr eattr = {.v_flag = fflag};
-          int vidx = v_origindex[l->v];
-          int eidx = e_origindex[l->e];
-          if (vidx != ORIGINDEX_NONE) {
-            BMVert *eve = BM_vert_at_index(bm, vidx);
-            mesh_render_data_vert_flag(rdata, eve, &eattr);
-          }
-          if (eidx != ORIGINDEX_NONE) {
-            BMEdge *eed = BM_edge_at_index(bm, eidx);
-            mesh_render_data_edge_flag(rdata, eed, &eattr);
-            if (efa) {
-              BMLoop *loop = BM_face_edge_share_loop(efa, eed);
-              if (loop) {
-                mesh_render_data_loop_flag(rdata, loop, cd_loop_uv_offset, &eattr);
-              }
-            }
-          }
-          memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr));
-        }
-        if (vbo_uv) {
-          MLoopUV *luv = &rdata->mloopuv[mpoly->loopstart + i];
-          copy_v2_v2(GPU_vertbuf_raw_step(&raw_uv), luv->uv);
-        }
-        /* Select Idx */
-        if (vbo_verts) {
-          int vidx = v_origindex[l->v];
-          *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-        }
-        if (vbo_edges) {
-          int eidx = e_origindex[l->e];
-          *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx;
-        }
-        if (vbo_faces) {
-          *((uint *)GPU_vertbuf_raw_step(&raw_faces)) = fidx;
-        }
-      }
-    }
-    /* Loose edges */
-    for (int j = 0; j < ledge_len; j++) {
-      const int e = rdata->mapped.loose_edges[j];
-      for (int i = 0; i < 2; ++i) {
-        int v = (i == 0) ? medge[e].v1 : medge[e].v2;
-        if (vbo_pos_nor) {
-          GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[v].no);
-          *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor;
-          copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[v].co);
-        }
-        if (vbo_lnor) {
-          memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal));
-        }
-        if (vbo_data) {
-          EdgeDrawAttr eattr = {0};
-          int vidx = v_origindex[v];
-          int eidx = e_origindex[e];
-          if (vidx != ORIGINDEX_NONE) {
-            BMVert *eve = BM_vert_at_index(bm, vidx);
-            mesh_render_data_vert_flag(rdata, eve, &eattr);
-          }
-          if (eidx != ORIGINDEX_NONE) {
-            BMEdge *eed = BM_edge_at_index(bm, eidx);
-            mesh_render_data_edge_flag(rdata, eed, &eattr);
-          }
-          memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr));
-        }
-        /* Select Idx */
-        if (vbo_verts) {
-          int vidx = v_origindex[v];
-          *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-        }
-        if (vbo_edges) {
-          int eidx = e_origindex[e];
-          *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx;
-        }
-      }
-    }
-    /* Loose verts */
-    for (int i = 0; i < lvert_len; i++) {
-      const int v = rdata->mapped.loose_verts[i];
-      if (vbo_pos_nor) {
-        GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[v].no);
-        *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor;
-        copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[v].co);
-      }
-      if (vbo_lnor) {
-        memset(GPU_vertbuf_raw_step(&raw_lnor), 0, sizeof(GPUPackedNormal));
-      }
-      if (vbo_data) {
-        EdgeDrawAttr eattr = {0};
-        int vidx = v_origindex[v];
-        if (vidx != ORIGINDEX_NONE) {
-          BMVert *eve = BM_vert_at_index(bm, vidx);
-          mesh_render_data_vert_flag(rdata, eve, &eattr);
-        }
-        memcpy(GPU_vertbuf_raw_step(&raw_data), &eattr, sizeof(EdgeDrawAttr));
-      }
-      /* Select Idx */
-      if (vbo_verts) {
-        int vidx = v_origindex[v];
-        *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-      }
-    }
-  }
-  else {
-    const MPoly *mpoly = rdata->mpoly;
-    const MVert *mvert = rdata->mvert;
-    const MLoop *mloop = rdata->mloop;
-
-    const int *v_origindex = CustomData_get_layer(&rdata->me->vdata, CD_ORIGINDEX);
-    const int *e_origindex = CustomData_get_layer(&rdata->me->edata, CD_ORIGINDEX);
-    const int *p_origindex = CustomData_get_layer(&rdata->me->pdata, CD_ORIGINDEX);
-
-    /* Face Loops */
-    for (int poly = 0; poly < poly_len; poly++, mpoly++) {
-      const MLoop *l = &mloop[mpoly->loopstart];
-      int fidx = p_origindex ? p_origindex[poly] : poly;
-      for (int i = 0; i < mpoly->totloop; i++, l++) {
-        if (vbo_pos_nor) {
-          copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[l->v].co);
-        }
-        if (vbo_lnor || vbo_pos_nor) {
-          GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[l->v].no);
-          if (vbo_pos_nor) {
-            *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_nor) = vnor;
-          }
-          if (vbo_lnor) {
-            /* Mapped does not support lnors yet. */
-            *(GPUPackedNormal *)GPU_vertbuf_raw_step(&raw_lnor) = vnor;
-          }
-        }
-        if (vbo_uv) {
-          MLoopUV *luv = &rdata->mloopuv[mpoly->loopstart + i];
-          copy_v2_v2(GPU_vertbuf_raw_step(&raw_uv), luv->uv);
-        }
-        /* Select Idx */
-        if (vbo_verts) {
-          int vidx = v_origindex ? v_origindex[l->v] : l->v;
-          *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-        }
-        if (vbo_edges) {
-          int eidx = e_origindex ? e_origindex[l->e] : l->e;
-          *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx;
-        }
-        if (vbo_faces) {
-          *((uint *)GPU_vertbuf_raw_step(&raw_faces)) = fidx;
-        }
-      }
-    }
-    /* TODO(fclem): Until we find a way to detect
-     * loose verts easily outside of edit mode, this
-     * will remain disabled. */
-#if 0
-    /* Loose edges */
-    for (int e = 0; e < edge_len; e++, medge++) {
-      int eidx = e_origindex[e];
-      if (eidx != ORIGINDEX_NONE && (medge->flag & ME_LOOSEEDGE)) {
-        for (int i = 0; i < 2; ++i) {
-          int vidx = (i == 0) ? medge->v1 : medge->v2;
-          if (vbo_pos) {
-            copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert[vidx].co);
-          }
-          if (vbo_verts) {
-            *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-          }
-          if (vbo_edges) {
-            *((uint *)GPU_vertbuf_raw_step(&raw_edges)) = eidx;
-          }
-        }
-      }
-    }
-    /* Loose verts */
-    for (int v = 0; v < vert_len; v++, mvert++) {
-      int vidx = v_origindex[v];
-      if (vidx != ORIGINDEX_NONE) {
-        MVert *eve = BM_vert_at_index(bm, vidx);
-        if (eve->e == NULL) {
-          if (vbo_pos) {
-            copy_v3_v3(GPU_vertbuf_raw_step(&raw_pos), mvert->co);
-          }
-          if (vbo_verts) {
-            *((uint *)GPU_vertbuf_raw_step(&raw_verts)) = vidx;
-          }
-        }
-      }
-    }
-#endif
-  }
-  /* Don't resize */
-}
-
-/* TODO: We could use gl_PrimitiveID as index instead of using another VBO. */
-static void mesh_create_edit_facedots_select_id(MeshRenderData *rdata,
-                                                GPUVertBuf *vbo,
-                                                Scene *scene,
-                                                Object *ob)
-{
-  const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata);
-
-  static GPUVertFormat format = {0};
-  static struct {
-    uint idx;
-  } attr_id;
-  if (format.attr_len == 0) {
-    attr_id.idx = GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
-  }
-
-  GPUVertBufRaw idx_step;
-  GPU_vertbuf_init_with_format(vbo, &format);
-  GPU_vertbuf_data_alloc(vbo, poly_len);
-  GPU_vertbuf_attr_get_raw_data(vbo, attr_id.idx, &idx_step);
-
-  /* Keep in sync with mesh_create_edit_facedots(). */
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      for (int poly = 0; poly < poly_len; poly++) {
-        const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, poly);
-        if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-          *((uint *)GPU_vertbuf_raw_step(&idx_step)) = poly;
-        }
-      }
-    }
-    else {
-      for (int poly = 0; poly < poly_len; poly++) {
-        *((uint *)GPU_vertbuf_raw_step(&idx_step)) = poly;
-      }
-    }
-  }
-  else {
-    const int *p_origindex = rdata->mapped.p_origindex;
-    if (modifiers_usesSubsurfFacedots(scene, ob)) {
-      Mesh *me_cage = rdata->mapped.me_cage;
-      const MPoly *mpoly = me_cage->mpoly;
-      for (int p = 0; p < poly_len; p++, mpoly++) {
-        const int p_orig = p_origindex[p];
-        if (p_orig != ORIGINDEX_NONE) {
-          const MLoop *mloop = me_cage->mloop + mpoly->loopstart;
-          for (int l = 0; l < mpoly->totloop; l++, mloop++) {
-            if (me_cage->mvert[mloop->v].flag & ME_VERT_FACEDOT) {
-              const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, p_orig);
-              if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-                *((uint *)GPU_vertbuf_raw_step(&idx_step)) = p_orig;
-              }
-            }
-          }
-        }
-      }
-    }
-    else {
-      for (int poly = 0; poly < poly_len; poly++) {
-        const int p_orig = p_origindex[poly];
-        if (p_orig != ORIGINDEX_NONE) {
-          const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, p_orig);
-          if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-            *((uint *)GPU_vertbuf_raw_step(&idx_step)) = p_orig;
-          }
-        }
-      }
-    }
-  }
-
-  /* Resize & Finish */
-  int facedot_len_used = GPU_vertbuf_raw_used(&idx_step);
-  if (facedot_len_used != poly_len) {
-    GPU_vertbuf_data_resize(vbo, facedot_len_used);
-  }
-}
-
-static void mesh_create_pos_and_nor(MeshRenderData *rdata, GPUVertBuf *vbo)
-{
-  static GPUVertFormat format = {0};
-  static struct {
-    uint pos, nor;
-  } attr_id;
-  if (format.attr_len == 0) {
-    attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-    attr_id.nor = GPU_vertformat_attr_add(
-        &format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-  }
-
-  GPU_vertbuf_init_with_format(vbo, &format);
-  const int vbo_len_capacity = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-  GPU_vertbuf_data_alloc(vbo, vbo_len_capacity);
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter iter;
-      BMVert *eve;
-      uint i;
-
-      mesh_render_data_ensure_vert_normals_pack(rdata);
-      GPUPackedNormal *vnor = rdata->vert_normals_pack;
-
-      BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
-        GPU_vertbuf_attr_set(vbo, attr_id.pos, i, eve->co);
-        GPU_vertbuf_attr_set(vbo, attr_id.nor, i, &vnor[i]);
-      }
-      BLI_assert(i == vbo_len_capacity);
-    }
-    else {
-      for (int i = 0; i < vbo_len_capacity; i++) {
-        const MVert *mv = &rdata->mvert[i];
-        GPUPackedNormal vnor_pack = GPU_normal_convert_i10_s3(mv->no);
-        vnor_pack.w = (mv->flag & ME_HIDE) ? -1 : ((mv->flag & SELECT) ? 1 : 0);
-        GPU_vertbuf_attr_set(vbo, attr_id.pos, i, rdata->mvert[i].co);
-        GPU_vertbuf_attr_set(vbo, attr_id.nor, i, &vnor_pack);
-      }
-    }
-  }
-  else {
-    const MVert *mvert = rdata->mapped.me_cage->mvert;
-    const int *v_origindex = rdata->mapped.v_origindex;
-    for (int i = 0; i < vbo_len_capacity; i++) {
-      const int v_orig = v_origindex[i];
-      if (v_orig != ORIGINDEX_NONE) {
-        const MVert *mv = &mvert[i];
-        GPUPackedNormal vnor_pack = GPU_normal_convert_i10_s3(mv->no);
-        vnor_pack.w = (mv->flag & ME_HIDE) ? -1 : ((mv->flag & SELECT) ? 1 : 0);
-        GPU_vertbuf_attr_set(vbo, attr_id.pos, i, mv->co);
-        GPU_vertbuf_attr_set(vbo, attr_id.nor, i, &vnor_pack);
-      }
-    }
-  }
-}
-
-static void mesh_create_weights(MeshRenderData *rdata,
-                                GPUVertBuf *vbo,
-                                DRW_MeshWeightState *wstate)
-{
-  static GPUVertFormat format = {0};
-  static struct {
-    uint weight;
-  } attr_id;
-  if (format.attr_len == 0) {
-    attr_id.weight = GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
-  }
-
-  const int vbo_len_capacity = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-
-  mesh_render_data_ensure_vert_weight(rdata, wstate);
-  const float *vert_weight = rdata->vert_weight;
-
-  GPU_vertbuf_init_with_format(vbo, &format);
-  /* Meh, another allocation / copy for no benefit.
-   * Needed because rdata->vert_weight is freed afterwards and
-   * GPU module don't have a GPU_vertbuf_data_from_memory or similar. */
-  /* TODO get rid of the extra allocation/copy. */
-  GPU_vertbuf_data_alloc(vbo, vbo_len_capacity);
-  GPU_vertbuf_attr_fill(vbo, attr_id.weight, vert_weight);
-}
-
-static float mesh_loop_edge_factor_get(const float f_no[3],
-                                       const float v_co[3],
-                                       const float v_no[3],
-                                       const float v_next_co[3])
-{
-  float enor[3], evec[3];
-  sub_v3_v3v3(evec, v_next_co, v_co);
-  cross_v3_v3v3(enor, v_no, evec);
-  normalize_v3(enor);
-  float d = fabsf(dot_v3v3(enor, f_no));
-  /* Rescale to the slider range. */
-  d *= (1.0f / 0.065f);
-  CLAMP(d, 0.0f, 1.0f);
-  return d;
-}
-
-static void vertbuf_raw_step_u8(GPUVertBufRaw *wd_step, const uchar wiredata)
-{
-  *((uchar *)GPU_vertbuf_raw_step(wd_step)) = wiredata;
-}
-
-static void vertbuf_raw_step_u8_to_f32(GPUVertBufRaw *wd_step, const uchar wiredata)
-{
-  *((float *)GPU_vertbuf_raw_step(wd_step)) = wiredata / 255.0f;
-}
-
-static void mesh_create_loop_edge_fac(MeshRenderData *rdata, GPUVertBuf *vbo)
-{
-  static GPUVertFormat format = {0};
-  static struct {
-    uint wd;
-  } attr_id;
-  static union {
-    float f;
-    uchar u;
-  } data;
-  static void (*vertbuf_raw_step)(GPUVertBufRaw *, const uchar);
-  if (format.attr_len == 0) {
-    if (!GPU_crappy_amd_driver()) {
-      /* Some AMD drivers strangely crash with a vbo with this format. */
-      attr_id.wd = GPU_vertformat_attr_add(
-          &format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
-      vertbuf_raw_step = vertbuf_raw_step_u8;
-      data.u = UCHAR_MAX;
-    }
-    else {
-      attr_id.wd = GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
-      vertbuf_raw_step = vertbuf_raw_step_u8_to_f32;
-      data.f = 1.0f;
-    }
-  }
-  const int poly_len = mesh_render_data_polys_len_get(rdata);
-  const int loop_len = mesh_render_data_loops_len_get(rdata);
-  const int edge_len = mesh_render_data_edges_len_get(rdata);
-
-  GPU_vertbuf_init_with_format(vbo, &format);
-  GPU_vertbuf_data_alloc(vbo, loop_len);
-
-  GPUVertBufRaw wd_step;
-  GPU_vertbuf_attr_get_raw_data(vbo, attr_id.wd, &wd_step);
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter iter_efa, iter_loop;
-      BMFace *efa;
-      BMLoop *loop;
-      uint f;
-
-      BM_ITER_MESH_INDEX (efa, &iter_efa, bm, BM_FACES_OF_MESH, f) {
-        BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-          float ratio = mesh_loop_edge_factor_get(
-              efa->no, loop->v->co, loop->v->no, loop->next->v->co);
-          vertbuf_raw_step(&wd_step, ratio * 253 + 1);
-        }
-      }
-      BLI_assert(GPU_vertbuf_raw_used(&wd_step) == loop_len);
-    }
-    else {
-      const MVert *mvert = rdata->mvert;
-      const MPoly *mpoly = rdata->mpoly;
-      const MLoop *mloop = rdata->mloop;
-      MEdge *medge = (MEdge *)rdata->medge;
-      bool use_edge_render = false;
-
-      /* TODO(fclem) We don't need them to be packed. But we need rdata->poly_normals */
-      mesh_render_data_ensure_poly_normals_pack(rdata);
-
-      /* Reset flag */
-      for (int edge = 0; edge < edge_len; ++edge) {
-        /* NOTE: not thread safe. */
-        medge[edge].flag &= ~ME_EDGE_TMP_TAG;
-
-        /* HACK(fclem) Feels like a hack. Detecting the need for edge render. */
-        if ((medge[edge].flag & ME_EDGERENDER) == 0) {
-          use_edge_render = true;
-        }
-      }
-
-      for (int a = 0; a < poly_len; a++, mpoly++) {
-        const float *fnor = rdata->poly_normals[a];
-        for (int b = 0; b < mpoly->totloop; b++) {
-          const MLoop *ml1 = &mloop[mpoly->loopstart + b];
-          const MLoop *ml2 = &mloop[mpoly->loopstart + (b + 1) % mpoly->totloop];
-
-          /* Will only work for edges that have an odd number of faces connected. */
-          MEdge *ed = (MEdge *)rdata->medge + ml1->e;
-          ed->flag ^= ME_EDGE_TMP_TAG;
-
-          if (use_edge_render) {
-            vertbuf_raw_step(&wd_step, (ed->flag & ME_EDGERENDER) ? 255 : 0);
-          }
-          else {
-            float vnor_f[3];
-            normal_short_to_float_v3(vnor_f, mvert[ml1->v].no);
-            float ratio = mesh_loop_edge_factor_get(
-                fnor, mvert[ml1->v].co, vnor_f, mvert[ml2->v].co);
-            vertbuf_raw_step(&wd_step, ratio * 253 + 1);
-          }
-        }
-      }
-      /* Gather non-manifold edges. */
-      for (int l = 0; l < loop_len; l++, mloop++) {
-        MEdge *ed = (MEdge *)rdata->medge + mloop->e;
-        if (ed->flag & ME_EDGE_TMP_TAG) {
-          GPU_vertbuf_attr_set(vbo, attr_id.wd, l, &data);
-        }
-      }
-
-      BLI_assert(loop_len == GPU_vertbuf_raw_used(&wd_step));
-    }
-  }
-  else {
-    BLI_assert(0);
-  }
-}
-
-static void mesh_create_loop_pos_and_nor(MeshRenderData *rdata, GPUVertBuf *vbo)
-{
-  /* TODO deduplicate format creation*/
-  static GPUVertFormat format = {0};
-  static struct {
-    uint pos, nor;
-  } attr_id;
-  if (format.attr_len == 0) {
-    attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-    attr_id.nor = GPU_vertformat_attr_add(
-        &format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-  }
-  const int poly_len = mesh_render_data_polys_len_get(rdata);
-  const int loop_len = mesh_render_data_loops_len_get(rdata);
-
-  GPU_vertbuf_init_with_format(vbo, &format);
-  GPU_vertbuf_data_alloc(vbo, loop_len);
-
-  GPUVertBufRaw pos_step, nor_step;
-  GPU_vertbuf_attr_get_raw_data(vbo, attr_id.pos, &pos_step);
-  GPU_vertbuf_attr_get_raw_data(vbo, attr_id.nor, &nor_step);
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      const GPUPackedNormal *vnor, *pnor;
-      const float(*lnors)[3] = rdata->loop_normals;
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter iter_efa, iter_loop;
-      BMFace *efa;
-      BMLoop *loop;
-      uint f;
-
-      if (rdata->loop_normals == NULL) {
-        mesh_render_data_ensure_poly_normals_pack(rdata);
-        mesh_render_data_ensure_vert_normals_pack(rdata);
-        vnor = rdata->vert_normals_pack;
-        pnor = rdata->poly_normals_pack;
-      }
-
-      BM_ITER_MESH_INDEX (efa, &iter_efa, bm, BM_FACES_OF_MESH, f) {
-        const bool face_smooth = BM_elem_flag_test(efa, BM_ELEM_SMOOTH);
-
-        BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-          BLI_assert(GPU_vertbuf_raw_used(&pos_step) == BM_elem_index_get(loop));
-          copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), loop->v->co);
-
-          if (lnors) {
-            GPUPackedNormal plnor = GPU_normal_convert_i10_v3(lnors[BM_elem_index_get(loop)]);
-            *((GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step)) = plnor;
-          }
-          else if (!face_smooth) {
-            *((GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step)) = pnor[f];
-          }
-          else {
-            *((GPUPackedNormal *)GPU_vertbuf_raw_step(
-                &nor_step)) = vnor[BM_elem_index_get(loop->v)];
-          }
-        }
-      }
-      BLI_assert(GPU_vertbuf_raw_used(&pos_step) == loop_len);
-    }
-    else {
-      const MVert *mvert = rdata->mvert;
-      const MPoly *mpoly = rdata->mpoly;
-
-      if (rdata->loop_normals == NULL) {
-        mesh_render_data_ensure_poly_normals_pack(rdata);
-      }
-
-      for (int a = 0; a < poly_len; a++, mpoly++) {
-        const MLoop *mloop = rdata->mloop + mpoly->loopstart;
-        const float(*lnors)[3] = (rdata->loop_normals) ? &rdata->loop_normals[mpoly->loopstart] :
-                                                         NULL;
-        const GPUPackedNormal *fnor = (mpoly->flag & ME_SMOOTH) ? NULL :
-                                                                  &rdata->poly_normals_pack[a];
-        const int hide_select_flag = (mpoly->flag & ME_HIDE) ?
-                                         -1 :
-                                         ((mpoly->flag & ME_FACE_SEL) ? 1 : 0);
-        for (int b = 0; b < mpoly->totloop; b++, mloop++) {
-          copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[mloop->v].co);
-          GPUPackedNormal *pnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step);
-          if (lnors) {
-            *pnor = GPU_normal_convert_i10_v3(lnors[b]);
-          }
-          else if (fnor) {
-            *pnor = *fnor;
-          }
-          else {
-            *pnor = GPU_normal_convert_i10_s3(mvert[mloop->v].no);
-          }
-          pnor->w = hide_select_flag;
-        }
-      }
-
-      BLI_assert(loop_len == GPU_vertbuf_raw_used(&pos_step));
-    }
-  }
-  else {
-    const int *p_origindex = rdata->mapped.p_origindex;
-    const MVert *mvert = rdata->mvert;
-    const MPoly *mpoly = rdata->mpoly;
-
-    if (rdata->loop_normals == NULL) {
-      mesh_render_data_ensure_poly_normals_pack(rdata);
-    }
-
-    for (int a = 0; a < poly_len; a++, mpoly++) {
-      const MLoop *mloop = rdata->mloop + mpoly->loopstart;
-      const float(*lnors)[3] = (rdata->loop_normals) ? &rdata->loop_normals[mpoly->loopstart] :
-                                                       NULL;
-      const GPUPackedNormal *fnor = (mpoly->flag & ME_SMOOTH) ? NULL :
-                                                                &rdata->poly_normals_pack[a];
-      if (p_origindex[a] == ORIGINDEX_NONE) {
-        continue;
-      }
-      for (int b = 0; b < mpoly->totloop; b++, mloop++) {
-        copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[mloop->v].co);
-        GPUPackedNormal *pnor = (GPUPackedNormal *)GPU_vertbuf_raw_step(&nor_step);
-        if (lnors) {
-          *pnor = GPU_normal_convert_i10_v3(lnors[b]);
-        }
-        else if (fnor) {
-          *pnor = *fnor;
-        }
-        else {
-          *pnor = GPU_normal_convert_i10_s3(mvert[mloop->v].no);
-        }
-      }
-    }
-  }
-
-  int vbo_len_used = GPU_vertbuf_raw_used(&pos_step);
-  if (vbo_len_used < loop_len) {
-    GPU_vertbuf_data_resize(vbo, vbo_len_used);
-  }
-}
-
-static void mesh_create_loop_orco(MeshRenderData *rdata, GPUVertBuf *vbo)
-{
-  const uint loops_len = mesh_render_data_loops_len_get(rdata);
-
-  /* initialize vertex format */
-  GPUVertFormat format = {0};
-  GPUVertBufRaw vbo_step;
-
-  /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex attribs.
-   * This is a substential waste of Vram and should be done another way. Unfortunately,
-   * at the time of writing, I did not found any other "non disruptive" alternative. */
-  uint attr_id = GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
-
-  GPU_vertbuf_init_with_format(vbo, &format);
-  GPU_vertbuf_data_alloc(vbo, loops_len);
-  GPU_vertbuf_attr_get_raw_data(vbo, attr_id, &vbo_step);
-
-  if (rdata->edit_bmesh) {
-    BMesh *bm = rdata->edit_bmesh->bm;
-    BMIter iter_efa, iter_loop;
-    BMFace *efa;
-    BMLoop *loop;
-
-    BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) {
-      BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-        float *data = (float *)GPU_vertbuf_raw_step(&vbo_step);
-        copy_v3_v3(data, rdata->orco[BM_elem_index_get(loop->v)]);
-        data[3] = 0.0; /* Tag as not a generic attrib */
-      }
-    }
-  }
-  else {
-    for (uint l = 0; l < loops_len; l++) {
-      float *data = (float *)GPU_vertbuf_raw_step(&vbo_step);
-      copy_v3_v3(data, rdata->orco[rdata->mloop[l].v]);
-      data[3] = 0.0; /* Tag as not a generic attrib */
-    }
-  }
-}
-
-static void mesh_create_loop_uv_and_tan(MeshRenderData *rdata, GPUVertBuf *vbo)
-{
-  const uint loops_len = mesh_render_data_loops_len_get(rdata);
-  const uint uv_len = rdata->cd.layers.uv_len;
-  const uint tangent_len = rdata->cd.layers.tangent_len;
-  const uint layers_combined_len = uv_len + tangent_len;
-
-  GPUVertBufRaw *layers_combined_step = BLI_array_alloca(layers_combined_step,
-                                                         layers_combined_len);
-  GPUVertBufRaw *uv_step = layers_combined_step;
-  GPUVertBufRaw *tangent_step = uv_step + uv_len;
-
-  uint *layers_combined_id = BLI_array_alloca(layers_combined_id, layers_combined_len);
-  uint *uv_id = layers_combined_id;
-  uint *tangent_id = uv_id + uv_len;
-
-  /* initialize vertex format */
-  GPUVertFormat format = {0};
-
-  for (uint i = 0; i < uv_len; i++) {
-    const char *attr_name = mesh_render_data_uv_layer_uuid_get(rdata, i);
-#if 0 /* these are clamped. Maybe use them as an option in the future */
-    uv_id[i] = GPU_vertformat_attr_add(
-        &format, attr_name, GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
-#else
-    uv_id[i] = GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
-#endif
-    /* Auto Name */
-    attr_name = mesh_render_data_uv_auto_layer_uuid_get(rdata, i);
-    GPU_vertformat_alias_add(&format, attr_name);
-
-    if (i == rdata->cd.layers.uv_render) {
-      GPU_vertformat_alias_add(&format, "u");
-    }
-    if (i == rdata->cd.layers.uv_active) {
-      GPU_vertformat_alias_add(&format, "au");
-    }
-    if (i == rdata->cd.layers.uv_mask_active) {
-      GPU_vertformat_alias_add(&format, "mu");
-    }
-  }
-
-  for (uint i = 0; i < tangent_len; i++) {
-    const char *attr_name = mesh_render_data_tangent_layer_uuid_get(rdata, i);
-#ifdef USE_COMP_MESH_DATA
-    tangent_id[i] = GPU_vertformat_attr_add(
-        &format, attr_name, GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-#else
-    tangent_id[i] = GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
-#endif
-    if (i == rdata->cd.layers.tangent_render) {
-      GPU_vertformat_alias_add(&format, "t");
-    }
-    if (i == rdata->cd.layers.tangent_active) {
-      GPU_vertformat_alias_add(&format, "at");
-    }
-  }
-
-  /* HACK: Create a dummy attribute in case there is no valid UV/tangent layer. */
-  if (layers_combined_len == 0) {
-    GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
-  }
-
-  GPU_vertbuf_init_with_format(vbo, &format);
-  GPU_vertbuf_data_alloc(vbo, loops_len);
-
-  for (uint i = 0; i < uv_len; i++) {
-    GPU_vertbuf_attr_get_raw_data(vbo, uv_id[i], &uv_step[i]);
-  }
-  for (uint i = 0; i < tangent_len; i++) {
-    GPU_vertbuf_attr_get_raw_data(vbo, tangent_id[i], &tangent_step[i]);
-  }
-
-  if (rdata->edit_bmesh) {
-    BMesh *bm = rdata->edit_bmesh->bm;
-    BMIter iter_efa, iter_loop;
-    BMFace *efa;
-    BMLoop *loop;
-
-    BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) {
-      BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-        /* UVs */
-        for (uint j = 0; j < uv_len; j++) {
-          const uint layer_offset = rdata->cd.offset.uv[j];
-          const float *elem = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->uv;
-          copy_v2_v2(GPU_vertbuf_raw_step(&uv_step[j]), elem);
-        }
-        /* TANGENTs */
-        for (uint j = 0; j < tangent_len; j++) {
-          float(*layer_data)[4] = rdata->cd.layers.tangent[j];
-          const float *elem = layer_data[BM_elem_index_get(loop)];
-#ifdef USE_COMP_MESH_DATA
-          normal_float_to_short_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem);
-#else
-          copy_v4_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem);
-#endif
-        }
-      }
-    }
-  }
-  else {
-    for (uint loop = 0; loop < loops_len; loop++) {
-      /* UVs */
-      for (uint j = 0; j < uv_len; j++) {
-        const MLoopUV *layer_data = rdata->cd.layers.uv[j];
-        const float *elem = layer_data[loop].uv;
-        copy_v2_v2(GPU_vertbuf_raw_step(&uv_step[j]), elem);
-      }
-      /* TANGENTs */
-      for (uint j = 0; j < tangent_len; j++) {
-        float(*layer_data)[4] = rdata->cd.layers.tangent[j];
-        const float *elem = layer_data[loop];
-#ifdef USE_COMP_MESH_DATA
-        normal_float_to_short_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem);
-#else
-        copy_v4_v4(GPU_vertbuf_raw_step(&tangent_step[j]), elem);
-#endif
-      }
-    }
-  }
-
-#ifndef NDEBUG
-  /* Check all layers are write aligned. */
-  if (layers_combined_len > 0) {
-    int vbo_len_used = GPU_vertbuf_raw_used(&layers_combined_step[0]);
-    for (uint i = 0; i < layers_combined_len; i++) {
-      BLI_assert(vbo_len_used == GPU_vertbuf_raw_used(&layers_combined_step[i]));
-    }
-  }
-#endif
-
-#undef USE_COMP_MESH_DATA
-}
-
-static void mesh_create_loop_vcol(MeshRenderData *rdata, GPUVertBuf *vbo)
-{
-  const uint loops_len = mesh_render_data_loops_len_get(rdata);
-  const uint vcol_len = rdata->cd.layers.vcol_len;
-
-  GPUVertBufRaw *vcol_step = BLI_array_alloca(vcol_step, vcol_len);
-  uint *vcol_id = BLI_array_alloca(vcol_id, vcol_len);
-
-  /* initialize vertex format */
-  GPUVertFormat format = {0};
-
-  for (uint i = 0; i < vcol_len; i++) {
-    const char *attr_name = mesh_render_data_vcol_layer_uuid_get(rdata, i);
-    vcol_id[i] = GPU_vertformat_attr_add(
-        &format, attr_name, GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
-    /* Auto layer */
-    if (rdata->cd.layers.auto_vcol[i]) {
-      attr_name = mesh_render_data_vcol_auto_layer_uuid_get(rdata, i);
-      GPU_vertformat_alias_add(&format, attr_name);
-    }
-    if (i == rdata->cd.layers.vcol_render) {
-      GPU_vertformat_alias_add(&format, "c");
-    }
-    if (i == rdata->cd.layers.vcol_active) {
-      GPU_vertformat_alias_add(&format, "ac");
-    }
-  }
-
-  GPU_vertbuf_init_with_format(vbo, &format);
-  GPU_vertbuf_data_alloc(vbo, loops_len);
-
-  for (uint i = 0; i < vcol_len; i++) {
-    GPU_vertbuf_attr_get_raw_data(vbo, vcol_id[i], &vcol_step[i]);
-  }
-
-  if (rdata->edit_bmesh) {
-    BMesh *bm = rdata->edit_bmesh->bm;
-    BMIter iter_efa, iter_loop;
-    BMFace *efa;
-    BMLoop *loop;
-
-    BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) {
-      BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-        for (uint j = 0; j < vcol_len; j++) {
-          const uint layer_offset = rdata->cd.offset.vcol[j];
-          const uchar *elem = &((MLoopCol *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->r;
-          copy_v3_v3_uchar(GPU_vertbuf_raw_step(&vcol_step[j]), elem);
-        }
-      }
-    }
-  }
-  else {
-    for (uint loop = 0; loop < loops_len; loop++) {
-      for (uint j = 0; j < vcol_len; j++) {
-        const MLoopCol *layer_data = rdata->cd.layers.vcol[j];
-        const uchar *elem = &layer_data[loop].r;
-        copy_v3_v3_uchar(GPU_vertbuf_raw_step(&vcol_step[j]), elem);
-      }
-    }
-  }
-
-#ifndef NDEBUG
-  /* Check all layers are write aligned. */
-  if (vcol_len > 0) {
-    int vbo_len_used = GPU_vertbuf_raw_used(&vcol_step[0]);
-    for (uint i = 0; i < vcol_len; i++) {
-      BLI_assert(vbo_len_used == GPU_vertbuf_raw_used(&vcol_step[i]));
-    }
-  }
-#endif
-
-#undef USE_COMP_MESH_DATA
-}
-
-static void mesh_create_edit_facedots(MeshRenderData *rdata,
-                                      GPUVertBuf *vbo_facedots_pos_nor_data,
-                                      Scene *scene,
-                                      Object *ob)
-{
-  const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata);
-  const int verts_facedot_len = poly_len;
-  int facedot_len_used = 0;
-
-  static struct {
-    uint fdot_pos, fdot_nor_flag;
-  } attr_id;
-  static GPUVertFormat facedot_format = {0};
-  if (facedot_format.attr_len == 0) {
-    attr_id.fdot_pos = GPU_vertformat_attr_add(
-        &facedot_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-    attr_id.fdot_nor_flag = GPU_vertformat_attr_add(
-        &facedot_format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-  }
-
-  if (DRW_TEST_ASSIGN_VBO(vbo_facedots_pos_nor_data)) {
-    GPU_vertbuf_init_with_format(vbo_facedots_pos_nor_data, &facedot_format);
-    GPU_vertbuf_data_alloc(vbo_facedots_pos_nor_data, verts_facedot_len);
-    /* TODO(fclem): Maybe move data generation to mesh_render_data_create() */
-    if (rdata->edit_bmesh) {
-      if (rdata->edit_data && rdata->edit_data->vertexCos != NULL) {
-        BKE_editmesh_cache_ensure_poly_normals(rdata->edit_bmesh, rdata->edit_data);
-        BKE_editmesh_cache_ensure_poly_centers(rdata->edit_bmesh, rdata->edit_data);
-      }
-    }
-  }
-
-  if (rdata->mapped.use == false) {
-    for (int i = 0; i < poly_len; i++) {
-      if (add_edit_facedot(rdata,
-                           vbo_facedots_pos_nor_data,
-                           attr_id.fdot_pos,
-                           attr_id.fdot_nor_flag,
-                           i,
-                           facedot_len_used)) {
-        facedot_len_used += 1;
-      }
-    }
-  }
-  else {
-    if (modifiers_usesSubsurfFacedots(scene, ob)) {
-      /* Facedots that follow surbsurf face center. */
-      Mesh *me_cage = rdata->mapped.me_cage;
-      const MPoly *mpoly = me_cage->mpoly;
-      for (int p = 0; p < poly_len; p++, mpoly++) {
-        const MLoop *mloop = me_cage->mloop + mpoly->loopstart;
-        for (int l = 0; l < mpoly->totloop; l++, mloop++) {
-          if (me_cage->mvert[mloop->v].flag & ME_VERT_FACEDOT) {
-            if (add_edit_facedot_subdiv(rdata,
-                                        vbo_facedots_pos_nor_data,
-                                        attr_id.fdot_pos,
-                                        attr_id.fdot_nor_flag,
-                                        mloop->v,
-                                        p,
-                                        facedot_len_used)) {
-              facedot_len_used += 1;
-            }
-          }
-        }
-      }
-    }
-    else {
-      for (int i = 0; i < poly_len; i++) {
-        if (add_edit_facedot_mapped(rdata,
-                                    vbo_facedots_pos_nor_data,
-                                    attr_id.fdot_pos,
-                                    attr_id.fdot_nor_flag,
-                                    i,
-                                    facedot_len_used)) {
-          facedot_len_used += 1;
-        }
-      }
-    }
-  }
-
-  /* Resize & Finish */
-  if (facedot_len_used != verts_facedot_len) {
-    if (vbo_facedots_pos_nor_data != NULL) {
-      GPU_vertbuf_data_resize(vbo_facedots_pos_nor_data, facedot_len_used);
-    }
-  }
-}
-
-static void mesh_create_edit_mesh_analysis(MeshRenderData *rdata, GPUVertBuf *vbo_mesh_analysis)
-{
-  const MeshStatVis *mesh_stat_vis = &rdata->toolsettings->statvis;
-
-  int mesh_analysis_len_used = 0;
-
-  const uint loops_len = mesh_render_data_loops_len_get(rdata);
-  BMesh *bm = rdata->edit_bmesh->bm;
-  BMIter iter_efa, iter_loop;
-  BMFace *efa;
-  BMLoop *loop;
-
-  static struct {
-    uint weight;
-  } attr_id;
-  static GPUVertFormat mesh_analysis_format = {0};
-  if (mesh_analysis_format.attr_len == 0) {
-    attr_id.weight = GPU_vertformat_attr_add(
-        &mesh_analysis_format, "weight_color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-  }
-
-  /* TODO(jbakker): Maybe move data generation to mesh_render_data_create() */
-  BKE_editmesh_statvis_calc(rdata->edit_bmesh, rdata->edit_data, mesh_stat_vis);
-
-  if (DRW_TEST_ASSIGN_VBO(vbo_mesh_analysis)) {
-    GPU_vertbuf_init_with_format(vbo_mesh_analysis, &mesh_analysis_format);
-    GPU_vertbuf_data_alloc(vbo_mesh_analysis, loops_len);
-  }
-
-  const bool is_vertex_data = mesh_stat_vis->type == SCE_STATVIS_SHARP;
-  if (is_vertex_data) {
-    BM_ITER_MESH (efa, &iter_efa, bm, BM_FACES_OF_MESH) {
-      BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-        uint vertex_index = BM_elem_index_get(loop->v);
-        GPU_vertbuf_attr_set(vbo_mesh_analysis,
-                             attr_id.weight,
-                             mesh_analysis_len_used,
-                             &rdata->edit_bmesh->derivedVertColor[vertex_index]);
-        mesh_analysis_len_used += 1;
-      }
-    }
-  }
-  else {
-    uint face_index;
-    BM_ITER_MESH_INDEX (efa, &iter_efa, bm, BM_FACES_OF_MESH, face_index) {
-      BM_ITER_ELEM (loop, &iter_loop, efa, BM_LOOPS_OF_FACE) {
-        GPU_vertbuf_attr_set(vbo_mesh_analysis,
-                             attr_id.weight,
-                             mesh_analysis_len_used,
-                             &rdata->edit_bmesh->derivedFaceColor[face_index]);
-        mesh_analysis_len_used += 1;
-      }
-    }
-  }
-
-  // Free temp data in edit bmesh
-  BKE_editmesh_color_free(rdata->edit_bmesh);
-
-  /* Resize & Finish */
-  if (mesh_analysis_len_used != loops_len) {
-    if (vbo_mesh_analysis != NULL) {
-      GPU_vertbuf_data_resize(vbo_mesh_analysis, mesh_analysis_len_used);
-    }
-  }
-}
-/* Indices */
-
-#define NO_EDGE INT_MAX
-static void mesh_create_edges_adjacency_lines(MeshRenderData *rdata,
-                                              GPUIndexBuf *ibo,
-                                              bool *r_is_manifold,
-                                              const bool use_hide)
-{
-  const MLoopTri *mlooptri;
-  const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-  const int tri_len = mesh_render_data_looptri_len_get_maybe_mapped(rdata);
-
-  *r_is_manifold = true;
-
-  /* Allocate max but only used indices are sent to GPU. */
-  GPUIndexBufBuilder elb;
-  GPU_indexbuf_init(&elb, GPU_PRIM_LINES_ADJ, tri_len * 3, vert_len);
-
-  if (rdata->mapped.use) {
-    Mesh *me_cage = rdata->mapped.me_cage;
-    mlooptri = BKE_mesh_runtime_looptri_ensure(me_cage);
-  }
-  else {
-    mlooptri = rdata->mlooptri;
-  }
-
-  EdgeHash *eh = BLI_edgehash_new_ex(__func__, tri_len * 3);
-  /* Create edges for each pair of triangles sharing an edge. */
-  for (int i = 0; i < tri_len; i++) {
-    for (int e = 0; e < 3; e++) {
-      uint v0, v1, v2;
-      if (rdata->mapped.use) {
-        const MLoop *mloop = rdata->mloop;
-        const MLoopTri *mlt = mlooptri + i;
-        const int p_orig = rdata->mapped.p_origindex[mlt->poly];
-        if (p_orig != ORIGINDEX_NONE) {
-          BMesh *bm = rdata->edit_bmesh->bm;
-          BMFace *efa = BM_face_at_index(bm, p_orig);
-          /* Assume 'use_hide' */
-          if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-            break;
-          }
-        }
-        v0 = mloop[mlt->tri[e]].v;
-        v1 = mloop[mlt->tri[(e + 1) % 3]].v;
-        v2 = mloop[mlt->tri[(e + 2) % 3]].v;
-      }
-      else if (rdata->edit_bmesh) {
-        const BMLoop **bm_looptri = (const BMLoop **)rdata->edit_bmesh->looptris[i];
-        if (BM_elem_flag_test(bm_looptri[0]->f, BM_ELEM_HIDDEN)) {
-          break;
-        }
-        v0 = BM_elem_index_get(bm_looptri[e]->v);
-        v1 = BM_elem_index_get(bm_looptri[(e + 1) % 3]->v);
-        v2 = BM_elem_index_get(bm_looptri[(e + 2) % 3]->v);
-      }
-      else {
-        const MLoop *mloop = rdata->mloop;
-        const MLoopTri *mlt = mlooptri + i;
-        const MPoly *mp = &rdata->mpoly[mlt->poly];
-        if (use_hide && (mp->flag & ME_HIDE)) {
-          break;
-        }
-        v0 = mloop[mlt->tri[e]].v;
-        v1 = mloop[mlt->tri[(e + 1) % 3]].v;
-        v2 = mloop[mlt->tri[(e + 2) % 3]].v;
-      }
-      bool inv_indices = (v1 > v2);
-      void **pval;
-      bool value_is_init = BLI_edgehash_ensure_p(eh, v1, v2, &pval);
-      int v_data = POINTER_AS_INT(*pval);
-      if (!value_is_init || v_data == NO_EDGE) {
-        /* Save the winding order inside the sign bit. Because the
-         * edgehash sort the keys and we need to compare winding later. */
-        int value = (int)v0 + 1; /* Int 0 bm_looptricannot be signed */
-        *pval = POINTER_FROM_INT((inv_indices) ? -value : value);
-      }
-      else {
-        /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */
-        *pval = POINTER_FROM_INT(NO_EDGE);
-        bool inv_opposite = (v_data < 0);
-        uint v_opposite = (uint)abs(v_data) - 1;
-
-        if (inv_opposite == inv_indices) {
-          /* Don't share edge if triangles have non matching winding. */
-          GPU_indexbuf_add_line_adj_verts(&elb, v0, v1, v2, v0);
-          GPU_indexbuf_add_line_adj_verts(&elb, v_opposite, v1, v2, v_opposite);
-          *r_is_manifold = false;
-        }
-        else {
-          GPU_indexbuf_add_line_adj_verts(&elb, v0, v1, v2, v_opposite);
-        }
-      }
-    }
-  }
-  /* Create edges for remaining non manifold edges. */
-  EdgeHashIterator *ehi;
-  for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false;
-       BLI_edgehashIterator_step(ehi)) {
-    uint v1, v2;
-    int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
-    if (v_data == NO_EDGE) {
-      continue;
-    }
-    BLI_edgehashIterator_getKey(ehi, &v1, &v2);
-    uint v0 = (uint)abs(v_data) - 1;
-    if (v_data < 0) { /* inv_opposite  */
-      SWAP(uint, v1, v2);
-    }
-    GPU_indexbuf_add_line_adj_verts(&elb, v0, v1, v2, v0);
-    *r_is_manifold = false;
-  }
-  BLI_edgehashIterator_free(ehi);
-  BLI_edgehash_free(eh, NULL);
+  cache->is_dirty = false;
+  cache->batch_ready = 0;
+  cache->batch_requested = 0;
 
-  GPU_indexbuf_build_in_place(&elb, ibo);
+  drw_mesh_weight_state_clear(&cache->weight_state);
 }
-#undef NO_EDGE
 
-static void mesh_create_edges_lines(MeshRenderData *rdata, GPUIndexBuf *ibo, const bool use_hide)
+void DRW_mesh_batch_cache_validate(Mesh *me)
 {
-  const int verts_len = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-  const int edges_len = mesh_render_data_edges_len_get_maybe_mapped(rdata);
-
-  GPUIndexBufBuilder elb;
-  GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edges_len, verts_len);
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter iter;
-      BMEdge *eed;
-
-      BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
-        /* use_hide always for edit-mode */
-        if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
-          continue;
-        }
-        GPU_indexbuf_add_line_verts(&elb, BM_elem_index_get(eed->v1), BM_elem_index_get(eed->v2));
-      }
-    }
-    else {
-      const MEdge *ed = rdata->medge;
-      for (int i = 0; i < edges_len; i++, ed++) {
-        if ((ed->flag & ME_EDGERENDER) == 0) {
-          continue;
-        }
-        if (!(use_hide && (ed->flag & ME_HIDE))) {
-          GPU_indexbuf_add_line_verts(&elb, ed->v1, ed->v2);
-        }
-      }
-    }
-  }
-  else {
-    BMesh *bm = rdata->edit_bmesh->bm;
-    const MEdge *edge = rdata->medge;
-    for (int i = 0; i < edges_len; i++, edge++) {
-      const int p_orig = rdata->mapped.e_origindex[i];
-      if (p_orig != ORIGINDEX_NONE) {
-        BMEdge *eed = BM_edge_at_index(bm, p_orig);
-        if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
-          GPU_indexbuf_add_line_verts(&elb, edge->v1, edge->v2);
-        }
-      }
-    }
+  if (!mesh_batch_cache_valid(me)) {
+    mesh_batch_cache_clear(me);
+    mesh_batch_cache_init(me);
   }
-
-  GPU_indexbuf_build_in_place(&elb, ibo);
 }
 
-static void mesh_create_surf_tris(MeshRenderData *rdata, GPUIndexBuf *ibo, const bool use_hide)
+static MeshBatchCache *mesh_batch_cache_get(Mesh *me)
 {
-  const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-  const int tri_len = mesh_render_data_looptri_len_get(rdata);
-
-  GPUIndexBufBuilder elb;
-  GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tri_len, vert_len * 3);
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      for (int i = 0; i < tri_len; i++) {
-        const BMLoop **bm_looptri = (const BMLoop **)rdata->edit_bmesh->looptris[i];
-        const BMFace *bm_face = bm_looptri[0]->f;
-        /* use_hide always for edit-mode */
-        if (BM_elem_flag_test(bm_face, BM_ELEM_HIDDEN)) {
-          continue;
-        }
-        GPU_indexbuf_add_tri_verts(&elb,
-                                   BM_elem_index_get(bm_looptri[0]->v),
-                                   BM_elem_index_get(bm_looptri[1]->v),
-                                   BM_elem_index_get(bm_looptri[2]->v));
-      }
-    }
-    else {
-      const MLoop *loops = rdata->mloop;
-      for (int i = 0; i < tri_len; i++) {
-        const MLoopTri *mlt = &rdata->mlooptri[i];
-        const MPoly *mp = &rdata->mpoly[mlt->poly];
-        if (use_hide && (mp->flag & ME_HIDE)) {
-          continue;
-        }
-        GPU_indexbuf_add_tri_verts(
-            &elb, loops[mlt->tri[0]].v, loops[mlt->tri[1]].v, loops[mlt->tri[2]].v);
-      }
-    }
-  }
-  else {
-    /* Note: mapped doesn't support lnors yet. */
-    BMesh *bm = rdata->edit_bmesh->bm;
-    Mesh *me_cage = rdata->mapped.me_cage;
-
-    const MLoop *loops = rdata->mloop;
-    const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me_cage);
-    for (int i = 0; i < tri_len; i++) {
-      const MLoopTri *mlt = &mlooptri[i];
-      const int p_orig = rdata->mapped.p_origindex[mlt->poly];
-      if (p_orig != ORIGINDEX_NONE) {
-        /* Assume 'use_hide' */
-        BMFace *efa = BM_face_at_index(bm, p_orig);
-        if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-          GPU_indexbuf_add_tri_verts(
-              &elb, loops[mlt->tri[0]].v, loops[mlt->tri[1]].v, loops[mlt->tri[2]].v);
-        }
-      }
-    }
-  }
-
-  GPU_indexbuf_build_in_place(&elb, ibo);
+  return me->runtime.batch_cache;
 }
 
-static void mesh_create_loops_lines(MeshRenderData *rdata, GPUIndexBuf *ibo, const bool use_hide)
+static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache,
+                                                const struct DRW_MeshWeightState *wstate)
 {
-  const int edge_len = mesh_render_data_edges_len_get(rdata);
-  const int loop_len = mesh_render_data_loops_len_get(rdata);
-  const int poly_len = mesh_render_data_polys_len_get(rdata);
-
-  GPUIndexBufBuilder elb;
-  GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edge_len, loop_len);
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter iter;
-      BMEdge *bm_edge;
-
-      BM_ITER_MESH (bm_edge, &iter, bm, BM_EDGES_OF_MESH) {
-        /* use_hide always for edit-mode */
-        if (!BM_elem_flag_test(bm_edge, BM_ELEM_HIDDEN) && bm_edge->l != NULL) {
-          BMLoop *bm_loop1 = bm_vert_find_first_loop_visible_inline(bm_edge->v1);
-          BMLoop *bm_loop2 = bm_vert_find_first_loop_visible_inline(bm_edge->v2);
-          int v1 = BM_elem_index_get(bm_loop1);
-          int v2 = BM_elem_index_get(bm_loop2);
-          if (v1 > v2) {
-            SWAP(int, v1, v2);
-          }
-          GPU_indexbuf_add_line_verts(&elb, v1, v2);
-        }
-      }
+  if (!drw_mesh_weight_state_compare(&cache->weight_state, wstate)) {
+    FOREACH_MESH_BUFFER_CACHE(cache, mbufcache)
+    {
+      GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.weights);
     }
-    else {
-      MLoop *mloop = (MLoop *)rdata->mloop;
-      MEdge *medge = (MEdge *)rdata->medge;
+    GPU_BATCH_CLEAR_SAFE(cache->batch.surface_weights);
 
-      /* Reset flag */
-      for (int edge = 0; edge < edge_len; ++edge) {
-        /* NOTE: not thread safe. */
-        medge[edge].flag &= ~ME_EDGE_TMP_TAG;
-      }
+    cache->batch_ready &= ~MBC_SURFACE_WEIGHTS;
 
-      for (int poly = 0; poly < poly_len; poly++) {
-        const MPoly *mp = &rdata->mpoly[poly];
-        if (!(use_hide && (mp->flag & ME_HIDE))) {
-          for (int j = 0; j < mp->totloop; j++) {
-            MEdge *ed = (MEdge *)rdata->medge + mloop[mp->loopstart + j].e;
-            if ((ed->flag & ME_EDGE_TMP_TAG) == 0) {
-              ed->flag |= ME_EDGE_TMP_TAG;
-              int v1 = mp->loopstart + j;
-              int v2 = mp->loopstart + (j + 1) % mp->totloop;
-              GPU_indexbuf_add_line_verts(&elb, v1, v2);
-            }
-          }
-        }
-      }
-    }
+    drw_mesh_weight_state_clear(&cache->weight_state);
   }
-
-  GPU_indexbuf_build_in_place(&elb, ibo);
 }
 
-static void mesh_create_loops_lines_paint_mask(MeshRenderData *rdata, GPUIndexBuf *ibo)
+static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
 {
-  const int loop_len = mesh_render_data_loops_len_get(rdata);
-  const int poly_len = mesh_render_data_polys_len_get(rdata);
-  const int edge_len = mesh_render_data_edges_len_get(rdata);
-
-  GPUIndexBufBuilder elb;
-  GPU_indexbuf_init(&elb, GPU_PRIM_LINES, loop_len, loop_len);
-
-  if (rdata->edit_bmesh) {
-    /* painting does not use the edit_bmesh */
-    BLI_assert(0);
-  }
-  else {
-    if (rdata->me->editflag & ME_EDIT_PAINT_FACE_SEL) {
-      /* Each edge has two bits used to count selected edges as 0, 1, 2+. */
-      BLI_bitmap *edges_used = BLI_BITMAP_NEW(edge_len * 2, __func__);
-
-      /* Fill the edge bitmap table. */
-      for (int poly = 0; poly < poly_len; poly++) {
-        const MPoly *mpoly = &rdata->mpoly[poly];
-
-        /* Do not check faces that are hidden and faces that aren't selected */
-        if (mpoly->flag & ME_HIDE || ((mpoly->flag & ME_FACE_SEL) == 0)) {
-          continue;
-        }
-
-        for (int loop_index = mpoly->loopstart, loop_index_end = mpoly->loopstart + mpoly->totloop;
-             loop_index < loop_index_end;
-             loop_index++) {
-          const MLoop *mloop = &rdata->mloop[loop_index];
-          const int e_a = mloop->e * 2;
-          const int e_b = e_a + 1;
-          if (!BLI_BITMAP_TEST(edges_used, e_a)) {
-            BLI_BITMAP_ENABLE(edges_used, e_a);
-          }
-          else {
-            BLI_BITMAP_ENABLE(edges_used, e_b);
-          }
-        }
-      }
-
-      for (int poly = 0; poly < poly_len; poly++) {
-        const MPoly *mpoly = &rdata->mpoly[poly];
-        if (!(mpoly->flag & ME_HIDE)) {
-
-          for (int loop_index_next = mpoly->loopstart,
-                   loop_index_end = mpoly->loopstart + mpoly->totloop,
-                   loop_index_curr = loop_index_end - 1;
-               loop_index_next < loop_index_end;
-               loop_index_curr = loop_index_next++) {
-            const MLoop *mloop = &rdata->mloop[loop_index_curr];
-            const int e_a = mloop->e * 2;
-            const int e_b = e_a + 1;
-
-            /* Draw if a boundary or entirely unselected. */
-            if (!BLI_BITMAP_TEST(edges_used, e_b)) {
-              GPU_indexbuf_add_line_verts(&elb, loop_index_curr, loop_index_next);
-            }
-          }
-        }
-      }
-
-      MEM_freeN(edges_used);
-    }
-    else {
-      /* Add edges. */
-      for (int poly = 0; poly < poly_len; poly++) {
-        const MPoly *mpoly = &rdata->mpoly[poly];
-        for (int loop_index_next = mpoly->loopstart,
-                 loop_index_end = mpoly->loopstart + mpoly->totloop,
-                 loop_index_curr = loop_index_end - 1;
-             loop_index_next < loop_index_end;
-             loop_index_curr = loop_index_next++) {
-          GPU_indexbuf_add_line_verts(&elb, loop_index_curr, loop_index_next);
-        }
-      }
-    }
+  FOREACH_MESH_BUFFER_CACHE(cache, mbufcache)
+  {
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv_tan);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco);
   }
 
-  GPU_indexbuf_build_in_place(&elb, ibo);
-}
-
-static void mesh_create_loops_line_strips(MeshRenderData *rdata,
-                                          GPUIndexBuf *ibo,
-                                          const bool use_hide)
-{
-  const int loop_len = mesh_render_data_loops_len_get(rdata);
-  const int poly_len = mesh_render_data_polys_len_get(rdata);
-
-  GPUIndexBufBuilder elb;
-  GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, loop_len + poly_len * 2, loop_len);
-
-  uint v_index = 0;
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter iter;
-      BMFace *bm_face;
-
-      BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
-        /* use_hide always for edit-mode */
-        if (!BM_elem_flag_test(bm_face, BM_ELEM_HIDDEN)) {
-          for (int i = 0; i < bm_face->len; i++) {
-            GPU_indexbuf_add_generic_vert(&elb, v_index + i);
-          }
-          /* Finish loop and restart primitive. */
-          GPU_indexbuf_add_generic_vert(&elb, v_index);
-          GPU_indexbuf_add_primitive_restart(&elb);
-        }
-        v_index += bm_face->len;
-      }
-    }
-    else {
-      for (int poly = 0; poly < poly_len; poly++) {
-        const MPoly *mp = &rdata->mpoly[poly];
-        if (!(use_hide && (mp->flag & ME_HIDE))) {
-          const int loopend = mp->loopstart + mp->totloop;
-          for (int j = mp->loopstart; j < loopend; j++) {
-            GPU_indexbuf_add_generic_vert(&elb, j);
-          }
-          /* Finish loop and restart primitive. */
-          GPU_indexbuf_add_generic_vert(&elb, mp->loopstart);
-          GPU_indexbuf_add_primitive_restart(&elb);
-        }
-        v_index += mp->totloop;
-      }
+  if (cache->surface_per_mat) {
+    for (int i = 0; i < cache->mat_len; i++) {
+      GPU_BATCH_DISCARD_SAFE(cache->surface_per_mat[i]);
     }
   }
-  else {
-    /* Implement ... eventually if needed. */
-    BLI_assert(0);
-  }
+  MEM_SAFE_FREE(cache->surface_per_mat);
 
-  GPU_indexbuf_build_in_place(&elb, ibo);
-}
+  cache->batch_ready &= ~MBC_SURF_PER_MAT;
 
-static void mesh_create_loose_edges_lines(MeshRenderData *rdata,
-                                          GPUIndexBuf *ibo,
-                                          bool *r_no_loose_wire,
-                                          const bool use_hide)
-{
-  const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-  const int edge_len = mesh_render_data_edges_len_get_maybe_mapped(rdata);
-
-  /* Alloc max (edge_len) and upload only needed range. */
-  GPUIndexBufBuilder elb;
-  GPU_indexbuf_init(&elb, GPU_PRIM_LINES, edge_len, vert_len);
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      /* No need to support since edit mesh already draw them.
-       * But some engines may want them ... */
-      BMesh *bm = rdata->edit_bmesh->bm;
-      BMIter eiter;
-      BMEdge *eed;
-      BM_ITER_MESH (eed, &eiter, bm, BM_EDGES_OF_MESH) {
-        if (bm_edge_is_loose_and_visible(eed)) {
-          GPU_indexbuf_add_line_verts(
-              &elb, BM_elem_index_get(eed->v1), BM_elem_index_get(eed->v2));
-        }
-      }
-    }
-    else {
-      for (int i = 0; i < edge_len; i++) {
-        const MEdge *medge = &rdata->medge[i];
-        if ((medge->flag & ME_LOOSEEDGE) && !(use_hide && (medge->flag & ME_HIDE))) {
-          GPU_indexbuf_add_line_verts(&elb, medge->v1, medge->v2);
-        }
-      }
-    }
-  }
-  else {
-    /* Hidden checks are already done when creating the loose edge list. */
-    Mesh *me_cage = rdata->mapped.me_cage;
-    for (int i_iter = 0; i_iter < rdata->mapped.loose_edge_len; i_iter++) {
-      const int i = rdata->mapped.loose_edges[i_iter];
-      const MEdge *medge = &me_cage->medge[i];
-      GPU_indexbuf_add_line_verts(&elb, medge->v1, medge->v2);
-    }
-  }
+  MEM_SAFE_FREE(cache->auto_layer_names);
+  MEM_SAFE_FREE(cache->auto_layer_is_srgb);
 
-  *r_no_loose_wire = (elb.index_len == 0);
+  mesh_cd_layers_type_clear(&cache->cd_used);
 
-  GPU_indexbuf_build_in_place(&elb, ibo);
+  cache->mat_len = 0;
 }
 
-static void mesh_create_loops_tris(MeshRenderData *rdata,
-                                   GPUIndexBuf **ibo,
-                                   int ibo_len,
-                                   const bool use_hide)
+static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache)
 {
-  const int loop_len = mesh_render_data_loops_len_get(rdata);
-  const int tri_len = mesh_render_data_looptri_len_get(rdata);
-
-  GPUIndexBufBuilder *elb = BLI_array_alloca(elb, ibo_len);
-
-  for (int i = 0; i < ibo_len; ++i) {
-    /* TODO alloc minmum necessary. */
-    GPU_indexbuf_init(&elb[i], GPU_PRIM_TRIS, tri_len, loop_len * 3);
-  }
-
-  if (rdata->mapped.use == false) {
-    if (rdata->edit_bmesh) {
-      for (int i = 0; i < tri_len; i++) {
-        const BMLoop **bm_looptri = (const BMLoop **)rdata->edit_bmesh->looptris[i];
-        const BMFace *bm_face = bm_looptri[0]->f;
-        /* use_hide always for edit-mode */
-        if (BM_elem_flag_test(bm_face, BM_ELEM_HIDDEN)) {
-          continue;
-        }
-        int mat = min_ii(ibo_len - 1, bm_face->mat_nr);
-        GPU_indexbuf_add_tri_verts(&elb[mat],
-                                   BM_elem_index_get(bm_looptri[0]),
-                                   BM_elem_index_get(bm_looptri[1]),
-                                   BM_elem_index_get(bm_looptri[2]));
-      }
-    }
-    else {
-      for (int i = 0; i < tri_len; i++) {
-        const MLoopTri *mlt = &rdata->mlooptri[i];
-        const MPoly *mp = &rdata->mpoly[mlt->poly];
-        if (use_hide && (mp->flag & ME_HIDE)) {
-          continue;
-        }
-        int mat = min_ii(ibo_len - 1, mp->mat_nr);
-        GPU_indexbuf_add_tri_verts(&elb[mat], mlt->tri[0], mlt->tri[1], mlt->tri[2]);
-      }
-    }
-  }
-  else {
-    /* Note: mapped doesn't support lnors yet. */
-    BMesh *bm = rdata->edit_bmesh->bm;
-    Mesh *me_cage = rdata->mapped.me_cage;
-
-    const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me_cage);
-    for (int i = 0; i < tri_len; i++) {
-      const MLoopTri *mlt = &mlooptri[i];
-      const int p_orig = rdata->mapped.p_origindex[mlt->poly];
-      if (p_orig != ORIGINDEX_NONE) {
-        /* Assume 'use_hide' */
-        BMFace *efa = BM_face_at_index(bm, p_orig);
-        if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-          int mat = min_ii(ibo_len - 1, efa->mat_nr);
-          GPU_indexbuf_add_tri_verts(&elb[mat], mlt->tri[0], mlt->tri[1], mlt->tri[2]);
-        }
-      }
-    }
+  FOREACH_MESH_BUFFER_CACHE(cache, mbufcache)
+  {
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.stretch_angle);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.stretch_area);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv_tan);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edituv_data);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_uv);
+    GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_edituv_data);
+    GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_tris);
+    GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_lines);
+    GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_points);
+    GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.edituv_fdots);
   }
+  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_area);
+  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_strech_angle);
+  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces);
+  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges);
+  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts);
+  GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_fdots);
+  GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops_uvs);
 
-  for (int i = 0; i < ibo_len; ++i) {
-    GPU_indexbuf_build_in_place(&elb[i], ibo[i]);
-  }
+  cache->batch_ready &= ~MBC_EDITUV;
 }
 
-/* Warning! this function is not thread safe!
- * It writes to MEdge->flag with ME_EDGE_TMP_TAG. */
-static void mesh_create_edit_loops_points_lines(MeshRenderData *rdata,
-                                                GPUIndexBuf *ibo_verts,
-                                                GPUIndexBuf *ibo_edges)
+void DRW_mesh_batch_cache_dirty_tag(Mesh *me, int mode)
 {
-  BMIter iter;
-  int i;
-
-  const int vert_len = mesh_render_data_verts_len_get_maybe_mapped(rdata);
-  const int edge_len = mesh_render_data_edges_len_get_maybe_mapped(rdata);
-  const int loop_len = mesh_render_data_loops_len_get_maybe_mapped(rdata);
-  const int poly_len = mesh_render_data_polys_len_get_maybe_mapped(rdata);
-  const int lvert_len = mesh_render_data_loose_verts_len_get_maybe_mapped(rdata);
-  const int ledge_len = mesh_render_data_loose_edges_len_get_maybe_mapped(rdata);
-  const int tot_loop_len = loop_len + ledge_len * 2 + lvert_len;
-
-  GPUIndexBufBuilder elb_vert, elb_edge;
-  if (DRW_TEST_ASSIGN_IBO(ibo_edges)) {
-    GPU_indexbuf_init(&elb_edge, GPU_PRIM_LINES, edge_len, tot_loop_len);
-  }
-  if (DRW_TEST_ASSIGN_IBO(ibo_verts)) {
-    GPU_indexbuf_init(&elb_vert, GPU_PRIM_POINTS, tot_loop_len, tot_loop_len);
-  }
-
-  int loop_idx = 0;
-  if (rdata->edit_bmesh && (rdata->mapped.use == false)) {
-    BMesh *bm = rdata->edit_bmesh->bm;
-    /* Edges not loose. */
-    if (ibo_edges) {
-      BMEdge *eed;
-      BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
-        if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
-          BMLoop *l = bm_edge_find_first_loop_visible_inline(eed);
-          if (l != NULL) {
-            int v1 = BM_elem_index_get(eed->l);
-            int v2 = BM_elem_index_get(eed->l->next);
-            GPU_indexbuf_add_line_verts(&elb_edge, v1, v2);
-          }
-        }
-      }
-    }
-    /* Face Loops */
-    if (ibo_verts) {
-      BMVert *eve;
-      BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
-        if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
-          BMLoop *l = bm_vert_find_first_loop_visible_inline(eve);
-          if (l != NULL) {
-            int v = BM_elem_index_get(l);
-            GPU_indexbuf_add_generic_vert(&elb_vert, v);
-          }
-        }
-      }
-    }
-    loop_idx = loop_len;
-    /* Loose edges */
-    for (i = 0; i < ledge_len; ++i) {
-      if (ibo_verts) {
-        GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 0);
-        GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 1);
-      }
-      if (ibo_edges) {
-        GPU_indexbuf_add_line_verts(&elb_edge, loop_idx + 0, loop_idx + 1);
-      }
-      loop_idx += 2;
-    }
-    /* Loose verts */
-    if (ibo_verts) {
-      for (i = 0; i < lvert_len; ++i) {
-        GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx);
-        loop_idx += 1;
-      }
-    }
+  MeshBatchCache *cache = me->runtime.batch_cache;
+  if (cache == NULL) {
+    return;
   }
-  else if (rdata->mapped.use) {
-    const MPoly *mpoly = rdata->mapped.me_cage->mpoly;
-    MVert *mvert = rdata->mapped.me_cage->mvert;
-    MEdge *medge = rdata->mapped.me_cage->medge;
-    BMesh *bm = rdata->edit_bmesh->bm;
-
-    const int *v_origindex = rdata->mapped.v_origindex;
-    const int *e_origindex = rdata->mapped.e_origindex;
-    const int *p_origindex = rdata->mapped.p_origindex;
-
-    /* Reset flag */
-    for (int edge = 0; edge < edge_len; ++edge) {
-      /* NOTE: not thread safe. */
-      medge[edge].flag &= ~ME_EDGE_TMP_TAG;
-    }
-    for (int vert = 0; vert < vert_len; ++vert) {
-      /* NOTE: not thread safe. */
-      mvert[vert].flag &= ~ME_VERT_TMP_TAG;
-    }
-
-    /* Face Loops */
-    for (int poly = 0; poly < poly_len; poly++, mpoly++) {
-      int fidx = p_origindex[poly];
-      if (fidx != ORIGINDEX_NONE) {
-        BMFace *efa = BM_face_at_index(bm, fidx);
-        if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
-          const MLoop *mloop = &rdata->mapped.me_cage->mloop[mpoly->loopstart];
-          for (i = 0; i < mpoly->totloop; ++i, ++mloop) {
-            if (ibo_verts && (v_origindex[mloop->v] != ORIGINDEX_NONE) &&
-                (mvert[mloop->v].flag & ME_VERT_TMP_TAG) == 0) {
-              mvert[mloop->v].flag |= ME_VERT_TMP_TAG;
-              GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + i);
-            }
-            if (ibo_edges && (e_origindex[mloop->e] != ORIGINDEX_NONE) &&
-                ((medge[mloop->e].flag & ME_EDGE_TMP_TAG) == 0)) {
-              medge[mloop->e].flag |= ME_EDGE_TMP_TAG;
-              int v1 = loop_idx + i;
-              int v2 = loop_idx + ((i + 1) % mpoly->totloop);
-              GPU_indexbuf_add_line_verts(&elb_edge, v1, v2);
-            }
-          }
-        }
-      }
-      loop_idx += mpoly->totloop;
-    }
-    /* Loose edges */
-    for (i = 0; i < ledge_len; ++i) {
-      int eidx = e_origindex[rdata->mapped.loose_edges[i]];
-      if (eidx != ORIGINDEX_NONE) {
-        if (ibo_verts) {
-          const MEdge *ed = &medge[rdata->mapped.loose_edges[i]];
-          if (v_origindex[ed->v1] != ORIGINDEX_NONE) {
-            GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 0);
-          }
-          if (v_origindex[ed->v2] != ORIGINDEX_NONE) {
-            GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + 1);
-          }
-        }
-        if (ibo_edges) {
-          GPU_indexbuf_add_line_verts(&elb_edge, loop_idx + 0, loop_idx + 1);
-        }
-      }
-      loop_idx += 2;
-    }
-    /* Loose verts */
-    if (ibo_verts) {
-      for (i = 0; i < lvert_len; ++i) {
-        int vidx = v_origindex[rdata->mapped.loose_verts[i]];
-        if (vidx != ORIGINDEX_NONE) {
-          GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx);
-        }
-        loop_idx += 1;
+  switch (mode) {
+    case BKE_MESH_BATCH_DIRTY_SELECT:
+      FOREACH_MESH_BUFFER_CACHE(cache, mbufcache)
+      {
+        GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.edit_data);
+        GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor);
       }
-    }
-  }
-  else {
-    const MPoly *mpoly = rdata->mpoly;
-
-    /* Face Loops */
-    for (int poly = 0; poly < poly_len; poly++, mpoly++) {
-      if ((mpoly->flag & ME_HIDE) == 0) {
-        for (i = 0; i < mpoly->totloop; ++i) {
-          if (ibo_verts) {
-            GPU_indexbuf_add_generic_vert(&elb_vert, loop_idx + i);
-          }
-          if (ibo_edges) {
-            int v1 = loop_idx + i;
-            int v2 = loop_idx + ((i + 1) % mpoly->totloop);
-            GPU_indexbuf_add_line_verts(&elb_edge, v1, v2);
-          }
-        }
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_triangles);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_vertices);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_edges);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_fdots);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_verts);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_edges);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_faces);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_selection_fdots);
+      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_mesh_analysis);
+      cache->batch_ready &= ~(MBC_EDIT_TRIANGLES | MBC_EDIT_VERTICES | MBC_EDIT_EDGES |
+                              MBC_EDIT_FACEDOTS | MBC_EDIT_SELECTION_FACEDOTS |
+                              MBC_EDIT_SELECTION_FACES | MBC_EDIT_SELECTION_EDGES |
+                              MBC_EDIT_SELECTION_VERTS | MBC_EDIT_MESH_ANALYSIS);
+      /* Because visible UVs depends on edit mode selection, discard everything. */
+      mesh_batch_cache_discard_uvedit(cache);
+      break;
+    case BKE_MESH_BATCH_DIRTY_SELECT_PAINT:
+      /* Paint mode selection flag is packed inside the nor attrib.
+       * Note that it can be slow if auto smooth is enabled. (see T63946) */
+      FOREACH_MESH_BUFFER_CACHE(cache, mbufcache)
+      {
+        GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.lines_paint_mask);
+        GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor);
       }
-      loop_idx += mpoly->totloop;
-    }
-    /* TODO(fclem): Until we find a way to detect
-     * loose verts easily outside of edit mode, this
-     * will remain disabled. */
-#if 0
-    /* Loose edges */
-    for (int e = 0; e < edge_len; e++, medge++) {
-      if (medge->flag & ME_LOOSEEDGE) {
-        int eidx = e_origindex[e];
-        if (eidx !=&n