Reac2023 Dunia Shader Pipeline
Reac2023 Dunia Shader Pipeline
This talk is about my time at Ubisoft, Coming from a background of graphics driver
junior 3D programmer on FC Primal in and SDK development, this talk is about my
2015 to 3D technical lead on FC6 in 2021 first AAA production experience.
Presentation Overview
● Challenges from a legacy codebase
● Long-term tech vision and planning
○ Architectural & workflow preparation during Far Cry 5
● Executing the vision
○ Improving the Architecture and getting ready for production
● Fine-tuning User Experience
Let's go back in time a bit
Far Cry 4 + D3D12 announcement
FC3 FC4
2012 2014
Far Cry 6 + D3D12 support
Memory Management
(By the way) You Can Use Vulkan Without Pipelines Today ...
Dunia Shader Authoring
Expressing Material connections
- Our shaders are authored as Shader Families
- Group of shaders that implement similar material responses
- Skin, Car Paint, Vegetation, Weapons, ….
FC6 - Shader ID to PSO transition
Each shader has a ShaderID: a 32-bits uint
Composition:
ShaderID = shaderFamilyBitMask | shaderOptionBitMask | shaderVariationBitMask
[Name string hashed, bitwise shift and OR] [bit shift based on previous masks]
- No Art restrictions
- Every model could be used with any shader and it was kinda expected to work
- Nearly every object in the world could switch to a fully different and unrelated shaders at any
point in game
The Plan
- Extend the shader compiler to allow state parsing
- Move PSO states into each family
technique {
BlendOp0 = Add;
RenderTargetFormat0 = R8_UNORM;
….
}
Can Art Help?
- Define clear rules for shader and mesh combinations
- Shaders define supported vertex format, only models with matching layout can use a shader
- Only shaders from the family can be switched on the same model
Memory Management
Barrier Work (Overview)
- One of FC5’s main task
- Annotate resources as it flows through the frame in the form of:
- Resource Stage + Resource Usage
4. Once we had a basic version working, it’s time to take advantage of the new
APIs
FC6 - Transition to Modern APIs
New APIs: Vulkan, D3D12 and AGC
New Platform: Stadia, XBSX, PS5
► Never stall rendering, but certain objects might be missing for few frames
Skipping Draw Calls
Dunia Philosophy:
If Dispatch or Draw shaders are not ready, do not stall. Skip the call
if (isSuccess) device.Draw(ShaderID_0);
isSuccess = device.Dispatch(BuildIndirectArgs); device.Draw(ShaderID_1);
Sidenode: UE5 supports a similar way now since 5.2 (for draw calls)
Expanding Skipping Draw Calls
Very helpful when porting to a new API as well
- We have a system that does a decent job of getting the game running
- QA can test, we can develop and improve all systems from here in parallel
Improve Shader Variations
- Started with a long list of variations Variations
- All of same importance, but that’s not true
- Vertex Formats
- Output Formats
- Burning
- Wetness
- Wind influence
- Skinning
- …..
Improve Shader Variations
- Started with a long list of variations Variations
- All of same importance, but that’s not true
- Vertex Formats
- Output Formats
- Burning
- Wetness
- Wind influence
- Skinning
- …..
Essential vs Optional
Essential Optional
- For example when a object starts burning we don’t mind if the shader is not swapped for a couple
of frames. As the objects gets burned slowly over time anyway
- QA will flag cases where this might be cause undesirable artifacts
Fallback to less specific PSOs
- Now when we hash the PSO, start looking for fallback options if it’s not ready
- Try to remove optional defines and see if we can find a match
- If we find one, use it instead
- Otherwise keep searching
- If we don’t find a match, we are back to skipping draw calls
Map ShaderID to PSO - The Journey begins
Transition Start
- Start with work on Xbox Series X
- Why? AAA reality of our team in Montreal owning the xbox platform
- And we had the time in the schedule set aside and we thought it was important
- Less worried about regressions as it’s the least “tested” platform at this stage
Two Sets of States
Runtime Shader Shader State
Output Output
Formats Formats
Blend Blend
States States
Primitive Primitive
Topology Topology
… …
… …
The two sets should match, but of course they don’t as the Shader State was setup during FC5 but
unused up to this point!
technique {
#include "BlendedStates.inc.fx"
#if defined(TWO_SIDED)
CullMode = None;
#endif
}
Expanding the Test Audience
- At this stage we were more confident and started enabling a warning on
screen if a mismatch is detected
- First on a small number of testers machine (the local 3D Programmer QA)
- Expanded globally once local QA didn’t see any warnings regularly
- But there are a couple of cases left that need art to resolve
- We want to provides technical directors with enough information to track down
the causes and assign issues to the right person without programmer
involvement
JIRA Generation
Custom internal API to generate a Jira from runtime code on any platform
We validate geometry layout vs. what the shader expect
- Mismatch information directly available in Jira bug report
- One JIRA for each geometry + shader combination
- Directly assigned to the responsible Technical Director
<Geometry Name> <Shader Name>
Geometry Layout Expected Shader Layout
● Name ? ● Name
● Index ● Index
● Stream == ● Stream
● Format ● Format
● Offset ● Offset
● Classification ● Classification
● Instance step rate ● Instance step rate
Final Testing and Validation
Test Coverage
- Environments
- Urban, dense vegetation, open field, etc. in day/night time and various weather conditions
- Gameplay settings
Automatic Testing
- Spawn at fixed locations, deterministic
- Automatically collects screenshots, profiler perf captures, and Pix captures
- Spawn in all map sectors, bot randomly playing the game
- Catch regressions, also perfect for detecting shader state mismatch
Final Testing Process
- A combination of manual testing by QA team and (mostly) automatic scripts
- QC team: mission, cutscenes, game menu
- Automation: all map sectors and environment setting permutations
- An iterative process to detect and fix most of state issues
1
QA playthrough
&
Auto-test passes
3 2
Shader state
Fix and validation mismatch, Jira
ticket generation
Interesting Edge Cases
Noticed a common pattern, whenever we would aim at an enemy we would get an
assert with mismatch
► Designed for reduced shader variation count but proved to be a very flexible
architecture as a side effect
Each ShaderID in the engine maps to a PSO!
- This was a great amount of work, we are grateful we broke this work down
over multiple games
- The runtime is super simple, most cases will never need to deal with render
state
- All state is managed centrally in a couple of common header files
- Shaders own the whole state in a single place
- No more state leaking
- Really worth the effort and an engine architecture is better if designed for it
- Large AAA games can do it, we proved that!
Fine-tuning User Experience
Shader Pre-compilation Process
We have all the runtime side of things in-place, but how to improve the first start
experience?
► This way we can be sure all shaders for the new location and virtual textures
are ready before the player can see them
Conclusion
Lessons Learned
1. When major technology change is required, identify and tackle the
architectural changes needed first
2. Start setting up the supporting systems early
3. See restrictions as an opportunity for better design, not annoyances
4. Use iterative design process, each step gets closer to a shippable solution
5. Think long term, don’t limit yourself to current project timeframe
Acknowledgements
Special thanks to all members of 3D graphics team who contributed over the years
to make Far Cry games possible.
Thank You &
Questions
Sources
Advanced Graphics Summit: 'Marvel's Spider-Man' Remastered: A PC
Postmortem - gdcvault - video