Dark Mode slows graphics down

My app allows people to drag the mouse about and draw rects of color where it passes.
WIth ‘supports dark mode’ on, the performance is painful, and it ‘skips’ areas when the mouse is moved.
With the setting turned off, it works correctly.

Why should .fillrect and .drawpicture be this much slower under dark Mode?
Its hard to see if anything is drawing or painting when it shouldnt be during this code, but I cant find anyplace where my code is wandering off to for a few milliseconds.

Are you using .Invalidate or .Refresh when you need to redraw?
If .Invalidate… do you notice a difference when using .Refresh(false) instead?

invalidate
I’ve been experimenting using refreshrect ( co-ords) today but that doesn’t actually seem to cause a refresh to occur.

Unchanged code is fine in Xojo2017 r3

reasonably OK in Xojo 2018r3 when supports dark mode is false
Painful in Xojo 2018r3 when dark mode is true

We’ve seen the same thing. Opting into the 10.14 Graphics model means that just about everything is drawn with translucency. You can see this if you put overlap two buttons in the layout editor in dark mode (same if you run).

Honestly : This just about makes me want to give up coding.
I’ve wasted nearly 4 months rewriting reams of code just to be “Dark Mode compatible”, and ‘in the wild’ it’s unusable.
A financial or database app can take this performance hit.
My app cannot… I’ve had to withdraw my 2019 release already and I have a potential few thousand people who got the update ready to start emailing.

Reasonable question: Why didn’t I spot it in testing?

It draws fine non-interactively in Dark Mode
Buttons etc all look fine.
Open documents and they display fine too.
And since I wrote most of the code in Xojo 2017 it was all working fine, thanks.
And not one beta tester said anything until after I had released.

I may have to resort to shipping a ‘lite only’ version despite the manual claiming dark mode compatible.

New info:
On a screen with some canvases and a listbox with owner drawn items, when in DarkMode.enabled = true, the listbox is constantly painting while the mouse is being used.
Disabling it speeds everything up, so it’s not the drawing itself which is an issue, it’s the unwanted paint events on the listbox.
If I disable it , and don’t paint while disabled, obviously it goes black.
Currently considering covering it with a canvas with a picture of ‘how it looked last’ while mouse work occurs.

I noticed the same thing when a textarea has got words colored: if the code is in the paint event of the window, just moving the mouse triggers the code setting the colors appropriate for light and dark mode.
The fix ha been to move the code to app.appearancechanged.

We always switch things in the app event instead of checking in paint event.

See
https://www.mbsplugins.net/archive/2019-01-23/Alternating_row_colors_for_Dar/monkeybreadsoftware_blog_archive

When using invalidate, are you passing in a rect?

Nope.
I know you can use Refreshrect and I do
Invalidaterect?

It seems that when DarkMode is enabled, I am getting constant paint events on controls that shouldn’t need repainting, (where it doesn’t happen when not enabled, and doesnt happen in Xojo 2017)

I tried disabling things, didnt make a difference.

I added if me.enabled = false then exit function to the CellTextPaint in the listbox - when it is disabled, all I get is a black listbox. On one screen I have resorted to throwing a canvas whose backdrop is a picture of the listbox before I disabled it, on top of the listbox until I have finished with the mouse.

Invalidate is better than Refresh usually.
And if you pass a rectangle you limit the area to be redrawn to minimize the work.

Sadly, I know this.
Sadly too, invalidate is now unreliable under dark mode and Mojave.

The problem seems to be controls which I am NOT updating, deciding they need to be repainted.
These controls ( primary culprit is a listbox) are nowhere near the moving mouse, and are not over or under the invalidated canvas,
This does not happen when the ‘Supports DarkMode’ setting is unticked.

To put this into perspective, from profiling - a sequence where I do 200 invalidates or refreshrects on a canvas,
for no apparent reason causes 7000 celltextpaint events in a nearby listbox that doesn’t need refreshing.
That doesn’t happen when the ‘Supports DarkMode’ setting is unticked.

control.invalidate( left, top, width, height, refreshBehind )

first try using it with refreshBehind = false, and pass in the dimensions of only the area that needs to be updated. Not the whole thing.

You may want to consider enabling layer-backed views; this can potential cause other problems, but it might help solve this one.
In the Open event of a window, add this code.

[code]#if targetMacOS then
declare Function NSWindowContentView lib “AppKit” selector “contentView” (NSWindowInstance as integer) as integer
declare Sub NSViewWantsLayer lib “AppKit” selector “setWantsLayer:” (NSViewInstance as integer, assigns value as boolean)

NSViewWantsLayer( NSWindowContentView( me.handle ) ) = true
#endif[/code]

Actually, that does make a difference , Sam.
Its not quite as fast as killing the listbox completely and covering it with a canvas, but its smoother
What does ‘layer backedness’ actually do?

Im still struggling with working out what triggers the repaint.
If I turn on profiling I can see thousands of calls to the celltextpaint, but I cant see the stack that leads there.
In unhandled exception, I can access error.stack
In debug mode, in theory, I can see the stack,
but when something gets called so many times, including a lot of ‘expected’ calls, I cant leave a breakpoint in there.
Is there an equivalent of error.stack() for normal operation?

refresh
invalidate
invalidatecell
change the cells contents
scroll the list
basically anything that might alter the visual appearance of the cell

no
you’d be looking at calls from the OS into the framework listbox C code then to your cellpaint code
the system basically figures out the regions (rectangles) that need to be redrawn and asks the runtime to redraw those
xojo then passes that along to the control to redraw
and in this case the control, a listbox, figures out what cell needs repainting and so calls cell paint etc

Those, I know, but thanks.

Im still trying to trace what it is about a mouse move/drag event on an unrelated canvas , which would make a listbox constantly repaint.

It does seem like it’s a graphics engine issue.

I completely commented out all the code in my canvas’ mousemove and mousedrag events, and from a mousemove event on the listbox (in case it was responding to mouse movements outside of its boundaries)

But the celltextpaint and cellbackground paint events were still called repeatedly.

As I said above, moving the mouse, resizing a window etc. repeatedly triggers paint events not only in a listbox but also in the paint event of a window.
It would be nice if also such events (or just the windows) contained a changedAppearance boolean. In fact I tried adding a global boolean appearanceChanged in app.AppearanceChanged. Then,

app.AppearanceChanged event:
changedAppearance = true
Window1.Refresh//invalidate works only the first time appearance changes.
Window2.Refresh
window3.refresh
changedAppearance = False

and in the paint event of the windows:
if changedAppearance then
//switch colors to light/dark mode or do whatever needed
end if

The other way, for me, is to use app.AppearanceChanged for heavy works like recoloring specific words in a textArea, and leave the rest, like applying different custom colors to labels etc., to the paint event of each window (even knowing that it is fired repeatedly).

[quote=430195:@Jeff Tullin]Actually, that does make a difference , Sam.
Its not quite as fast as killing the listbox completely and covering it with a canvas, but its smoother
What does ‘layer backedness’ actually do?[/quote]
It forces the OS to triple buffer the window, where each control (NSView) has it’s own “layer” or cache if you like.

Do you have a timer on the window?