【转】【shader面板】Shader Compilation in Unity 4.5

Unity 4.5 introduces a significant improvement in shader compilation speed and error reporting, reducing the time it takes to compile shaders by up to 95% for complex surfaces like Marmoset Skyshop. Errors are now reported on correct lines, and shader include (.cginc) errors have correct filenames and line numbers. The debugging workflow has been greatly enhanced with an 'Show generated code' button in the inspector, and generated shader code is now readable with comments and better indentation.

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【原文】Shader Compilation in Unity 4.5

 

 

A story in several parts. 1) how shader compilation is done in upcoming Unity 4.5; and 2) how it was developed. First one is probably interesting to Unity users; whereas second one for the ones curious onhow we work and develop stuff.

Short summary: Unity 4.5 will have a “wow, many shaders, much fast” shader importing and better error reporting.

Current state (Unity <=4.3)

When you create a new shader file (.shader) in Unity or edit existing one, we launch a “shader importer”. Just like for any other changed asset. That shader importer does some parsing, and then compiles thewhole shader into all platform backends we support.

Typically when you create a simple surface shader, it internally expands into 50 or so internal shader variants (classic “preprocessor driven uber-shader” approach). And typically there 7 or so platform backends to compile into (d3d9, d3d11, opengl, gles, gles3, d3d11_9x, flash – more if you have console licenses). This means, each time you change anything in the shader, a couple hundred shaders are being compiled. And all that assuming you have a fairly simple shader - if you throw in some multi_compile directives, you’ll be looking at thousands or tens of thousands shaders being compiled. Each. And. Every. Time.

Does it make sense to do that? Not really.

Like most of “why are we doing this?” situations, this one also evolved organically, and can be explained with “it sounded like a good idea at the time” and “it does not fix itself unless someone works on it”.

A long time ago, Unity only had one or two shader platform backends (opengl and d3d9). And the amount of shader variants people were doing was much lower. With time, we got both more backends, and more variants; and it became very apparent that someone needs to solve this problem.

In addition to the above, there were other problems with shader compilation, for example:

  • Errors in shaders were reported, well, “in a funny way”. Sometimes the line numbers did not make any sense – which is quite confusing.
  • Debugging generated surface shader code involved quite some voodoo tricks (#pragma debug etc.).
  • Shader importer tried to multi-thread compilation of these hundreds of shaders, but some backend compilers (Cg) have internal global mutexes and do not parallelize well.
  • Shader importer process was running out of memory for really large multi_compile variant counts.

So we’re changing how shader importing works in Unity 4.5. The rest of this post will be mostly dumps of our internal wiki pages.

Shader importing in Unity 4.5

  • No runtime/platforms changes compared to 4.3/4.5 – all changes are editor only.
  • No shader functionality changes compared to 4.3/4.5.
  • Shader importing is much faster; especially complex surface shaders (Marmoset Skyshop etc.).
    • Reimporting all shaders in graphics tests project: 3 minutes with 4.3, 15 seconds with this.
  •  Errors in shaders are reported on correct lines; errors in shader include (.cginc) files are reported with the filename & line number correctly.
    • Was mostly “completely broken” before, especially when include files came into play.
    • On d3d11 backend we were reporting error column as the line, hah :) At some point during d3dcompiler DLL upgrade it changed error printing syntax and we were parsing it wrong. Now added unit tests so hopefully it will never break again.
  •  Surface shader debugging workflow is much better.
    • No more “add #pragma debug, open compiled shader, remove tons of assembly” nonsense. Just one button in inspector, “Show generated code”.
    • Generated surface shader code has some comments and better indentation. It is actually readable code now!
  • Shader inspector improvements:
    • Errors list has scrollview when it’s long; can double click on errors to open correct file/line; can copy error text via context click menu; each error clearly indicates which platform it happened for.
    • Investigating compiled shader is saner. One button to show compiled results for currently active platform; another button to show for all platforms.
  • Misc bugfixes
    • Fixed multi_compile preprocessor directives in surface shaders sometimes producing very unexpected results.
    • UTF8 BOM markers in .shader or .cginc files don’t produce errors.
    • Shader include files can be at non-ASCII folders and filenames.

