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

Add a way to store rendered "frames" #687

Closed
iddm opened this issue Dec 14, 2022 · 7 comments
Closed

Add a way to store rendered "frames" #687

iddm opened this issue Dec 14, 2022 · 7 comments

Comments

@iddm
Copy link

iddm commented Dec 14, 2022

Is your feature request related to a problem? Please describe.
I want to support both, opengl and vulkan renderers of imgui. However, the opengl renderer is relying on having an object with a limited lifetime passed to the "render" function. At the same time, for Vulkan it is better to separate the "drawing" and "presentation" stages, due to the nature of vulkan. So I was sort of trying to support both APIs by separating the stages for opengl renderer too. However, this would require storing the imgui::Ui object with a limited lifetime which I can't guarantee. In other words, I'd have to own the data in my structure. With current imgui-rs API it is impossible to obtain the imgui draw state (vertex buffers and others) and own them. We only can get a reference to the current frame's data and guarantee its lifetime.

Another problem is that now it is impossible to, let's say, "draw" a bunch of frames of the imgui, then store these states, and later render those either to a frame buffer of an API of choice or dump to a file, or render without any API to a file, you would be able to do anything with it. The only option now is to render immediately at the same place where the imgui::Ui object is obtained.

Describe the solution you'd like
I'd like to have a way to obtain an owned copy of imgui::DrawData, namely vertex and index buffers, so that I am not limited by the lifetime of a single frame but can postpone the rendering and split it into stages and actually can do whatever I want with this data at any time later.
Currently, the imgui::Context::render returns only a reference to imgui::DrawData that can't be owned. Due to the limitations of imgui::Context, it is also not possible to store it directly as it is neither copyable nor clonable. The only solution now, which is dirty here, is to store an Rc<RefCell<imgui::Context>> that is ugly and not really a solution.

Describe alternatives you've considered
Can't think of any alternatives.

Additional context
imgui::DrawData implements neither Copy nor Clone.

@dbr
Copy link
Contributor

dbr commented Jan 4, 2023

Sorry for the slow reply!

This would be a good thing to support. Main issue is we can't trivially add #[derive(Clone)] to the DrawData as its private members contain pointers to imgui::sys::ImDrawList (and to imgui::sys::ImGuiViewport in the docking branch)

There was a similar request to (de)serialize the draw data in #606 which would work in this case - the solution there was to basically have a duplicate set of structs:
https://github.com/rmccrystal/imgui-rs-serialize/blob/a3e2288ee48a718153fd327041b7780fa5a32dae/src/draw_data.rs

We could provide these in imgui-rs itself (maybe as DrawDataOwned), but that has some downsides (like do renderers need to take both imgui::DrawData and imgui::DrawDataOwned?)

I suspect possibly having something like imgui-rs-serialize as a separate crate might be the best solution, given this is a somewhat uncommon use case - any thoughts?

@dbr dbr added the enhancement label Jan 6, 2023
@jbrd
Copy link
Contributor

jbrd commented Mar 22, 2023

Hello, and first of all, thank you for writing this library!

I stumbled across this issue as I am also working on integrating imgui-rs into a multithreaded renderer (Bevy), and also need a means of cloning and temporarily storing an ImDrawData object so that it can be passed to the render thread. In Bevy, the only time in which the CPU and the renderer are synchronising is during the 'Extract' phase, which is supposed to be as cheap as possible. So, it would be advantageous if the clone didn't depend on third party serialisation libraries here. The solution would also need to be able to expose a DrawData object, since, as you've pointed out in your reply above, many ImGui renderers already operate on this object.

When looking into potential avenues here, I came across this issue in the ImGui repo and the use case feels very similar to ocornut/imgui#2646. So I thought I'd try prototyping a similar solution, which you can find here:

https://github.com/jbrd/imgui-rs/commit/a6454917989daba33f6bb7432fba64ac46df758a

It'd be great to get your thoughts on this. Essentially, the idea is to introduce an OwnedDrawData struct that holds a cheaply-cloned ImDrawData structure that has been allocated and cloned on the heap. It leverages ImDrawList::CloneOutput for cloning the underlying draw lists, and all allocations are made via ImGui, which has a nice consequence of them being tracked in the IO object's allocation metrics. The OwnedDrawData struct provides an accessor to the underlying DrawData object, so that it can still be passed to renderers without modifying them.

Would something like this be possible to provide?

@dbr
Copy link
Contributor

dbr commented Mar 22, 2023

That sounds pretty much perfect - it's consistent with the upstream library, plus it solves the concern of having two objects for renderers to deal with, and avoids having to keep the duplicated structs up to date 🥳

@iddm
Copy link
Author

iddm commented Mar 22, 2023

Hello, and first of all, thank you for writing this library!

I stumbled across this issue as I am also working on integrating imgui-rs into a multithreaded renderer (Bevy), and also need a means of cloning and temporarily storing an ImDrawData object so that it can be passed to the render thread. In Bevy, the only time in which the CPU and the renderer are synchronising is during the 'Extract' phase, which is supposed to be as cheap as possible. So, it would be advantageous if the clone didn't depend on third party serialisation libraries here. The solution would also need to be able to expose a DrawData object, since, as you've pointed out in your reply above, many ImGui renderers already operate on this object.

When looking into potential avenues here, I came across this issue in the ImGui repo and the use case feels very similar to ocornut/imgui#2646. So I thought I'd try prototyping a similar solution, which you can find here:

https://github.com/jbrd/imgui-rs/commit/a6454917989daba33f6bb7432fba64ac46df758a

It'd be great to get your thoughts on this. Essentially, the idea is to introduce an OwnedDrawData struct that holds a cheaply-cloned ImDrawData structure that has been allocated and cloned on the heap. It leverages ImDrawList::CloneOutput for cloning the underlying draw lists, and all allocations are made via ImGui, which has a nice consequence of them being tracked in the IO object's allocation metrics. The OwnedDrawData struct provides an accessor to the underlying DrawData object, so that it can still be passed to renderers without modifying them.

Would something like this be possible to provide?

My two cents: this is exactly what I expected.

@jbrd
Copy link
Contributor

jbrd commented Mar 22, 2023

Excellent, thanks both! I'll try to get this tidied up and into a PR over the next few days...

dbr added a commit that referenced this issue Apr 3, 2023
Issue #687: Add OwnedDrawData for deep cloning DrawData
@dbr
Copy link
Contributor

dbr commented Apr 5, 2023

Done in v0.11 🥳

@dbr dbr closed this as completed Apr 5, 2023
@iddm
Copy link
Author

iddm commented Apr 5, 2023

Nice, thanks!

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

No branches or pull requests

3 participants