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 the ability to update/re-render a Viewport manually #1010

Open
SIsilicon opened this issue Jun 4, 2020 · 19 comments · May be fixed by godotengine/godot#75436
Open

Add the ability to update/re-render a Viewport manually #1010

SIsilicon opened this issue Jun 4, 2020 · 19 comments · May be fixed by godotengine/godot#75436

Comments

@SIsilicon
Copy link

Describe the project you are working on:
Any project concerning viewport wizardry (smoke simulation, multi pass blur, etc..)

Describe the problem or limitation you are having in your project:
Sometimes, you want to update a viewport multiple times per frame, such as during "ping ponging". You might have shaders that require multiple iterations to work properly, but the only work around at the time would be to make lots of viewports and chain then together. Obviously this can become resource intensive real quick.

Describe the feature / enhancement and how it helps to overcome the problem or limitation:
I propose that we give viewports a render function. That way we can update them whenever we want. It should only work when the viewport's update mode is never.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
Currently, all viewports set to update on the current frame are all drawn at once in one function. It shouldn't be too hard to separate some of the code into a function in VisualServer and use it in the Viewport node. Multi threading must be taken in consideration obviously.

If this enhancement will not be used often, can it be worked around with a few lines of script?:
I've tried to do it before, but there seems to be no other way of manually updating a viewport through scripting.

Is there a reason why this should be core and not an add-on in the asset library?:
Same reason as above.

@Zylann
Copy link

Zylann commented Jun 6, 2020

I think issues were open about this problem. I have that use case too in my terrain generator, where erosion has to use multiple passes.
The bottom line is the ability to tell the renderer to do a render or calculation immediately, rather than waiting for the main rendering.
It's possible that this may be solved in a much more lower-level way in 4.0, with RenderingDevice, though I don't know how the API will be. It might be more efficient than setting up a viewport, cuz the latter comes with a lot of things on its own which are often not required.

@Setadokalo
Copy link

Seconded. I could really use this for my current project, which requires ping-ponging a texture sometimes off-beat with when it's rendering (so I can have a shader update the texture and then use the updated version the next frame). Sad that we most likely won't get something like this until 4.0 at the earliest.

@Calinou Calinou changed the title Manual viewport updating ability Add the ability to update/re-render a Viewport manually Aug 11, 2020
@mausedev
Copy link

mausedev commented May 2, 2021

The ability to update a viewport manually would be really useful when using it to process an improvised compute shader, or just to manipulate textures. You wouldn't have to wait for the next frame to get your result, and if you needed to run multiple passes you wouldn't be bound by the framerate of the game.

@hstubbs3
Copy link

hstubbs3 commented May 20, 2021

I'd like to jump on this bandwagon as well - my use case has various 2D particle systems and multimesh2D forming a dynamic heightfield via blend add / blend sub, which I still need to simulate physics against ( and potentially simulate multiple physics frames per render frame depending on the networking code ).

@butkeim
Copy link

butkeim commented May 27, 2021

Don't a call to force_draw() from the VisualServer do the job?

@Zylann
Copy link

Zylann commented May 27, 2021

@butkeim I believe this redraws every viewport, this is not suitable

@butkeim
Copy link

butkeim commented May 27, 2021

@Zylann you're totally right, a bit naive from me to think that updating every viewports wouldn't be an issue.

But it may mean that with a bit of work, it could be possible to add the granularity needed to that function call.

@YellowAfterlife
Copy link

Being able to re-render viewports on demand is all but essential for making portals - without this your only chance to have "nested" portals (be it a loop or just one portal being in visibility of another) is to make additional viewports and "portal planes" to be only visible from each respective portal, which gets weird quickly (as you must update all of them before the viewports render) and uses up culling flags.

@bionick7
Copy link

Also second this. I need to render to a small texture from possible multiple perspecives within a frame. I don't see a lot of alternatives other than having a refreshable viewport.
I have no idea about the rendering backend here, so I might embarrass myself, but a little bit of digging revealed a "RendererViewport::_draw_viewport" function. Wouldn't it just be as easy as exposing that?

@Calinou
Copy link
Member

Calinou commented Oct 15, 2022

Wouldn't it just be as easy as exposing that?

Try it and see 🙂

Exposing methods is done in the _bind_methods() method in each class.

@bionick7
Copy link

Wouldn't it just be as easy as exposing that?

Try it and see slightly_smiling_face

Exposing methods is done in the _bind_methods() method in each class.

