Files
test2/source/blender/blenkernel/intern/blender.cc
Aras Pranckevicius 11a99fb2b1 Fix #130463, #114592: VSE prefetching uses wrong font for text strips
Prefetching happens on a background thread by design, and so it was not loading
any fonts and using the default monospace font.

Address this by making all font usage within VSE use "unique" BLF font objects,
and protecting concurrent access to them or their state within VSE itself:

- SeqFontMap structure to hold a mutex, file path -> font ID map (for file
  based fonts), name -> font ID map (for datablock fonts).
- seq_load_font_file, seq_load_font_mem, seq_unload_font that use the above
  instead of calling into BLF directly.
- Text effect rendering part guards with a mutex, so that font render state and
  glyph cache does not get trashed.
- SeqFontMap is global instead of some Scene-specific runtime data (e.g.
  scene->ed), because right now there's a hard max limit of total number of
  fonts (64), and if each copy of the VSE data would start to  load their own
  unique fonts, that limit could get easily reached.
- BLF font loading/unloading code is now thread safe. The rest of BLF drawing
  is still not!

If at some point in the future BLF font usage becomes thread safe, and font
drawing becomes "stateless", this whole machinery can be removed.

Pull Request: https://projects.blender.org/blender/blender/pulls/130542
2024-11-25 12:15:22 +01:00

