Unity5内部渲染的优化3:移除固定功能

译自aras的博客,总共3篇文章,讲述unity5优化自己渲染器的过程

吸取大神调试与优化经验,了解unity5内部渲染器的优化方法

第一篇:Unity5内部渲染的优化1:介绍

第二篇:Unity5内部渲染的优化2:清理

上篇文章写了关于清理和优化。从那时起,我已经转变到做一些unity5.1的工作了,移除了固定功能着色器Fixed Function Shaders和一些别的事。

固定功能是什么

以前,GPU还没有“可编程着色器programmable shaders”;通过启用和禁用某些功能,配置或多或少(大多很少)变得灵活。例如,让他们去计算每个顶点的光照;或者在每个像素上把两个贴图颜色相加。

Unity在很久之前就有了,所以很自然地支持固定功能着色器。它们的语法很简单,如果只是写一些简单的着色器,运行速度要比顶点/像素着色器要快。

例如:一个shader pass设置为alpha混合,输出纹理与颜色的乘积:

ass

{

Blend SrcAlpha OneMinusSrcAlpha

SetTexture [_MainTex] { constantColor[_Color] combine texture * contant }

}

与顶点+像素做完全相同的事情,产生的结果也相同

Pass

{

Blend SrcAlpha OneMinusSrcAlpha

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#include "UnityCG.cginc"

struct v2f

{

float2 uv : TEXCOORD0;

float4 pos : SV_POSITION;

};

float4 _MainTex_ST;

v2f vert (float4 pos : POSITION, float2 uv : TEXCOORD0)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, pos);

o.uv = TRANSFORM_TEX(uv, _MainTex);

return o;

}

sampler2D _MainTex;

fixed4 _Color;

fixed4 frag (v2f i) : SV_Target

{

return tex2D(_MainTex, i.uv) * _Color;

}

ENDCG

}

现在我们已经移除了对fixed function 固定功能GPU的支持,和在unity4.3(这2013年的时候)上的一些平台(OpenGL ES 1.1 on mobile and Direct3D 7 GPUs on Windows)。现在对写固定功能着色器没有什么技术需求了、没有必要了。除非:1.在现有项目中有很多这些着色器。2.只是想少打字。

事实上,固定功能着色器也有很多缺点:

1.    他们在主机上不工作(PS4,Xbox one,vita),在这些平台上实时生成着色器是非常困难的。

2.    它们不能和MaterialPropertyBlocks一起工作,不能用在unity 的Sprite的渲染上,也不能用在动画材质上。

3.    它们只适合做非常简单的事情,你做一个简单的固定功能着色器,之后却发现需要添加更多的功能,也没办法再做更多。

固定功能着色器在unity上是如何执行的?为什么?

大多数平台我们不支持 “固定功能渲染管线” ,它们内部转化为“actual shaders”才能用于渲染。只有一处固定功能着色器可以存在的例外是legacy desktop OpenGL (GL 1.x-2.x) 和 Direct3D 9.

到更多的平台出现,在OpenGL ES 2.0上我们实现了一个和D3D9差不多的东西,替代D3D9shader的二进制连接装配为连接GLSL片段。然后又更多的平台出现(D3D11, Flash, Metal);每个平台执行“固定功能”代码。代码不是特别复杂,问题很好理解并且我们有做足够的图形测试验证工作。

在这个过程中的每一步,没有人质疑为什么我们要继续做“为什么要实时生成?而不是离线的,在固定功能着色器导入的时候就把它转换好了呢”?(如果有人问这个问题,答案就是“这样做会很有意义,只是需要有人去做”一段时间。。。)”

在很久之前,离线转换固定功能着色器并不是很实用,因为有大量的可能的变体需要被支持。最棘手的部分是对纹理坐标的支持(发送uv到纹理阶段,可选的纹理变换矩阵,可选的纹理投影,和可选的texture coordinate generation纹理坐标生成)。但是嘿,我们在unity5移除了很多东西。它变得更简单了吗?是的。

在导入时将固定功能着色器转化为普通着色器

上图翻译:

