godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.1k stars 69 forks source link

Add support for Customizable/Scriptable Render Pipelines #644

Open samdze opened 4 years ago

samdze commented 4 years ago

Describe the project you are working on: Top-down 2D game that needs a custom rendering pipeline to permit the wanted visual effects.

Describe the problem or limitation you are having in your project: I'm working on a 2D project that needs a pretty custom way of rendering things. It needs to create multiple render buffers in the process, render objects to them using particular shader passes (custom shadow mapping, lighting, etc.), then feed them later to objects rendering themselves on screen (using another shader pass).

Right now I have to implement all of this in Unity thanks to the Scriptable Render Pipeline, as there's no way of doing it with Godot. So, my proposal is focused on general purpose things but also on things I need that I already do successfully in Unity.

Describe the feature / enhancement and how it helps to overcome the problem or limitation: I think this feature is already planned for 4.0, this issue can eventually give some advice.

Customizable/Scriptable render pipelines should be able to:

  1. Completely or partially override the rendering pipeline of any camera/viewport, even the editor one.
  2. Provide the user shortcuts and modular utility methods to quickly replicate key parts of the Godot render pipeline (and/or a generic standard pipeline)
  3. Let the user create custom render targets for any use: to render to, to set as a global property in shaders/materials, etc.
  4. Let the user decide when, how and what objects to render, filtering them by shader "tags", shader passes, culling etc.
  5. Let the user decide the rules of each "batch" of object rendering, like rendering order, render targets, global shader properties, filtering, etc.
  6. Freely draw gizmos, the skybox and other extras.
  7. Optionally include a depth buffer to render textures, available and usable in 2D rendering too with depth writing and checking in fragment shaders.
  8. Profile and debug each rendering step thanks to (other feature proposal?) a frame debugger.

This proposal goes hand in hand with #496, as it would allow several of the points in the list, especially thanks to multiple shader passes in a single shader definition and to shader tags.

Other suggestions are welcome!

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:

Unity implementation example Unity achieves the feature allowing the user to create a custom ScriptableObject asset (a Resource in Godot) extending RenderPipelineAsset and letting the user define serializable properties the pipeline can then use to change the rendering process. Then this new ScriptableObject has to implement a method that must return the real RenderPipeline object that does the actual rendering. The RenderPipeline object implements a method that does the rendering, receiving all the available cameras (game and editor ones) and the rendering context. The engine calls the ScriptableObject's RenderPipeline creation method every time its properties change and they can be fed to the RenderPipeline constructor. Unity allows the assignation of one of this assets for each rendering "profile", targeting different platforms or different performance targets. Then Unity pushes all of this a step further providing its standard pipelines as implementations of this Scriptable Render Pipelines system. I thinks this approach is good enough, but a more Godot way of doing the same thing would be welcome too.
Godot implementation proposal I'd suggest an approach that is similar to the Unity one: 1. The user begins creating a custom Resource RPS extending `RenderPipelineSettings `(new Resource type). 2. RPS then can be filled with exported variables to define quality settings and other values the user might want to tweak in their rendering pipeline (e.g. directional shadow size, shadow atlas size and many more in the default render pipeline). 3. RPS has to implement a function, let's say `_create_pipeline` that must return an object of type `RenderPipeline` (a new Reference type?) In this function the user can initialize a `RenderPipeline` as he wishes following the settings that have been defined in the exported properties. Ideally, **even the default Godot render pipelines should be implementations of `RenderPipeline`** and the user should be able to return a properly tweaked version (within the limits of its quality settings) of one of them if he wants to. 4. Then if the user wants to use a custom pipeline, he has to create a new implementation of `RenderPipeline` RP and return an instance of it in RPS's `_create_pipeline` function. 5. RP in turn has to implement a function, let's call it `_render`. `_render` receives as arguments all it needs to properly create command queues, rendering rules, buffers, as well as providing the cameras in the scene, the renderable obejcts, etc. This should not be very difficult thanks to the RenderingDevice abstraction. 6. Finally the user can instantiate a RPS, change its settings, assign the newly created Resource to a new property in the Project Settings (Quality tab?), and see the RPS exported properties there too, like the standard render pipeline ones. (current quality settings window) ![immagine](https://user-images.githubusercontent.com/19392104/78138969-0931e480-7428-11ea-9bac-92e6273776d6.png) 7. The user can create a few instances of RPS in the project, tweak a bit their settings and assign them to different performance targets/profiles in the future, if it will ever be supported. If we want to directly support partial customization of the default render pipeline too, the default `RenderPipeline` implementation could be extended by the user or it could provide several hooks.

If this enhancement will not be used often, can it be worked around with a few lines of script?: No, something like that could be achieved only modifying the engine source code.

Is there a reason why this should be core and not an add-on in the asset library?: This can't be implemented with an add-on.

clayjohn commented 4 years ago

I think it would be worthwhile to revise this Proposal thinking about your particular challenges and how to solve them inside Godot. Right now your proposal is specifically asking for Godot to adopt a feature from Unity exactly as it is implemented in Unity. That is not going to happen as Godot is very different from Unity internally.

Second, Godot uses a problem-centered approach to decide when to add new features. Specific problems reqiure specific solutions. Here you are providing a very general problem with no specificity and then proposing that the only solution is to copy a (huge) core feature from Unity. Try to be more specific about exactly what problem you are facing, and then try to target your proposal to solve that problem.

Your current proposal is great! But as a result of the above issues you have missed the point. For example, many of the things you suggest are already possible, but since you are stuck thinking in the Unity way of doing things you have missed that Godot already provides them.

  1. Let the user create custom render targets for any use: to render to, to set as a global property in shaders/materials, etc.

Already implemented using Viewports, which can be accessed using the low-level VisualServer API to make them even more customizable.

  1. Let the user decide when, how and what objects to render, filtering them by shader "tags", shader passes, culling etc.

Already implemented using cull masks, material overrides, geometry properties.

  1. Freely draw gizmos, the skybox and other extras.

No limitation on drawing any of these, skybox is accessed in the Environment

  1. each rendering step thanks to (other feature proposal?) a frame debugger.

Already done in 4.0 :)

