Should be able to load bitmaps in non-UI threads
Proposal: Should be able to load bitmaps in non-UI threads
Summary
We should be able to load bitmaps in non-UI threads. I'm talking about BitmapImage and WriteableBitmap classes.
Rationale
Forcing loading of bitmaps in the UI thread is a huge bottleneck when you need to deal with hundreds of bitmaps.
I completely understand forcing drawing the bitmaps only in the UI thread, but just loading them should not be bound in the UI thread.
Important Notes
Simply doing var bmp = new BitmapImage(); on a non UI thread throws an exception. This should not be the case. This piece of code should work:
Task.Run(async () => {
var root = ApplicationData.Current.LocalFolder.Path + "\\test.png";
var bmp = new BitmapImage();
var file = await StorageFile.GetFileFromPathAsync(root);
var stream = await file.OpenAsync(FileAccessMode.Read);
bmp.SetSource(stream);
});
Or, give me an equivalent, like, static BitmapImage BitmapImage.LoadFromStream(...).
For WriteableBitmap, again, doing var bmp = new WriteableBitmap(100, 100); on a non-UI thread will throw an exception. Should not be the case.
Executing the following in a non-UI thread should work.
public static BitmapSource soft_bmp_to_bmp(SoftwareBitmap soft_bmp) {
release.assert(soft_bmp != null);
WriteableBitmap bmp = new WriteableBitmap(soft_bmp.PixelWidth, soft_bmp.PixelHeight);
soft_bmp.CopyToBuffer( bmp.PixelBuffer);
return bmp;
}
As mention in the Optimize Image Resources section here https://docs.microsoft.com/en-gb/windows/uwp/debug-test-perf/optimize-animations-and-media#optimize-image-resources , decoding is actually already done off the UI thread even if you create the BitmapImage itself on the UI thread, as long as you use SetSourceAsync(...)
To preserve XAML optimisations you should also make sure the BitmapImage is in the UI tree before calling SetSourceAsync, to make sure the image is decoded at a preferred resolution for it's target display area.
@JohnnyWestlake That is fine. But at this time, you can't even create a BitmapImage on a non-UI thread.
@JohnnyWestlake That is fine. But at this time, you can't even create a BitmapImage on a non-UI thread.
Yes, but it is a UI construct for use on UI only, and by design of how XAML works, UI thread elements need to be manipulated on the UI thread to stop the universe imploding / requiring thousands of locks and waits.
Creating a blank BitmapImage on the UI thread doesn't cost much, seeing as it doesn't do much on creation. It's just a container. Calling SetSourceAsync doesn't cost much either, as the decoding will typically not be handled on the UI thread... creating on the UI and calling SetSource does however, but it's also explicitly advised not to do it.
So as to your original issue of I completely understand forcing drawing the bitmaps only in the UI thread, but just loading them should not be bound in the UI thread., well, bitmap loading already is not forced to the UI thread at the moment, if you follow the advised pattern.
There are options if you want to work entirely off the UI thread (using the composition layer), but you'll lose a ton of automatic optimisation.
@JohnnyWestlake Sorry for the late reply. I've done some tests creating lots of BitmapImages on the UI thread. It is indeed very fast.
In this case, SetSourceAsync should be callable from any thread - since you're doing the processing on another thread, why force calling this from the UI thread? (I did test, and an exception gets thrown if SetSourceAsync is called from a non-UI thread)
Also, my initial proposal for WritableImages still stands - we should be able to create/modify them on a different thread.
We can consider this, but this tends to be a bit challenging. Allowing SetSourceAsync to be called on a background thread limits the complexity of the change, but is only really useful if the UI thread has already created all of the BitmapImages it needs. And if those BitmapImages haven't already been added into the UI on the UI thread, then it is still necessary to hop back to the UI thread to add them, which limits the value to likely just being starting the download slightly sooner (where "slightly" assumes the UI thread isn't blocking for a longer time).
Being able to also create BitmapImages on a background thread would be a little more useful, but is also significantly more complex to support, due to its ties to various UI thread objects. And, of course, it would still be necessary to hop to the UI thread in order to add the BitmapImages to the UI.
Another general concern we've had here is that most of XAML requires the UI thread, with the DependencyObject.Dispatcher property being the big exception, since that helps to be able to hop back to the UI thread. Adding more non-UI-thread APIs means we now need to see how to make it clear to developers what APIs do or don't require the UI thread.
@codenone Sorry for the late reply. It would be really useful to allow SetSourceAsync from a non-UI thread. This way, I don't have to call into the UI thread, while caching bitmaps.
Adding more non-UI-thread APIs means we now need to see how to make it clear to developers what APIs do or don't require the UI thread.
Agreed 100%. Personally, I think loading bitmaps in non-UI thread pretty much means I'll add them to the UI programmatically, not via XAML.
Also , please don't forget about WriteableBitmap :)
Microsoft, you know, you just suck with your own "native" WinUI. It's just a joke! I've spend a half of a day just trying to load a primitive png manually to the ImageSource from file, and even GPT couldn't help me with that. It's just nonsense that you are trying to implement something cross-platform, when can't even manage to create something convenient for your own native devs! In 2024th it's... "System.Runtime.InteropServices.COMException: '' with empty Message! 2024th! Seriously?!! COMException?!!!
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
With .Net 8 WinUI is still full of mess! Such a shame!
Seriously?!!!
Microsoft, you know, you just suck with your own "native" WinUI. It's just a joke! I've spend a half of a day just trying to load a primitive png manually to the ImageSource from file, and even GPT couldn't help me with that. It's just nonsense that you are trying to implement something cross-platform, when can't even manage to create something convenient for your own native devs! In 2024th it's... "System.Runtime.InteropServices.COMException: '' with empty Message! 2024th! Seriously?!! COMException?!!!
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);With .Net 8 WinUI is still full of mess! Such a shame!
Seriously?!!!
Same here.