在unity中的shader可以写“固定功能”形式的,例如:你可以只写“Light On“来得到逐顶点的光,在超级采样shader(键入内容少)中也很有用,及大量的shader是这样写出的。

现在(4.,5.0/5.1)这些固定功能着色器是在运行时处理的:

他们载入&解析到内部shaderlab表现。

无论何时需要新的“固定功能状态“,产生并使用一个新的“actual shader”来计算

完全依靠平台:D3D9,D3D11,OpenGL,Metal,GLES,PSM

在主机/DX12不能完全执行

在shader导入时“生成actual shader”与之相比要好很多,然后移除所有运行时代码。

好处:移除了很多代码!

好处:移除了在渲染循环的无用的一小部分,它只是由固定功能产生的。

好处:可以在主机,DX12,Vulkan等上面工作了

好处:跨平台行为更加一致(现在还是有细微的不同,如:在移动端的高光就是不同的;雾的工作也有些不同)

缺点:从一个脚本通过“new Material(string)”产生一个固定功能shader会停止工作

“new Material(string)”被5.1标记为过时的。

意味着向后兼容.破坏改变。意味着5.2应该把5.0/5.1的webplayer分开为一个分离的通道。

所以我打算这样做,移除了“固定功能着色器”所有运行时的代码;替换为只在Unity editor中导入shader是把它们转化为“普通着色器”。在wiki上创建一个数据&计划工作的概述,然后开始编程。我以为最终的结果会是“我新写了1000行代码移除了4000行”但是我错了!

一次我在做导入shader方面的基础的工作(结果,大约1000行代码),我开始移除整个固定功能部分。那是快乐的一天(如下图:)

大约一万两千行代码,消失了,真是惊人!

我完全不记得固定功能有那么多代码,你为了一个平台写它,然后它基本上工作了;然后一些新的平台出现又要写关于它的新的代码,然后它基本上工作了。之后又出现N各平台,所有代码加在一起的量是巨大的,因为它不是一下子就那么多代码,所以没人发现这个问题。

拿走:偶尔,看看整个子系统。你也许会震惊,在多年后它扩张了那么多。也许其中的一些东西因为某些原因已经不适用了。

旁注:在一个vertex shader的逐顶点光照

如果有一件事能在固定功能管线上变得简单,那就是很容易组合很多功能。你能使用很多个光(最多8个)他们是direction light,point light 或者spot light 。只是一个flag标志控制高光开闭,fog雾也一样。

感觉像是“特征的简单构成”当我们把所有都放到shader里时,我们失去一件重要的事情。我们知道的shader(vertex/fragment/… stages)一个都不能组合!想要添加一些可选的特征->这几乎意味着“加两倍shader”,或者分支shader,或者实时生成shader,这些方法都有利弊。

例如,你怎样写一个可以接近8个光源的顶点shader?有很多种方法,我现在正在做的是:

分离顶点着色器为“有spot light?”“有 point light?”“只有directional light”这些情况。我猜spot light 很少用在逐顶点固定功能光照上;它们看起来效果特别不好。所以在很多种情况,不会有“计算spot light”的消耗。

光源的数量作为整数被传入到shader中,并且shader在它们中循环。复杂:OpenGL ES 2.0 / WebGL,循环是你只能由常数次的循环次数:实践中发现,OpenGL ES 2.0有很多次都没有这个限制,然而webGL是肯定存在这个限制的。在此时我没有好的答案。在ES2/WebGL我只是循环遍历8个可能的光源(不使用的光源设置为黑色)。一个真实的解决方法,一个常规的循环是这样的:

uniform int lightCount;

// ...

for (int i = 0; i < lightCount; ++i)

{

// compute light #i

}

当在ES2.0/WebGL下编译时,我要这样编辑shader:(博主注:根据上文说的8个都循环一遍)

uniform int lightCount;

// ...

for (int i = 0; i < 8; ++i)

{

if (i == lightCount)

break;

// compute light #i

}

很讨厌处理像这样看似任意的限制(我听说webGL2 没有这样的限制,这真是太好了)