As you pointed out already, there are plans to allow users to customize the render pipeline more in 4.0. So what needs to be discussed (and I think what this proposal should propose) is what that access will look like and how it will blend with other Godot features. IMO saying "do it exactly like Unity" doesn't really get us anywhere.

I can see you have put a lot of thought into what you need for your project. I think just moving away from the Unity mindset and getting more specific about your problems and your needs would really help this proposal.

samdze commented 4 years ago

I see, my proposal may seem very much like a "I want this Unity feature in Godot" but in reality I have only outlined some characteristics that a completely customizable render pipeline should have. This is quite engine agnostic as a concept. Then I only made a broad example of how Unity is doing it, for context.

My list of features are not what the engine should provide in general, but are the features the custom render pipelines defined by users should be able to do, this is because I could for example override the entire Godot default pipeline and replicate it with a few key changes.

I already tried to implement the game I'm thinking of in Godot, and had to switch to Unity due to the impossibility to define a very customized way of rendering things in Godot. (even if I don't like Unity all that much)

The features you mentioned that are already available in the engine are meant to be used in line with the standard pipeline and are absolutely not feasible in practice (due to usability, memory or performance reasons) if you want to bend them in an extreme way to emulate how you would want the render pipeline to render in the first place. E.g. I'm talking about creating custom depth mapping, custom shadow mapping, very custom lighting and in general any other type of buffer and data you would want to use during rendering.

Hope this helps, I thought I'd write this issue to track the realization of this feature, to provide a few examples and to give a prospective on the needs concerning this feature.

samdze commented 4 years ago

Updated the issue with a Godot implementation proposal.

reduz commented 4 years ago

I am now granting a huge amount of low level rendering access to the scripting API, but most of the stuff in the proposal above are IMO too vague. While technically you should be able to override the entire 3D or 2D rendering code, I highly doubt this is really what you need.

It would be more interesting to discuss the proposals in a real life scenario of what you actually want to achieve, so we can see how to properly expose what needs to be exposed.

reduz commented 4 years ago

The only thing missing right now is basically a singleton class that sits between RenderingServer and RenderingDevice, that lets you interact with the higher level aspect of the data in RenderingServer (basically what scene nodes put into it) and the low level data in RenderingDevice.

I can think of many things to put in there, as an example:

But then again, this needs practical examples of how it needs to be used, otherwise I find it difficult to decide what to do.

samdze commented 4 years ago

