Skip to content

Commit 297c3aa

Browse files
committed
Reduce number of calls to nvgText[Box]Bounds()
Rendering and layout computation of NanoGUI widgets can sometimes spend a significant amount of time in the functions ``nvgText[Box]Bounds()``. This commit introduces a simple local caching scheme that stores the bounds of relevant strings in each widget.
1 parent d5ce44a commit 297c3aa

File tree

18 files changed

+129
-26
lines changed

18 files changed

+129
-26
lines changed

include/nanogui/button.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ class NANOGUI_EXPORT Button : public Widget {
161161

162162
/// The button group for radio buttons.
163163
std::vector<Button *> m_button_group;
164+
165+
/// Cache for text bounds calculations
166+
mutable NVGTextBoundsCache m_caption_cache;
167+
mutable NVGTextBoundsCache m_icon_cache;
164168
};
165169

166170
NAMESPACE_END(nanogui)

include/nanogui/checkbox.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ class NANOGUI_EXPORT CheckBox : public Widget {
9494

9595
/// The function to execute when \ref nanogui::CheckBox::m_checked is changed.
9696
std::function<void(bool)> m_callback;
97+
98+
/// Cache for text bounds calculations
99+
mutable NVGTextBoundsCache m_caption_cache;
97100
};
98101

99102
NAMESPACE_END(nanogui)

include/nanogui/common.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,45 @@ extern NANOGUI_EXPORT std::vector<std::pair<int, std::string>>
354354
extern NANOGUI_EXPORT int __nanogui_get_image(NVGcontext *ctx, std::string_view name,
355355
uint8_t *data, uint32_t size);
356356

357+
/**
358+
* \brief Cache for text bounds calculations
359+
*
360+
* Stores the result of nvgTextBounds or nvgTextBoxBounds to avoid redundant
361+
* calculations. The cache must be invalidated when font properties change.
362+
*/
363+
struct NANOGUI_EXPORT NVGTextBoundsCache {
364+
bool valid = false;
365+
std::string text;
366+
float bounds[4] = {0, 0, 0, 0}; // Bounds at origin (0,0)
367+
float width = 0; // Return value from nvgTextBounds
368+
float break_width = 0; // For nvgTextBoxBounds only
369+
};
370+
371+
/**
372+
* \brief Cached version of nvgTextBounds
373+
*
374+
* This function is equivalent to \ref nvgTextBounds(), except that it tries to
375+
* avoid the actual (costly) computation by consulting a provided cache. The
376+
* caching scheme assumes that the other font parameters (font type, size, blur
377+
* radius, etc.), are fixed. Otherwise, the cache should be manually reset.
378+
*/
379+
extern NANOGUI_EXPORT float nvgTextBoundsCached(NVGTextBoundsCache& cache, NVGcontext* ctx,
380+
float x, float y, const char* string,
381+
const char* end, float* bounds);
382+
383+
/**
384+
* \brief Caching version of nvgTextBoxBounds
385+
*
386+
* This function is equivalent to \ref nvgTextBoxBounds(), except that it tries
387+
* to avoid the actual (costly) computation by consulting a provided cache. The
388+
* caching scheme assumes that the other font parameters (font type, size, blur
389+
* radius, etc.), are fixed. Otherwise, the cache should be manually reset.
390+
*/
391+
extern NANOGUI_EXPORT void nvgTextBoxBoundsCached(NVGTextBoundsCache& cache, NVGcontext* ctx,
392+
float x, float y, float breakRowWidth,
393+
const char* string, const char* end,
394+
float* bounds);
395+
357396
NAMESPACE_END(nanogui)
358397

359398
NAMESPACE_BEGIN(drjit)

include/nanogui/label.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class NANOGUI_EXPORT Label : public Widget {
5656
std::string m_caption;
5757
std::string m_font;
5858
Color m_color;
59+
mutable NVGTextBoundsCache m_caption_cache;
5960
};
6061

6162
NAMESPACE_END(nanogui)

include/nanogui/popupbutton.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class NANOGUI_EXPORT PopupButton : public Button {
4646
protected:
4747
Popup *m_popup;
4848
int m_chevron_icon;
49+
mutable NVGTextBoundsCache m_chevron_cache;
4950
};
5051

