OpenSubdiv: Use pool for delayed OpenGL buffers free when freeing from non-main thread

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
This commit is contained in:
Sergey Sharybin 2015-11-04 21:30:25 +05:00
parent 4ff6e08a6d
commit a5f4f1e2ce
Notes: blender-bot 2023-02-14 11:34:30 +01:00
Referenced by issue #46589, OpenSubdiv misbehavior and crash with vertex-parented children and drivers.
9 changed files with 112 additions and 77 deletions

View 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

View 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);

View 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;

View File

@ -28,12 +28,15 @@
#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 */

View 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);

View File

@ -142,9 +142,6 @@ endif()
if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
list(APPEND INC
../../../intern/opensubdiv
)
endif()
if(WIN32)

View 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] )

View File

@ -69,6 +69,10 @@
#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) {

View File

@ -118,7 +118,7 @@
#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) {