Jump to content
domus

Is TBitmap.LoadFromStream blocking when used in a BG thread?

Recommended Posts

Hi,

 

I'm using TBitmap.LoadFromStream in a background thread (TTask.Run), but it appears to be blocking the main thread (and UI), since I see pauses in execution when this function is executing.

 

It's loading a PNG into a TBitmap. There is absolutely no interaction needed with the UI. The bitmap is not linked to any component.

 

If this function needs to synchronize with the main thread for a part of its algorithm, is there an alternative one available that doesn't?

 

Thx!

Edited by domus

Share this post


Link to post
1 hour ago, Olli73 said:

 

Try Bitmap32 from https://github.com/graphics32

 

TBitmap is not Threadsafe in most cases.

Does Graphics32 support FMX?

 

Just stumbled upon Image32. Anyone know how it handles TBitmap streaming...?

Edited by domus

Share this post


Link to post
Just now, DelphiUdIT said:

Of course this doesn't means that it doesnt'use main thread in sync way.

 

Indeed, that's what I'm trying to find out. If it does, it kind of defeats the purpose of having it run in the background.

Share this post


Link to post

@domus It seems to be specific to the D2D implementation (as the TBitmap.LoadFromStream indirectly uses map/unmap):

image.thumb.png.49859575856a4b1d6e4ffffba39b6a8f.png

On this case, you can switch to another render, like Skia. (Just right click on your app > Enable Skia).
 

Although TBitmap.LoadFromStream does not use TCanvas, it is worth remembering that another UI block occurs when using the TCanvas of the bitmap in the background thread (as reported in the docwiki posted earlier). In this case, using Skia, you can add `GlobalSkiaBitmapsInParallel := True;` on your dpr to avoid this.

Share this post


Link to post
Just now, vfbb said:

On this case, you can switch to another render, like Skia. (Just right click on your app > Enable Skia).

After quite a bit of back and forth with the o1 GPT model, it suggested using TBitmapSurface directly, like TBitmapCodecManager.LoadFromStream(strm, Surf) (where strm is the PNG stream), which it claims uses only the CPU, steering clear from any blocking caused by GPU access, but having tried that, I see no difference.

 

Still on Delphi 11, but I could try Skia anyway. It's just that the last time I tried it, a while back, it seemed slower than the native FMX library (using D2D).

 

Thanks!

Share this post


Link to post

My goodness. The thing that was blocking everything was a TThreadedQueue. Days of debugging, just to find out this thing is absolutely unreliable inside a TTask (in the sense that it absolutely blocks the main thread when popping an item).

Share this post


Link to post
1 hour ago, domus said:

My goodness. The thing that was blocking everything was a TThreadedQueue. Days of debugging, just to find out this thing is absolutely unreliable inside a TTask (in the sense that it absolutely blocks the main thread when popping an item).

I very much doubt that. You are probably just using it incorrectly. Can you provide some code? 

  • Like 1

Share this post


Link to post
8 hours ago, Remy Lebeau said:

You are probably just using it incorrectly.

Hello, Remy.

 

You're absolutely right. I was assuming TThreadedQueue had dynamic size, like TQueue, but it's a fixed-capicity ring buffer, apparently.

 

As an aside, do you know why its QueueSize property is returning the amount of items in the queue, contrary to the documentation, stating it returns the fixed size of the queue? (not that it matters, since I can keep the fixed size in a separate parameter, but it's misleading)

 

Also, why are they using PushItem instead of Enqueue and PopItem instead of Dequeue?

 

And, as a final question, why isn't there an option to make it grow dynamically?

 

None of these are questions you're supposed to answer, of course, but just wondering...

 

Thanks for making me dig deeper into this.

 

Cheers,

Dom

 

Share this post


Link to post
8 hours ago, domus said:

I was assuming TThreadedQueue had dynamic size, like TQueue, but it's a fixed-capicity ring buffer, apparently.

TThreadedQueue has a public Grow() method to increase the queue size.  What I find odd, though, is that Grow() is not called automatically by PushItem().  Instead, if the queue is full then PushItem() simply waits (up to the specified PushTimeout at creation) for outside code to call either PopItem() or Grow() to make room for the new item.  So, in that regard, I suppose it acts as a fixed-capacity buffer.  But the capacity can be changed dynamically nonetheless.

8 hours ago, domus said:

As an aside, do you know why its QueueSize property is returning the amount of items in the queue

Because it really does.

8 hours ago, domus said:

contrary to the documentation, stating it returns the fixed size of the queue?

Then the documentation is wrong.  The implementation code tells a different story.

8 hours ago, domus said:

Also, why are they using PushItem instead of Enqueue and PopItem instead of Dequeue?

🤷‍♂️ I don't know.

8 hours ago, domus said:

And, as a final question, why isn't there an option to make it grow dynamically?

There is - the public Grow() method.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×