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

Improve perf for CTFramesetterSuggestFrameSizeWithConstraints #1603

Merged
merged 7 commits into from
Jan 12, 2017

Conversation

aballway
Copy link
Contributor

@aballway aballway commented Dec 21, 2016

CTFramesetterSuggestFrameSizeWithConstraints was going through the entire process of creating a frame to get the size, which was very wasteful. Now only does the minimum amount of layout to determine the suggested size. Cleans up NSString+UIKitAdditions for better perf analysis.


This change is Reviewable


[frame release];
return ret;
return framesetter ?
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

ramesetter ? [](start = 12, length = 12)

LOL #Closed

[frame release];
return ret;
return framesetter ?
_DWriteGetSize((CFAttributedStringRef) static_cast<_CTFramesetter*>(framesetter)->_typesetter->_attributedString.get(),
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

ingRef) static_cast<_CTFra [](start = 46, length = 26)

mix of type casting conventions. #Closed

@@ -522,4 +523,56 @@ HRESULT STDMETHODCALLTYPE GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _
}

return frame;
}

static CGSize _DWriteGetSize(CFAttributedStringRef string, CFRange range, CGSize maxSize, CFRange* fitRange) {
Copy link
Contributor

Choose a reason for hiding this comment

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

CFRange [](start = 59, length = 7)

github id

uint32_t lineCount = 0;

// Ignore return value, we only want the lineCount for now
textLayout->GetLineMetrics(nullptr, 0, &lineCount);
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

textLayout->GetLineMetrics(nullptr, 0, &lineCount); [](start = 7, length = 52)

is this guaranteed to return the correct line count if the function fails? #Closed

Copy link
Contributor Author

@aballway aballway Dec 21, 2016

Choose a reason for hiding this comment

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

yup #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

tried looking at the documentation could not find it :(.
any insights?


In reply to: 93508293 [](ancestors = 93508293)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's in the documentation

see https://msdn.microsoft.com/en-us/library/windows/desktop/dd316763(v=vs.85).aspx

" If maxLineCount is not large enough E_NOT_SUFFICIENT_BUFFER, which is equivalent to HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), is returned and *actualLineCount is set to the number of lines needed. "


In reply to: 93508458 [](ancestors = 93508458,93508293)

Copy link
Contributor

Choose a reason for hiding this comment

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

I saw that, but that is only if E_NOT_SUFFICIENT_BUFFER is returned as the HRESULT, it doesn't specify for other errors.


In reply to: 93509971 [](ancestors = 93509971,93508458,93508293)

Copy link
Contributor

Choose a reason for hiding this comment

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

safer to check for that exact case (also good for unwanted behavior)
Given the implementation and documentation only mention it for the specific error above.
e.g someone decides to change the LINE_METRICS to and invalid state, this would help catch the error faster.


In reply to: 93510430 [](ancestors = 93510430,93509971,93508458,93508293)

// Ignore return value, we only want the lineCount for now
textLayout->GetLineMetrics(nullptr, 0, &lineCount);

std::vector<DWRITE_LINE_METRICS> metrics(lineCount);
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

::vector<DWRITE_LINE_METRICS> metrics(lineCount); [](start = 11, length = 49)

since this is about performance, use array rather than a vector here (you will have a slight gain) #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

hmm I guess you might not know the size? nvm


In reply to: 93508769 [](ancestors = 93508769)

}

/**
@Status Interoperable
*/
- (CGSize)drawAtPoint:(CGPoint)pt withFont:(UIFont*)font {
CGSize fontExtent;
- (CGSize)drawInRect:(CGRect)rect withFont:(UIFont*)font lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment {
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

font [](start = 52, length = 4)

does the validity of the font matter? #Closed

Copy link
Contributor Author

@aballway aballway Dec 21, 2016

Choose a reason for hiding this comment

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

Drawing with nil font just uses default value. Actually didn't mean to get rid of that in this change, but I think it makes sense to do. @ms-jihua any thoughts? #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

uses default value as in, WinObjC currently uses a default value if a nil font is passed to DWrite, or the reference platform uses a default value? if the former, how?

my recommendation is to verify whether the reference platform uses a default font if nil is passed - if so, set font = [UIFont defaultFont] instead of putting nil in a dict


In reply to: 93509601 [](ancestors = 93509601)

CGContextRef context = UIGraphicsGetCurrentContext();
if (rect.size.width == 0) {
rect.size.width = std::numeric_limits<CGFloat>::max();
}
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

is the intended behavior is to extend the region, specifically if one of the constraints are 0? #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

some comments on the extension of area would be good to have.


In reply to: 93510034 [](ancestors = 93510034)


if (!string || range.length <= 0L) {
return ret;
}
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

"If the length portion of the range is set to 0, then the framesetter continues to add lines until it runs out of text or space."
This does not seem to match that behaviour #Closed

Copy link
Contributor Author

@aballway aballway Dec 22, 2016

Choose a reason for hiding this comment

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

wut? #Closed

CFAttributedStringRef string =
static_cast<CFAttributedStringRef>(static_cast<_CTFramesetter*>(framesetter)->_typesetter->_attributedString.get());

_DWriteGetSize(string, stringRange, constraints, fitRange);
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

should be returning this value :) #Closed


CGRect rct;
CTParagraphStyleSetting styles[2];
CTTextAlignment align = _NSTextAlignmentToCTTextAlignment(alignment);
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

_NSTextAlignmentToCTTextAlignment [](start = 28, length = 33)

this seem to be a a public function, why is it private?
move the implementation to the actual function #Closed

rct.origin.y = pt.y;
rct.size.width = 0;
rct.size.height = 0;
CTLineBreakMode breakMode = static_cast<CTLineBreakMode>(lineBreakMode);
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

lineBreakMode [](start = 61, length = 13)

some of the below functions indicate this is not fully supported, update? #Closed

NSAttributedString* attr =
[[[NSAttributedString alloc] initWithString:self
attributes:@{
(NSString*)kCTForegroundColorFromContextAttributeName : (id)kCFBooleanTrue,
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

(NSString*) [](start = 45, length = 11)

switching to static cast is going to really obscure readability.
I guess we can leave it as be #Closed

[[[NSAttributedString alloc] initWithString:self
attributes:@{
(NSString*)kCTForegroundColorFromContextAttributeName : (id)kCFBooleanTrue,
(NSString*)kCTParagraphStyleAttributeName : (id)CTParagraphStyleCreate(styles, 2),
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

hmmm given it's a compile time allocated array, can deduce the count from the array? #Closed


drawString(font, UIGraphicsGetCurrentContext(), self, rct, UILineBreakModeClip, UITextAlignmentLeft, &fontExtent);
return _CTFrameGetSize(frame.get());
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

returning a zero size here if something fails above.
Does that match the intended behavior? #Closed

woc::unique_cf<CTFramesetterRef> framesetter{ CTFramesetterCreateWithAttributedString(static_cast<CFAttributedStringRef>(attr)) };

woc::unique_cf<CGPathRef> path{ CGPathCreateWithRect(rect, nullptr) };
woc::unique_cf<CTFrameRef> frame{ CTFramesetterCreateFrame(framesetter.get(), {}, path.get(), nullptr) };
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

I would perfer you catch the failures of creation here rather than adding more functions calls, attempt to allocate a vector of size 0 and then returning the size. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

also this ends up propagating the errors and makes it harder to catch later on.


In reply to: 93514860 [](ancestors = 93514860)

Copy link
Contributor

Choose a reason for hiding this comment

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

+1


In reply to: 93514919 [](ancestors = 93514919,93514860)

}

/**
@Status Interoperable
*/
- (CGSize)drawAtPoint:(CGPoint)pt withFont:(UIFont*)font {
CGSize fontExtent;
- (CGSize)drawInRect:(CGRect)rect withFont:(UIFont*)font lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment {
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

how are these being verified? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just visually using test apps, we don't have any unit/drawing tests for UI kit

(NSString*)kCTFontAttributeName : font
}] autorelease];

woc::unique_cf<CTFramesetterRef> framesetter{ CTFramesetterCreateWithAttributedString(static_cast<CFAttributedStringRef>(attr)) };
Copy link
Contributor

Choose a reason for hiding this comment

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

framesetter [](start = 37, length = 11)

RETURN_RESULT_IF_NULL(framesetter,CGSizeZero);


woc::unique_cf<CTFramesetterRef> framesetter{ CTFramesetterCreateWithAttributedString(static_cast<CFAttributedStringRef>(attr)) };

woc::unique_cf<CGPathRef> path{ CGPathCreateWithRect(rect, nullptr) };
Copy link
Contributor

Choose a reason for hiding this comment

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

{ [](start = 34, length = 1)

RETURN_RESULT_IF_NULL(path,CGSizeZero);

woc::unique_cf<CTFramesetterRef> framesetter{ CTFramesetterCreateWithAttributedString(static_cast<CFAttributedStringRef>(attr)) };

woc::unique_cf<CGPathRef> path{ CGPathCreateWithRect(rect, nullptr) };
woc::unique_cf<CTFrameRef> frame{ CTFramesetterCreateFrame(framesetter.get(), {}, path.get(), nullptr) };
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

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

frame [](start = 31, length = 5)

RETURN_RESULT_IF_NULL(frame,CGSizeZero);
:) #Closed

Copy link
Contributor

@msft-Jeyaram msft-Jeyaram left a comment

Choose a reason for hiding this comment

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

🍰

@ms-jihua
Copy link
Contributor

ms-jihua commented Dec 27, 2016

#endif

unrelated, but can you see if we can ditch these? UIKIT_EXPORT already has 'extern "C"' when __cplusplus is defined #Closed


Refers to: include/UIKit/UIGraphics.h:40 in c3bbc8f. [](commit_id = c3bbc8f, deletion_comment = False)

@ms-jihua
Copy link
Contributor

any idea of the savings? rough measurements are fine

static CGSize _DWriteGetSize(CFAttributedStringRef string, CFRange range, CGSize maxSize, CFRange* fitRange) {
CGSize ret = CGSizeZero;
if (range.length == 0L) {
range.length = CFAttributedStringGetLength(string);
Copy link
Contributor

@ms-jihua ms-jihua Dec 27, 2016

Choose a reason for hiding this comment

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

I'm guessing @msft-Jeyaram missed this part up here wrt the comment below? (thinking that probably at least one of these two deserves a callout in a comment) #Closed

@@ -51,6 +51,7 @@ struct _DWriteGlyphRunDetails {

bool _CloneDWriteGlyphRun(_In_ DWRITE_GLYPH_RUN const* src, _Outptr_ DWRITE_GLYPH_RUN* dest);

CGSize _DWriteGetSize(CFAttributedStringRef string, CFRange range, CGSize maxSize, CFRange* fitRange);
Copy link
Contributor

@ms-jihua ms-jihua Dec 27, 2016

Choose a reason for hiding this comment

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

_DWriteGetSize [](start = 7, length = 14)

nit: would prefer more descriptive name #Closed


float totalHeight = 0;
CFIndex endPos = range.location;
for (size_t i = 0; i < lineCount; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: prefer enhanced for

Copy link
Contributor

Choose a reason for hiding this comment

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

extra nit: const auto&


In reply to: 93963412 [](ancestors = 93963412)

- (CGSize)drawAtPoint:(CGPoint)pt withFont:(UIFont*)font {
CGSize fontExtent;
- (CGSize)drawInRect:(CGRect)rect withFont:(UIFont*)font lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment {
CGContextRef context = UIGraphicsGetCurrentContext();
Copy link
Contributor

@ms-jihua ms-jihua Dec 27, 2016

Choose a reason for hiding this comment

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

create this lower, this isn't needed for a while #Closed

@ms-jihua
Copy link
Contributor

ms-jihua commented Dec 27, 2016

:shipit:

@rajsesh
Copy link
Contributor

rajsesh commented Jan 4, 2017

#endif

Or include the new definition before the ending #ifdef to be consistent.


In reply to: 269369006 [](ancestors = 269369006)


Refers to: include/UIKit/UIGraphics.h:40 in c3bbc8f. [](commit_id = c3bbc8f, deletion_comment = False)

@aballway
Copy link
Contributor Author

aballway commented Jan 9, 2017

It cuts down frame measurement time a little more than half, saving about 25% of CPU time drawing in the XAML custom text editor.


In reply to: 269371016 [](ancestors = 269371016)

@aballway
Copy link
Contributor Author

Ping! Please update reviews

@aballway aballway merged commit caf13e1 into microsoft:develop Jan 12, 2017
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.

5 participants