Skip to content
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

Make types Send + Sync #544

Closed
Xaeroxe opened this issue Sep 28, 2022 · 9 comments
Closed

Make types Send + Sync #544

Xaeroxe opened this issue Sep 28, 2022 · 9 comments

Comments

@Xaeroxe
Copy link

Xaeroxe commented Sep 28, 2022

I have a couple branches where I did this. Tests still pass and as far as I can tell there's no performance penalty. I'll open a PR if you're interested in reviewing it.

@RazrFalcon
Copy link
Owner

Which types? We have hundreds of them.

Also, why? What exactly are you doing in parallel? resvg/usvg are strictly single-threaded.

@Xaeroxe
Copy link
Author

Xaeroxe commented Sep 28, 2022

Which types? We have hundreds of them.

Well I was specifically targeting Node, and I believe I got many other types in the process.

Also, why? What exactly are you doing in parallel? resvg/usvg are strictly single-threaded.

Even if resvg can't be made parallel it's still useful to make it Send at minimum so that way it can be processed in a tokio::spawn call. In my code I'm using resvg alongside things that need .await, which with tokio requires types held during the .await to be Send. My alternatives are to either recreate the SVG Tree every time I want to render it, which seems bad, or stop using tokio::spawn, which means my code instead has to block on those futures. I chose instead to investigate if the Tree could be made Send and I determined that it can.

@RazrFalcon
Copy link
Owner

Ok, I saw your rctree fork. I'm sorry, but this is not a solution. rctree uses Rc for a reason. It's in the name. This will not change.

Why you cannot simply put the whole usvg::Tree inside Arc<Mutex<T>>?
I can only imagine how serious the Arc overhead would be.

Also, I never used async once in Rust, so I have no idea how it should even be handled. For now, this is a caller problem, not the library one.

@Xaeroxe
Copy link
Author

Xaeroxe commented Sep 28, 2022

Alright. I thought I might get a response like this but I thought you deserved a chance to at least consider it.

@Xaeroxe Xaeroxe closed this as not planned Won't fix, can't repro, duplicate, stale Sep 28, 2022
@dhardy
Copy link
Contributor

dhardy commented Jan 23, 2023

I also ran into this (working branch). If large SVG (or several small SVGs) need(s) resizing it would be nice to do the drawing without blocking the program, but there are only a few options:

  1. Send the tree to the worker thread
  2. Send a reference to the tree (Sync) to the worker thread
  3. Re-construct (parse) the tree in the worker thread each time
  4. Spawn a new thread for each SVG object and keep it alive indefinitely

Why you cannot simply put the whole usvg::Tree inside Arc<Mutex<T>>?

Mutex is only Send and Sync when T: Send. Rc is not Send. Sure, it's possible to work around this unsafe code, but that is unsafe for a reason (what happens if an Rc clone on the main thread is cloned/dropped at the same time as on the worker thread)?


Note: saying no to this request is still a perfectly valid option. Arguably I should be using GPU-accelerated rendering anyway for my use-case (GUI toolkit).

@RazrFalcon
Copy link
Owner

My position on this issue is still the same: resvg isn't designed for a multi-threaded use case. If you want to render many files - parse them in the same thread. Parsing is usually way faster than rendering, so it's not a performance bottleneck.
If you need to re-render SVG multiple times then first - you're doing something wrong, and second - you need a GPU.

SVG, in general, is a horrible format. If you need dynamic content - use something else (probably custom) or use usvg just for parsing, but store the render tree in your own data type.

Basically, there are no benefits in making resvg "thread-safe".

@dhardy
Copy link
Contributor

dhardy commented Jan 23, 2023

My use-case ends up re-rendering any time the (window) size changes, otherwise it simply draws from the texture.

SVG, in general, is a horrible format.

😆
Interesting points, though I have no desire to invent a custom format. Thanks for the thoughts.

@RazrFalcon
Copy link
Owner

I can probably write a book about how bad SVG is. And probably will, someday.

As for rendering, SVG isn't designed for fast rendering by design. On top of that, resvg isn't that fast as well.
I do plan a significant renderer rewrite in the near future, which will significantly improve performance on large images. And re-rendering speed as well. But it will inventibely increase memory usage.

Overall, you cannot render SVG at 60 FPS even on GPU. Not generic vector graphics, SVG specifically. Mainly due to patterns, masking and filters.
But even if you could, there only option you have is Skia, which is a monster in itself.

As for your specific use case, maybe the new render tree in resvg would be sendable. Will see.

@dhardy
Copy link
Contributor

dhardy commented Jan 24, 2023

The sendable crate is dedicated to solving this problem (like Rc, but Send, with minimal overhead). The README also contains a nice write-up of alternatives.

For my purposes an unsafe Send-wrapper works ... with the caveat that the Svg widget no longer supports Clone. (Sync on its own would solve the clone issue, but cause lifetime issues. Arc<..> is thus the most usable solution. But back to the case now, re-parsing the SVG tree may still be the best option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants