View Issue Details

IDProjectCategoryView StatusLast Update
0034829LazarusLCLpublic2019-09-19 22:55
ReporterPavol SAssigned To 
Status newResolutionopen 
PlatformCocoa/CarbonOSMacOSOS VersionHigh Sierra
Product Version2.0RC3Product Build 
Target VersionFixed in Version 
Summary0034829: Cocoa: Slow scrolling performance (nearly all controls)
DescriptionScrolling content of many controls is slow on MacOS. Content of controls is painted instead of COPYRECT while scrolling.

In test app there is 3 controls with "random number paint functions". TDrawGrid and TScrollBox with really slow performance and TListBox with great performance. Only TListBox use COPYRECT (or something similar) and all numbers are same during scrolling.

Threads about this issue in Lazarus editor (TSynEdit):,23494.0.html

Under Win32 all controls scrolls smooth (without changing numbers).
TagsNo tags attached.
Fixed in Revision
Attached Files


Pavol S

2019-01-07 15:32


form.png (23,883 bytes)
form.png (23,883 bytes)

Pavol S

2019-01-07 15:33

reporter (3,910 bytes)

Zoë Peterson

2019-09-10 05:58

reporter   ~0118012

We're looking into this area, but there may not be an easy answer for it. NSView has a scrollRect:by: function that can be used with translateRectsNeedingDisplay(in:by:) to get similar behavior to the Windows ScrollWindow API, but it's currently always doing a full invalidate on 10.14 when we call it. It's also deprecated as of 10.14; the recommended approach is to use NSScrollView instead, but it's not a drop-in replacement and can't scroll arbitrary regions of a control.

Zoë Peterson

2019-09-18 18:25

reporter   ~0118107

Just a followup with some more info from our (Scooter Software) research:

If a view is layer backed, [NSView scrollRect:by] invalidates the entire control, and on macOS 10.14 all of the views are layer backed. On 10.13 and earlier it does work if they're not layer backed, but it's easy to trigger a layer and break it. We spent a lot of time trying unsuccessfully to make it work on 10.14. LCLCOCOA's current ScrollWindowEx implementation is just an invalidate right now, but given the above info, there isn't really a more correct approach. We do have an implementation of ScrollWindow that uses scrollRect:by and translateRectsNeedingDisplay(in:by:), but I'd say it's overly complicated to merge since it's obsolete.

If you need fast scrolling you need to use NSView's scrollPoint: and scrollRectToVisible: instead. It might be possible to implement scrollBy in TCocoaWSScrollingWinControl and TCocoaWSCustomControl using those, but I don't know whether the semantics match. Also, some higher level controls would need to change. TCustomGrid calls ScrollWindowEx directly, for example, and it won't be possible to make that fast unless it special cases specific controls or full view scrolls.

For our own controls we're looking at cobbling together a custom view that draws everything to a bitmap and just copies from that to the view when it receives a drawRect request. It's a temporary hack specific to some of our custom controls though, and I don't think it would be appropriate for all TCustomControl/TScrollingWinControl subclasses. Long term, designing the controls to use NSView's non-deprecated functions is a better plan.

Zoë Peterson

2019-09-18 18:31

reporter   ~0118108

See also

Alexey Tor.

2019-09-19 12:12

reporter   ~0118118

Why cannot you set Ctl.DoubleBuffered:=true only inside {$ifdef darwin} ?

Zoë Peterson

2019-09-19 22:55

reporter   ~0118124

The DoubleBuffered property only exists to produce flicker-free drawing by blitting everything to the screen at the end of the Paint routines. It will never be faster than drawing directly to the window surface, because there's an additional image copy at the end. It's also entirely unnecessary on LCLCOCOA because Cocoa already effectively does that automatically. Even if none of that were true, the LCL's DoubleBuffered bitmap only exists for the duration of the Paint routine; in this case it would have to exist the entire time the control exists so that ScrollWindowEx can do the copy manually.

It doesn't appear to matter though. We're seeing weird performance issues with the double buffered approach we tried to implement. I'm going to keep looking into it for a little bit more to see if there's something in the widgetset layer that's introducing an inefficiency, but we're very close to cutting our losses and just living with slow scrolling until we can switch our controls to use NSScrollView and scrollRectToVisible: natively. Maybe Dmitry will have a better solution

Issue History

Date Modified Username Field Change
2019-01-07 15:32 Pavol S New Issue
2019-01-07 15:32 Pavol S File Added: form.png
2019-01-07 15:33 Pavol S File Added:
2019-09-09 14:49 Dmitry Boyarintsev LazTarget => -
2019-09-09 14:49 Dmitry Boyarintsev Widgetset => Cocoa
2019-09-10 05:58 Zoë Peterson Note Added: 0118012
2019-09-18 18:25 Zoë Peterson Note Added: 0118107
2019-09-18 18:31 Zoë Peterson Note Added: 0118108
2019-09-19 12:12 Alexey Tor. Note Added: 0118118
2019-09-19 22:55 Zoë Peterson Note Added: 0118124