ARKit系列文章目录html
学习ARKit前,须要先学习SceneKit,参考SceneKit系列文章目录c++
界面与模型调试仍然是使用View Debuger,进入调试状态后可看到图层状态 数组
点击3D图层,会进入3D模型编辑器,显示的是实时状态的模型 缓存
右侧能够调整材质等信息,点击右下角的按钮,能够查看Shader和Action,选中能够查看咱们使用的Shader,再点击问号可查看参数输入: 网络
性能调试分为实时查看(用Gauge),与录制分析(用Instruments)app
FPS Gauge不只是实时的,还能显示负载的分类 框架
通常先使用SceneKit
工具分析,能够精确到每帧,但须要先录制一段;分析问题找到缘由后再处理,或使用Metal System Trace
再分析Metal程序 机器学习
例如,苹果官方的例子中,先用SceneKit工具分析出问题出在Metal代码上,再用 Metal System Trace
分析出问题出在Shader编译太慢上.对应的解决方法是:提早编译Shader. 编辑器
本部份内容来自于WWDC2017中的Metal 2 Optimization and Debuggingide
示例程序的代码苹果貌似没有所有提供,反正我在苹果文档库中只找到了一个示例MetalDeferredLighting.
其实就是对原来的GPU Frame Debugger的加强,使用方法仍是和原来同样,在运行中点击下方工具栏中的照相机图标捕捉Frame,如今长按也能够弹出菜单显示更多内容了.
原有功能:
新增了一些功能:
下面让咱们结合一个Demo来演示新增功能的使用. 首先,运行一下,这是一个有问题的程序,注意雪花附近出现了异常
接着开始调试,先看纹理自己有没有异常,在对应位置打断点,就能够直接预览GPU上的纹理,很是方便:
Rendering
,进入了
Metal Frame Debuger中:
要找到绘制雪花颗粒(particle
)的地方,能够在左下角使用高级搜索,添加条件Forward Render
和Particle
,搜索结果中只有一个API知足要求,点击这个搜索结果,显示详情:
看看顶点数据是否是有问题,双击Vertex Attributes
查看顶点的输入输出数据
数据看起来没有肉眼可见的异常,估计应该是正常的.再找别的缘由. 先回到左侧导航栏搜索结果中,展开当前API调用涉及的全部资源,选中Attachments
,打开右下角的像素检查器:
两张图:左边是渲染目标的色彩图,右侧是渲染目标的深度图,移动圆形的像素检查器Pixel Inspector
来检查像素级的问题.
通过查找,咱们发现右侧的深度图上,雪花边缘附近的深度值不一致,这就是bug所在,正常状况下particle
不该该写入深度值到深度缓冲器depth buffer
中.
修复这个bug便可,此处略过...
这是集成在Metal Frame Debugger
中的Shader分析利器,能够分析出编译后的Shader哪里有性能问题.例如:
这个工具也是集成在Metal Frame Debugger
中的Shader分析利器.功能以下:
Per-shader metrics:每一个着色器节奏 编译器产生的统计数据:
Compiler remarks :编译器评价 能避免如下状况出现:
仍是经过一个Demo来演示
打开项目运行,点击相机图标,进入帧调试器,切换显示方式,找到Pipeline Statistics
界面中:
中间的上方显示出编译器给出的优化建议,咱们先处理和栈相关的第2个和第4个,双击进入shader中:
部分代码以下,发现其中的可变数组会形成影响:
//问题代码
float3 v = in.v_view * (scene_z / in.v_view.z);
// Now, we have everything we need to calculate our view-space lighting vectors.
FragOutput output;
output.light = float4(0);
output.albedo = gBuffers.albedo;
output.normal = gBuffers.normal;
output.depth = gBuffers.depth;
float4 lightingFinal[FAIRY_GROUP_SIZE];
for(int i = 0; i < FAIRY_GROUP_SIZE; i++) {
lightingFinal[i] = float4(0);
float3 l = (lightData->view_light_position.xyz - v);
float n_ls = dot(n, n);
float v_ls = dot(v, v);
float l_ls = dot(l, l);
float3 h = (l * rsqrt(l_ls / v_ls) - v);
float h_ls = dot(h, h);
float nl = dot(n, l) * rsqrt(n_ls * l_ls);
float nh = dot(n, h) * rsqrt(n_ls * h_ls);
float d_atten = sqrt(l_ls);
float atten = fmax(1.0 - d_atten / lightData->light_color_radius.w, 0.0);
float diffuse = fmax(nl, 0.0) * atten;
float4 light = gBuffers.light;
light.rgb += lightData->light_color_radius.xyz * diffuse;
light.a += pow(fmax(nh, 0.0), 32.0) * step(0.0, nl) * atten * 1.0001;
lightingFinal[i] = light / FAIRY_GROUP_SIZE;
}
for(int i = 0; i < FAIRY_GROUP_SIZE; i++) {
output.light += lightingFinal[i];
}
return output;
复制代码
修改代码,移除数组相关代码,直接计算光线最终值:
//改后代码,移除lightingFinal数组相关代码
float3 v = in.v_view * (scene_z / in.v_view.z);
// Now, we have everything we need to calculate our view-space lighting vectors.
FragOutput output;
output.light = float4(0);
output.albedo = gBuffers.albedo;
output.normal = gBuffers.normal;
output.depth = gBuffers.depth;
for(int i = 0; i < FAIRY_GROUP_SIZE; i++) {
float3 l = (lightData->view_light_position.xyz - v);
float n_ls = dot(n, n);
float v_ls = dot(v, v);
float l_ls = dot(l, l);
float3 h = (l * rsqrt(l_ls / v_ls) - v);
float h_ls = dot(h, h);
float nl = dot(n, l) * rsqrt(n_ls * l_ls);
float nh = dot(n, h) * rsqrt(n_ls * h_ls);
float d_atten = sqrt(l_ls);
float atten = fmax(1.0 - d_atten / lightData->light_color_radius.w, 0.0);
float diffuse = fmax(nl, 0.0) * atten;
float4 light = gBuffers.light;
light.rgb += lightData->light_color_radius.xyz * diffuse;
light.a += pow(fmax(nh, 0.0), 32.0) * step(0.0, nl) * atten * 1.0001;
output.light += light / FAIRY_GROUP_SIZE;
}
return output;
复制代码
点击按钮,从新加载Shader,各项数值已减少:
这个工具也是集成在Metal Frame Debugger
中的,但并不针对于Shader.功能有图形化列表展现和瓶颈分析:
仍是分析一个Demo,运行捕捉,进入帧调试器,选中GPU
展现GPU Counter Profiling
界面:
先看左侧图形化列表,双指放大,选中启动时的最高峰:
咱们发现问题出在Fragment Shader Time
和Pixels Stored
上面,先展开第一个Fragment Shader Time
进行分析:
发现FS Stall Time
很高,说明等待的时间很是长,这通常是因为从内存中读取图片或数据形成的.向下滚动查看纹理缓存的状况:
看到Texture Cache Miss Rate
很高,也就说明纹理命中率只有不到40%,因此须要不断从内存中读取纹理,形成性能问题.这也解释了为何前面Pixels Stored
也很高.
具体哪里出现了问题,还须要看右侧的瓶颈分析数据表:
提示纹理可能没有mipmaps,点击右侧箭头:
原来是加载了一个256M的高度地图,形成了缓存被大量占用,缓存命中率低,不断从内存读取图片,GPU不断等待.
缘由找到!!
Metal相关的调试技巧也适用于苹果的机器学习框架(基于Metal)中.学习相关技巧,受益不少.
因为水平所限,本文第二部分的高级调试基本是照搬苹果WWDC2017的演讲,具体在本身的项目中使用时,由于不一样平台(macOS/iOS/tvOS),不一样技术(SceneKit/SpriteKit,Metal/OpenGLES)仍是会有一些不一样的限制. 固然,最大限制仍是在于本身是否足够了解图形学的相关知识,对此我深感力不从心,须要学习更多相关基础.