OpenSubdiv: Use pool for delayed OpenGL buffers free when freeing from non-main thread
authorSergey Sharybin <sergey.vfx@gmail.com>
Wed, 4 Nov 2015 16:30:25 +0000 (21:30 +0500)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 4 Nov 2015 16:30:25 +0000 (21:30 +0500)
This is really similar to what GPU module was already doing. There are number of
possible improvements still:

- Re-use allocated VAOs when requesting new ones instead of going to the trouble
  of freeing VAO and then re-creating it again.

- Move VAO handling to GPU module.

Fixes T46589: OpenSubdiv crash with drivers

source/blender/blenkernel/BKE_subsurf.h
source/blender/blenkernel/intern/CCGSubSurf.c
source/blender/blenkernel/intern/CCGSubSurf_intern.h
source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c
source/blender/blenkernel/intern/scene.c
source/blender/windowmanager/CMakeLists.txt
source/blender/windowmanager/SConscript
source/blender/windowmanager/intern/wm_draw.c
source/blender/windowmanager/intern/wm_init_exit.c

index 161ab2f2fbd104aa4051aec32819a6e025103550..f52bb2ab9cbc800dcf4698ba468b4857028cfe6c 100644 (file)
@@ -140,5 +140,11 @@ typedef struct CCGDerivedMesh {
        struct EdgeHash *ehash;
 } CCGDerivedMesh;
 
+#ifdef WITH_OPENSUBDIV
+/* TODO(sergey): Not really ideal place, but we don't currently have better one. */
+void BKE_subsurf_osd_init(void);
+void BKE_subsurf_free_unused_buffers(void);
+void BKE_subsurf_osd_cleanup(void);
 #endif
 
