最近一直在思考如何能更好的做优化渲染,本篇文章只是另一种实现的思路,其实我也没完全想好怎么应用到实际游戏中来统计,希望各位看官多多提宝贵意见。 1.本例Unity的版本是Unity2019.3.
.
最近一直在思考如何能更好的做优化渲染,本篇文章只是另一种实现的思路,其实我也没完全想好怎么应用到实际游戏中来统计,希望各位看官多多提宝贵意见。
1.本例Unity的版本是Unity2019.3.1.4
2.采用URP渲染管线,老的渲染管线没有试过,大家可以试试看。
3.FrameDebugger会将每一这数据存入RT中,名字对应如下。
实际代码中就可以这样取到它的Texture了
Texture texture = Shader.GetGlobalTexture("_CameraColorTexture");
if (!texture)
{
texture = Shader.GetGlobalTexture("_CameraOpaqueTexture");
}
并非所有都能取,比如shadowmap的RT,这名字是没有意义的,如果真想取,那就用C#反射吧。但其实有上面两个基本已经够用了。
4.为了让代码更快的比较两帧图片的颜色,我采用了Burst编译比暴力的for循环快的可不是一点点。
5.为了让工具更加方便,不得不在代码中做了很多反射FrameDebugger的代码。
工具使用之前,大家可以先用FrameDebugger看一下自己需要截那些帧的数据。接着运行Unity,填入开始和结束帧的索引后,点击开始截取按钮即可。
截取完毕后,左边会保存每帧截取的图片,最后还会生成一张第1张和最后一张的中像素变化的图片(红色的区域就表示变化)还会输出最终像素数,重复像素数,总渲染顶点数。
在回到优化上来
1.重复像素数越多,其实就是半透明overdraw比较多。
2.最终像素数表示,光栅化后mesh最终呈现的颜色数。
3.总渲染顶点数,这个数值就很重要的。比如模型参与渲染了好几万个顶点,然而结果最终只贡献给屏幕20个像素,那这显然就不太合理了。
一些问题
1.如果摄像机会移动,那么就会造成有时候离模型近,有时候离的远,这样统计就不准确的。
2.如果使用RenderDoc能看到的信息会更多,我也比较推荐用Renderdoc,这篇文章只是开放一下思路。
上代码
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Unity.EditorCoroutines.Editor;
using Unity.Collections;
using Unity.Burst;
using Unity.Jobs;
using Unity.Mathematics;
public class FrameDebugExamplle : EditorWindow
{
static Type s_frameDebugType = Type.GetType("UnityEditorInternal.FrameDebuggerUtility,UnityEditor");
static Type s_frameDebugWindowsType = Type.GetType("UnityEditor.FrameDebuggerWindow,UnityEditor");
static bool s_HasLatSample;
static NativeArray<Color> s_LastSample2DColor;
static NativeArray<Color> s_FirstSample2DColor;
static int s_FinalPixel = 0;
static int s_ProcessPixel = 0;
static int s_VertexCount = 0;
static int s_StartDC;
static int s_EndDC;
static string s_Result;
static string DirectoryPath = "Assets/采样";
static EditorWindow s_FrameDebugWindows;
[MenuItem("Example/开始")]
public static void ShowWindow()
{
OpenAndEnableFrameDebugger();
}
void OnGUI()
{
int count = (int)s_frameDebugType.GetProperty("count", BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static).GetValue(null);
if (count == 0)
{
OpenAndEnableFrameDebugger();
}
GUILayout.Label(string.Format($"DC区间 0 - {count}"));
s_StartDC = Mathf.Clamp(EditorGUILayout.IntField("开始", s_StartDC),0,count - 1);
s_EndDC = Mathf.Max(Mathf.Clamp(EditorGUILayout.IntField("结束", s_EndDC),0,count), s_StartDC);
if (GUILayout.Button($"开始截取: {s_StartDC}DC-{s_EndDC}DC", GUILayout.Width(200), GUILayout.Height(50)))
{
OpenAndEnableFrameDebugger();
EditorCoroutineUtility.StartCoroutineOwnerless(StartGetData());
}
GUILayout.Label(s_Result);
}
IEnumerator WaitFive(int count)
{
for (int i = 0; i < count; i++)
{
yield return null;
}
}
IEnumerator StartGetData()
{
s_VertexCount = 0;
s_ProcessPixel = 0;
s_FinalPixel = 0;
s_Result = string.Empty;
s_HasLatSample = false;
FileUtil.DeleteFileOrDirectory(DirectoryPath);
Directory.CreateDirectory(DirectoryPath);
for (int i = s_StartDC; i <= s_EndDC; i++)
{
s_frameDebugType.GetProperty("limit", BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static).SetValue(null, i);
yield return WaitFive(1);
RefreshFrameDebuggerWindows();
yield return WaitFive(1);
Texture2D textureSample = TextureSample();
Color[] colorBuffer = textureSample.GetPixels();
if (textureSample)
{
File.WriteAllBytes($"{DirectoryPath}/开始{i}.jpg", textureSample.EncodeToJPG());
if (!s_FirstSample2DColor.IsCreated)
{
s_FirstSample2DColor = new NativeArray<Color>(colorBuffer, Allocator.Persistent);
}
if (s_HasLatSample)
{
var Job = new JobDiff
{
result = new NativeArray<int>(1, Allocator.TempJob),
current = new NativeArray<Color>(colorBuffer, Allocator.TempJob),
last = s_LastSample2DColor,
};
Job.Schedule(s_LastSample2DColor.Length, new JobHandle()).Complete();
s_ProcessPixel += Job.result[0];
Job.current.Dispose();
Job.result.Dispose();
s_LastSample2DColor.Dispose();
EditorWindow windows = EditorWindow.GetWindow(s_frameDebugWindowsType);
FieldInfo info = windows.GetType().GetField("m_CurEventData", BindingFlags.Instance | BindingFlags.NonPublic);
object FrameDebuggerEventData = info.GetValue(windows);
if (FrameDebuggerEventData != null)
{
s_VertexCount += (int)FrameDebuggerEventData.GetType().GetField("vertexCount", BindingFlags.Instance | BindingFlags.Public).GetValue(FrameDebuggerEventData);
}
}
if (i != s_EndDC)
{
s_HasLatSample = true;
s_LastSample2DColor = new NativeArray<Color>(colorBuffer, Allocator.Persistent);
}
else
{
Texture2D diff = textureSample;
var Job = new JobFinalDiff
{
result = new NativeArray<int>(1, Allocator.TempJob),
first = s_FirstSample2DColor,
end = new NativeArray<Color>(colorBuffer, Allocator.TempJob),
};
Job.Schedule(s_FirstSample2DColor.Length, new JobHandle()).Complete();
s_FinalPixel = Job.result[0];
diff.SetPixels(Job.end.ToArray());
Job.end.Dispose();
Job.result.Dispose();
s_FirstSample2DColor.Dispose();
File.WriteAllBytes($"{DirectoryPath}/变化.jpg", diff.EncodeToJPG());
}
}
}
int pixelSqrt = (int)Mathf.Sqrt((s_ProcessPixel - s_FinalPixel));
int finalPixelSqrt = (int)Mathf.Sqrt(s_FinalPixel);
s_Result = $"最终像素 {finalPixelSqrt} X {finalPixelSqrt} 重复像素 {pixelSqrt} x {pixelSqrt} 总渲染顶点数 {s_VertexCount}";
AssetDatabase.Refresh();
}
static Texture2D TextureSample()
{
try
{
Texture texture = Shader.GetGlobalTexture("_CameraColorTexture");
if (!texture)
{
texture = Shader.GetGlobalTexture("_CameraOpaqueTexture");
}
if (!texture)
{
Debug.LogError("没有截到图输出错误");
}
if (texture)
{
var width = texture.width;
var height = texture.height;
RenderTexture previous = RenderTexture.active;
RenderTexture tmp = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.sRGB);
Graphics.Blit(texture, tmp);
RenderTexture.active = tmp;
Texture2D @new = new Texture2D(width, height);
@new.ReadPixels(new Rect(0, 0, width, height), 0, 0);
@new.Apply();
RenderTexture.active = previous;
return @new;
}
}
catch (Exception e)
{
Debug.LogError("没有截到图输出错误: " + e);
}
return null;
}
static void OpenAndEnableFrameDebugger()
{
EditorWindow.GetWindow(typeof(FrameDebugExamplle), true, "标题", true);
s_FrameDebugWindows = EditorWindow.GetWindow(s_frameDebugWindowsType);
s_frameDebugWindowsType.GetMethod("EnableIfNeeded", BindingFlags.Instance | BindingFlags.Public).Invoke(s_FrameDebugWindows, null);
}
static void RefreshFrameDebuggerWindows()
{
Type windowsType = Type.GetType("UnityEditor.FrameDebuggerWindow,UnityEditor");
var windows = EditorWindow.GetWindow(windowsType);
windowsType.GetMethod("RepaintOnLimitChange", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(windows, null);
}
[BurstCompile]
struct JobDiff : IJobFor
{
[ReadOnly] public NativeArray<Color> current;
[ReadOnly] public NativeArray<Color> last;
public NativeArray<int> result;
public void Execute(int index)
{
result[0] += math.select(0, 1, current[index] != last[index]);
}
}
[BurstCompile]
struct JobFinalDiff : IJobFor
{
[ReadOnly] public NativeArray<Color> first;
public NativeArray<Color> end;
public NativeArray<int> result;
public void Execute(int index)
{
if(end[index] != first[index])
{
end[index] = Color.red;
result[0]++;
}
}
}
}
往期精选
Unity3D游戏开发中100+效果的实现和源码大全 - 收藏起来肯定用得着
Shader学习应该如何切入?
喵的Unity游戏开发之路 - 从入门到精通的学习线路和全教程
声明:发布此文是出于传递更多知识以供交流学习之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与我们联系,我们将及时更正、删除,谢谢。
作者:雨松MOMO
原文:https://www.xuanyusong.com/archives/4745
More:【微信公众号】 u3dnotes
本文分享自微信公众号 - Unity3D游戏开发精华教程干货(u3dnotes)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。