I tried and it reaches the code without the desired effect. It seams there's more involved to make the viewport render and it probably needs someone with more knowledge of the rendering backend to know all the things you need to setup

@QbieShay
Copy link

Related to #7379

@zachHixson
Copy link

Just chiming in. I was doing some more advanced viewport experiments today, and would love it if there was an "viewport.update()" method or something similar that would allow me to disable the automatic updates, and just have a script on the scene node update them in the proper order.

@amygobrrr
Copy link

I would also love this functionality, proposal #7379 would not cover my use case. I am trying to render 3d meshes with their full materials to a texture (ie. what a viewport is designed for) so manually drawing on that texture is not what I'm after. Being able to instant-update a viewport would perfectly handle what I need.

@StefanH-AT
Copy link

Also needed here. I'm trying to implement 3d viewport region selection using a subviewport and without the ability to draw viewports whenever I want I need to wait for the next frame which is really unnecessary. This should be a pretty basic feature in any game engine

@Xinart

This comment was marked as off-topic.

@Calinou
Copy link
Member

Calinou commented Mar 14, 2024

@Xinart Please don't bump issues without contributing significant new information. Use the 👍 reaction button on the first post instead.

@lostminds
Copy link

I've encountered this limitation a couple of times as well, and reading godotengine/godot#75436 it seems like there are structural issues with rendering things outside of the RenderServer process. Because of that a different proposal for Drawable Textures has been proposed instead. And while that would be useful for cases like advanced texture effects I think it misses an important use-case I see for drawing a single viewports like in this proposal: Non-realtime 'setup' rendering situations.

Basically where you want to render a dynamic scene into a texture once then save that texture for later use during some setup/loading phase. For example rendering a representation of some 3d level or object into a simple texture you can use in a UI element or shader. In these cases performance would not be critical, so it wouldn't really matter how it fit into the rendering sequence because the output wouldn't just be intended for use in the same frame. However, it would be very much easier if this could be done with just a Viewport.force_draw() line of code so you can for example perform multiple such rendering operations in sequence during loading or when opening a menu, instead of needing to set up a viewport, wait a frame for it to render, grab the texture and then repeat for each such rendered item.

From a technical perspective this would not need break the separate rendering server process structure, since this code could pause the main thread, tell the rendering server to temporarily disable all other viewports, disable any VSync or framerate restrictions and render just this single viewport in a render pass as soon as possible, and then when this is done unpause the main thread again with the rendered viewport texture available on the next line. It doesn't matter that this is not very efficient, since it wouldn't (shouldn't) be used at a performance-critical time. But it does matter that it's easy to use enabling users to get even more use out of Viewports without needing to understand how the internal rendering processes work.

@ydeltastar
Copy link

ydeltastar commented Sep 28, 2024

This is how to implement a manual viewport render with the current API.

func _render_subviewport(subviewport: SubViewport) -> Image:
	# Disable main viewport so it doesn't redrawn
	var scene_tree = Engine.get_main_loop() as SceneTree
	var root_viewport = scene_tree.root.get_viewport_rid()
	RenderingServer.viewport_set_active(root_viewport, false)
	
 	# Render SubViewport once
	RenderingServer.viewport_set_update_mode(subviewport.get_viewport_rid(), RenderingServer.VIEWPORT_UPDATE_ONCE)
	RenderingServer.force_draw()
	await RenderingServer.frame_post_draw
	
	# Enable main viewport again
	RenderingServer.viewport_set_active(root_viewport, true)

	return subviewport.get_texture().get_image()

However, there are issues. I still need to await RenderingServer.frame_post_draw again after enabling the main viewport. Otherwise, the image data will be corrupt when setting it to a texture.

Also, some rendering features are temporal-based. Global illumination (SDFGI), for example, takes a few frames to settle down for good quality but even if you render multiple times in a loop, the result still looks like the first frame.
Left: expected. Right: result of multiple manual renders in a for loop, lighting didn't iterate.

image

A workaround is to keep the main viewport enabled while doing the manual renders. After about 30 iterations, the result now looks right but then I'm wasting rendering time on the main viewport which I don't need.

image

Basically where you want to render a dynamic scene into a texture

I just implemented a texture resource that can do this using the method above. It renders a PackedScene "offscreen" and uses it as image data. It's a custom resource extending Texture2D so it can be used like any regular texture. Very useful to generate icons and thumbnails without bloating source control with image data since it can bake on the fly.

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

Successfully merging a pull request may close this issue.