-
Notifications
You must be signed in to change notification settings - Fork 806
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
Conversation
|
||
[frame release]; | ||
return ret; | ||
return framesetter ? |
There was a problem hiding this comment.
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(), |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup #Closed
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
/** | ||
@Status Interoperable | ||
*/ | ||
- (CGSize)drawAtPoint:(CGPoint)pt withFont:(UIFont*)font { | ||
CGSize fontExtent; | ||
- (CGSize)drawInRect:(CGRect)rect withFont:(UIFont*)font lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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(); | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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) }; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
/** | ||
@Status Interoperable | ||
*/ | ||
- (CGSize)drawAtPoint:(CGPoint)pt withFont:(UIFont*)font { | ||
CGSize fontExtent; | ||
- (CGSize)drawInRect:(CGRect)rect withFont:(UIFont*)font lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)) }; |
There was a problem hiding this comment.
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) }; |
There was a problem hiding this comment.
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) }; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🍰
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: prefer enhanced for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- (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(); |
There was a problem hiding this comment.
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
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) |
Ping! Please update reviews |
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