Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKévin Dietrich <kevin.dietrich@mailoo.org>2021-12-27 18:34:47 +0300
committerKévin Dietrich <kevin.dietrich@mailoo.org>2021-12-27 18:35:54 +0300
commiteed45d2a239a2a18a2420ba15dfb55e0f8dc5630 (patch)
treeaa55ce966caa8e28db4853d7d755003ed249805b /source/blender/blenkernel
parent31e120ef4997583332aa9b5af93521e7e666e9f3 (diff)
OpenSubDiv: add support for an OpenGL evaluator
This evaluator is used in order to evaluate subdivision at render time, allowing for faster renders of meshes with a subdivision surface modifier placed at the last position in the modifier list. When evaluating the subsurf modifier, we detect whether we can delegate evaluation to the draw code. If so, the subdivision is first evaluated on the GPU using our own custom evaluator (only the coarse data needs to be initially sent to the GPU), then, buffers for the final `MeshBufferCache` are filled on the GPU using a set of compute shaders. However, some buffers are still filled on the CPU side, if doing so on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose logic is hardly GPU compatible). This is done at the mesh buffer extraction level so that the result can be readily used in the various OpenGL engines, without having to write custom geometry or tesselation shaders. We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in order to control the data layout, and interpolation. For example, we store vertex colors as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float types. In order to still access the modified geometry on the CPU side, for use in modifiers or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`. Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will create such a wrapper if possible. If the final subdivision surface is not needed on the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used. Enabling or disabling GPU subdivision can be done through the user preferences (under Viewport -> Subdivision). See patch description for benchmarks. Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport Differential Revision: https://developer.blender.org/D12406
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_mesh_wrapper.h3
-rw-r--r--source/blender/blenkernel/BKE_object.h11
-rw-r--r--source/blender/blenkernel/BKE_subdiv.h14
-rw-r--r--source/blender/blenkernel/BKE_subdiv_eval.h14
-rw-r--r--source/blender/blenkernel/BKE_subdiv_foreach.h3
-rw-r--r--source/blender/blenkernel/BKE_subdiv_modifier.h71
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/mesh_normals.cc1
-rw-r--r--source/blender/blenkernel/intern/mesh_wrapper.c85
-rw-r--r--source/blender/blenkernel/intern/modifier.c1
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_smooth.c5
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_util.c2
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_vertcos.c3
-rw-r--r--source/blender/blenkernel/intern/multires_versioning.c2
-rw-r--r--source/blender/blenkernel/intern/object.cc36
-rw-r--r--source/blender/blenkernel/intern/subdiv.c12
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg.c3
-rw-r--r--source/blender/blenkernel/intern/subdiv_deform.c6
-rw-r--r--source/blender/blenkernel/intern/subdiv_eval.c112
-rw-r--r--source/blender/blenkernel/intern/subdiv_foreach.c3
-rw-r--r--source/blender/blenkernel/intern/subdiv_mesh.c6
-rw-r--r--source/blender/blenkernel/intern/subdiv_modifier.c162
22 files changed, 517 insertions, 40 deletions
diff --git a/source/blender/blenkernel/BKE_mesh_wrapper.h b/source/blender/blenkernel/BKE_mesh_wrapper.h
index 2fe264fd0f7..12e8fd71503 100644
--- a/source/blender/blenkernel/BKE_mesh_wrapper.h
+++ b/source/blender/blenkernel/BKE_mesh_wrapper.h
@@ -22,6 +22,7 @@
struct BMEditMesh;
struct CustomData_MeshMasks;
struct Mesh;
+struct Object;
#ifdef __cplusplus
extern "C" {
@@ -51,6 +52,8 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const struct Mesh *me,
int vert_coords_len,
const float mat[4][4]);
+struct Mesh *BKE_mesh_wrapper_ensure_subdivision(const struct Object *ob, struct Mesh *me);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 03565bd3bda..a7d39598e54 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -48,6 +48,7 @@ struct RegionView3D;
struct RigidBodyWorld;
struct Scene;
struct ShaderFxData;
+struct SubsurfModifierData;
struct View3D;
struct ViewLayer;
@@ -512,6 +513,7 @@ bool BKE_object_obdata_texspace_get(struct Object *ob,
float **r_loc,
float **r_size);
+struct Mesh *BKE_object_get_evaluated_mesh_no_subsurf(const struct Object *object);
/** Get evaluated mesh for given object. */
struct Mesh *BKE_object_get_evaluated_mesh(const struct Object *object);
/**
@@ -712,6 +714,15 @@ void BKE_object_modifiers_lib_link_common(void *userData,
struct ID **idpoin,
int cb_flag);
+/**
+ * Return the last subsurf modifier of an object, this does not check whether modifiers on top of
+ * it are disabled. Return NULL if no such modifier is found.
+ *
+ * This does not check if the modifier is enabled as it is assumed that the caller verified that it
+ * is enabled for its evaluation mode.
+ */
+struct SubsurfModifierData *BKE_object_get_last_subsurf_modifier(const struct Object *ob);
+
void BKE_object_replace_data_on_shallow_copy(struct Object *ob, struct ID *new_data);
struct PartEff;
diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h
index 2fb27fad30d..169a4337f6a 100644
--- a/source/blender/blenkernel/BKE_subdiv.h
+++ b/source/blender/blenkernel/BKE_subdiv.h
@@ -188,7 +188,16 @@ typedef struct Subdiv {
/* Cached values, are not supposed to be accessed directly. */
struct {
/* Indexed by base face index, element indicates total number of ptex
- * faces created for preceding base faces. */
+ * faces created for preceding base faces. This also stores the final
+ * ptex offset (the total number of PTex faces) at the end of the array
+ * so that algorithms can compute the number of ptex faces for a given
+ * face by computing the delta with the offset for the next face without
+ * using a separate data structure, e.g.:
+ *
+ * const int num_face_ptex_faces = face_ptex_offset[i + 1] - face_ptex_offset[i];
+ *
+ * In total this array has a size of `num base faces + 1`.
+ */
int *face_ptex_offset;
} cache_;
} Subdiv;
@@ -257,6 +266,9 @@ void BKE_subdiv_displacement_detach(Subdiv *subdiv);
/* ============================ TOPOLOGY HELPERS ============================ */
+/* For each element in the array, this stores the total number of ptex faces up to that element,
+ * with the total number of ptex faces being the last element in the array. The array is of length
+ * `base face count + 1`. */
int *BKE_subdiv_face_ptex_offset_get(Subdiv *subdiv);
/* =========================== PTEX FACES AND GRIDS ========================= */
diff --git a/source/blender/blenkernel/BKE_subdiv_eval.h b/source/blender/blenkernel/BKE_subdiv_eval.h
index 0b61e62c89c..177d5f386a8 100644
--- a/source/blender/blenkernel/BKE_subdiv_eval.h
+++ b/source/blender/blenkernel/BKE_subdiv_eval.h
@@ -31,15 +31,25 @@ extern "C" {
struct Mesh;
struct Subdiv;
+struct OpenSubdiv_EvaluatorCache;
+
+typedef enum eSubdivEvaluatorType {
+ SUBDIV_EVALUATOR_TYPE_CPU,
+ SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE,
+} eSubdivEvaluatorType;
/* Returns true if evaluator is ready for use. */
-bool BKE_subdiv_eval_begin(struct Subdiv *subdiv);
+bool BKE_subdiv_eval_begin(struct Subdiv *subdiv,
+ eSubdivEvaluatorType evaluator_type,
+ struct OpenSubdiv_EvaluatorCache *evaluator_cache);
/* coarse_vertex_cos is an optional argument which allows to override coordinates of the coarse
* mesh. */
bool BKE_subdiv_eval_begin_from_mesh(struct Subdiv *subdiv,
const struct Mesh *mesh,
- const float (*coarse_vertex_cos)[3]);
+ const float (*coarse_vertex_cos)[3],
+ eSubdivEvaluatorType evaluator_type,
+ struct OpenSubdiv_EvaluatorCache *evaluator_cache);
bool BKE_subdiv_eval_refine_from_mesh(struct Subdiv *subdiv,
const struct Mesh *mesh,
const float (*coarse_vertex_cos)[3]);
diff --git a/source/blender/blenkernel/BKE_subdiv_foreach.h b/source/blender/blenkernel/BKE_subdiv_foreach.h
index 3f74299455d..f63e23917ef 100644
--- a/source/blender/blenkernel/BKE_subdiv_foreach.h
+++ b/source/blender/blenkernel/BKE_subdiv_foreach.h
@@ -38,7 +38,8 @@ typedef bool (*SubdivForeachTopologyInformationCb)(const struct SubdivForeachCon
const int num_vertices,
const int num_edges,
const int num_loops,
- const int num_polygons);
+ const int num_polygons,
+ const int *subdiv_polygon_offset);
typedef void (*SubdivForeachVertexFromCornerCb)(const struct SubdivForeachContext *context,
void *tls,
diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h
new file mode 100644
index 00000000000..94068613101
--- /dev/null
+++ b/source/blender/blenkernel/BKE_subdiv_modifier.h
@@ -0,0 +1,71 @@
+/*
+ * 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) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+#include "BLI_sys_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Mesh;
+struct Object;
+struct Scene;
+struct Subdiv;
+struct SubdivSettings;
+struct SubsurfModifierData;
+
+void BKE_subsurf_modifier_subdiv_settings_init(struct SubdivSettings *settings,
+ const struct SubsurfModifierData *smd,
+ const bool use_render_params);
+
+/* If skip_check_is_last is true, we assume that the modifier passed is the last enabled modifier
+ * in the stack. */
+bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const struct Scene *scene,
+ const struct Object *ob,
+ const struct SubsurfModifierData *smd,
+ int required_mode,
+ bool skip_check_is_last);
+
+bool BKE_subsurf_modifier_can_do_gpu_subdiv(const struct Scene *scene,
+ const struct Object *ob,
+ const int required_mode);
+
+extern void (*BKE_subsurf_modifier_free_gpu_cache_cb)(struct Subdiv *subdiv);
+
+struct Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(
+ const struct SubsurfModifierData *smd,
+ const struct SubdivSettings *subdiv_settings,
+ const struct Mesh *mesh,
+ const bool for_draw_code);
+
+struct SubsurfRuntimeData *BKE_subsurf_modifier_ensure_runtime(struct SubsurfModifierData *smd);
+
+/* Return the #ModifierMode required for the evaluation of the subsurf modifier, which should be
+ * used to check if the modifier is enabled. */
+int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index fe33abd17c0..3c780a933d3 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -275,6 +275,7 @@ set(SRC
intern/subdiv_eval.c
intern/subdiv_foreach.c
intern/subdiv_mesh.c
+ intern/subdiv_modifier.c
intern/subdiv_stats.c
intern/subdiv_topology.c
intern/subsurf_ccg.c
@@ -453,6 +454,7 @@ set(SRC
BKE_subdiv_eval.h
BKE_subdiv_foreach.h
BKE_subdiv_mesh.h
+ BKE_subdiv_modifier.h
BKE_subdiv_topology.h
BKE_subsurf.h
BKE_text.h
diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc
index da5b4ccc764..47ea55be871 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -319,6 +319,7 @@ void BKE_mesh_ensure_normals(Mesh *mesh)
void BKE_mesh_ensure_normals_for_display(Mesh *mesh)
{
switch ((eMeshWrapperType)mesh->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_SUBD:
case ME_WRAPPER_TYPE_MDATA:
/* Run code below. */
break;
diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c
index bc1ffeb8cf4..5956f2802b5 100644
--- a/source/blender/blenkernel/intern/mesh_wrapper.c
+++ b/source/blender/blenkernel/intern/mesh_wrapper.c
@@ -36,6 +36,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_ghash.h"
@@ -50,8 +51,14 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_subdiv.h"
+#include "BKE_subdiv_mesh.h"
+#include "BKE_subdiv_modifier.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
const CustomData_MeshMasks *cd_mask_extra,
@@ -106,7 +113,8 @@ static void mesh_wrapper_ensure_mdata_isolated(void *userdata)
me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
switch (geom_type_orig) {
- case ME_WRAPPER_TYPE_MDATA: {
+ case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD: {
break; /* Quiet warning. */
}
case ME_WRAPPER_TYPE_BMESH: {
@@ -157,6 +165,7 @@ bool BKE_mesh_wrapper_minmax(const Mesh *me, float min[3], float max[3])
case ME_WRAPPER_TYPE_BMESH:
return BKE_editmesh_cache_calc_minmax(me->edit_mesh, me->runtime.edit_data, min, max);
case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD:
return BKE_mesh_minmax(me, min, max);
}
BLI_assert_unreachable();
@@ -191,7 +200,8 @@ void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me,
}
return;
}
- case ME_WRAPPER_TYPE_MDATA: {
+ case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD: {
BLI_assert(vert_coords_len <= me->totvert);
const MVert *mvert = me->mvert;
for (int i = 0; i < vert_coords_len; i++) {
@@ -228,7 +238,8 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me,
}
return;
}
- case ME_WRAPPER_TYPE_MDATA: {
+ case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD: {
BLI_assert(vert_coords_len == me->totvert);
const MVert *mvert = me->mvert;
for (int i = 0; i < vert_coords_len; i++) {
@@ -252,6 +263,7 @@ int BKE_mesh_wrapper_vert_len(const Mesh *me)
case ME_WRAPPER_TYPE_BMESH:
return me->edit_mesh->bm->totvert;
case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD:
return me->totvert;
}
BLI_assert_unreachable();
@@ -264,6 +276,7 @@ int BKE_mesh_wrapper_edge_len(const Mesh *me)
case ME_WRAPPER_TYPE_BMESH:
return me->edit_mesh->bm->totedge;
case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD:
return me->totedge;
}
BLI_assert_unreachable();
@@ -276,6 +289,7 @@ int BKE_mesh_wrapper_loop_len(const Mesh *me)
case ME_WRAPPER_TYPE_BMESH:
return me->edit_mesh->bm->totloop;
case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD:
return me->totloop;
}
BLI_assert_unreachable();
@@ -288,6 +302,7 @@ int BKE_mesh_wrapper_poly_len(const Mesh *me)
case ME_WRAPPER_TYPE_BMESH:
return me->edit_mesh->bm->totface;
case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD:
return me->totpoly;
}
BLI_assert_unreachable();
@@ -295,3 +310,67 @@ int BKE_mesh_wrapper_poly_len(const Mesh *me)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CPU Subdivision Evaluation
+ * \{ */
+
+Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
+{
+ ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex;
+ BLI_mutex_lock(mesh_eval_mutex);
+
+ if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_SUBD) {
+ BLI_mutex_unlock(mesh_eval_mutex);
+ return me->runtime.mesh_eval;
+ }
+
+ SubsurfModifierData *smd = BKE_object_get_last_subsurf_modifier(ob);
+ if (!smd) {
+ BLI_mutex_unlock(mesh_eval_mutex);
+ return me;
+ }
+
+ const bool apply_render = me->runtime.subsurf_apply_render;
+
+ SubdivSettings subdiv_settings;
+ BKE_subsurf_modifier_subdiv_settings_init(&subdiv_settings, smd, apply_render);
+ if (subdiv_settings.level == 0) {
+ return me;
+ }
+
+ SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd);
+
+ Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &subdiv_settings, me, false);
+ if (subdiv == NULL) {
+ /* Happens on bad topology, but also on empty input mesh. */
+ return me;
+ }
+
+ SubdivToMeshSettings mesh_settings;
+ mesh_settings.resolution = me->runtime.subsurf_resolution;
+ mesh_settings.use_optimal_display = me->runtime.subsurf_use_optimal_display;
+
+ if (mesh_settings.resolution < 3) {
+ return me;
+ }
+
+ Mesh *subdiv_mesh = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me);
+
+ if (subdiv != runtime_data->subdiv) {
+ BKE_subdiv_free(subdiv);
+ }
+
+ if (subdiv_mesh != me) {
+ if (me->runtime.mesh_eval != NULL) {
+ BKE_id_free(NULL, me->runtime.mesh_eval);
+ }
+ me->runtime.mesh_eval = subdiv_mesh;
+ me->runtime.wrapper_type = ME_WRAPPER_TYPE_SUBD;
+ }
+
+ BLI_mutex_unlock(mesh_eval_mutex);
+ return me->runtime.mesh_eval;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index e1d201d7806..f3b6c2544bf 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -970,6 +970,7 @@ static void modwrap_dependsOnNormals(Mesh *me)
}
break;
}
+ case ME_WRAPPER_TYPE_SUBD:
case ME_WRAPPER_TYPE_MDATA:
BKE_mesh_calc_normals(me);
break;
diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c
index 3665d01926b..50b4410a28e 100644
--- a/source/blender/blenkernel/intern/multires_reshape_smooth.c
+++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c
@@ -566,7 +566,8 @@ static bool foreach_topology_info(const SubdivForeachContext *foreach_context,
const int num_vertices,
const int num_edges,
const int num_loops,
- const int num_polygons)
+ const int num_polygons,
+ const int *UNUSED(subdiv_polygon_offset))
{
MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
const int max_edges = reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR ?
@@ -1037,7 +1038,7 @@ static void reshape_subdiv_create(MultiresReshapeSmoothContext *reshape_smooth_c
converter_init(reshape_smooth_context, &converter);
Subdiv *reshape_subdiv = BKE_subdiv_new_from_converter(settings, &converter);
- BKE_subdiv_eval_begin(reshape_subdiv);
+ BKE_subdiv_eval_begin(reshape_subdiv, SUBDIV_EVALUATOR_TYPE_CPU, NULL);
reshape_smooth_context->reshape_subdiv = reshape_subdiv;
diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c
index b7572204182..07a5d7c4a61 100644
--- a/source/blender/blenkernel/intern/multires_reshape_util.c
+++ b/source/blender/blenkernel/intern/multires_reshape_util.c
@@ -65,7 +65,7 @@ Subdiv *multires_reshape_create_subdiv(Depsgraph *depsgraph,
SubdivSettings subdiv_settings;
BKE_multires_subdiv_settings_init(&subdiv_settings, mmd);
Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, base_mesh);
- if (!BKE_subdiv_eval_begin_from_mesh(subdiv, base_mesh, NULL)) {
+ if (!BKE_subdiv_eval_begin_from_mesh(subdiv, base_mesh, NULL, SUBDIV_EVALUATOR_TYPE_CPU, NULL)) {
BKE_subdiv_free(subdiv);
return NULL;
}
diff --git a/source/blender/blenkernel/intern/multires_reshape_vertcos.c b/source/blender/blenkernel/intern/multires_reshape_vertcos.c
index ed2df1ba8c5..c009349ff1b 100644
--- a/source/blender/blenkernel/intern/multires_reshape_vertcos.c
+++ b/source/blender/blenkernel/intern/multires_reshape_vertcos.c
@@ -114,7 +114,8 @@ static bool multires_reshape_vertcos_foreach_topology_info(
const int num_vertices,
const int UNUSED(num_edges),
const int UNUSED(num_loops),
- const int UNUSED(num_polygons))
+ const int UNUSED(num_polygons),
+ const int *UNUSED(subdiv_polygon_offset))
{
MultiresReshapeAssignVertcosContext *reshape_vertcos_context = foreach_context->user_data;
if (num_vertices != reshape_vertcos_context->num_vert_coords) {
diff --git a/source/blender/blenkernel/intern/multires_versioning.c b/source/blender/blenkernel/intern/multires_versioning.c
index 4c0d7165cd0..18708c43f26 100644
--- a/source/blender/blenkernel/intern/multires_versioning.c
+++ b/source/blender/blenkernel/intern/multires_versioning.c
@@ -61,7 +61,7 @@ static Subdiv *subdiv_for_simple_to_catmull_clark(Object *object, MultiresModifi
Subdiv *subdiv = BKE_subdiv_new_from_converter(&subdiv_settings, &converter);
BKE_subdiv_converter_free(&converter);
- if (!BKE_subdiv_eval_begin_from_mesh(subdiv, base_mesh, NULL)) {
+ if (!BKE_subdiv_eval_begin_from_mesh(subdiv, base_mesh, NULL, SUBDIV_EVALUATOR_TYPE_CPU, NULL)) {
BKE_subdiv_free(subdiv);
return NULL;
}
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index 6cc6219b7d7..d08ea74d2c6 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -1773,8 +1773,9 @@ static void object_update_from_subsurf_ccg(Object *object)
if (!object->runtime.is_data_eval_owned) {
return;
}
- /* Object was never evaluated, so can not have CCG subdivision surface. */
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object);
+ /* Object was never evaluated, so can not have CCG subdivision surface. If it were evaluated, do
+ * not try to compute OpenSubDiv on the CPU as it is not needed here. */
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(object);
if (mesh_eval == nullptr) {
return;
}
@@ -4496,7 +4497,7 @@ bool BKE_object_obdata_texspace_get(Object *ob, char **r_texflag, float **r_loc,
return true;
}
-Mesh *BKE_object_get_evaluated_mesh(const Object *object)
+Mesh *BKE_object_get_evaluated_mesh_no_subsurf(const Object *object)
{
/* First attempt to retrieve the evaluated mesh from the evaluated geometry set. Most
* object types either store it there or add a reference to it if it's owned elsewhere. */
@@ -4523,6 +4524,20 @@ Mesh *BKE_object_get_evaluated_mesh(const Object *object)
return nullptr;
}
+Mesh *BKE_object_get_evaluated_mesh(const Object *object)
+{
+ Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf(object);
+ if (!mesh) {
+ return nullptr;
+ }
+
+ if (object->data && GS(((const ID *)object->data)->name) == ID_ME) {
+ mesh = BKE_mesh_wrapper_ensure_subdivision(object, mesh);
+ }
+
+ return mesh;
+}
+
Mesh *BKE_object_get_pre_modified_mesh(const Object *object)
{
if (object->type == OB_MESH && object->runtime.data_orig != nullptr) {
@@ -5779,6 +5794,21 @@ void BKE_object_modifiers_lib_link_common(void *userData,
}
}
+SubsurfModifierData *BKE_object_get_last_subsurf_modifier(const Object *ob)
+{
+ ModifierData *md = (ModifierData *)(ob->modifiers.last);
+
+ while (md) {
+ if (md->type == eModifierType_Subsurf) {
+ break;
+ }
+
+ md = md->prev;
+ }
+
+ return (SubsurfModifierData *)(md);
+}
+
void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data)
{
ob->type = BKE_object_obdata_to_type(new_data);
diff --git a/source/blender/blenkernel/intern/subdiv.c b/source/blender/blenkernel/intern/subdiv.c
index fd32f52351a..45810e29565 100644
--- a/source/blender/blenkernel/intern/subdiv.c
+++ b/source/blender/blenkernel/intern/subdiv.c
@@ -29,6 +29,9 @@
#include "BLI_utildefines.h"
+#include "BKE_modifier.h"
+#include "BKE_subdiv_modifier.h"
+
#include "MEM_guardedalloc.h"
#include "subdiv_converter.h"
@@ -189,6 +192,12 @@ Subdiv *BKE_subdiv_update_from_mesh(Subdiv *subdiv,
void BKE_subdiv_free(Subdiv *subdiv)
{
if (subdiv->evaluator != NULL) {
+ const eOpenSubdivEvaluator evaluator_type = subdiv->evaluator->type;
+ if (evaluator_type != OPENSUBDIV_EVALUATOR_CPU) {
+ /* Let the draw code do the freeing, to ensure that the OpenGL context is valid. */
+ BKE_subsurf_modifier_free_gpu_cache_cb(subdiv);
+ return;
+ }
openSubdiv_deleteEvaluator(subdiv->evaluator);
}
if (subdiv->topology_refiner != NULL) {
@@ -214,12 +223,13 @@ int *BKE_subdiv_face_ptex_offset_get(Subdiv *subdiv)
}
const int num_coarse_faces = topology_refiner->getNumFaces(topology_refiner);
subdiv->cache_.face_ptex_offset = MEM_malloc_arrayN(
- num_coarse_faces, sizeof(int), "subdiv face_ptex_offset");
+ num_coarse_faces + 1, sizeof(int), "subdiv face_ptex_offset");
int ptex_offset = 0;
for (int face_index = 0; face_index < num_coarse_faces; face_index++) {
const int num_ptex_faces = topology_refiner->getNumFacePtexFaces(topology_refiner, face_index);
subdiv->cache_.face_ptex_offset[face_index] = ptex_offset;
ptex_offset += num_ptex_faces;
}
+ subdiv->cache_.face_ptex_offset[num_coarse_faces] = ptex_offset;
return subdiv->cache_.face_ptex_offset;
}
diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c
index 77962ec924c..7d876acf776 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg.c
@@ -603,7 +603,8 @@ Mesh *BKE_subdiv_to_ccg_mesh(Subdiv *subdiv,
{
/* Make sure evaluator is ready. */
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG);
- if (!BKE_subdiv_eval_begin_from_mesh(subdiv, coarse_mesh, NULL)) {
+ if (!BKE_subdiv_eval_begin_from_mesh(
+ subdiv, coarse_mesh, NULL, SUBDIV_EVALUATOR_TYPE_CPU, NULL)) {
if (coarse_mesh->totpoly) {
return NULL;
}
diff --git a/source/blender/blenkernel/intern/subdiv_deform.c b/source/blender/blenkernel/intern/subdiv_deform.c
index 7a2d639e4e5..c385b1b291d 100644
--- a/source/blender/blenkernel/intern/subdiv_deform.c
+++ b/source/blender/blenkernel/intern/subdiv_deform.c
@@ -117,7 +117,8 @@ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_contex
const int UNUSED(num_vertices),
const int UNUSED(num_edges),
const int UNUSED(num_loops),
- const int UNUSED(num_polygons))
+ const int UNUSED(num_polygons),
+ const int *UNUSED(subdiv_polygon_offset))
{
SubdivDeformContext *subdiv_context = foreach_context->user_data;
subdiv_mesh_prepare_accumulator(subdiv_context, subdiv_context->coarse_mesh->totvert);
@@ -202,7 +203,8 @@ void BKE_subdiv_deform_coarse_vertices(struct Subdiv *subdiv,
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
/* Make sure evaluator is up to date with possible new topology, and that
* is refined for the new positions of coarse vertices. */
- if (!BKE_subdiv_eval_begin_from_mesh(subdiv, coarse_mesh, vertex_cos)) {
+ if (!BKE_subdiv_eval_begin_from_mesh(
+ subdiv, coarse_mesh, vertex_cos, SUBDIV_EVALUATOR_TYPE_CPU, NULL)) {
/* This could happen in two situations:
* - OpenSubdiv is disabled.
* - Something totally bad happened, and OpenSubdiv rejected our
diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c
index 0001eb8a205..9733a1498a6 100644
--- a/source/blender/blenkernel/intern/subdiv_eval.c
+++ b/source/blender/blenkernel/intern/subdiv_eval.c
@@ -28,6 +28,7 @@
#include "BLI_bitmap.h"
#include "BLI_math_vector.h"
+#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
@@ -38,7 +39,28 @@
#include "opensubdiv_evaluator_capi.h"
#include "opensubdiv_topology_refiner_capi.h"
-bool BKE_subdiv_eval_begin(Subdiv *subdiv)
+/* ============================ Helper Function ============================ */
+
+static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type(
+ eSubdivEvaluatorType evaluator_type)
+{
+ switch (evaluator_type) {
+ case SUBDIV_EVALUATOR_TYPE_CPU: {
+ return OPENSUBDIV_EVALUATOR_CPU;
+ }
+ case SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE: {
+ return OPENSUBDIV_EVALUATOR_GLSL_COMPUTE;
+ }
+ }
+ BLI_assert_msg(0, "Unknown evaluator type");
+ return OPENSUBDIV_EVALUATOR_CPU;
+}
+
+/* ====================== Main Subdivision Evaluation ====================== */
+
+bool BKE_subdiv_eval_begin(Subdiv *subdiv,
+ eSubdivEvaluatorType evaluator_type,
+ OpenSubdiv_EvaluatorCache *evaluator_cache)
{
BKE_subdiv_stats_reset(&subdiv->stats, SUBDIV_STATS_EVALUATOR_CREATE);
if (subdiv->topology_refiner == NULL) {
@@ -47,8 +69,11 @@ bool BKE_subdiv_eval_begin(Subdiv *subdiv)
return false;
}
if (subdiv->evaluator == NULL) {
+ eOpenSubdivEvaluator opensubdiv_evaluator_type =
+ opensubdiv_evalutor_from_subdiv_evaluator_type(evaluator_type);
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_EVALUATOR_CREATE);
- subdiv->evaluator = openSubdiv_createEvaluatorFromTopologyRefiner(subdiv->topology_refiner);
+ subdiv->evaluator = openSubdiv_createEvaluatorFromTopologyRefiner(
+ subdiv->topology_refiner, opensubdiv_evaluator_type, evaluator_cache);
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_EVALUATOR_CREATE);
if (subdiv->evaluator == NULL) {
return false;
@@ -80,6 +105,9 @@ static void set_coarse_positions(Subdiv *subdiv,
BLI_BITMAP_ENABLE(vertex_used_map, loop->v);
}
}
+ /* Use a temporary buffer so we do not upload vertices one at a time to the GPU. */
+ float(*buffer)[3] = MEM_mallocN(sizeof(float[3]) * mesh->totvert, "subdiv tmp coarse positions");
+ int manifold_vertex_count = 0;
for (int vertex_index = 0, manifold_vertex_index = 0; vertex_index < mesh->totvert;
vertex_index++) {
if (!BLI_BITMAP_TEST_BOOL(vertex_used_map, vertex_index)) {
@@ -93,13 +121,49 @@ static void set_coarse_positions(Subdiv *subdiv,
const MVert *vertex = &mvert[vertex_index];
vertex_co = vertex->co;
}
- subdiv->evaluator->setCoarsePositions(subdiv->evaluator, vertex_co, manifold_vertex_index, 1);
+ copy_v3_v3(&buffer[manifold_vertex_index][0], vertex_co);
manifold_vertex_index++;
+ manifold_vertex_count++;
}
+ subdiv->evaluator->setCoarsePositions(
+ subdiv->evaluator, &buffer[0][0], 0, manifold_vertex_count);
MEM_freeN(vertex_used_map);
+ MEM_freeN(buffer);
+}
+
+/* Context which is used to fill face varying data in parallel. */
+typedef struct FaceVaryingDataFromUVContext {
+ OpenSubdiv_TopologyRefiner *topology_refiner;
+ const Mesh *mesh;
+ const MLoopUV *mloopuv;
+ float (*buffer)[2];
+ int layer_index;
+} FaceVaryingDataFromUVContext;
+
+static void set_face_varying_data_from_uv_task(void *__restrict userdata,
+ const int face_index,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ FaceVaryingDataFromUVContext *ctx = userdata;
+ OpenSubdiv_TopologyRefiner *topology_refiner = ctx->topology_refiner;
+ const int layer_index = ctx->layer_index;
+ const Mesh *mesh = ctx->mesh;
+ const MPoly *mpoly = &mesh->mpoly[face_index];
+ const MLoopUV *mluv = &ctx->mloopuv[mpoly->loopstart];
+
+ /* TODO(sergey): OpenSubdiv's C-API converter can change winding of
+ * loops of a face, need to watch for that, to prevent wrong UVs assigned.
+ */
+ const int num_face_vertices = topology_refiner->getNumFaceVertices(topology_refiner, face_index);
+ const int *uv_indices = topology_refiner->getFaceFVarValueIndices(
+ topology_refiner, face_index, layer_index);
+ for (int vertex_index = 0; vertex_index < num_face_vertices; vertex_index++, mluv++) {
+ copy_v2_v2(ctx->buffer[uv_indices[vertex_index]], mluv->uv);
+ }
}
static void set_face_varying_data_from_uv(Subdiv *subdiv,
+ const Mesh *mesh,
const MLoopUV *mloopuv,
const int layer_index)
{
@@ -107,25 +171,37 @@ static void set_face_varying_data_from_uv(Subdiv *subdiv,
OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
const int num_faces = topology_refiner->getNumFaces(topology_refiner);
const MLoopUV *mluv = mloopuv;
- /* TODO(sergey): OpenSubdiv's C-API converter can change winding of
- * loops of a face, need to watch for that, to prevent wrong UVs assigned.
- */
- for (int face_index = 0; face_index < num_faces; face_index++) {
- const int num_face_vertices = topology_refiner->getNumFaceVertices(topology_refiner,
- face_index);
- const int *uv_indices = topology_refiner->getFaceFVarValueIndices(
- topology_refiner, face_index, layer_index);
- for (int vertex_index = 0; vertex_index < num_face_vertices; vertex_index++, mluv++) {
- evaluator->setFaceVaryingData(evaluator, layer_index, mluv->uv, uv_indices[vertex_index], 1);
- }
- }
+
+ const int num_fvar_values = topology_refiner->getNumFVarValues(topology_refiner, layer_index);
+ /* Use a temporary buffer so we do not upload UVs one at a time to the GPU. */
+ float(*buffer)[2] = MEM_mallocN(sizeof(float[2]) * num_fvar_values, "temp UV storage");
+
+ FaceVaryingDataFromUVContext ctx;
+ ctx.topology_refiner = topology_refiner;
+ ctx.layer_index = layer_index;
+ ctx.mloopuv = mluv;
+ ctx.mesh = mesh;
+ ctx.buffer = buffer;
+
+ TaskParallelSettings parallel_range_settings;
+ BLI_parallel_range_settings_defaults(&parallel_range_settings);
+ parallel_range_settings.min_iter_per_thread = 1;
+
+ BLI_task_parallel_range(
+ 0, num_faces, &ctx, set_face_varying_data_from_uv_task, &parallel_range_settings);
+
+ evaluator->setFaceVaryingData(evaluator, layer_index, &buffer[0][0], 0, num_fvar_values);
+
+ MEM_freeN(buffer);
}
bool BKE_subdiv_eval_begin_from_mesh(Subdiv *subdiv,
const Mesh *mesh,
- const float (*coarse_vertex_cos)[3])
+ const float (*coarse_vertex_cos)[3],
+ eSubdivEvaluatorType evaluator_type,
+ OpenSubdiv_EvaluatorCache *evaluator_cache)
{
- if (!BKE_subdiv_eval_begin(subdiv)) {
+ if (!BKE_subdiv_eval_begin(subdiv, evaluator_type, evaluator_cache)) {
return false;
}
return BKE_subdiv_eval_refine_from_mesh(subdiv, mesh, coarse_vertex_cos);
@@ -146,7 +222,7 @@ bool BKE_subdiv_eval_refine_from_mesh(Subdiv *subdiv,
const int num_uv_layers = CustomData_number_of_layers(&mesh->ldata, CD_MLOOPUV);
for (int layer_index = 0; layer_index < num_uv_layers; layer_index++) {
const MLoopUV *mloopuv = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPUV, layer_index);
- set_face_varying_data_from_uv(subdiv, mloopuv, layer_index);
+ set_face_varying_data_from_uv(subdiv, mesh, mloopuv, layer_index);
}
/* Update evaluator to the new coarse geometry. */
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_EVALUATOR_REFINE);
diff --git a/source/blender/blenkernel/intern/subdiv_foreach.c b/source/blender/blenkernel/intern/subdiv_foreach.c
index 061c196df2a..69bead27fe6 100644
--- a/source/blender/blenkernel/intern/subdiv_foreach.c
+++ b/source/blender/blenkernel/intern/subdiv_foreach.c
@@ -1877,7 +1877,8 @@ bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv,
ctx.num_subdiv_vertices,
ctx.num_subdiv_edges,
ctx.num_subdiv_loops,
- ctx.num_subdiv_polygons)) {
+ ctx.num_subdiv_polygons,
+ ctx.subdiv_polygon_offset)) {
subdiv_foreach_ctx_free(&ctx);
return false;
}
diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c
index e5c7d13edab..1f31d0543ad 100644
--- a/source/blender/blenkernel/intern/subdiv_mesh.c
+++ b/source/blender/blenkernel/intern/subdiv_mesh.c
@@ -514,7 +514,8 @@ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_contex
const int num_vertices,
const int num_edges,
const int num_loops,
- const int num_polygons)
+ const int num_polygons,
+ const int *UNUSED(subdiv_polygon_offset))
{
/* Multires grid data will be applied or become invalid after subdivision,
* so don't try to preserve it and use memory. */
@@ -1193,7 +1194,8 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
/* Make sure evaluator is up to date with possible new topology, and that
* it is refined for the new positions of coarse vertices. */
- if (!BKE_subdiv_eval_begin_from_mesh(subdiv, coarse_mesh, NULL)) {
+ if (!BKE_subdiv_eval_begin_from_mesh(
+ subdiv, coarse_mesh, NULL, SUBDIV_EVALUATOR_TYPE_CPU, NULL)) {
/* This could happen in two situations:
* - OpenSubdiv is disabled.
* - Something totally bad happened, and OpenSubdiv rejected our
diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c
new file mode 100644
index 00000000000..bafcb631f59
--- /dev/null
+++ b/source/blender/blenkernel/intern/subdiv_modifier.c
@@ -0,0 +1,162 @@
+/*
+ * 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) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "BKE_subdiv_modifier.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_modifier.h"
+#include "BKE_subdiv.h"
+
+#include "GPU_capabilities.h"
+#include "GPU_context.h"
+
+#include "opensubdiv_capi.h"
+
+void BKE_subsurf_modifier_subdiv_settings_init(SubdivSettings *settings,
+ const SubsurfModifierData *smd,
+ const bool use_render_params)
+{
+ const int requested_levels = (use_render_params) ? smd->renderLevels : smd->levels;
+
+ settings->is_simple = (smd->subdivType == SUBSURF_TYPE_SIMPLE);
+ settings->is_adaptive = !(smd->flags & eSubsurfModifierFlag_UseRecursiveSubdivision);
+ settings->level = settings->is_simple ?
+ 1 :
+ (settings->is_adaptive ? smd->quality : requested_levels);
+ settings->use_creases = (smd->flags & eSubsurfModifierFlag_UseCrease);
+ settings->vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
+ smd->boundary_smooth);
+ settings->fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
+ smd->uv_smooth);
+}
+
+static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene,
+ const Object *ob,
+ int required_mode)
+{
+ ModifierData *md = ob->modifiers.last;
+
+ while (md) {
+ if (BKE_modifier_is_enabled(scene, md, required_mode)) {
+ break;
+ }
+
+ md = md->prev;
+ }
+
+ return md;
+}
+
+bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene,
+ const Object *ob,
+ const SubsurfModifierData *smd,
+ int required_mode,
+ bool skip_check_is_last)
+{
+ if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) {
+ return false;
+ }
+
+ if (!skip_check_is_last) {
+ ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode);
+ if (md != (const ModifierData *)smd) {
+ return false;
+ }
+ }
+
+ /* Only OpenGL is supported for OpenSubdiv evaluation for now. */
+ if (GPU_backend_get_type() != GPU_BACKEND_OPENGL) {
+ return false;
+ }
+
+ if (!GPU_compute_shader_support()) {
+ return false;
+ }
+
+ const int available_evaluators = openSubdiv_getAvailableEvaluators();
+ if ((available_evaluators & OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene,
+ const Object *ob,
+ int required_mode)
+{
+ ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode);
+
+ if (!md) {
+ return false;
+ }
+
+ if (md->type != eModifierType_Subsurf) {
+ return false;
+ }
+
+ return BKE_subsurf_modifier_can_do_gpu_subdiv_ex(
+ scene, ob, (SubsurfModifierData *)md, required_mode, true);
+}
+
+void (*BKE_subsurf_modifier_free_gpu_cache_cb)(Subdiv *subdiv) = NULL;
+
+/* Main goal of this function is to give usable subdivision surface descriptor
+ * which matches settings and topology. */
+Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(const SubsurfModifierData *smd,
+ const SubdivSettings *subdiv_settings,
+ const Mesh *mesh,
+ const bool for_draw_code)
+{
+ SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime;
+ if (runtime_data->subdiv && runtime_data->set_by_draw_code != for_draw_code) {
+ BKE_subdiv_free(runtime_data->subdiv);
+ runtime_data->subdiv = NULL;
+ }
+ Subdiv *subdiv = BKE_subdiv_update_from_mesh(runtime_data->subdiv, subdiv_settings, mesh);
+ runtime_data->subdiv = subdiv;
+ runtime_data->set_by_draw_code = for_draw_code;
+ return subdiv;
+}
+
+SubsurfRuntimeData *BKE_subsurf_modifier_ensure_runtime(SubsurfModifierData *smd)
+{
+ SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime;
+ if (runtime_data == NULL) {
+ runtime_data = MEM_callocN(sizeof(*runtime_data), "subsurf runtime");
+ smd->modifier.runtime = runtime_data;
+ }
+ return runtime_data;
+}
+
+int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode)
+{
+ if (is_final_render) {
+ return eModifierMode_Render;
+ }
+
+ return eModifierMode_Realtime | (is_edit_mode ? eModifierMode_Editmode : 0);
+}