Fix: USD hooks leak
If hooks are not unregistered Blender will report a leak on exit. Store the hooks as unique_ptrs to remove manual memory management and encapsulate the previous global list inside a function. These changes ensure that everything is cleaned up on termination. Also makes a small change to the hook documentation for a missing `import` statement. Pull Request: https://projects.blender.org/blender/blender/pulls/118294
This commit is contained in:
parent
401a2397db
commit
9e8bbc1129
@ -55,6 +55,7 @@ import pxr.Gf as Gf
|
|||||||
import pxr.Sdf as Sdf
|
import pxr.Sdf as Sdf
|
||||||
import pxr.Usd as Usd
|
import pxr.Usd as Usd
|
||||||
import pxr.UsdShade as UsdShade
|
import pxr.UsdShade as UsdShade
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
|
||||||
class USDHookExample(bpy.types.USDHook):
|
class USDHookExample(bpy.types.USDHook):
|
||||||
|
@ -9,11 +9,10 @@
|
|||||||
#include <boost/python/call_method.hpp>
|
#include <boost/python/call_method.hpp>
|
||||||
#include <boost/python/class.hpp>
|
#include <boost/python/class.hpp>
|
||||||
#include <boost/python/import.hpp>
|
#include <boost/python/import.hpp>
|
||||||
#include <boost/python/object.hpp>
|
|
||||||
#include <boost/python/return_value_policy.hpp>
|
#include <boost/python/return_value_policy.hpp>
|
||||||
#include <boost/python/to_python_converter.hpp>
|
#include <boost/python/to_python_converter.hpp>
|
||||||
|
|
||||||
#include "BLI_listbase.h"
|
#include "BLI_utildefines.h"
|
||||||
|
|
||||||
#include "BKE_report.h"
|
#include "BKE_report.h"
|
||||||
|
|
||||||
@ -22,49 +21,52 @@
|
|||||||
#include "RNA_types.hh"
|
#include "RNA_types.hh"
|
||||||
#include "bpy_rna.h"
|
#include "bpy_rna.h"
|
||||||
|
|
||||||
#include "WM_api.hh"
|
|
||||||
#include "WM_types.hh"
|
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
using namespace boost;
|
using namespace boost;
|
||||||
|
|
||||||
namespace blender::io::usd {
|
namespace blender::io::usd {
|
||||||
|
|
||||||
using USDHookList = std::list<USDHook *>;
|
using USDHookList = std::list<std::unique_ptr<USDHook>>;
|
||||||
|
|
||||||
/* USD hook type declarations */
|
/* USD hook type declarations */
|
||||||
static USDHookList g_usd_hooks;
|
static USDHookList &hook_list()
|
||||||
|
|
||||||
void USD_register_hook(USDHook *hook)
|
|
||||||
{
|
{
|
||||||
if (std::find(g_usd_hooks.begin(), g_usd_hooks.end(), hook) != g_usd_hooks.end()) {
|
static USDHookList hooks{};
|
||||||
|
return hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USD_register_hook(std::unique_ptr<USDHook> hook)
|
||||||
|
{
|
||||||
|
if (USD_find_hook_name(hook->idname)) {
|
||||||
/* The hook is already in the list. */
|
/* The hook is already in the list. */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add hook type to the list. */
|
/* Add hook type to the list. */
|
||||||
g_usd_hooks.push_back(hook);
|
hook_list().push_back(std::move(hook));
|
||||||
}
|
}
|
||||||
|
|
||||||
void USD_unregister_hook(USDHook *hook)
|
void USD_unregister_hook(USDHook *hook)
|
||||||
{
|
{
|
||||||
g_usd_hooks.remove(hook);
|
hook_list().remove_if(
|
||||||
|
[hook](const std::unique_ptr<USDHook> &item) { return item.get() == hook; });
|
||||||
}
|
}
|
||||||
|
|
||||||
USDHook *USD_find_hook_name(const char name[])
|
USDHook *USD_find_hook_name(const char idname[])
|
||||||
{
|
{
|
||||||
/* sanity checks */
|
/* sanity checks */
|
||||||
if (g_usd_hooks.empty() || (name == nullptr) || (name[0] == 0)) {
|
if (hook_list().empty() || (idname == nullptr) || (idname[0] == 0)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
USDHookList::iterator hook_iter = std::find_if(
|
USDHookList::iterator hook_iter = std::find_if(
|
||||||
g_usd_hooks.begin(), g_usd_hooks.end(), [name](USDHook *hook) {
|
hook_list().begin(), hook_list().end(), [idname](const std::unique_ptr<USDHook> &item) {
|
||||||
return STREQ(hook->idname, name);
|
return STREQ(item->idname, idname);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (hook_iter == g_usd_hooks.end()) ? nullptr : *hook_iter;
|
return (hook_iter == hook_list().end()) ? nullptr : hook_iter->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert PointerRNA to a PyObject*. */
|
/* Convert PointerRNA to a PyObject*. */
|
||||||
@ -136,7 +138,7 @@ void register_hook_converters()
|
|||||||
static bool registered = false;
|
static bool registered = false;
|
||||||
|
|
||||||
/* No need to register if there are no hooks. */
|
/* No need to register if there are no hooks. */
|
||||||
if (g_usd_hooks.empty()) {
|
if (hook_list().empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,22 +198,22 @@ class USDHookInvoker {
|
|||||||
/* Attempt to call the function, if defined by the registered hooks. */
|
/* Attempt to call the function, if defined by the registered hooks. */
|
||||||
void call() const
|
void call() const
|
||||||
{
|
{
|
||||||
if (g_usd_hooks.empty()) {
|
if (hook_list().empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||||
|
|
||||||
/* Iterate over the hooks and invoke the hook function, if it's defined. */
|
/* Iterate over the hooks and invoke the hook function, if it's defined. */
|
||||||
USDHookList::const_iterator hook_iter = g_usd_hooks.begin();
|
USDHookList::const_iterator hook_iter = hook_list().begin();
|
||||||
while (hook_iter != g_usd_hooks.end()) {
|
while (hook_iter != hook_list().end()) {
|
||||||
|
|
||||||
/* XXX: Not sure if this is necessary:
|
/* XXX: Not sure if this is necessary:
|
||||||
* Advance the iterator before invoking the callback, to guard
|
* Advance the iterator before invoking the callback, to guard
|
||||||
* against the unlikely error where the hook is de-registered in
|
* against the unlikely error where the hook is de-registered in
|
||||||
* the callback. This would prevent a crash due to the iterator
|
* the callback. This would prevent a crash due to the iterator
|
||||||
* getting invalidated. */
|
* getting invalidated. */
|
||||||
USDHook *hook = *hook_iter;
|
USDHook *hook = hook_iter->get();
|
||||||
++hook_iter;
|
++hook_iter;
|
||||||
|
|
||||||
if (!hook->rna_ext.data) {
|
if (!hook->rna_ext.data) {
|
||||||
@ -329,7 +331,7 @@ class OnImportInvoker : public USDHookInvoker {
|
|||||||
|
|
||||||
void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports)
|
void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports)
|
||||||
{
|
{
|
||||||
if (g_usd_hooks.empty()) {
|
if (hook_list().empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +344,7 @@ void call_material_export_hooks(pxr::UsdStageRefPtr stage,
|
|||||||
pxr::UsdShadeMaterial &usd_material,
|
pxr::UsdShadeMaterial &usd_material,
|
||||||
ReportList *reports)
|
ReportList *reports)
|
||||||
{
|
{
|
||||||
if (g_usd_hooks.empty()) {
|
if (hook_list().empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +354,7 @@ void call_material_export_hooks(pxr::UsdStageRefPtr stage,
|
|||||||
|
|
||||||
void call_import_hooks(pxr::UsdStageRefPtr stage, ReportList *reports)
|
void call_import_hooks(pxr::UsdStageRefPtr stage, ReportList *reports)
|
||||||
{
|
{
|
||||||
if (g_usd_hooks.empty()) {
|
if (hook_list().empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,13 +204,12 @@ struct USDHook {
|
|||||||
ExtensionRNA rna_ext;
|
ExtensionRNA rna_ext;
|
||||||
};
|
};
|
||||||
|
|
||||||
void USD_register_hook(USDHook *hook);
|
void USD_register_hook(std::unique_ptr<USDHook> hook);
|
||||||
/**
|
/**
|
||||||
* Remove the given entry from the list of registered hooks.
|
* Remove the given entry from the list of registered hooks and
|
||||||
* Note that this does not free the allocated memory for the
|
* free the allocated memory for the hook instance.
|
||||||
* hook instance, so a separate call to `MEM_freeN(hook)` is required.
|
|
||||||
*/
|
*/
|
||||||
void USD_unregister_hook(USDHook *hook);
|
void USD_unregister_hook(USDHook *hook);
|
||||||
USDHook *USD_find_hook_name(const char name[]);
|
USDHook *USD_find_hook_name(const char idname[]);
|
||||||
|
|
||||||
}; // namespace blender::io::usd
|
}; // namespace blender::io::usd
|
||||||
|
@ -46,8 +46,6 @@ static bool rna_USDHook_unregister(Main * /*bmain*/, StructRNA *type)
|
|||||||
/* unlink Blender-side data */
|
/* unlink Blender-side data */
|
||||||
USD_unregister_hook(hook);
|
USD_unregister_hook(hook);
|
||||||
|
|
||||||
MEM_freeN(hook);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +58,7 @@ static StructRNA *rna_USDHook_register(Main *bmain,
|
|||||||
StructFreeFunc free)
|
StructFreeFunc free)
|
||||||
{
|
{
|
||||||
const char *error_prefix = "Registering USD hook class:";
|
const char *error_prefix = "Registering USD hook class:";
|
||||||
USDHook dummy_hook = {{0}};
|
USDHook dummy_hook{};
|
||||||
USDHook *hook;
|
|
||||||
|
|
||||||
/* setup dummy type info to store static properties in */
|
/* setup dummy type info to store static properties in */
|
||||||
PointerRNA dummy_hook_ptr = RNA_pointer_create(nullptr, &RNA_USDHook, &dummy_hook);
|
PointerRNA dummy_hook_ptr = RNA_pointer_create(nullptr, &RNA_USDHook, &dummy_hook);
|
||||||
@ -82,8 +79,7 @@ static StructRNA *rna_USDHook_register(Main *bmain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check if we have registered this hook before, and remove it */
|
/* check if we have registered this hook before, and remove it */
|
||||||
hook = USD_find_hook_name(dummy_hook.idname);
|
if (USDHook *hook = USD_find_hook_name(dummy_hook.idname)) {
|
||||||
if (hook) {
|
|
||||||
BKE_reportf(reports,
|
BKE_reportf(reports,
|
||||||
RPT_INFO,
|
RPT_INFO,
|
||||||
"%s '%s', bl_idname '%s' has been registered before, unregistering previous",
|
"%s '%s', bl_idname '%s' has been registered before, unregistering previous",
|
||||||
@ -105,23 +101,24 @@ static StructRNA *rna_USDHook_register(Main *bmain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* create a new KeyingSetInfo type */
|
/* create a new KeyingSetInfo type */
|
||||||
hook = static_cast<USDHook *>(MEM_mallocN(sizeof(USDHook), "python USD hook"));
|
auto hook = std::make_unique<USDHook>();
|
||||||
memcpy(hook, &dummy_hook, sizeof(USDHook));
|
*hook = dummy_hook;
|
||||||
|
|
||||||
/* set RNA-extensions info */
|
/* set RNA-extensions info */
|
||||||
hook->rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, hook->idname, &RNA_USDHook);
|
hook->rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, hook->idname, &RNA_USDHook);
|
||||||
hook->rna_ext.data = data;
|
hook->rna_ext.data = data;
|
||||||
hook->rna_ext.call = call;
|
hook->rna_ext.call = call;
|
||||||
hook->rna_ext.free = free;
|
hook->rna_ext.free = free;
|
||||||
RNA_struct_blender_type_set(hook->rna_ext.srna, hook);
|
RNA_struct_blender_type_set(hook->rna_ext.srna, hook.get());
|
||||||
|
|
||||||
/* add and register with other info as needed */
|
/* add and register with other info as needed */
|
||||||
USD_register_hook(hook);
|
StructRNA *srna = hook->rna_ext.srna;
|
||||||
|
USD_register_hook(std::move(hook));
|
||||||
|
|
||||||
WM_main_add_notifier(NC_WINDOW, nullptr);
|
WM_main_add_notifier(NC_WINDOW, nullptr);
|
||||||
|
|
||||||
/* return the struct-rna added */
|
/* return the struct-rna added */
|
||||||
return hook->rna_ext.srna;
|
return srna;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
Loading…
x
Reference in New Issue
Block a user