Overview of how it works

  • Instead of compiling all shader variants for all possible platforms at import time:
    • Only do minimal processing of the shader (surface shader generation etc.).
    • Actually compile the shader variants only when needed.
    • Instead of typical work of compiling 100-1000 internal shaders at import time, this usually ends up compiling just a handful.
  • At player build time, compile all the shader variants for that target platform
    • Cache identical shaders under Library/ShaderCache.
    • So at player build time, only not-yet-ever-compiled shaders are compiled; and always only for the platforms that need them. If you never ever use Flash, for example, then none of shaders will be compiled for Flash (as opposed to 4.3, where all shaders are compiled to all platforms, even if you never ever need them).
  • Shader compiler (CgBatch) changes from being invoked for each shader import, into being run as a “service process”
    • Inter-process communication between compiler process & Unity; using same infrastructure as for VersionControl plugins integration.
    • At player build time, go wide and use all CPU cores to do shader compilation. Old compiler tried to internally multithread, but couldn’t due to some platforms not being thread-safe. Now, we just launch one compiler process per core and they can go fully parallel.
    • Helps with out-of-memory crashes as well, since shader compiler process never needs to hold bazillion of shader variants in memory all at once - what it sees is one variant at a time.

How it was developed

This was mostly a one-or-two person effort, and developed in several “sprints”. For this one we used our internal wiki for detailed task planning (Confluence “task lists”), but we could have just as well use Trello or something similar. Overall this was probably around two months of actual work – but spread out during much longer time. Initial sprint started in 2013 March, and landed in a “we think we can ship this tomorrow” state to 4.5 codebase just in time for 1st alpha build (2013 October). Minor tweaks and fixes were done during 4.5 alpha & beta period. Should ship anyday now, fingers crossed :)

Surprisingly (or perhaps not), largest piece of work was around “how do you report errors in shaders?” area. Since now shader variants are imported only on demand, that means some errors can be discovered only “some time after initial import”. This is a by-design change, however - as the previous approach of “let’s compile all possible variants for all possible platforms” clearly does not scale in terms of iteration time. However, this “shader seemed like it did not have any errors, but whoops now it has” is clearly a potential downside. Oh well; as with almost everything there are upsides & downsides.

Most of development was done on a Unity 4.3-based branch, and after something was working we were sending off custom “4.3 + new shader importer” builds to the beta testing group. We were doing this before any 4.5 alpha even started to get early feedback. Perhaps the nicest feedback I ever got:

I’ve now used the build for about a week and I’m completely blown away with how it has changed how I work with shaders.

I can try out things way quicker.
I am no longer scared of making a typo in an include file.
These two combine into making me play around a LOT more when working.
Because of this I found out how to do fake HDR with filmic tonemapping [on my mobile target].

The thought of going back to regular beta without this [shader compiler] really scares me ;)

Anyhoo, here’s a dump of tasks from our wiki (all of them had little checkboxes that we’d tick off when done). As usual, “it basically works and is awesome!” was achieved after first week of work (1st sprint). What was left after that was “fix all the TODOs, do all the boring remaining work” etc.

2013 March Sprint:

  • Make CgBatch a DLL
    • Run unit tests
    • Import shaders from DLL
    • Don’t use temp files all over the place
  • Shader importer changes
    • Change surface shader part to only generate source code and not do any compilation
    • Make a “Open surface compiler output” button
    • At import time, do surface shader generation & cache the result (serialize in Shader, editor only)
    • Also process all CGINCLUDE blocks and actually do #includes at import time, and cache the result (after this, left with CGPROGRAM blocks, with no #include statements)
    • ShaderLab::Pass needs to know it will have yet-uncompiled programs inside, and able to find appropriate CGPROGRAM block:
      • Add syntax to shaderlab, something like Pass { GpuProgramID int }
      • Make CgBatch not do any compilation, just extract CGPROGRAM blocks, assign IDs to them, and replace them with “GpuProgramID xxx”
      • “cache the result” as editor-only data in shader: map of snippet ID -> CGPROGRAM block text
    • CgBatch, add function to compile one shader variant (cg program block source + platform + keywords in, bytecode + errors out)
    • Remove all #include handling from actual shader compilers in CgBatch
    • Change output of single shader compilation to not be in shaderlab program/subprogram/bindings syntax, but to produce data directly. Shader code as a string, some virtual interface that would report all uniforms/textures/… for the reflection data.
  • Compile shaders on demand
    • Data file format for gpu programs & their params
    • ShaderLab Pass has map: m_GpuProgramLookup (keywords -> GPUProgram).
    • GetMatchingSubProgram:
      • return one from m_GpuProgramLookup if found. Get from cache if found
      • Compile program snippet if not found
      • Write into cache