509 lines
12 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*
* Application level startup/shutdown functionality.
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "IMB_imbuf.hh"
#include "IMB_moviecache.hh"
#include "BKE_addon.h"
#include "BKE_asset.hh"
#include "BKE_blender.hh" /* own include */
#include "BKE_blender_user_menu.hh" /* own include */
#include "BKE_blender_version.h" /* own include */
#include "BKE_brush.hh"
#include "BKE_cachefile.hh"
#include "BKE_callbacks.hh"
#include "BKE_global.hh"
#include "BKE_idprop.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "BKE_report.hh"
#include "BKE_screen.hh"
#include "BKE_studiolight.h"
#include "BKE_writeffmpeg.hh"
#include "DEG_depsgraph.hh"
#include "RE_texture.h"
#include "BLF_api.hh"
#include "SEQ_utils.hh"
Global G;
UserDef U;
/* -------------------------------------------------------------------- */
/** \name Blender Free on Exit
* \{ */
void BKE_blender_free()
{
/* samples are in a global list..., also sets G_MAIN->sound->sample nullptr */
/* Needs to run before main free as window-manager is still referenced for icons preview jobs. */
BKE_studiolight_free();
BKE_blender_globals_clear();
if (G.log.file != nullptr) {
fclose(static_cast<FILE *>(G.log.file));
}
BKE_spacetypes_free(); /* after free main, it uses space callbacks */
IMB_exit();
BKE_cachefiles_exit();
DEG_free_node_types();
BKE_brush_system_exit();
RE_texture_rng_exit();
BKE_callback_global_finalize();
IMB_moviecache_destruct();
SEQ_fontmap_clear();
#ifdef WITH_FFMPEG
BKE_ffmpeg_exit();
#endif
blender::bke::node_system_exit();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender Version Access
* \{ */
static char blender_version_string[48] = "";
/* Only includes patch if non-zero. */
static char blender_version_string_compact[48] = "";
static void blender_version_init()
{
const char *version_cycle = "";
const char *version_cycle_compact = "";
if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) {
version_cycle = " Alpha";
version_cycle_compact = " a";
}
else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) {
version_cycle = " Beta";
version_cycle_compact = " b";
}
else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) {
version_cycle = " Release Candidate";
version_cycle_compact = " RC";
}
else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
version_cycle = "";
version_cycle_compact = "";
}
else {
BLI_assert_msg(0, "Invalid Blender version cycle");
}
const char *version_suffix = BKE_blender_version_is_lts() ? " LTS" : "";
SNPRINTF(blender_version_string,
"%d.%01d.%d%s%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
BLENDER_VERSION_PATCH,
version_suffix,
version_cycle);
SNPRINTF(blender_version_string_compact,
"%d.%01d.%d%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
BLENDER_VERSION_PATCH,
version_cycle_compact);
}
const char *BKE_blender_version_string()
{
return blender_version_string;
}
const char *BKE_blender_version_string_compact()
{
return blender_version_string_compact;
}
void BKE_blender_version_blendfile_string_from_values(char *str_buff,
const size_t str_buff_maxncpy,
const short file_version,
const short file_subversion)
{
const short file_version_major = file_version / 100;
const short file_version_minor = file_version % 100;
if (file_subversion >= 0) {
BLI_snprintf(str_buff,
str_buff_maxncpy,
"%d.%d (sub %d)",
file_version_major,
file_version_minor,
file_subversion);
}
else {
BLI_snprintf(str_buff, str_buff_maxncpy, "%d.%d", file_version_major, file_version_minor);
}
}
bool BKE_blender_version_is_alpha()
{
bool is_alpha = STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha");
return is_alpha;
}
bool BKE_blender_version_is_lts()
{
return STREQ(STRINGIFY(BLENDER_VERSION_SUFFIX), "LTS");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender #Global Initialize/Clear
* \{ */
void BKE_blender_globals_init()
{
blender_version_init();
memset(&G, 0, sizeof(Global));
U.savetime = 1;
BKE_blender_globals_main_replace(BKE_main_new());
STRNCPY(G.filepath_last_image, "//");
#ifndef WITH_PYTHON_SECURITY /* default */
G.f |= G_FLAG_SCRIPT_AUTOEXEC;
#else
G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
#endif
G.log.level = 1;
}
void BKE_blender_globals_clear()
{
if (G_MAIN == nullptr) {
return;
}
BLI_assert(G_MAIN->is_global_main);
BKE_main_free(G_MAIN); /* free all lib data */
G_MAIN = nullptr;
}
void BKE_blender_globals_main_replace(Main *bmain)
{
BLI_assert(!bmain->is_global_main);
BKE_blender_globals_clear();
bmain->is_global_main = true;
G_MAIN = bmain;
}
Main *BKE_blender_globals_main_swap(Main *new_gmain)
{
Main *old_gmain = G_MAIN;
BLI_assert(old_gmain->is_global_main);
BLI_assert(!new_gmain->is_global_main);
new_gmain->is_global_main = true;
G_MAIN = new_gmain;
old_gmain->is_global_main = false;
return old_gmain;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender Preferences
* \{ */
static void keymap_item_free(wmKeyMapItem *kmi)
{
if (kmi->properties) {
IDP_FreeProperty(kmi->properties);
}
if (kmi->ptr) {
MEM_delete(kmi->ptr);
}
}
void BKE_blender_userdef_data_swap(UserDef *userdef_a, UserDef *userdef_b)
{
blender::dna::shallow_swap(*userdef_a, *userdef_b);
}
void BKE_blender_userdef_data_set(UserDef *userdef)
{
BKE_blender_userdef_data_swap(&U, userdef);
BKE_blender_userdef_data_free(userdef, true);
}
void BKE_blender_userdef_data_set_and_free(UserDef *userdef)
{
BKE_blender_userdef_data_set(userdef);
MEM_freeN(userdef);
}
static void userdef_free_keymaps(UserDef *userdef)
{
for (wmKeyMap *km = static_cast<wmKeyMap *>(userdef->user_keymaps.first), *km_next; km;
km = km_next)
{
km_next = km->next;
LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &km->diff_items) {
if (kmdi->add_item) {
keymap_item_free(kmdi->add_item);
MEM_freeN(kmdi->add_item);
}
if (kmdi->remove_item) {
keymap_item_free(kmdi->remove_item);
MEM_freeN(kmdi->remove_item);
}
}
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &km->items) {
keymap_item_free(kmi);
}
BLI_freelistN(&km->diff_items);
BLI_freelistN(&km->items);
MEM_freeN(km);
}
BLI_listbase_clear(&userdef->user_keymaps);
}
static void userdef_free_keyconfig_prefs(UserDef *userdef)
{
for (wmKeyConfigPref *kpt = static_cast<wmKeyConfigPref *>(userdef->user_keyconfig_prefs.first),
*kpt_next;
kpt;
kpt = kpt_next)
{
kpt_next = kpt->next;
IDP_FreeProperty(kpt->prop);
MEM_freeN(kpt);
}
BLI_listbase_clear(&userdef->user_keyconfig_prefs);
}
static void userdef_free_user_menus(UserDef *userdef)
{
for (bUserMenu *um = static_cast<bUserMenu *>(userdef->user_menus.first), *um_next; um;
um = um_next)
{
um_next = um->next;
BKE_blender_user_menu_item_free_list(&um->items);
MEM_freeN(um);
}
}
static void userdef_free_addons(UserDef *userdef)
{
for (bAddon *addon = static_cast<bAddon *>(userdef->addons.first), *addon_next; addon;
addon = addon_next)
{
addon_next = addon->next;
BKE_addon_free(addon);
}
BLI_listbase_clear(&userdef->addons);
}
void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
{
#define U BLI_STATIC_ASSERT(false, "Global 'U' not allowed, only use arguments passed in!")
#ifdef U
/* Quiet warning. */
#endif
userdef_free_keymaps(userdef);
userdef_free_keyconfig_prefs(userdef);
userdef_free_user_menus(userdef);
userdef_free_addons(userdef);
if (clear_fonts) {
LISTBASE_FOREACH (uiFont *, font, &userdef->uifonts) {
BLF_unload_id(font->blf_id);
}
BLF_default_set(-1);
}
BLI_freelistN(&userdef->autoexec_paths);
BLI_freelistN(&userdef->script_directories);
BLI_freelistN(&userdef->asset_libraries);
LISTBASE_FOREACH_MUTABLE (bUserExtensionRepo *, repo_ref, &userdef->extension_repos) {
MEM_SAFE_FREE(repo_ref->access_token);
MEM_freeN(repo_ref);
}
BLI_listbase_clear(&userdef->extension_repos);
LISTBASE_FOREACH_MUTABLE (bUserAssetShelfSettings *, settings, &userdef->asset_shelves_settings)
{
BKE_asset_catalog_path_list_free(settings->enabled_catalog_paths);
MEM_freeN(settings);
}
BLI_listbase_clear(&userdef->asset_shelves_settings);
BLI_freelistN(&userdef->uistyles);
BLI_freelistN(&userdef->uifonts);
BLI_freelistN(&userdef->themes);
#undef U
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender Preferences (Application Templates)
* \{ */
void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *userdef_b)
{
/* TODO:
* - various minor settings (add as needed).
*/
#define VALUE_SWAP(id) \
{ \
std::swap(userdef_a->id, userdef_b->id); \
}
#define DATA_SWAP(id) \
{ \
UserDef userdef_tmp; \
memcpy(&(userdef_tmp.id), &(userdef_a->id), sizeof(userdef_tmp.id)); \
memcpy(&(userdef_a->id), &(userdef_b->id), sizeof(userdef_tmp.id)); \
memcpy(&(userdef_b->id), &(userdef_tmp.id), sizeof(userdef_tmp.id)); \
} \
((void)0)
#define FLAG_SWAP(id, ty, flags) \
{ \
CHECK_TYPE(&(userdef_a->id), ty *); \
const ty f = flags; \
const ty a = userdef_a->id; \
const ty b = userdef_b->id; \
userdef_a->id = (userdef_a->id & ~f) | (b & f); \
userdef_b->id = (userdef_b->id & ~f) | (a & f); \
} \
((void)0)
VALUE_SWAP(uistyles);
VALUE_SWAP(uifonts);
VALUE_SWAP(themes);
VALUE_SWAP(addons);
VALUE_SWAP(user_keymaps);
VALUE_SWAP(user_keyconfig_prefs);
DATA_SWAP(font_path_ui);
DATA_SWAP(font_path_ui_mono);
DATA_SWAP(keyconfigstr);
DATA_SWAP(gizmo_flag);
DATA_SWAP(app_flag);
/* We could add others. */
FLAG_SWAP(uiflag, int, USER_SAVE_PROMPT | USER_SPLASH_DISABLE | USER_SHOW_GIZMO_NAVIGATE);
DATA_SWAP(ui_scale);
#undef VALUE_SWAP
#undef DATA_SWAP
#undef FLAG_SWAP
}
void BKE_blender_userdef_app_template_data_set(UserDef *userdef)
{
BKE_blender_userdef_app_template_data_swap(&U, userdef);
BKE_blender_userdef_data_free(userdef, true);
}
void BKE_blender_userdef_app_template_data_set_and_free(UserDef *userdef)
{
BKE_blender_userdef_app_template_data_set(userdef);
MEM_freeN(userdef);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender's AtExit
*
* \note Don't use MEM_mallocN so functions can be registered at any time.
* \{ */
static struct AtExitData {
AtExitData *next;
void (*func)(void *user_data);
void *user_data;
} *g_atexit = nullptr;
void BKE_blender_atexit_register(void (*func)(void *user_data), void *user_data)
{
AtExitData *ae = static_cast<AtExitData *>(malloc(sizeof(*ae)));
ae->next = g_atexit;
ae->func = func;
ae->user_data = user_data;
g_atexit = ae;
}
void BKE_blender_atexit_unregister(void (*func)(void *user_data), const void *user_data)
{
AtExitData *ae = g_atexit;
AtExitData **ae_p = &g_atexit;
while (ae) {
if ((ae->func == func) && (ae->user_data == user_data)) {
*ae_p = ae->next;
free(ae);
return;
}
ae_p = &ae->next;
ae = ae->next;
}
}
void BKE_blender_atexit()
{
AtExitData *ae = g_atexit, *ae_next;
while (ae) {
ae_next = ae->next;
ae->func(ae->user_data);
free(ae);
ae = ae_next;
}
g_atexit = nullptr;
}
/** \} */