The concept of Unity Shader based on Shader

Table of contents

Table of contents

Shader compilation

Conditionals in shaders

Different types of conditionals

Switch code branch at runtime

Branching in shaders

Static branching

How to use static branching

Dynamic branching

How to use dynamic branching

Shader variants

Deduplication of shader variants

Check how many shader variants you have

Get the number of variants used under editor

Get the number of variants at build time

Shader keywords

Definition type: “multi compile” or “shader feature”

Local or global scope

Stage-specific keywords

Create a shader variant for disabled keywords

Using shader keywords with C# scripts

Local Shader Keywords:

Global Shader Keywords: 

How Unity loads and uses shaders

Prewarming shader variants

Marker for shader loading in Profiler:

Shader compilation

1. Compilation is divided into two types: Editor and Runtime

Editor:

  • Pre-compiled, the compiled version will be cached under Library/ShaderCache. The next time you use this variant, you can directly retrieve it from the cache, delete the Library folder, and recompile.
  • The compilation is asynchronous under the Editor, that is, it is compiled in the background, and the object is cyan before it is completed;
  • You can see the progress of compilation in the lower right corner of the Unity editor. You can turn on/off asynchronous compilation under preference ->Editor. Generally, one core of the CPU corresponds to one compilation job.

Runtime:

  • When building, if you use shader_feature to define variants, unused variants will be eliminated, and then all used variants will be compiled and placed in the package body.

2. Different platforms, Shader compiler will be different

3. A shader contained in multiple materials and put into an assetbundle will result in multiple copies of the shader and also interrupt the batching. You can use the  Asset Bundle Browser  to check the dependencies of the bundle.

Conditionals in shaders

  • Sometimes, you want the same shader to do different things in different situations.
  • For example, configure different settings for different materails, define capabilities for different hardware, or dynamically change the behavior of shaders at runtime. It may also be desirable to avoid executing computationally expensive code such as texture fetches, vertex input, interpolators, or loops when not needed.
  • Use conditions to define behaviors that the GPU will only perform under certain conditions.

Different types of conditionals

There are three ways for shaders to use conditions:

  • Static branching: the shader compiler evaluates conditional code at compile time.Use preprocesser constants and macros.
  • Dynamic branching: the GPU evaluates conditional code at runtime.
  • Shader variants
    : Unity uses static branching to compile the shader source code into multiple shader programs. Unity then uses the shader program that matches the conditions at runtime.

Using shader variants, unused variants will be eliminated during build . Therefore, in this case, you should avoid enable/disable keywords in C#   shader_feature , because it is possible that the enabled variants are not included in the package. If the changes are body does not exist, a default will be selected using

If you need to enable/disable keywords in c#  shader_feature , you should include all shader variants in the package body, using the following two methods:

  • Create a  shader variant collection  resource, include all variants, and add it
  • Include a Material in your build for every combination of shader_feature keywords you want to use.

Switch code branch at runtime

There are two ways to switch shader branches in c# code:

  • Declare the multi_compile keyword in the shader. The disadvantage is that there are many compiled variants and occupy memory.
  • The disadvantage of using dynamic branch is that it consumes GPU
#pragma shader_feature REFLECTION_TYPE1 REFLECTION_TYPE2 REFLECTION_TYPE3
Shader directive Branching type Shader variants Unity creates
shader_feature Static branching

Variants for keyword combinations you enable at build time

(just enable the combination of shader_feature)

multi_compile Static branching Variants for every possible combination of keywords
dynamic_branch Dynamic branching No variants

Branching in shaders

According to the way the shader executes branch, it is divided into two types:

Static branching

It has been compiled before running and is called static branch.

  • Advantages: Since unused branches will be eliminated during construction, the built package is small, takes a short time, and has no impact on performance.
  • Disadvantages: Some branches cannot be used in runtime due to elimination

How to use static branching

three methods:

  • Handwritten shaders:
    • Use #if, #elif, #else, and #endif preprocessor directives, or #ifdef and #ifndef preprocessor directives to create static branches.
    • Use an if statement that evaluates a compile-time constant value. Although if statements can also be used for dynamic branches, the compiler detects the compile-time constant value and creates a static branch instead.
    • Unity provides built-in macros for some compile-time constants that you can use with static branches.

Note: Static branching is available only in hand-coded shaders. You cannot create static branches in Shader Graph.

Dynamic branching

It is divided into two types:

  • Based on uniform variables
  • based on any other runtime value

Based on uniform variables is usually more efficient, because uniform variables are constant for the entire drawcall

  • Advantages: Different strategies can be adopted in real time according to dynamic values
  • Disadvantages: GPU consumption, because the GPU execution process is parallelized unit by unit, non-uniform variables will interrupt this parallelism, because some units process another branch, and some process the opposite branch. If one is processed, The other one will wait for the other one. The value of the uniform variables means that all units will process one of the branches, and the performance will be relatively good.

Note: All branches will be compiled into programs and written into the shader. This will increase the file size, but compared to multiple variants, it is still small.

How to use dynamic branching