2013 July Sprint:

  • Pull and merge last 3 months of trunk
  • Player build pipeline
    • When building player/bundle, compile all shader snippets and include them
    • exclude_renderers/include_renderers, trickle down to shader snippet data
    • Do that properly when building for a “no target” (everything in) platforms
      • Snippets are saved in built-in resource files (needed? not?)
    • Make building built-in resource files work
      • DX11 9.x shaders aren’t included
      • Make building editor resource file work
    • Multithread the “missing combinations” compilation while building the player.
      • Ensure thread safety in snippet cache
  • Report errors sensibly
  • Misc
    • Each shader snippet needs to know keyword permutation possibly needed: CgBatch extracts that, serialized in snippet (like vector< vector >)
    • Fix GLSLPROGRAM snippets
    • Separate “compiler version” from “cgbatch version”; embed compiler version into snippet data & hash
    • Fix UsePass

2013 August Sprint:

  • Move to a 4.3-based branch
  • Gfx test failures
    • Metro, failing shadow related tests
    • Flash, failing custom lightmap function test
  • Error reporting: Figure out how to deal with late-discovered errors. If there’s bad syntax, typo etc.; effectively shader is “broken”. If a backend shader compiler reports an error:
    • Return pink “error shader” for all programs ­ i.e. if any of vertex/pixel/… had an error, we need to use the pink shaders for all of them.
    • Log the error to console.
    • Add error to the shader, so it’s displayed in the editor. Can’t serialize shader at that time, so add shaders to some database under Library (guid­>errors).
      • SQLite database with shader GUID -> set of errors.
    • Add shader to list of “shaders with errors”; after rendering loop is done go over them and make them use pink error shader. (Effectively this does not change current (4.2) behavior: if you have a syntax error, shader is pink).
  • Misc
    • Fix shader Fallback when it pulls in shader snippets
    • “Mesh components required by shader” part at build time - need to figure them out! Problem; needs to compile the variants to even know it.
    • Better #include processing, now includes same files multiple times
  • Make CgBatch again into an executable (for future 64 bit mac…)
    • Adapt ExternalProcess for all communication
    • Make unit tests work again
    • Remove all JobScheduler/Mutex stuff from CgBatch; spawn multiple processes instead
    • Feels like is leaking memory, have to check
  • Shader Inspector
    • Only show “open surface shader” button for surface shaders
    • “open compiled shader” is useless now, doesn’t display shader asm. Need to redo it somehow.

2013 September Sprint:

  • Make ready for 4.5 trunk
    • Merge with current trunk
    • Make TeamCity green
    • Land to trunk!
  • Make 4.3-based TeamCity green
    • Build Builtin Resources, fails with shader compiler RPC errors GL-only gfx test failures (CgProps test)
    • GLSLPROGRAM preprocessing broken, add tests
    • Mobile gfx test failures in ToonyColors
  • Error reporting and #include handling
    • Fixing line number reporting once and for all, with tests.
    • Report errors on correct .cginc files and correct lines on them
    • Solve multiple includes & preprocessor affecting includes this way: at snippet extraction time, do not do include processing! Just hash include contents and feed that into the snippet hash.
    • UTF8 BOM in included files confusing some compilers
    • Unicode paths to files confusing some compilers
    • After shader import, immediately compile at least one variant, so that any stupid errors are caught & displayed immediately.
  • Misc
    • Make flags like “does this shader support shadows?” work with new gpu programs coming in
    • Check up case 550197
    • multi_compile vs. surface shaders, fix that
  • Shader Inspector
    • Better display of errors (lines & locations)
    • Button to “exhaustively check shader” - compiles all variants / platforms.
    • Shader snippet / total size stats

What’s next?

Some more in shader compilation land will go into Unity 5.0 and 5.x. Outline of our another wiki page describing 5.x related work:

    • 4.5 fixes “compiling shaders is slow” problem.
    • Need to fix “New standard shader produces very large shader files” (due to lots of variants - 5000 variants, 100MB) problem.
    • Need to fix “how to do shader LOD with new standard shader” problem.

转载于:https://www.cnblogs.com/sbCat/p/4757025.html

