diff options
Diffstat (limited to 'source/blender/blenkernel')
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(¶llel_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, ¶llel_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); +} |