我们现在有什么

所以现在的情况是这样的,移除了大量的代码,我得到了下面的好处:

1.    “固定功能风格”shader可以在所有平台下运行了(主机!DX12!)

2.    他们跨平台工作更加稳定(例如:之前在PC&手机上高光和衰减有着细微的区别)

3.    “固定功能风格”shader可以使用MaterialPropertyBlocks工作了,意味着它可以渲染sprite,等等。

4.    固定功能着色器在windows phone 上不会再有光栅中怪异的半像素偏移了

5.    固定功能shader转换到actual shader变得更加简单;我在 shader inspector中加了一个按钮,可以显示所有生成的代码;你可以复制走并扩展它。

6.    代码变少了,转换成的可执行文件大小也变小了。例如:Windows 64 bit小了 300 kilobytes.

7.    渲染稍微变得快了些(即使没有使用固定功能着色器)

最后的一点并不是主要目的,但是是个不错的福利。没有特别大的影响,但是相当多的分支和数据从平台图形抽象中移除(只有在支持运行时固定功能的地方)。我根据项目试过了,在渲染线程中节省了5%的时间(如:10.2ms->9.6ms),效果非常好。

有什么缺点吗?是的,有几个:

1.    你不能再在运行时创建固定功能着色器了。之前你可以这样做var mat = new Material("<fixed function shader string>")除了在主机上都可以运行。由于这个原因,我做了Material(string)在unity5.1会标记为过时,而且发出警告,但是实际上它会停止工作。

2.    对web player的向后兼容产生破坏性的变化,如果代码在unity 5.2开发了,这意味着不能再在unity5.0/5.1上运行

3.    在几种特殊情况下可能不工作,例如:固定功能着色器使用一个全局设置纹理而不是一个2D纹理。在shader资源本身,任何关于那个纹理都不会被指定;所以在我在固定功能shader导入时生成actual shader,我不知道它是一个2D还是Cubemap 纹理。所以对于全局纹理,我只是假设他们都是2D纹理。

4.    大概就是这样!

移除实时固定功能的支持还有很多潜在的好处。在内部对于所有纹理的改变我们通过像“纹理类型(2D,cubemap等)”这样的东西-但是貌似只有固定功能管线会用到它。同样的,对于每个draw call我们通过一个vertex-declaration-like结构;但是现在我认为再也不需要它们了。

三篇结束。。。

        ------译自: wolf96 http://blog.csdn.net/wolf96

时间: 2024-10-14 00:59:27

Unity5内部渲染的优化3:移除固定功能的相关文章

Unity5内部渲染的优化2:清理

译自aras的博客,总共3篇文章,讲述unity5优化自己渲染器的过程 吸取大神调试与优化经验,了解unity5内部渲染器的优化方法 前篇:Unity5内部渲染的优化1:介绍 介绍过去后,让我们来进行实际工作 在以前的文章已经提到的,首先我尝试想起/找出现有代码,做一些分析并且写下突出的地方. 分析多个项目主要揭示了两件事: 1.    渲染代码使用多线程真的比使用我们现有的"一个主线程和一个渲染线程" 更广阔.这里有一个从unity5的timeline profiler的截屏: 在这

Unity5内部渲染的优化1:介绍

译自aras的博客,总共3篇文章,讲述unity5优化自己渲染器的过程 吸取大神调试与优化经验 在工作中我们形成一个小组"strike team"来优化unity渲染的cpu部分. 基本信息/父本的警告 在很多情况下,我要严厉的说"这段代码很烂!".当要努力改善代码的时候,你当然想提高不好的地方,这是重点. 一般来说并不意味着代码库是坏的,或者它不能用于做出好东西.就在今年3月,我们有Pillars of Eternity, Ori and the Blind Fo

android app性能优化大汇总(UI渲染性能优化)

