Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some LVFormatter::measureText() changes #574

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

bbshelper
Copy link
Contributor

@bbshelper bbshelper commented Jun 27, 2024

I'm finalizing a major optimization to LVFormatter::measureText(), so I'd like these smaller ones to be reviewed and merged first.

The first 2 commits have around 0.5% and 0.2% performance gain in my Load and Render test.


This change is Reviewable

Find first tab as needed, and don't check beyond marker width.
Calculate bidi direction on measuring, not on every char.
It doesn't seem to be necessary to set usingHarfbuzz on the first font
met: the variable doesn't change, and when it's checked, there is always
a valid font.
@bbshelper bbshelper changed the title Some minor LVFormatter::measureText() changes Some LVFormatter::measureText() changes Jun 27, 2024
Copy link
Contributor

@poire-z poire-z left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure these microoptimizations are worth your time and the 60 minutes I had to spend on reviewing to get back into the code and made sure you did not change the behaviour :/

I'm finalizing a major optimization to LVFormatter::measureText()

I'm really fearing that :\ (If you PR that and don't hear from me, it's because I'll soon be on vacation, not because of fear :))

Comment on lines -2425 to +2419
if ( tabIndex >= 0 && m_srcs[0]->indent < 0) {
if (m_srcs[0] && m_srcs[0]->indent < 0) {
// Used by obsolete rendering of css_d_list_item_legacy when css_lsp_outside,
// where the marker width is provided as negative/hanging indent.
int tabPosition = -m_srcs[0]->indent; // has been set to marker_width
if ( tabPosition>0 && tabPosition > m_widths[tabIndex] ) {
int dx = tabPosition - m_widths[tabIndex];
for ( i=tabIndex; i<m_length; i++ )
m_widths[i] += dx;
for (int j = 0; j < m_length && m_widths[j] >= tabPosition; ++j) {
if (m_text[j] == '\t') {
int dx = tabPosition - m_widths[j];
for ( int i=j; i<m_length; i++ )
m_widths[i] += dx;
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really familiar with what that does (even now just trying to understand the purpose).
I get that now, we won't do anything as long as m_srcs[0]->indent < 0 is false, and if I trust my comment, this should rarely be true. So ok with avoiding these checks in the main loop.
I also think that you too probably don't get what this does, and that you just tried to ensure the same logic (looks like you did), so trusting you :)

In crengine, there are mostly only i++, --i is rare. As in the inner loop you kept the i++, may be make the outer loop also use the j++ style.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's exactly the case. I still see the condition to be true in some epubs, but as you say it's rare.

Comment on lines +1961 to +1967
#if (USE_FRIBIDI==1)
if (m_has_bidi) {
hints |= LFNT_HINT_DIRECTION_KNOWN;
if (FRIBIDI_LEVEL_IS_RTL(lastBidiLevel))
hints |= LFNT_HINT_DIRECTION_IS_RTL;
}
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like in the existing code, we set hints |= LFNT_HINT_DIRECTION_KNOWN always (when #if (USE_FRIBIDI==1), so: always).
Now, you set it only when if (m_has_bidi). I guess this first hints |= should be moved above.

Ok, I get what you're doing. The commit message should be:
Calculate bidi direction on measuring segment change only, not on every char.

The other (now it is a second call) FRIBIDI_LEVEL_IS_RTL(lastBidiLevel) below would only be done when PAD_CHAR_INDEX (inline margin/border/padding) which should be rare.

(Is that FRIBIDI_LEVEL_IS_RTL() really expensive, and worth the pain for you and me ? :) It's just #define FRIBIDI_LEVEL_IS_RTL(lev) ((lev) & 1)).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention was to remove writes to lastDirection in the main loop. As you've pointed out, this commit is problematic. I'll rework it.

Comment on lines -1854 to +1853
bool checkIfHarfbuzz = true;
bool usingHarfbuzz = false;
bool usingHarfbuzz = m_kerning_mode == KERNING_MODE_HARFBUZZ;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to be necessary to set usingHarfbuzz on the first font
met: the variable doesn't change, and when it's checked, there is always
a valid font.

Ok, right.
These usingHarfbuzz were introcuded by 737f37e in 2019.
m_kerning_mode by a7cea02 only in 2022. I could have removed all that at the time, so better late than never :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

glad to hear that :)

@bbshelper
Copy link
Contributor Author

I'm not sure these microoptimizations are worth your time and the 60 minutes I had to spend on reviewing to get back into the code

I'm not sure either :) I must have spent more time optimizing crengine than what I can ever save in reading on e-ink in my lifetime.

and made sure you did not change the behaviour :/

I try very hard not to. I've setup a regression test, which is quite useful in catching corner cases. That said, I don't really know a thing about bidi and harfbuzz, and they are not covered by my regression test.

I'm really fearing that :\ (If you PR that and don't hear from me, it's because I'll soon be on vacation, not because of fear :))

I promise that the new code is clearer than the current one. It's a major optimization and not a rewrite. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants