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/modifiers
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/modifiers')
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c87
1 files changed, 38 insertions, 49 deletions
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index 7470f2abb15..00870d076ef 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -39,6 +39,7 @@
#include "DNA_screen_types.h"
#include "BKE_context.h"
+#include "BKE_editmesh.h"
#include "BKE_mesh.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -46,6 +47,7 @@
#include "BKE_subdiv_ccg.h"
#include "BKE_subdiv_deform.h"
#include "BKE_subdiv_mesh.h"
+#include "BKE_subdiv_modifier.h"
#include "BKE_subsurf.h"
#include "UI_interface.h"
@@ -65,11 +67,6 @@
#include "intern/CCGSubSurf.h"
-typedef struct SubsurfRuntimeData {
- /* Cached subdivision surface descriptor, with topology and settings. */
- struct Subdiv *subdiv;
-} SubsurfRuntimeData;
-
static void initData(ModifierData *md)
{
SubsurfModifierData *smd = (SubsurfModifierData *)md;
@@ -155,37 +152,6 @@ static int subdiv_levels_for_modifier_get(const SubsurfModifierData *smd,
return get_render_subsurf_level(&scene->r, requested_levels, use_render_params);
}
-static void subdiv_settings_init(SubdivSettings *settings,
- const SubsurfModifierData *smd,
- const ModifierEvalContext *ctx)
-{
- const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER);
- 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);
-}
-
-/* Main goal of this function is to give usable subdivision surface descriptor
- * which matches settings and topology. */
-static Subdiv *subdiv_descriptor_ensure(SubsurfModifierData *smd,
- const SubdivSettings *subdiv_settings,
- const Mesh *mesh)
-{
- SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime;
- Subdiv *subdiv = BKE_subdiv_update_from_mesh(runtime_data->subdiv, subdiv_settings, mesh);
- runtime_data->subdiv = subdiv;
- return subdiv;
-}
-
/* Subdivide into fully qualified mesh. */
static void subdiv_mesh_settings_init(SubdivToMeshSettings *settings,
@@ -240,14 +206,17 @@ static Mesh *subdiv_as_ccg(SubsurfModifierData *smd,
return result;
}
-static SubsurfRuntimeData *subsurf_ensure_runtime(SubsurfModifierData *smd)
+/* Cache settings for lazy CPU evaluation. */
+
+static void subdiv_cache_cpu_evaluation_settings(const ModifierEvalContext *ctx,
+ Mesh *me,
+ 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;
+ SubdivToMeshSettings mesh_settings;
+ subdiv_mesh_settings_init(&mesh_settings, smd, ctx);
+ me->runtime.subsurf_apply_render = (ctx->flag & MOD_APPLY_RENDER) != 0;
+ me->runtime.subsurf_resolution = mesh_settings.resolution;
+ me->runtime.subsurf_use_optimal_display = mesh_settings.use_optimal_display;
}
/* Modifier itself. */
@@ -261,12 +230,30 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
#endif
SubsurfModifierData *smd = (SubsurfModifierData *)md;
SubdivSettings subdiv_settings;
- subdiv_settings_init(&subdiv_settings, smd, ctx);
+ BKE_subsurf_modifier_subdiv_settings_init(
+ &subdiv_settings, smd, (ctx->flag & MOD_APPLY_RENDER) != 0);
if (subdiv_settings.level == 0) {
return result;
}
- SubsurfRuntimeData *runtime_data = subsurf_ensure_runtime(smd);
- Subdiv *subdiv = subdiv_descriptor_ensure(smd, &subdiv_settings, mesh);
+ SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd);
+
+ /* Delay evaluation to the draw code if possible, provided we do not have to apply the modifier.
+ */
+ if ((ctx->flag & MOD_APPLY_TO_BASE_MESH) == 0) {
+ Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
+ const bool is_render_mode = (ctx->flag & MOD_APPLY_RENDER) != 0;
+ /* Same check as in `DRW_mesh_batch_cache_create_requested` to keep both code coherent. */
+ const bool is_editmode = (mesh->edit_mesh != NULL) &&
+ (mesh->edit_mesh->mesh_eval_final != NULL);
+ const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode);
+ if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex(scene, ctx->object, smd, required_mode, false)) {
+ subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd);
+ return result;
+ }
+ }
+
+ Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(
+ smd, &subdiv_settings, mesh, false);
if (subdiv == NULL) {
/* Happens on bad topology, but also on empty input mesh. */
return result;
@@ -320,12 +307,14 @@ static void deformMatrices(ModifierData *md,
SubsurfModifierData *smd = (SubsurfModifierData *)md;
SubdivSettings subdiv_settings;
- subdiv_settings_init(&subdiv_settings, smd, ctx);
+ BKE_subsurf_modifier_subdiv_settings_init(
+ &subdiv_settings, smd, (ctx->flag & MOD_APPLY_RENDER) != 0);
if (subdiv_settings.level == 0) {
return;
}
- SubsurfRuntimeData *runtime_data = subsurf_ensure_runtime(smd);
- Subdiv *subdiv = subdiv_descriptor_ensure(smd, &subdiv_settings, mesh);
+ SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd);
+ Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(
+ smd, &subdiv_settings, mesh, false);
if (subdiv == NULL) {
/* Happens on bad topology, but also on empty input mesh. */
return;