UI性能测试 性能优化都需要有一个目标,UI的性能优化也是一样.你可能会觉得“我的app加载很快”很重要,但我们还需要了解终端用户的期望,是否可以去量化这些期望呢?我们可以从人机交互心理学的角度来考虑这个问题.研究表明,0-100毫秒以内的延迟对人来说是瞬时的,100-300毫秒则会感觉明显卡顿,300-1000毫秒会让用户觉得“手机卡死了”,超过1000ms就会让用户想去干别等事情了. 这是人类心理学最基础的理论,我们可以从这个角度去优化页面/view/app的加载时间. Ilya Grigo

react+redux渲染性能优化原理

大家都知道,react的一个痛点就是非父子关系的组件之间的通信,其官方文档对此也并不避讳: For communication between two components that don't have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount(), unsubscribe in componentWillU

浏览器渲染速度优化

前言: 要实现网站的大提速,必须在各个环节进行精确的设置和安排.网站一旦打开速度变慢,往常,站长们第一时间肯定会认为“服务器慢”,其实看完本章后,你会发现或许结果并不完全是这样.影响网站速度的因素千差万别,服务器仅是其中一小部分因素而已. 有一种常见的情况,同样的服务器,网站与网站之间的打开速度也千差万别,这就和网站的制作工艺有相当大的关系:本节重点讲一下网站制作工艺优化. 我们可以大致将影响网络速度的因素分为五个来进行分别优化: 一.服务器硬件配置和设置: 二.服务器的线路及带宽: 三.用户电

浏览器渲染性能优化

染性能 页面不仅要快速加载,而且要顺畅地运行:滚动应与手指的滑动一样快,并且动画和交互应如丝绸般顺滑. 60fps 与设备刷新率 目前大多数设备的屏幕刷新率为 60 次/秒.因此,如果在页面中有一个动画或渐变效果,或者用户正在滚动页面,那么浏览器渲染动画或页面的每一帧的速率也需要跟设备屏幕的刷新率保持一致. 其中每个帧的预算时间仅比 16 毫秒多一点 (1 秒/ 60 = 16.66 毫秒).但实际上,浏览器有整理工作要做,因此所有工作需要在 10 毫秒内完成.如果无法符合此预算,帧率将下降,并

游戏渲染性能优化

忘记了以前是不是写过类似的文章了,不管了,结合近几年经验总结一下. 先从最核心的部分开始.如果熟悉图形API的朋友都应该可以理解,要想渲染一个模型,我们至少需要做两个操作,设置各种状态,调用绘图函数.这两个操作都会引起性能损失.我们分头来说 先说设置各种状态.都什么状态呢,比如使用什么纹理,是用什么着色器,是否开启Alpha测试,是否双面显示,是否使用混合以及怎么混合,等等.我们可以管这些状态统称为材质.这些信息通过调用图形API,再通过驱动进而设置给显卡.这需要时间,而且有些状态改变很耗时.

cocos2d-x ios游戏开发初认识(六) 渲染的优化

做程序开发肯定要考虑到内存的优化,毕竟iphone本身的内存就不是非常大.这一节主要说这个cocos2d开发对内存的优化,详细表如今,既能够对同样的精灵(图片)仅仅渲染一次,也能够对不能的精灵仅仅渲染一次. 以下是程序的做法,先屏蔽前面的代码. 先简介一下 CCSpriteBatchNode类,它是用来优化游戏渲染效率的.CCSpriteBatchNode 中的全部CCSprite仅仅会被渲染1次,因此能够提高游戏的FPS.限制:增加到CCSpriteBatchNode 中的CCSprite必须

转:Ogre内部渲染流程

以下是 Ogre 的代码中的详细说明: Renderable是OGRE中所有可渲染对象的抽象接口 这个接口抽象出了在渲染管线中的被分组的离散的可渲染对象基本的方法. 此接口的实现类必须是基于单一的材质.单一的世界矩阵(或者是一组通过权重混合的世界矩阵),以及单一的渲染操作. 通过这个说明,应该能明确的是,Renderable 封装了3D世界中被渲染对象的基本属性和数据,这包括:渲染操作,材质属性,光照信息.变换矩阵(四元组).LOD信息.渲染方式等信息.这些信息在渲染循环中被取出,并应用在图形渲