app: remove old TEXT_CTX and replace with parley new pattern

This commit is contained in:
jkds
2026-01-06 09:56:34 +01:00
committed by darkfi
parent c4ec4de713
commit 5c2bd621df
8 changed files with 125 additions and 171 deletions

View File

@@ -115,7 +115,6 @@ impl God {
// Abort the application on panic right away // Abort the application on panic right away
std::panic::set_hook(Box::new(panic_hook)); std::panic::set_hook(Box::new(panic_hook));
text::init_txt_ctx();
let file_logging_guard = logger::setup_logging(); let file_logging_guard = logger::setup_logging();
info!(target: "main", "Creating the app"); info!(target: "main", "Creating the app");

View File

@@ -24,7 +24,7 @@ use crate::{
gfx::Point, gfx::Point,
mesh::Color, mesh::Color,
prop::{PropertyAtomicGuard, PropertyColor, PropertyFloat32, PropertyStr}, prop::{PropertyAtomicGuard, PropertyColor, PropertyFloat32, PropertyStr},
text::{TextContext, TEXT_CTX}, text,
}; };
use std::cmp::{max, min}; use std::cmp::{max, min};
@@ -111,8 +111,7 @@ impl Editor {
underlines.push(compose_start..compose_end); underlines.push(compose_start..compose_end);
} }
let mut txt_ctx = TEXT_CTX.get().await; self.layout = text::make_layout(
self.layout = txt_ctx.make_layout(
&self.state.text, &self.state.text,
text_color, text_color,
font_size, font_size,
@@ -175,7 +174,7 @@ impl Editor {
pub fn driver<'a>( pub fn driver<'a>(
&'a mut self, &'a mut self,
_txt_ctx: &'a mut TextContext, _layout_ctx: &'a mut parley::LayoutContext<Color>,
) -> Option<parley::PlainEditorDriver<'a, Color>> { ) -> Option<parley::PlainEditorDriver<'a, Color>> {
None None
} }

View File

@@ -20,7 +20,7 @@ use crate::{
gfx::Point, gfx::Point,
mesh::Color, mesh::Color,
prop::{PropertyAtomicGuard, PropertyColor, PropertyFloat32, PropertyStr}, prop::{PropertyAtomicGuard, PropertyColor, PropertyFloat32, PropertyStr},
text::{TextContext, FONT_STACK, TEXT_CTX}, text::{self, FONT_STACK},
}; };
pub struct Editor { pub struct Editor {
@@ -83,9 +83,11 @@ impl Editor {
styles.insert(parley::StyleProperty::OverflowWrap(parley::OverflowWrap::Anywhere)); styles.insert(parley::StyleProperty::OverflowWrap(parley::OverflowWrap::Anywhere));
*self.editor.edit_styles() = styles; *self.editor.edit_styles() = styles;
let mut txt_ctx = TEXT_CTX.get().await; let mut font_ctx = text::GLOBAL_FONT_CTX.clone();
let (font_ctx, layout_ctx) = txt_ctx.borrow(); text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
self.editor.refresh_layout(font_ctx, layout_ctx); let mut layout_ctx = layout_ctx.borrow_mut();
self.editor.refresh_layout(&mut font_ctx, &mut layout_ctx);
});
} }
pub fn layout(&self) -> &parley::Layout<Color> { pub fn layout(&self) -> &parley::Layout<Color> {
@@ -106,19 +108,20 @@ impl Editor {
} }
pub async fn insert(&mut self, txt: &str, atom: &mut PropertyAtomicGuard) { pub async fn insert(&mut self, txt: &str, atom: &mut PropertyAtomicGuard) {
let mut txt_ctx = TEXT_CTX.get().await; text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
let (font_ctx, layout_ctx) = txt_ctx.borrow(); let mut layout_ctx = layout_ctx.borrow_mut();
let mut drv = self.editor.driver(font_ctx, layout_ctx); let mut drv = self.driver(&mut layout_ctx);
drv.insert_or_replace_selection(&txt); drv.insert_or_replace_selection(&txt);
self.on_buffer_changed(atom).await; self.on_buffer_changed(atom).await;
});
} }
pub fn driver<'a>( pub fn driver<'a>(
&'a mut self, &'a mut self,
txt_ctx: &'a mut TextContext, layout_ctx: &'a mut parley::LayoutContext<Color>,
) -> Option<parley::PlainEditorDriver<'a, Color>> { ) -> Option<parley::PlainEditorDriver<'a, Color>> {
let (font_ctx, layout_ctx) = txt_ctx.borrow(); let mut font_ctx = text::GLOBAL_FONT_CTX.clone();
Some(self.editor.driver(font_ctx, layout_ctx)) Some(self.editor.driver(&mut font_ctx, layout_ctx))
} }
pub fn set_width(&mut self, w: f32) { pub fn set_width(&mut self, w: f32) {
@@ -141,10 +144,11 @@ impl Editor {
/// Android uses byte indexes whereas parley has its own things. So this API is a compromise /// Android uses byte indexes whereas parley has its own things. So this API is a compromise
/// between them both. /// between them both.
pub async fn set_selection(&mut self, select_start: usize, select_end: usize) { pub async fn set_selection(&mut self, select_start: usize, select_end: usize) {
let mut txt_ctx = TEXT_CTX.get().await; text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
let (font_ctx, layout_ctx) = txt_ctx.borrow(); let mut layout_ctx = layout_ctx.borrow_mut();
let mut drv = self.editor.driver(font_ctx, layout_ctx); let mut drv = self.driver(&mut layout_ctx);
drv.select_byte_range(select_start, select_end); drv.select_byte_range(select_start, select_end);
});
} }
#[allow(dead_code)] #[allow(dead_code)]

View File

@@ -16,12 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use async_lock::Mutex as AsyncMutex;
use std::{ use std::{
cell::RefCell,
ops::Range, ops::Range,
sync::{Arc, OnceLock}, sync::Arc,
}; };
use fontique::{Collection, CollectionOptions, SourceCache, SourceCacheOptions};
use once_cell::sync::Lazy;
use crate::{mesh::Color, util::spawn_thread}; use crate::{mesh::Color, util::spawn_thread};
pub mod atlas; pub mod atlas;
@@ -30,107 +33,84 @@ pub use editor::Editor;
mod render; mod render;
pub use render::{render_layout, render_layout_with_opts, DebugRenderOptions}; pub use render::{render_layout, render_layout_with_opts, DebugRenderOptions};
use darkfi::system::CondVar; // Global shared FontContext (thread-safe via internal Arc<Mutex<>>)
pub static GLOBAL_FONT_CTX: Lazy<parley::FontContext> = Lazy::new(|| {
pub struct AsyncGlobal<T> { let collection = Collection::new(CollectionOptions {
cv: CondVar, shared: true,
val: OnceLock<AsyncMutex<T>>, system_fonts: false,
}
impl<T> AsyncGlobal<T> {
const fn new() -> Self {
Self { cv: CondVar::new(), val: OnceLock::new() }
}
fn set(&self, val: T) {
self.val.set(AsyncMutex::new(val)).ok().unwrap();
self.cv.notify();
}
pub async fn get<'a>(&'a self) -> async_lock::MutexGuard<'a, T> {
self.cv.wait().await;
self.val.get().unwrap().lock().await
}
}
pub static TEXT_CTX: AsyncGlobal<TextContext> = AsyncGlobal::new();
pub fn init_txt_ctx() {
spawn_thread("init_txt_ctx", || {
// This is quite slow. It takes 300ms
let txt_ctx = TextContext::new();
TEXT_CTX.set(txt_ctx);
}); });
let source_cache = SourceCache::new(SourceCacheOptions {
shared: true,
});
let mut font_ctx = parley::FontContext { collection, source_cache };
let font_data = include_bytes!("../../ibm-plex-mono-regular.otf") as &[u8];
font_ctx.collection.register_fonts(
peniko::Blob::new(Arc::new(font_data)),
None
);
let font_data = include_bytes!("../../NotoColorEmoji.ttf") as &[u8];
font_ctx.collection.register_fonts(
peniko::Blob::new(Arc::new(font_data)),
None
);
font_ctx
});
// Thread-local LayoutContext
thread_local! {
pub static THREAD_LAYOUT_CTX: RefCell<parley::LayoutContext<'static, Color>> =
RefCell::new(parley::LayoutContext::new());
} }
/// Initializing this is expensive ~300ms, but storage is ~2kb. // Public constants
/// It has to be created once and reused. Currently we use thread local storage. pub const FONT_STACK: &[parley::FontFamily<'_>] = &[
pub struct TextContext { parley::FontFamily::Named(std::borrow::Cow::Borrowed("IBM Plex Mono")),
font_ctx: parley::FontContext, parley::FontFamily::Named(std::borrow::Cow::Borrowed("Noto Color Emoji")),
layout_ctx: parley::LayoutContext<Color>, ];
// FREE FUNCTIONS (no TextContext wrapper!)
pub fn make_layout(
text: &str,
text_color: Color,
font_size: f32,
lineheight: f32,
window_scale: f32,
width: Option<f32>,
underlines: &[Range<usize>],
) -> parley::Layout<Color> {
make_layout2(
text,
text_color,
font_size,
lineheight,
window_scale,
width,
underlines,
&[],
)
} }
impl TextContext { pub fn make_layout2(
fn new() -> Self { text: &str,
let layout_ctx = parley::LayoutContext::new(); text_color: Color,
let mut font_ctx = parley::FontContext::new(); font_size: f32,
lineheight: f32,
window_scale: f32,
width: Option<f32>,
underlines: &[Range<usize>],
foreground_colors: &[(Range<usize>, Color)],
) -> parley::Layout<Color> {
THREAD_LAYOUT_CTX.with(|layout_ctx| {
let mut layout_ctx = layout_ctx.borrow_mut();
let mut font_ctx = GLOBAL_FONT_CTX.clone();
let font_data = include_bytes!("../../ibm-plex-mono-regular.otf") as &[u8];
let _font_inf =
font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
let font_data = include_bytes!("../../NotoColorEmoji.ttf") as &[u8];
let _font_inf =
font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
//for (family_id, _) in font_inf {
// let family_name = font_ctx.collection.family_name(family_id).unwrap();
// trace!(target: "text", "Loaded font: {family_name}");
//}
Self { font_ctx, layout_ctx }
}
#[cfg(not(target_os = "android"))]
pub fn borrow(&mut self) -> (&mut parley::FontContext, &mut parley::LayoutContext<Color>) {
(&mut self.font_ctx, &mut self.layout_ctx)
}
pub fn make_layout(
&mut self,
text: &str,
text_color: Color,
font_size: f32,
lineheight: f32,
window_scale: f32,
width: Option<f32>,
underlines: &[Range<usize>],
) -> parley::Layout<Color> {
self.make_layout2(
text,
text_color,
font_size,
lineheight,
window_scale,
width,
underlines,
&[],
)
}
pub fn make_layout2(
&mut self,
text: &str,
text_color: Color,
font_size: f32,
lineheight: f32,
window_scale: f32,
width: Option<f32>,
underlines: &[Range<usize>],
foreground_colors: &[(Range<usize>, Color)],
) -> parley::Layout<Color> {
let mut builder = let mut builder =
self.layout_ctx.ranged_builder(&mut self.font_ctx, &text, window_scale, false); layout_ctx.ranged_builder(&mut font_ctx, text, window_scale, false);
builder.push_default(parley::LineHeight::FontSizeRelative(lineheight)); builder.push_default(parley::LineHeight::FontSizeRelative(lineheight));
builder.push_default(parley::StyleProperty::FontSize(font_size)); builder.push_default(parley::StyleProperty::FontSize(font_size));
builder.push_default(parley::StyleProperty::FontStack(parley::FontStack::List( builder.push_default(parley::StyleProperty::FontStack(parley::FontStack::List(
@@ -147,14 +127,9 @@ impl TextContext {
builder.push(parley::StyleProperty::Brush(*color), range.clone()); builder.push(parley::StyleProperty::Brush(*color), range.clone());
} }
let mut layout: parley::Layout<Color> = builder.build(&text); let mut layout: parley::Layout<Color> = builder.build(text);
layout.break_all_lines(width); layout.break_all_lines(width);
layout.align(width, parley::Alignment::Start, parley::AlignmentOptions::default()); layout.align(width, parley::Alignment::Start, parley::AlignmentOptions::default());
layout layout
} })
} }
pub const FONT_STACK: &[parley::FontFamily<'_>] = &[
parley::FontFamily::Named(std::borrow::Cow::Borrowed("IBM Plex Mono")),
parley::FontFamily::Named(std::borrow::Cow::Borrowed("Noto Color Emoji")),
];

View File

@@ -109,7 +109,6 @@ impl PrivMessage {
fn cache_txt_layout( fn cache_txt_layout(
&mut self, &mut self,
txt_ctx: &mut text::TextContext,
clip: &Rectangle, clip: &Rectangle,
line_height: f32, line_height: f32,
timestamp_width: f32, timestamp_width: f32,
@@ -130,7 +129,7 @@ impl PrivMessage {
let nick_color = select_nick_color(&self.nick, nick_colors); let nick_color = select_nick_color(&self.nick, nick_colors);
let txt_layout = if self.nick == "NOTICE" { let txt_layout = if self.nick == "NOTICE" {
txt_ctx.make_layout( text::make_layout(
&linetext, &linetext,
text_color, text_color,
self.font_size, self.font_size,
@@ -142,7 +141,7 @@ impl PrivMessage {
} else { } else {
let body_color = if self.confirmed { text_color } else { UNCONF_COLOR }; let body_color = if self.confirmed { text_color } else { UNCONF_COLOR };
let nick_end = self.nick.len() + 1; let nick_end = self.nick.len() + 1;
txt_ctx.make_layout2( text::make_layout2(
&linetext, &linetext,
body_color, body_color,
self.font_size, self.font_size,
@@ -173,11 +172,9 @@ impl PrivMessage {
return instrs.clone() return instrs.clone()
} }
let mut txt_ctx = text::TEXT_CTX.get().await;
// Timestamp layout // Timestamp layout
let timestr = Self::gen_timestr(self.timestamp); let timestr = Self::gen_timestr(self.timestamp);
let timestamp_layout = txt_ctx.make_layout( let timestamp_layout = text::make_layout(
&timestr, &timestr,
timestamp_color, timestamp_color,
self.timestamp_font_size, self.timestamp_font_size,
@@ -188,7 +185,6 @@ impl PrivMessage {
); );
self.cache_txt_layout( self.cache_txt_layout(
&mut txt_ctx,
clip, clip,
line_height, line_height,
timestamp_width, timestamp_width,
@@ -196,8 +192,6 @@ impl PrivMessage {
text_color, text_color,
); );
drop(txt_ctx);
let mut all_instrs = vec![]; let mut all_instrs = vec![];
// Draw selection background if selected // Draw selection background if selected
@@ -307,8 +301,7 @@ impl DateMessage {
let datestr = Self::datestr(self.timestamp); let datestr = Self::datestr(self.timestamp);
let mut txt_ctx = text::TEXT_CTX.get().await; let layout = text::make_layout(
let layout = txt_ctx.make_layout(
&datestr, &datestr,
timestamp_color, timestamp_color,
self.font_size, self.font_size,
@@ -317,7 +310,6 @@ impl DateMessage {
None, None,
&[], &[],
); );
drop(txt_ctx);
let mut instrs = text::render_layout(&layout, render_api, gfxtag!("chatview_datemsg")); let mut instrs = text::render_layout(&layout, render_api, gfxtag!("chatview_datemsg"));
// Cache the instructions // Cache the instructions
@@ -519,9 +511,8 @@ impl FileMessage {
let file_strs = Self::filestr(&self.file_url, &self.status); let file_strs = Self::filestr(&self.file_url, &self.status);
let mut layouts = Vec::with_capacity(file_strs.len()); let mut layouts = Vec::with_capacity(file_strs.len());
let mut txt_ctx = text::TEXT_CTX.get().await;
for file_str in &file_strs { for file_str in &file_strs {
let layout = txt_ctx.make_layout( let layout = text::make_layout(
file_str, file_str,
color, color,
self.font_size, self.font_size,
@@ -532,7 +523,6 @@ impl FileMessage {
); );
layouts.push(layout); layouts.push(layout);
} }
drop(txt_ctx);
all_instrs all_instrs
.push(DrawInstruction::Move(Point::new(timestamp_width + Self::BOX_PADDING_X, 0.))); .push(DrawInstruction::Move(Point::new(timestamp_width + Self::BOX_PADDING_X, 0.)));
@@ -654,7 +644,6 @@ impl Message {
fn cache_txt_layout( fn cache_txt_layout(
&mut self, &mut self,
txt_ctx: &mut text::TextContext,
clip: &Rectangle, clip: &Rectangle,
line_height: f32, line_height: f32,
timestamp_width: f32, timestamp_width: f32,
@@ -664,7 +653,6 @@ impl Message {
match self { match self {
Self::Priv(m) => { Self::Priv(m) => {
m.cache_txt_layout( m.cache_txt_layout(
txt_ctx,
clip, clip,
line_height, line_height,
timestamp_width, timestamp_width,
@@ -871,16 +859,13 @@ impl MessageBuffer {
height += msg_spacing; height += msg_spacing;
} }
let mut txt_ctx = text::TEXT_CTX.get().await;
msg.cache_txt_layout( msg.cache_txt_layout(
&mut txt_ctx,
&rect, &rect,
line_height, line_height,
timestamp_width, timestamp_width,
&nick_colors, &nick_colors,
text_color, text_color,
); );
drop(txt_ctx);
height += msg.height(line_height); height += msg.height(line_height);
} }
@@ -939,16 +924,13 @@ impl MessageBuffer {
text, text,
); );
let mut txt_ctx = text::TEXT_CTX.get().await;
msg.cache_txt_layout( msg.cache_txt_layout(
&mut txt_ctx,
&rect, &rect,
line_height, line_height,
timestamp_width, timestamp_width,
&nick_colors, &nick_colors,
text_color, text_color,
); );
drop(txt_ctx);
if self.msgs.is_empty() { if self.msgs.is_empty() {
self.msgs.push(msg); self.msgs.push(msg);
@@ -1012,16 +994,13 @@ impl MessageBuffer {
text, text,
); );
let mut txt_ctx = text::TEXT_CTX.get().await;
msg.cache_txt_layout( msg.cache_txt_layout(
&mut txt_ctx,
rect, rect,
line_height, line_height,
timestamp_width, timestamp_width,
&nick_colors, &nick_colors,
text_color, text_color,
); );
drop(txt_ctx);
let msg_height = msg.height(self.line_height.get()); let msg_height = msg.height(self.line_height.get());
self.msgs.push(msg); self.msgs.push(msg);

View File

@@ -501,6 +501,7 @@ impl BaseEdit {
async fn handle_shortcut( async fn handle_shortcut(
&self, &self,
layout_ctx: &mut parley::LayoutContext<Color>,
key: char, key: char,
mods: &KeyMods, mods: &KeyMods,
atom: &mut PropertyAtomicGuard, atom: &mut PropertyAtomicGuard,
@@ -516,9 +517,8 @@ impl BaseEdit {
match key { match key {
'a' => { 'a' => {
if action_mod { if action_mod {
let mut txt_ctx = text::TEXT_CTX.get().await;
let mut editor = self.lock_editor().await; let mut editor = self.lock_editor().await;
let mut drv = editor.driver(&mut txt_ctx).unwrap(); let mut drv = editor.driver(layout_ctx);
drv.select_all(); drv.select_all();
if let Some(seltext) = editor.selected_text() { if let Some(seltext) = editor.selected_text() {
@@ -553,6 +553,7 @@ impl BaseEdit {
async fn handle_key( async fn handle_key(
&self, &self,
layout_ctx: &mut parley::LayoutContext<Color>,
key: &KeyCode, key: &KeyCode,
mods: &KeyMods, mods: &KeyMods,
atom: &mut PropertyAtomicGuard, atom: &mut PropertyAtomicGuard,
@@ -565,9 +566,8 @@ impl BaseEdit {
t!("handle_key({:?}, {:?}) action_mod={action_mod}", key, mods); t!("handle_key({:?}, {:?}) action_mod={action_mod}", key, mods);
let mut txt_ctx = text::TEXT_CTX.get().await;
let mut editor = self.lock_editor().await; let mut editor = self.lock_editor().await;
let mut drv = editor.driver(&mut txt_ctx).unwrap(); let mut drv = editor.driver(layout_ctx);
match key { match key {
KeyCode::Left => { KeyCode::Left => {
@@ -1594,7 +1594,10 @@ impl UIObject for BaseEdit {
if repeat { if repeat {
return false return false
} }
return self.handle_shortcut(key, &mods, atom).await return text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
let mut layout_ctx = layout_ctx.borrow_mut();
self.handle_shortcut(&mut layout_ctx, key, &mods, atom).await
})
} }
// Do nothing // Do nothing
@@ -1637,9 +1640,10 @@ impl UIObject for BaseEdit {
let mut is_handled = false; let mut is_handled = false;
for _ in 0..actions { for _ in 0..actions {
if self.handle_key(&key, &mods, atom).await { is_handled = text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
is_handled = true; let mut layout_ctx = layout_ctx.borrow_mut();
} self.handle_key(&mut layout_ctx, &key, &mods, atom).await
});
} }
is_handled is_handled
} }
@@ -1680,12 +1684,12 @@ impl UIObject for BaseEdit {
// Move mouse pos within this widget // Move mouse pos within this widget
self.abs_to_local(&mut mouse_pos); self.abs_to_local(&mut mouse_pos);
{ text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
let mut txt_ctx = text::TEXT_CTX.get().await; let mut layout_ctx = layout_ctx.borrow_mut();
let mut editor = self.lock_editor().await; let mut editor = self.lock_editor().await;
let mut drv = editor.driver(&mut txt_ctx).unwrap(); let mut drv = editor.driver(&mut layout_ctx);
drv.move_to_point(mouse_pos.x, mouse_pos.y); drv.move_to_point(mouse_pos.x, mouse_pos.y);
} });
if !self.select_text.is_null(0).unwrap() { if !self.select_text.is_null(0).unwrap() {
self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap(); self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();

View File

@@ -68,11 +68,8 @@ impl EmojiMeshes {
/// Make mesh for this emoji centered at (0, 0) /// Make mesh for this emoji centered at (0, 0)
async fn gen_emoji_mesh(&self, emoji: &str) -> DrawMesh { async fn gen_emoji_mesh(&self, emoji: &str) -> DrawMesh {
//d!("rendering emoji: '{emoji}'"); //d!("rendering emoji: '{emoji}'");
let mut txt_ctx = text::TEXT_CTX.get().await;
// The params here don't actually matter since we're talking about BMP fixed sizes // The params here don't actually matter since we're talking about BMP fixed sizes
let layout = txt_ctx.make_layout(emoji, COLOR_WHITE, self.emoji_size, 1., 1., None, &[]); let layout = text::make_layout(emoji, COLOR_WHITE, self.emoji_size, 1., 1., None, &[]);
drop(txt_ctx);
let instrs = text::render_layout(&layout, &self.render_api, gfxtag!("emoji_mesh")); let instrs = text::render_layout(&layout, &self.render_api, gfxtag!("emoji_mesh"));

View File

@@ -30,7 +30,7 @@ use crate::{
PropertyRect, PropertyStr, PropertyUint32, Role, PropertyRect, PropertyStr, PropertyUint32, Role,
}, },
scene::{Pimpl, SceneNodeWeak}, scene::{Pimpl, SceneNodeWeak},
text::{self, TEXT_CTX}, text,
util::i18n::I18nBabelFish, util::i18n::I18nBabelFish,
ExecutorPtr, ExecutorPtr,
}; };
@@ -123,10 +123,7 @@ impl Text {
text text
}; };
let layout = { let layout = text::make_layout(&text, text_color, font_size, lineheight, window_scale, None, &[]);
let mut txt_ctx = TEXT_CTX.get().await;
txt_ctx.make_layout(&text, text_color, font_size, lineheight, window_scale, None, &[])
};
let mut debug_opts = text::DebugRenderOptions::OFF; let mut debug_opts = text::DebugRenderOptions::OFF;
if self.debug.get() { if self.debug.get() {