Ok I'll try to dive a little deeper into the proposal. It is not so easy because the main concept of it is just "override the default render pipeline and being able to do whatever it was able to do, and anything else you want" and my use case would be limited compared to the possibilities this features would provide.

As said before, a very good implementation of this features is Unity's Scriptable Render Pipeline: https://docs.unity3d.com/Packages/com.unity.render-pipelines.core@8.0/manual/index.html (see This is a tutorial that better showcases a more or less full-fledged custom pipeline: https://catlikecoding.com/unity/tutorials/custom-srp/custom-render-pipeline/

Unity offers its default render pipelines as implementations of the RenderPipeline class and lets you do virtually anything with the pipelines you define. This is also because their shading language is quite feature-packed and allows for any type of advanced handling and drawing (so, no native GLSL or the like is needed). Godot is still a little bit behind in this regard.

This is a narrowed down overview of what I would like to achieve in Godot (there is more, but I think this covers the key aspects) and successfully doing in Unity right now. For this reason it may seem like a too much Unity-focused approach, but I think it is very good, and for the flexibility it provides I don't know if there are better alternatives. Again, this is just a quite limited use case but offers a perspective on the feature, that could do a lot more:

  1. Create a new multi-pass material M, each pass does a specific thing: Pass 1: renders the height map of the sprite on the render target, checking the depth buffer to ensure no pixels are rendered over previously drawn higher ones. Pass 2: renders the "shadow map" of the sprite on the render target also taking in input the previously generated global height map. Pass 3: renders the final sprite, inputs are the global height map and the global shadow map to decide which fragments to render and how.
  2. Discard the entire default render pipeline and create a new one (RP) that only renders the material(s) I want. This new pipeline RP on every frame takes in the cameras, lights, the renderable objects, etc., does culling and proceeds to render.
  3. RP creates/recycles a few new render textures, sets the one chosen for global height mapping as the render target, takes all the renderables using the material M and makes them render the first pass only.
