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

Async shader compilation (KHR_parallel_shader_compile) #1855

Closed
wants to merge 2 commits into from

Conversation

Maksims
Copy link
Collaborator

@Maksims Maksims commented Feb 5, 2020

Fixes #1474

Restored code of: #1685
After so many years of using git, I still mess up :D

I confirm I have signed the Contributor License Agreement.

@willeastcott
Copy link
Contributor

So if async defaults to true, won't this change the behavior of existing PlayCanvas projects out there? There'll be meshes popping into view instead of the main thread blocking until the shader is ready, no?

@Maksims
Copy link
Collaborator Author

Maksims commented Mar 26, 2020

So if async defaults to true, won't this change the behavior of existing PlayCanvas projects out there? There'll be meshes popping into view instead of the main thread blocking until the shader is ready, no?

Yes, by default it does.
But there are exceptions, in which cases async is ignored:

  1. Preloading
  2. Post effects
  3. Shadows and similar passes non-forward passes
  4. Transform feedback
  5. Lightmapper

The goal would be to make everyone benefit from it straight away.
But we could do it slightly different way, and make it by default false and if developer wants to optimise more his application, they could tick async flag on materials explicitly. I believe that would be a better way, indeed. Agree I should change it?

@willeastcott
Copy link
Contributor

Yeah, that way sounds better. @mvaligursky - what do you think about all this?

@mvaligursky
Copy link
Contributor

From engine usability point of view, I'm not very keen on a solution where object might sometimes not show because shaders are compiling. Async shader compilation is optimization, and as such it's not something I'd want users to have to worry about by exposing some material API to enable / disable it. Material interface is predominantly used by artists, and they will not understand the implications of this. It would lead to a hard to find bugs that depend on timing, on some low end devices perhaps.

I would prefer a solution without public API for this. I'd be happy with per project "use async shader compilation" option but that would allow users to disable it while there are perhaps issues with the system, as a work around.

In ideal world we'd integrate async shaders in a way it has no impact on engine users. I'm not familiar with engine enough to cover most cases, but some points of what I'm thinking would be a part of good solution:

  • if we async load scene / objects, we can freely do async shader compilation there, but don't return loaded objects to the user till they are compiled.
  • if we need to compile shaders while rendering (ie a user creates new material with some custom chunk), we should trigger all shaders to async compile but wait with rendering of the frame till they are done. This should still win user some time assuming there are more cores used on the system as they will compile in parallel.

Examples of possible issue with current implementation:

  • lightmap baking - this gets baked right when the scene is loaded .. can we have missing shadows if some custom shader is not compiled yet? The same goes for once off cube map / render to texture generation. People settings up the Scene / materials in Editor would need to turn off async loading on everything, as they don't know what goes to these render targets.

@Maksims
Copy link
Collaborator Author

Maksims commented Mar 26, 2020

Valid points @mvaligursky.
Let me assess:

  • if we async load scene / objects, we can freely do async shader compilation there, but don't return loaded objects to the user till they are compiled.

Currently it is this flow:

  1. If asset is preloaded, it will show and render immediately on first render. With stalls on each shader compilation for each material.
  2. If asset is not preloaded, it will start loading, and render nothing. Once model is loaded, it will render it with standard materials. Start loading referenced textures. Once textures are loaded it will add them to material, and swap standard material with loaded one. Triggering shader compilation on each render after swap. So with multiple materials, potentially multiple shader recompilation stalls.

So in case of non-preloaded, assets, setting them by code will not have an immediate effect. Same goes with created by code assets. Developer has to call load on them explicitly.

  • if we need to compile shaders while rendering (ie a user creates new material with some custom chunk), we should trigger all shaders to async compile but wait with rendering of the frame till they are done. This should still win user some time assuming there are more cores used on the system as they will compile in parallel.