<think>好的,我现在需要帮助用户了解如何在Unity中配置Shader Stripping以优化构建大小和性能。首先,我得回忆一下Unity Shader Stripping的相关知识,并结合提供的引用内容来组织回答。 首先,用户的问题是关于配置方法和优化建议。根据引用[2],UnityShaderStripper是一个模块化工具,可以显著减少构建时间,这说明可能需要提到这个工具的使用。引用[1]提到Shader变体的灵活性和管理挑战,所以优化建议需要包括如何管理变体,避免不必要的生成。引用[3]提到的记录着色器编译选项可能是在调试或分析时需要用到的步骤。引用[4]提到的裁剪问题提醒我要注意避免过度裁剪导致的问题。 接下来,我需要整理配置步骤。可能包括图形项目设置中的选项,如Shader Stripping的启用,设置变体过滤条件,以及使用UnityShaderStripper工具。然后,优化建议应该涵盖变体管理、变体剔除、预处理指令的使用、定期审查和测试等。 需要注意的是,用户可能需要具体的操作步骤,比如在哪里找到这些设置,如何编辑配置文件。同时,需要引用相关的引用来源,比如引用[2]提到的项目地址,引用[3]的日志选项,以及引用[1]的管理建议。 另外,用户可能不了解Shader变体的概念,需要简要解释一下,但根据问题,用户可能已经有一定的基础,所以可能不需要深入解释。但确保回答清晰,步骤明确。 现在,要确保回答结构清晰,分步骤说明配置方法,然后给出优化建议。同时,生成的相关问题需要与主题相关,比如工具使用、常见问题、性能平衡等。 最后,检查引用是否正确标注,比如引用[1]、[2]、[3]对应的内容是否在回答中正确引用,并避免格式错误。确保使用中文,数学公式正确,但这个问题可能不涉及数学内容,所以主要关注代码块和引用格式。</think>### Unity Shader Stripping 配置方法及优化建议 #### 一、Shader Stripping 配置方法 1. **启用基础配置** 在 Unity Editor 中打开 **图形项目设置(Graphics Project Settings)**,启用以下功能: - **Shader Stripping**:勾选 `Strip Unused Variants`,移除未使用的着色器变体[^1][^3]。 - **记录编译日志**:启用 `Log Shader Compilation`,便于分析编译过程和变体生成情况。 2. **配置变体过滤规则** 通过编辑 `ProjectSettings/GraphicsSettings.asset` 文件或使用代码 API,定义保留或剔除的变体条件。例如: ```csharp // 示例:保留所有包含 "_NORMALMAP" 关键字的变体 Shader.SetShaderGlobalVariantsFilter("_NORMALMAP", ShaderVariantFilter.Include); ``` 3. **使用 UnityShaderStripper 工具** 引用[2]提到的开源工具 **UnityShaderStripper** 提供模块化支持,可进一步优化: - 克隆项目并集成到 Unity 工程:`https://gitcode.com/gh_mirrors/un/UnityShaderStripper` - 自定义剔除规则(如按渲染管线、平台或关键字过滤变体)。 #### 二、优化建议 1. **精简变体数量** - 避免在 Shader 中定义过多冗余的关键字组合,优先使用 `#pragma multi_compile` 的简化版本(如 `multi_compile_fragment`)。 - 合并功能相似的变体,例如通过宏定义复用代码逻辑。 2. **按平台剔除变体** 在 `Edit > Project Settings > Player > Other Settings` 中,针对不同平台(如移动端)关闭不支持的图形特性(如曲面细分)。 3. **动态加载变体** 使用 `ShaderVariantCollection` 预加载关键变体,避免运行时因缺失变体导致的卡顿。 4. **定期审查与测试** - 利用 Unity 的 **Build Report** 分析着色器变体占用情况。 - 测试所有功能场景,避免过度裁剪导致渲染错误(类似引用[4]提到的 IL2CPP 裁剪问题)。 #### 三、示例配置代码 ```csharp // 自定义变体过滤脚本示例 #if UNITY_EDITOR using UnityEditor; using UnityEditor.Build; using UnityEditor.Rendering; class ShaderStripper : IPreprocessShaders { public int callbackOrder => 0; public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data) { // 剔除移动端不需要的变体 if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) { for (int i = data.Count - 1; i >= 0; --i) { if (data[i].shaderKeywordSet.IsEnabled("_ALPHATEST_ON")) data.RemoveAt(i); } } } } #endif ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值