Preview ![2020-04-20_10-58-01](https://user-images.githubusercontent.com/19392104/79733853-0b37e680-82f6-11ea-8778-6645e4c42f5e.png)
  1. RP sets another texture as the render target (the global shadow map) and gives the newly generated global height map as global input. RP then renders pass 2 of every M material.
Preview ![2020-04-20_10-58-39](https://user-images.githubusercontent.com/19392104/79733871-112dc780-82f6-11ea-9eb8-08c85902144c.png)
  1. RP sets the screen/viewport as the render target, gives the generated global textures as inputs and renders pass 3 of M materials.
Preview ![2020-04-20_10-59-13](https://user-images.githubusercontent.com/19392104/79733882-1559e500-82f6-11ea-9515-78c1bf207b97.png)

To implement something like that in Godot right now it is very, very annoying and inefficient. Having hooks in the standard rendering pipelines maybe would work for a limited amount of use cases, but it is not what this proposal is aiming to. See: https://docs.unity3d.com/Packages/com.unity.render-pipelines.core@8.0/manual/Problems-That-SRP-Solves.html The thing is: if I ever come to a new idea on rendering special effects, new buffers, etc. I'm almost sure the Unity approach covers my use case. The Scriptable Render Pipeline approach (that should be for intermediate to advanced users only) would provide a clean way to do literally any kind of rendering.

reduz commented 4 years ago

Well, the draw per camera approach is very Unity, Instead what I was planning to implement in Godot is:

And of course, if you really want to, the ability to reimplement the entire 2D or 3D rendering for the engine, although again, you probably don't want to do that because it's a lot of work.

Some things will just not work like in Unity, as an example, 2D engine does not use (and will never use) a depth buffer. Unity has the advantage here that it uses the 3D engine for 2D, so they can do it. As the priority in Godot is usability, and using depth buffer in 2D is an extremely rare use case, it will not be supported by default. Still, If you want to do this, you will need to reimplement the 2D renderer part, but at least it should be possible, or you can use the 3D engine for 2D but it will not have as many tools for this.

Controlling the render pipeline yourself by calling functions is definitely not going to happen unless you want to re-implement it. (There is too much optimization opportunity lost by allowing that and the use case of something that you can't do with what was proposed above is too small) The re-implemented one could allow this, so if somebody wants to take the job of making an alternative one that works like this it's fine.

samdze commented 4 years ago

(you already have multipass support, though it probably needs to be added to 2D)

Yes (however, maybe it's not the best way of doing multipass, since each pass has to be in its own shader and in its own material, and there's no shader parameters sharing between passes), it is currently not supported for anything that is not a spatial shader.

Anyway, what you were planning to implement seems good enough for most uses, yes. So, a few questions:

  1. Would you be able to define as much distinct render passes as you want?
  2. What would be the configuration options for each render pass other than material masks?
  3. Will it be possible to render two or more shader passes per render pass? (in case I don't want to wait for the next global render pass to render with the next shader pass)
  4. What would keep you from providing a set of pre-made and somewhat configurable utility functions that would make it easy to replicate key/complex parts of the standard pipeline? (so that a user can easily rewrite an entire pipeline with little effort) This is what Unity does in practice, a Render function that contains all the calls to perform render passes, to configure them and to do other things in between.
  5. To enable the depth buffer in 2D I'd have to necessarily change the source code of the engine? No alternatives? If not possible to offer this toggle in-engine could this be provided via a compile-flag? What is the downside usability-wise? If a user doesn't need the depth buffer he will simply not enable it. Others requesting a depth-like buffer for 2D: https://github.com/godotengine/godot/issues/23847
  6. Will it be possible to make the edited scene viewport reflect the changes made to the pipeline?
  7. How/where were you thinking to let the user define all those render passes, framebuffers, masking, etc.? What do you think about the proposal in the opening post?
hayahane commented 6 months ago

Would be happy to see this feature implemented in Godot. What this proposal really asks is to provide a simple and clear abstraction of a render pipeline. This not only lets users to customize the rendering process but also help the rendering team to better maintain and upgrade the officially provided SRPs.

I'm new to Godot and have found myself struggling to read the rendering code and have no idea how to modify it. Current rendering code is not as clear as how unity did their SRP abstraction, and I cannot change the pipeline without modifying current code. For example, the cull class calls the render method without letting user to decide what to cull. I can see that the server design is so great that allows you to do whatever rendering you want, but it is just too hard to make one from scratch.

This proposal can really make a little team make great visual effects, for example many SRP projects show great anime/illustration like rendering with specially made GI or shadow. Unreal in the opposite has a powerful rendering system but is also criticised not friendly enough for making NPR visuals. Currently Godot can produce nice NPR images, but just like in Unreal (make NPR using post processing) it is costly. Maybe with this done we can find it easier to maintain the Forward+ and Forward Mobile pipeline and to make the upcoming deferred pipeline?

octanejohn commented 6 months ago

@hayahane what about this pr

https://github.com/godotengine/godot/pull/80214

hayahane commented 6 months ago

@hayahane what about this pr

https://github.com/godotengine/godot/pull/80214

A step forward but not as flexible as this proposal wants to achieve. For example, This pr cannot let you make the pipeline used in npr games like genshin impact, where characters are rendered using forward shading and environment using deferred shading, which makes great and vast open world and stylized characters.

octanejohn commented 6 months ago

what about this

https://github.com/godotengine/godot-proposals/issues/8366

hayahane commented 6 months ago

what about this

https://github.com/godotengine/godot-proposals/issues/8366

Probably not enough. Shader code is strongly related to a pass that the pipeline calls. Customizing shader codes doesn't allow you to customize passes.

hayahane commented 6 months ago

what about this

8366

Probably not enough. Shader code is strongly related to a pass that the pipeline calls. Customizing shader codes doesn't allow you to customize passes.

Compared to unity's shader language, ShaderLab, godot's shader language is quite limited. A shader language with more features like ShaderLab (Like what this proposal mentioned #496) could help realize both this proposal and the shader template proposal #8366. Modifying the visual effect should be much faster since there would be no need to build from source. As said before, this can help both users and engine developers.

octanejohn commented 6 months ago

I see, i think this one is more recent

https://github.com/godotengine/godot-proposals/issues/7870

ODtian commented 3 months ago

Current render pipeline is really nested, though compositior api bring some flexibility here but with the cost of complexity. The problem is that no middle level api here: render server is too abstract and lost many detail in pipeline, and it's design is too related to the engine design. Meanwhile render device is too low level, you can't rely on it to custom the pipeline unless you want to write a new pipeline.