You can use dynamic branching in your shaders in the following ways:

  • In hand-coded shaders, use an if statement that evaluates runtime state. You can use attributes to force the GPU to execute both branches, or to execute only one branch.
  • In Shader Graph, use a Branch Node. This always executes both branches.

Shader variants

Use  shader keywords . to configure variants. When building, each variant is a piece of shader code.

  • Advantages: consumes less GPU performance than dynamic branch
  • Disadvantages: If there are too many, it will increase memory and loading time.

Deduplication of shader variants

  • After compilation, Unity automatically recognizes identical variants within the same channel and ensures that these identical variants point to the same bytecode. This is called  deduplication .
  • Deduplication prevents the same variants in the same pass from increasing the file size, but it also increases the compilation time and requires stripping unneeded  variants .

Check how many shader variants you have

Get the number of variants used under editor

Select Save to asset… to create a shader variant collection asset.

Get the number of variants at build time

Open Editor.log the file ( menu: Window > General > Console  and select  Open Editor Log  from the Console window menu.) and search Compiling shader

Shader keywords

Through the method of shader keywords, conditional statements can be used in the shader.

A set of keywords is called Keyword, and a single keyword in it is called state.

Definition type: “multi compile” or “shader feature”

There are two types of keywords declared in shader:

1.multi compile: Any keyword declared here will be compiled into a variant

2.shader feature: Only enable keywords will be compiled into variants and put into the package, and the others will be cut out.

Note:  If you add the shader to projectsetting->graphics->  Always Included Shaders  , unity will include all the keywords in it in the package body, even if the type is shader_feature.

Local or global scope

The keywords declared by default are all global, but when declaring the keyword type, you can add the suffix _local to indicate that it is local, and after adding it, it means that it cannot be overridden, that is, it cannot be copied with the same name

Stage-specific keywords

By default, Unity declares the same variant for each stage. For example, if the shader contains a vertex stage and a fragment stage, Unity will declare the same variant for them. If keywords are only used in one stage, it will result in a variant It is repeated in another stage, Unity will automatically recognize and crop them, so it will not increase the package size, but they still result in wasted compilation time, increased shader loading times, and increased runtime memory usage.

In order to avoid this problem, when declaring the keywords type, add a specific suffix to indicate which stage it is used for.

for example:

  • _vertex
  • _fragment
  • _hull
  • _domain
  • _geometry
  • _raytracing

#pragma shader_feature_fragment RED GREEN BLUE--indicates that it is used in the fragment stage

Create a shader variant for disabled keywords

If you  shader_feature create a single keyword, Unity will automatically create a second variant when the keyword is disabled. This helps reduce the number of keywords you need to enable and disable. For example, the following code creates 2 variants:

#pragma shader_feature EXAMPLE_ON

If  multi_compile/ shader_feature created using two or more keywords, use  _ creates a shader variant when all keywords in the set are disabled

#pragma multi_compile _ EXAMPLE_ON
#pragma shader_feature _ RED GREEN BLUE WHITE

Using shader keywords with C# scripts

  • The concept of shader keywords in c# is divided into two types: Local Shader Keywords and Global Shader Keywords, which are concepts in c#. In shader, the difference between local and global is that local needs to be suffixed with _local after type to declare its It is local, otherwise it is global.
  • The keywords of the shader in c# are stored in   the LocalKeywordSpace  structure, accessed through Shader.keywordSpace and ComputeShader-keywordSpace in the compute shader
  • Local and global are indicated by the attribute   isOverridable  . If true, it means global, if false, it means local. Global does not need to be declared actively. It is either local or global.

Local Shader Keywords:

Global Shader Keywords: 

At runtime, switching between different variants may easily affect performance. If the variant has not been loaded before and is a complex variant, it may cause the screen to freeze. If you use global keywords and modify multiple shaders at the same time, it is easy to cause the same problem.

How Unity loads and uses shaders

  • Unity loads compiled variants within the application
  • When loading a scene or assetbundle (resource in resource) , all variants used by the scene or resource will be loaded into the CPU, and then decompressed to another memory of the CPU. The size of the memory occupied can be found in  Player   Change within settings ->Other Settings  >  Shader Variant Loading
  • When a variant is used for the first time, the CPU will send the resource data to the GPU, instead of putting all variants into the GPU at once. The GPU will generate a GPU-specific version of the corresponding variant. shader variant, cache it and won’t resend it next time you use it.
  • If the variant does not have any references in the scene, Unity will remove it from the CPU and GPU

Prewarming shader variants

In order to avoid loading stagnation when using it, you can use preloading:

也可以在projectsetting->graphics  里面配置Preloaded shaders section of the Graphics Settings window. Unity uses the ShaderVariantCollection.WarmUp API to load and prewarm the shader variant collections when your built application starts.

Marker for shader loading in Profiler:

  • Shader.ParseThreaded、 Shader.ParseMainThread for Unity loading the shader object from serialized data.
  • Shader.CreateGPUProgram for Unity creating a GPU-specific version of a shader variant

Guess you like

Origin blog.csdn.net/qq_37672438/article/details/131527205