+#endif
index 95ddb9d5498158d3875f4bb1cbf87cfe9dc46429..828a6bb16ac66d95ca43ef464cdce22f836451e6 100644 (file)
@@ -334,11 +334,10 @@ void ccgSubSurf_free(CCGSubSurf *ss)
                openSubdiv_deleteEvaluatorDescr(ss->osd_evaluator);
        }
        if (ss->osd_mesh != NULL) {
-               /* TODO(sergey): Make sure free happens form the main thread! */
-               openSubdiv_deleteOsdGLMesh(ss->osd_mesh);
+               ccgSubSurf__delete_osdGLMesh(ss->osd_mesh);
        }
        if (ss->osd_vao != 0) {
-               glDeleteVertexArrays(1, &ss->osd_vao);
+               ccgSubSurf__delete_vertex_array(ss->osd_vao);
        }
        if (ss->osd_coarse_coords != NULL) {
                MEM_freeN(ss->osd_coarse_coords);
index e2b42065382e3ec8c99e180d783dc48433d52700..e0771020350aee4b94bef38bcdf02afe031d65cd 100644 (file)
@@ -303,6 +303,14 @@ void ccgSubSurf__sync_legacy(CCGSubSurf *ss);
 
 void ccgSubSurf__sync_opensubdiv(CCGSubSurf *ss);
 
+/* Delayed free routines. Will do actual free if called from
+ * main thread and schedule free for later free otherwise.
+ */
+
+void ccgSubSurf__delete_osdGLMesh(struct OpenSubdiv_GLMesh *osd_mesh);
+void ccgSubSurf__delete_vertex_array(unsigned int vao);
+void ccgSubSurf__delete_pending(void);
+
 /* * CCGSubSurf_opensubdiv_converter.c * */
 
 struct OpenSubdiv_Converter;
index 9d5ef1079b8c035e64bf3aacf9901bd82690f7dd..0f61a50ed13966ea4187dc4525b087bae4b3ec75 100644 (file)
 #include "BLI_sys_types.h" // for intptr_t support
 
 #include "BLI_utildefines.h" /* for BLI_assert */
+#include "BLI_listbase.h"
 #include "BLI_math.h"
+#include "BLI_threads.h"
 
 #include "CCGSubSurf.h"
 #include "CCGSubSurf_intern.h"
 
 #include "BKE_DerivedMesh.h"
+#include "BKE_subsurf.h"
 
 #include "DNA_userdef_types.h"
 
@@ -236,7 +239,7 @@ bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, bool use_osd_glsl)
 
        if (ss->osd_mesh_invalid) {
                if (ss->osd_mesh != NULL) {
-                       openSubdiv_deleteOsdGLMesh(ss->osd_mesh);
+                       ccgSubSurf__delete_osdGLMesh(ss->osd_mesh);
                        ss->osd_mesh = NULL;
                }
                ss->osd_mesh_invalid = false;
@@ -897,8 +900,7 @@ void ccgSubSurf__sync_opensubdiv(CCGSubSurf *ss)
 void ccgSubSurf_free_osd_mesh(CCGSubSurf *ss)
 {
        if (ss->osd_mesh != NULL) {
-               /* TODO(sergey): Make sure free happens form the main thread! */
-               openSubdiv_deleteOsdGLMesh(ss->osd_mesh);
+               ccgSubSurf__delete_osdGLMesh(ss->osd_mesh);
                ss->osd_mesh = NULL;
        }
        if (ss->osd_vao != 0) {
@@ -921,4 +923,84 @@ void ccgSubSurf_getMinMax(CCGSubSurf *ss, float r_min[3], float r_max[3])
        }
 }
 
+/* ** Delayed delete routines ** */
+
+typedef struct OsdDeletePendingItem {
+       struct OsdDeletePendingItem *next, *prev;
+       OpenSubdiv_GLMesh *osd_mesh;
+       unsigned int vao;
+} OsdDeletePendingItem;
+
+static SpinLock delete_spin;
+static ListBase delete_pool = {NULL, NULL};
+
+static void delete_pending_push(OpenSubdiv_GLMesh *osd_mesh,
+                                unsigned int vao)
+{
+       OsdDeletePendingItem *new_entry = MEM_mallocN(sizeof(OsdDeletePendingItem),
+                                                     "opensubdiv delete entry");
+       new_entry->osd_mesh = osd_mesh;
+       new_entry->vao = vao;
+       BLI_spin_lock(&delete_spin);
+       BLI_addtail(&delete_pool, new_entry);
+       BLI_spin_unlock(&delete_spin);
+}
+
+void ccgSubSurf__delete_osdGLMesh(OpenSubdiv_GLMesh *osd_mesh)
+{
+       if (BLI_thread_is_main()) {
+               openSubdiv_deleteOsdGLMesh(osd_mesh);
+       }
+       else {
+               delete_pending_push(osd_mesh, 0);
+       }
+}
+
+void ccgSubSurf__delete_vertex_array(unsigned int vao)
+{
+       if (BLI_thread_is_main()) {
+               glDeleteVertexArrays(1, &vao);
+       }
+       else {
+               delete_pending_push(NULL, vao);
+       }
+}
+
+void ccgSubSurf__delete_pending(void)
+{
+       OsdDeletePendingItem *entry;
+       BLI_assert(BLI_thread_is_main());
+       BLI_spin_lock(&delete_spin);
+       for (entry = delete_pool.first; entry != NULL; entry = entry->next) {
+               if (entry->osd_mesh != NULL) {
+                       openSubdiv_deleteOsdGLMesh(entry->osd_mesh);
+               }
+               if (entry->vao != 0) {
+                       glDeleteVertexArrays(1, &entry->vao);
+               }
+       }
+       BLI_freelistN(&delete_pool);
+       BLI_spin_unlock(&delete_spin);
+}
+
+/* ** Public API ** */
+
+void BKE_subsurf_osd_init(void)
+{
+       openSubdiv_init();
+       BLI_spin_init(&delete_spin);
+}
+
+void BKE_subsurf_free_unused_buffers(void)
+{
+       ccgSubSurf__delete_pending();
+}
+
+void BKE_subsurf_osd_cleanup(void)
+{
+       openSubdiv_cleanup();
+       ccgSubSurf__delete_pending();
+       BLI_spin_end(&delete_spin);
+}
+
 #endif  /* WITH_OPENSUBDIV */
index b3fa60185173f83bf8db994a2e50255d68f4aa13..47f9aecfc63a1b2a4637643b54271277c494af5f 100644 (file)
@@ -1376,11 +1376,6 @@ static void scene_do_rb_simulation_recursive(Scene *scene, float ctime)
  */
 #define MBALL_SINGLETHREAD_HACK
 
-/* Need this because CCFDM holds some OpenGL resources. */
-#ifdef WITH_OPENSUBDIV
-#  define OPENSUBDIV_GL_WORKAROUND
-#endif
-
 #ifdef WITH_LEGACY_DEPSGRAPH
 typedef struct StatisicsEntry {
        struct StatisicsEntry *next, *prev;
@@ -1582,56 +1577,6 @@ static bool scene_need_update_objects(Main *bmain)
                DAG_id_type_tagged(bmain, ID_AR);     /* Armature */
 }
 
-#ifdef OPENSUBDIV_GL_WORKAROUND
-/* CCG DrivedMesh currently hold some OpenGL handles, which could only be
- * released from the main thread.
- *
- * Ideally we need to use gpu_buffer_free, but it's a bit tricky because
- * some buffers are only accessible from OpenSubdiv side.
- */
-static void scene_free_unused_opensubdiv_cache(Scene *scene)
-{
-       Base *base;
-       for (base = scene->base.first; base; base = base->next) {
-               Object *object = base->object;
-               if (object->type == OB_MESH && object->recalc & OB_RECALC_DATA) {
-                       ModifierData *md = object->modifiers.last;
-                       if (md != NULL && md->type == eModifierType_Subsurf) {
-                               SubsurfModifierData *smd = (SubsurfModifierData *) md;
-                               const bool object_in_editmode = (object->mode == OB_MODE_EDIT);
-                               const bool use_simple = (smd->subdivType == ME_SIMPLE_SUBSURF);
-                               if (!smd->use_opensubdiv ||
-                                   DAG_get_eval_flags_for_object(scene, object) & DAG_EVAL_NEED_CPU)
-                               {
-                                       if (smd->mCache != NULL) {
-                                               ccgSubSurf_free_osd_mesh(smd->mCache);
-                                       }
-                                       if (smd->emCache != NULL) {
-                                               ccgSubSurf_free_osd_mesh(smd->emCache);
-                                       }
-                               }
-                               if (smd->mCache != NULL) {
-                                       if (object_in_editmode ||
-                                           ccgSubSurf_getSimpleSubdiv(smd->mCache) != use_simple)
-                                       {
-                                               ccgSubSurf_free(smd->mCache);
-                                               smd->mCache = NULL;
-                                       }
-                               }
-                               if (smd->emCache != NULL) {
-                                       if (!object_in_editmode ||
-                                           ccgSubSurf_getSimpleSubdiv(smd->emCache) != use_simple)
-                                       {
-                                               ccgSubSurf_free(smd->emCache);
-                                               smd->emCache = NULL;
-                                       }
-                               }
-                       }
-               }
-       }
-}
-#endif
-
 static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene *scene, Scene *scene_parent)
 {
        TaskScheduler *task_scheduler = BLI_task_scheduler_get();
@@ -1650,10 +1595,6 @@ static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene
                return;
        }
 