5152
NAMESPACE_END(nanogui)

include/nanogui/screen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ class NANOGUI_EXPORT Screen : public Widget {
331331
std::function<void(Vector2i)> m_resize_callback;
332332
RunMode m_last_run_mode;
333333
ref<Texture> m_depth_stencil_texture;
334+
mutable NVGTextBoundsCache m_tooltip_cache, m_tooltip_cache_box;
334335
#if defined(NANOGUI_USE_METAL)
335336
void *m_metal_texture = nullptr;
336337
void *m_metal_drawable = nullptr;

include/nanogui/tabwidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ class NANOGUI_EXPORT TabWidgetBase : public Widget {
134134
std::function<void(int)> m_close_callback;
135135
std::function<Popup*(int, Screen*)> m_popup_callback;
136136
Color m_background_color;
137+
mutable std::vector<NVGTextBoundsCache> m_tab_caption_cache;
138+
mutable NVGTextBoundsCache m_close_icon_cache;
137139
};
138140

139141
/**

include/nanogui/textbox.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ class NANOGUI_EXPORT TextBox : public Widget {
154154
float m_text_offset;
155155
double m_last_click;
156156
Color m_solid_color;
157+
mutable NVGTextBoundsCache m_value_cache;
158+
mutable NVGTextBoundsCache m_units_cache;
159+
mutable NVGTextBoundsCache m_value_temp_cache;
157160
};
158161

159162
/**

include/nanogui/window.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class NANOGUI_EXPORT Window : public Widget {
6767
Widget *m_button_panel;
6868
bool m_modal;
6969
bool m_drag;
70+
mutable NVGTextBoundsCache m_title_cache;
7071
};
7172

7273
NAMESPACE_END(nanogui)

src/button.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ Vector2i Button::preferred_size(NVGcontext *ctx) const {
2626
int font_size = m_font_size == -1 ? m_theme->m_button_font_size : m_font_size;
2727
nvgFontSize(ctx, font_size);
2828
nvgFontFace(ctx, "sans-bold");
29-
float tw = nvgTextBounds(ctx, 0,0, m_caption.c_str(), nullptr, nullptr);
29+
float tw = nvgTextBoundsCached(m_caption_cache, ctx, 0,0, m_caption.c_str(), nullptr, nullptr);
3030
float iw = 0.0f, ih = font_size;
3131

3232
if (m_icon) {
3333
if (nvg_is_font_icon(m_icon)) {
3434
ih *= icon_scale();
3535
nvgFontFace(ctx, "icons");
3636
nvgFontSize(ctx, ih);
37-
iw = nvgTextBounds(ctx, 0, 0, utf8(m_icon).data(), nullptr, nullptr)
37+
iw = nvgTextBoundsCached(m_icon_cache, ctx, 0, 0, utf8(m_icon).data(), nullptr, nullptr)
3838
+ m_size.y() * 0.15f;
3939
} else {
4040
int w, h;
@@ -164,7 +164,7 @@ void Button::draw(NVGcontext *ctx) {
164164
int font_size = m_font_size == -1 ? m_theme->m_button_font_size : m_font_size;
165165
nvgFontSize(ctx, font_size);
166166
nvgFontFace(ctx, "sans-bold");
167-
float tw = nvgTextBounds(ctx, 0,0, m_caption.c_str(), nullptr, nullptr);
167+
float tw = nvgTextBoundsCached(m_caption_cache, ctx, 0,0, m_caption.c_str(), nullptr, nullptr);
168168

169169
Vector2f center = Vector2f(m_pos) + Vector2f(m_size) * 0.5f;
170170
Vector2f text_pos(center.x() - tw * 0.5f, center.y() - 1);
@@ -181,7 +181,7 @@ void Button::draw(NVGcontext *ctx) {
181181
ih *= icon_scale();
182182
nvgFontSize(ctx, ih);
183183
nvgFontFace(ctx, "icons");
184-
iw = nvgTextBounds(ctx, 0, 0, icon.data(), nullptr, nullptr);
184+
iw = nvgTextBoundsCached(m_icon_cache, ctx, 0, 0, icon.data(), nullptr, nullptr);
185185
} else {
186186
int w, h;
187187
ih *= 0.9f;

0 commit comments

Comments
 (0)