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