-#ifdef OPENSUBDIV_GL_WORKAROUND
-       scene_free_unused_opensubdiv_cache(scene);
-#endif
-
        state.eval_ctx = eval_ctx;
        state.scene = scene;
        state.scene_parent = scene_parent;
@@ -1827,11 +1768,6 @@ void BKE_scene_update_tagged(EvaluationContext *eval_ctx, Main *bmain, Scene *sc
        else
 #endif
        {
-#ifdef OPENSUBDIV_GL_WORKAROUND
-               if (DEG_needs_eval(scene->depsgraph)) {
-                       scene_free_unused_opensubdiv_cache(scene);
-               }
-#endif
                DEG_evaluate_on_refresh(eval_ctx, scene->depsgraph, scene);
                /* TODO(sergey): This is to beocme a node in new depsgraph. */
                BKE_mask_update_scene(bmain, scene);
index c5e8cbf1260cb725fc904d5144c040dd8f9d63dc..eaea70adc8361afe023eebd0bf69964cacd0d39a 100644 (file)
@@ -142,9 +142,6 @@ endif()
 
 if(WITH_OPENSUBDIV)
        add_definitions(-DWITH_OPENSUBDIV)
-       list(APPEND INC
-               ../../../intern/opensubdiv
-       )
 endif()
 
 if(WIN32)
index ec1b265d800b116a73a759009ae92e3e556e36de..1a058ca313661cce48a56d0028a3ab925f1e6add 100644 (file)
@@ -93,6 +93,5 @@ if env['OURPLATFORM'] in ('linux', 'openbsd3', 'sunos5', 'freebsd7', 'freebsd8',
 
 if env['WITH_BF_OPENSUBDIV']:
     defs.append("WITH_OPENSUBDIV")
-    incs += ' #intern/opensubdiv'
 
 env.BlenderLib ( 'bf_windowmanager', sources, Split(incs), defines=defs, libtype=['core'], priority=[5] )
index 16fe9ca5142a22d2d81b809ce71b0807550bf030..6ad0405c56f3fa5d59167073532204e5a6ec69a4 100644 (file)
 #include "wm_window.h"
 #include "wm_event_system.h"
 
+#ifdef WITH_OPENSUBDIV
+#  include "BKE_subsurf.h"
+#endif
+
 /* swap */
 #define WIN_NONE_OK     0
 #define WIN_BACK_OK     1
@@ -1002,6 +1006,10 @@ void wm_draw_update(bContext *C)
        wmWindow *win;
        int drawmethod;
 
+#ifdef WITH_OPENSUBDIV
+       BKE_subsurf_free_unused_buffers();
+#endif
+
        GPU_free_unused_buffers();
        
        for (win = wm->windows.first; win; win = win->next) {
index ba4a807dbd7a94c44bb088810bf152a4ee57c18f..d528b6588365d60d37c2f241f8774a3be2932f0d 100644 (file)
 #include "COM_compositor.h"
 
 #ifdef WITH_OPENSUBDIV
-#  include "opensubdiv_capi.h"
+#  include "BKE_subsurf.h"
 #endif
 
 static void wm_init_reports(bContext *C)
@@ -196,7 +196,7 @@ void WM_init(bContext *C, int argc, const char **argv)
                GPU_set_gpu_mipmapping(U.use_gpu_mipmap);
 
 #ifdef WITH_OPENSUBDIV
-               openSubdiv_init();
+               BKE_subsurf_osd_init();
 #endif
 
                UI_init();
@@ -547,7 +547,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
 #endif
 
 #ifdef WITH_OPENSUBDIV
-       openSubdiv_cleanup();
+       BKE_subsurf_osd_cleanup();
 #endif
 
        if (!G.background) {