UI: Use vector instead of linked lists for context store

Duplicating context lists took a measurable amount of time when drawing
large node trees in the node editor. Instead of using a linked list of
entries, which results in many small allocations, use a vector. Also,
use std::string and StringRefNull instead of char buffers and pointers.
This commit is contained in:
Hans Goudey 2022-12-18 19:13:15 -06:00
parent d59f6ffdcb
commit 7d7e90ca68
Notes: blender-bot 2023-08-31 18:03:27 +02:00
Referenced by commit 15c433d7d5, Fix: Missing UI context members after recent refactor
Referenced by issue #103417, Regression: Submenus in toolbar are empty
Referenced by issue #103405, Regression: Some modifiers not working
Referenced by commit 1763a46cbf, Cleanup: UI: Store uiBlock context stores in vector instead of list
3 changed files with 53 additions and 66 deletions

View File

@ -16,6 +16,11 @@
#include "DNA_object_enums.h"
#include "RNA_types.h"
#ifdef __cplusplus
# include "BLI_string_ref.hh"
# include "BLI_vector.hh"
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -84,19 +89,22 @@ typedef int /*eContextResult*/ (*bContextDataCallback)(const bContext *C,
const char *member,
bContextDataResult *result);
typedef struct bContextStoreEntry {
struct bContextStoreEntry *next, *prev;
#ifdef __cplusplus
char name[128];
struct bContextStoreEntry {
std::string name;
PointerRNA ptr;
} bContextStoreEntry;
};
typedef struct bContextStore {
struct bContextStore *next, *prev;
struct bContextStore {
bContextStore *next = nullptr;
bContextStore *prev = nullptr;
ListBase entries;
bool used;
} bContextStore;
blender::Vector<bContextStoreEntry> entries;
bool used = false;
};
#endif
/* for the context's rna mode enum
* keep aligned with data_mode_strings in context.cc */
@ -132,18 +140,23 @@ void CTX_free(bContext *C);
bContext *CTX_copy(const bContext *C);
#ifdef __cplusplus
/* Stored Context */
bContextStore *CTX_store_add(ListBase *contexts, const char *name, const PointerRNA *ptr);
bContextStore *CTX_store_add(ListBase *contexts,
blender::StringRefNull name,
const PointerRNA *ptr);
bContextStore *CTX_store_add_all(ListBase *contexts, bContextStore *context);
bContextStore *CTX_store_get(bContext *C);
void CTX_store_set(bContext *C, bContextStore *store);
const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
const char *name,
const StructRNA *type CPP_ARG_DEFAULT(nullptr));
bContextStore *CTX_store_copy(bContextStore *store);
blender::StringRefNull name,
const StructRNA *type = nullptr);
bContextStore *CTX_store_copy(const bContextStore *store);
void CTX_store_free(bContextStore *store);
void CTX_store_free_list(ListBase *contexts);
#endif
/* need to store if python is initialized or not */
bool CTX_py_init_get(bContext *C);

View File

