Add some thoughts about the future render pipeline
This commit is contained in:
parent
fc08ab08d4
commit
1d8051747e
63
docs/NOTES_render_pipeline.md
Normal file
63
docs/NOTES_render_pipeline.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Render Pipeline
|
||||||
|
|
||||||
|
## Frame Graph
|
||||||
|
|
||||||
|
We use [KHR_dynamic_rendering](https://lesleylai.info/en/vk-khr-dynamic-rendering/) for vulkan.
|
||||||
|
DX12 render passes are basically equivalent to this.
|
||||||
|
|
||||||
|
Nevertheless, we need to define which render targets we need and when each RT will be written and read from.
|
||||||
|
This is because we
|
||||||
|
1. Need to allocate render targets and
|
||||||
|
2. Want to parallelize independent parts of the frame.
|
||||||
|
|
||||||
|
Two API concepts:
|
||||||
|
1. **Render Targets**: Contain format info and sizes. For vulkan, these get "compiled" to a VkImage(View) and a Rect for the render area
|
||||||
|
2. **Passes**: Contain the information necessary for VkRenderingInfoKHR structs and their DX12/Metal/... equivalents.
|
||||||
|
|
||||||
|
A render target should actually contain N copies of the image, where N is the max. number of frames in flight. (Usually 2 or 3).
|
||||||
|
Multiple render targets with the same format could be aliased, if they are never accessed concurrently, to save VRAM.
|
||||||
|
|
||||||
|
A **frame-graph** is then:
|
||||||
|
- A set of render targets. Each render target gets an unique name by which it can be referenced.
|
||||||
|
- A ordered list of passes. The passes are - logically - executed in the order given, but may be scheduled concurrently, if this yields the same results.
|
||||||
|
|
||||||
|
To determine the possibilities of concurrent execution and aliasing, each pass needs to list the render targets it accesses.
|
||||||
|
|
||||||
|
|
||||||
|
## Interfacing with the scene
|
||||||
|
|
||||||
|
The update thread computes a scene representation that is made available to the render thread.
|
||||||
|
This should contain a list of objects that, potentially (sans culling) need to be rendered.
|
||||||
|
|
||||||
|
Each object has a material.
|
||||||
|
A material contains a effect, which lists the passes during which the object needs to be rendered
|
||||||
|
and shader pipelines for each of these passes (sets of shaders + fixed state + meta information about uniforms etc.).
|
||||||
|
It could also define the expected vertex layout for rendered meshes.
|
||||||
|
|
||||||
|
This information enables us to provide each pass of the frame graph with a list of objects that need to be rendered by that pass.
|
||||||
|
It also allows us to skip passes without any input objects, except when the passes are marked as "execute always" (for example a tone-mapping pass).
|
||||||
|
|
||||||
|
The material also binds effect properties to "real" values.
|
||||||
|
For example an effect could expose a property like "albedo-map" and the material would bind this to a particular resource.
|
||||||
|
|
||||||
|
## Interfacing with the resource system
|
||||||
|
|
||||||
|
Effects can be loaded from resources (we already have a parser for PSOs and a shader compiler).
|
||||||
|
These would now be tied to a particular frame-graph (set of passes).
|
||||||
|
|
||||||
|
We could also load frame-graph information from resources, but how do we provide the logic for that pass?
|
||||||
|
**Idea:** Have the game "bind" a struct like the following to a pass:
|
||||||
|
|
||||||
|
```
|
||||||
|
/* rt_pass is some kind of identifier/handle for the pass)
|
||||||
|
struct rt_pass_functions_s {
|
||||||
|
void (*pass_prepare)(rt_pass pass);
|
||||||
|
void (*pass_execute)(rt_pass pass, rt_render_object_list objects);
|
||||||
|
void (*pass_finalize)(rt_pass pass);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The runtime (or a separate library?) can provide some default pass-implementations for this.
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user