There are few cases for userflow with async shaders:

  1. Modify material, and ensure it is available on next render - this leads to stalls, regardless of async or sync flows. Using async in this case, has very little win, even on multi-core systems (tested).
  2. Modify material, keep rendering, trigger shader compilation and be notified when shader is compiled. That shader is not used during render (user case). This user case is not straight forward, as there is not enough information to trigger shader compilation before it is rendered. And material might have multiple shader variants due to different flags definitions.
  3. Modify material, keep rendering. Shader will be swapped automatically once compiled in async. Leads to popping of materials. This is where async shaders dramatically reduces stalls.
  • lightmap baking

This will enforce sync compilation by engine. Same for post effects and other similar cases.

This PR is pretty simple and naive, and only solves either 1st or 3rd user cases.

Material interface is predominantly used by artists, and they will not understand the implications of this.

Same goes for preload toggle, and shader chunks (which I wish were added to Editor). Async is purely optimisation feature, for developers to have more control of how they can optimise application flow. Same as for preload.

The challenge here, is that pc.Shader - is throw away object, managed by material system. Each material can have many of them depending on flag states. Different ModelComponents with different flags that use same Material, will lead that Material to have multiple Shaders. static flag on ModelComponent and Light creates even more variations.

So developer does not have explicit handle of shader during application update loop. It only has abstraction over it, in form of Material.

There can't be a simple function compileShader for material, before it has been rendered.

@willeastcott willeastcott added the area: graphics Graphics related issue label Apr 15, 2020
@raytranuk
Copy link
Contributor

I wanted to follow-up on a this PR, with an idea that came up from discussions with @slimbuck + @mvaligursky - it became apparent that we could expose a way for engine users to explicitly choose to use async compilation by defaulting to non-async when using the standard material interface:material.update() but if the user wants to use asynchronous compilation they can provide a call back: material.update(callbackFunction) - the callback can also be used as a way to do error handling (which will potentially be very useful when editing visual shader graphs (and sub graphs for per node previews)). @Maksims @willeastcott - let me know what your thoughts are on this?

@raytranuk
Copy link
Contributor

I should add that internally in the engine (once we have support) we would use the asynchronous shader compilation where possible - but with guarantees to developers that their projects will look and behave the same - and hopefully load faster and run with fewer stalls - a prime example (covered to a degree in @mvaligursky comments) would be while preloading a scene asynchronously (perhaps while the player is interacting with a basic start-up scene) we could also precompile the new scene's material shaders asynchronously (after any dependencies are loaded) - and any shaders not compiled by the time the new scene is switched to, will block until done (an alternative to blocking - which was also discussed - is to temporarily switch to use a slower 'uber' shader until a specific optimized shader is ready)?

@mvaligursky
Copy link
Contributor

Also, to avoid having to recompile shader due to async loaded textures, we should create and attach some dummy 1x1 textures to them when material is created to build the appropriate shader right away instead of building different variations for each loaded resource.

@Maksims
Copy link
Collaborator Author

Maksims commented Jul 11, 2020

Also, to avoid having to recompile shader due to async loaded textures, we should create and attach some dummy 1x1 textures to them when material is created to build the appropriate shader right away instead of building different variations for each loaded resource.

I think we already do this: #753

@Maksims
Copy link
Collaborator Author

Maksims commented Sep 2, 2020

@mvaligursky
Copy link
Contributor

lightly related: #3559

@mvaligursky
Copy link
Contributor

I tried to add support for the extension to the engine, but I wasn't very convinced by the benefits, and so I've created a separate example to investigate: https://github.com/mvaligursky/webgl-parallel_shader_compile

@mvaligursky
Copy link
Contributor

mvaligursky commented Feb 1, 2023

I added issues with the extension to both chromium and webkit
https://bugs.chromium.org/p/chromium/issues/detail?id=1412083
https://bugs.webkit.org/show_bug.cgi?id=251514

@willeastcott
Copy link
Contributor

This PR is now so old, it's going to be pretty much impossible to resolve the files even if we decided to merge. I think we should close it for now. Thanks for the initial work on this though, @Maksims!

@Maksims Maksims deleted the asyncShaderCompilation branch January 22, 2024 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: graphics Graphics related issue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Async Shader Compilation (KHR_parallel_shader_compile)
4 participants