@ -126,7 +126,9 @@ void CTX_free(bContext *C)
/* store */
bContextStore *CTX_store_add(ListBase *contexts, const char *name, const PointerRNA *ptr)
bContextStore *CTX_store_add(ListBase *contexts,
const blender::StringRefNull name,
const PointerRNA *ptr)
{
/* ensure we have a context to put the entry in, if it was already used
* we have to copy the context to ensure */
@ -134,23 +136,16 @@ bContextStore *CTX_store_add(ListBase *contexts, const char *name, const Pointer
if (!ctx || ctx->used) {
if (ctx) {
bContextStore *lastctx = ctx;
ctx = MEM_new<bContextStore>(__func__);
*ctx = *lastctx;
BLI_duplicatelist(&ctx->entries, &lastctx->entries);
ctx = MEM_new<bContextStore>(__func__, *ctx);
}
else {
ctx = MEM_cnew<bContextStore>(__func__);
ctx = MEM_new<bContextStore>(__func__);
}
BLI_addtail(contexts, ctx);
}
bContextStoreEntry *entry = MEM_cnew<bContextStoreEntry>(__func__);
BLI_strncpy(entry->name, name, sizeof(entry->name));
entry->ptr = *ptr;
BLI_addtail(&ctx->entries, entry);
ctx->entries.append(bContextStoreEntry{name, *ptr});
return ctx;
}
@ -163,22 +158,17 @@ bContextStore *CTX_store_add_all(ListBase *contexts, bContextStore *context)
if (!ctx || ctx->used) {
if (ctx) {
bContextStore *lastctx = ctx;
ctx = MEM_new<bContextStore>(__func__);
*ctx = *lastctx;
BLI_duplicatelist(&ctx->entries, &lastctx->entries);
ctx = MEM_new<bContextStore>(__func__, *ctx);
}
else {
ctx = MEM_cnew<bContextStore>(__func__);
ctx = MEM_new<bContextStore>(__func__);
}
BLI_addtail(contexts, ctx);
}
LISTBASE_FOREACH (bContextStoreEntry *, tentry, &context->entries) {
bContextStoreEntry *entry = MEM_cnew<bContextStoreEntry>(__func__);
*entry = *tentry;
BLI_addtail(&ctx->entries, entry);
for (const bContextStoreEntry &src_entry : context->entries) {
ctx->entries.append(src_entry);
}
return ctx;
@ -195,42 +185,27 @@ void CTX_store_set(bContext *C, bContextStore *store)
}
const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
const char *name,
const blender::StringRefNull name,
const StructRNA *type)
{
bContextStoreEntry *entry = static_cast<bContextStoreEntry *>(
BLI_rfindstring(&store->entries, name, offsetof(bContextStoreEntry, name)));
if (!entry) {
return nullptr;
for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) {
if (entry->name == name) {
if (type && RNA_struct_is_a(entry->ptr.type, type)) {
return &entry->ptr;
}
}
}
if (type && !RNA_struct_is_a(entry->ptr.type, type)) {
return nullptr;
}
return &entry->ptr;
return nullptr;
}
bContextStore *CTX_store_copy(bContextStore *store)
bContextStore *CTX_store_copy(const bContextStore *store)
{
bContextStore *ctx = MEM_cnew<bContextStore>(__func__);
*ctx = *store;
BLI_duplicatelist(&ctx->entries, &store->entries);
return ctx;
return MEM_new<bContextStore>(__func__, *store);
}
void CTX_store_free(bContextStore *store)
{
BLI_freelistN(&store->entries);
MEM_freeN(store);
}
void CTX_store_free_list(ListBase *contexts)
{
bContextStore *ctx;
while ((ctx = static_cast<bContextStore *>(BLI_pophead(contexts)))) {
CTX_store_free(ctx);
}
MEM_delete(store);
}
/* is python initialized? */
@ -592,11 +567,8 @@ ListBase CTX_data_dir_get_ex(const bContext *C,
RNA_PROP_END;
}
if (use_store && C->wm.store) {
bContextStoreEntry *entry;
for (entry = static_cast<bContextStoreEntry *>(C->wm.store->entries.first); entry;
entry = entry->next) {
data_dir_add(&lb, entry->name, use_all);
for (const bContextStoreEntry &entry : C->wm.store->entries) {
data_dir_add(&lb, entry.name.c_str(), use_all);
}
}
if ((region = CTX_wm_region(C)) && region->type && region->type->context) {

View File

@ -3470,7 +3470,9 @@ void UI_block_free(const bContext *C, uiBlock *block)
MEM_freeN(block->func_argN);
}
CTX_store_free_list(&block->contexts);
LISTBASE_FOREACH_MUTABLE (bContextStore *, store, &block->contexts) {
CTX_store_free(store);
}
BLI_freelistN(&block->saferct);
BLI_freelistN(&block->color_pickers.list);