开发
开发
选择平台

合成器图层

更新时间: 2025年3月11日
本指南解释了合成图层背后的技术细节,以及它们在 Quest 操作系统中的实现方式。要了解如何在您的应用程序中实现合成图层,请前往 Unity空间 SDKUnreal 或文档页面。
合成器图层是由合成器提供的一种工具,它允许您以合成器的帧率渲染更清晰、更易于查看的纹理。该帧率比应用程序的帧率更快,并且永远不会低于该帧率。通过使用合成器图层渲染文本和用户界面,即使使用较小的字体和用户界面元素,也能获得较高的清晰度。
合成图层可用于:
  • 文本、用户界面和视频。更清晰的纹理可以让您的应用程序使用更小的字体大小和用户界面元素,同时仍能让用户易于查看。(备注:您可以使用圆柱体形状的切片来创建大型、弯曲的用户界面。)
  • 凝视光标、十字线和其他纹理,旨在用作场景中的焦点对象。
  • 加载屏幕或其他预期应用性能较差的场景。加载屏幕中的合成图层元素看起来总是以全帧率更新,因为即使应用未能按时提交帧,合成器也会以帧率运行。
Comparison of a texture rendered with and without compositor layers

为什么合成图层更清晰

要了解合成图层如何在独立帧率下提供更清晰的渲染,就必须了解应用渲染到 VR 头戴设备的路径。

应用渲染

正如在合成器:帧失真中讨论的那样,Unity 和 Unreal 等游戏引擎将其应用程序场景的内容呈现成两个单独的、非失真的 eyebuffer 纹理。然后,合成器图层会让这些纹理失真,以抵消 VR 头戴设备上的镜头造成的失真。
在整个管道中,在虚拟世界中渲染的输入纹理会被采样两次:
  1. 对输入纹理进行采样,以便在虚拟世界场景中为 Eyebuffer 纹理进行渲染
  2. 对 Eyebuffer 纹理进行采样,将其渲染为失真的屏幕输出
Details of texture samples in an application-rendered pipeline
注意:每一轮纹理采样都会添加模糊伪影,因为源纹理上的纹素大小与输出纹理并不完全一致。尽管可以通过增加 Eyebuffer 的大小来减少模糊量,但额外的采样轮次始终会影响原始图像或文本的清晰度。

合成图层渲染

作为减少采样轮数的解决方案,我们允许游戏引擎向合成器提交额外的纹理,以便直接渲染到屏幕上,并进行预失真
这些额外的纹理不会由应用程序渲染。相反,合成器会使用特殊的顶点着色器来渲染选定的形状,该着色器在顶点着色器时间处理失真纠正。这导致输入纹理只需采样一次:从源纹理到失真的输出纹理。
这种方法存在一些限制:
  • 您只能从预定义的形状列表中进行选择,如四边形、圆柱体和立方体图,这些形状都有预失真的顶点着色器。您可以任意改变这些形状。
  • 您无法控制所使用的像素着色器。您的纹理在渲染时没有灯光或特效。不过,如果需要,您可以在纹理中烘焙灯光和特效(并按帧更改纹理)。
Details of texture samples in an application-rendered pipeline
该管道可为您的应用程序带来众多优势:
  • 由于跳过一层采样,纹理看起来更清晰。
  • 纹理是以合成器的帧率渲染的,而合成器的帧率总是大于或等于应用程序的帧率。
要了解更多关于如何在引擎中实现合成图层的信息,请参阅:

分析和调试合成图层

合成图层具有以下离散的性能成本:
  • 应用渲染纹理以绘制合成图层的成本
    由于这完全是在应用程序空间内完成的,因此大多数游戏引擎中内置的分析器工具可以为您提供有关生成纹理成本的详细信息。通过渲染到较小的纹理或仅每隔一帧渲染,可以降低成本。
  • 每层固定成本
    在 CPU/GPU 等级为 4 的 Meta Quest 2 上,每增加一个合成图层,成本大约需要 0.1 毫秒。注意,合成图层会使用 QUAD 形状将头部锁定的 FIXED_TO_VIEW 图层合并为一个图层。合并的图层没有额外的每层成本。
  • 根据该层触及的像素数量,每层额外增加的成本
    这一成本与该图层所覆盖的屏幕像素数量成正比。即使图层没有渲染到这些像素,这一成本仍然存在。例如,如果图层是一个底层,而上一层向该像素渲染了一个不透明对象,或者如果图层显示字幕,但此刻没有任何字幕。
    在 CPU/GPU 等级为 4 的 Meta Quest 2 上,全屏合成图层的成本大约需要 0.6 毫秒。
应用开发者通常会创建合成图层,这通常会为他们提供 0 阿尔法纹理,而不会破坏合成图层。请注意,在这种情况下,开发者需要继续支付渲染合成图层的费用。
为了确定合成图层的成本,并识别仍在渲染的“隐藏”合成图层,Meta 提供了以下工具:

Meta Quest 开发者中心

Meta Quest 开发者中心的“Performance Analyzer”选项卡包含切换各合成图层可见度的控制选项,还可显示包含在合成图层属性记录中的信息,而无需使用命令提示。
更多信息,请参阅图层可见度控制

VrApi 记录

正如 VrApi 统计定义指南中所述,通过在已连接 adb 实例的终端中运行以下命令,您可以获得 Quest 应用的详细实时性能统计信息:
adb logcat -s VrApi
VrApi 统计输出中的以下部分对剖析合成器图层的开发人员很有意义:
TW=2.80ms,App=1.11ms,GD=0.23ms,CPU&GPU=6.41ms,LCnt=5(DR14,LM2)
  • TW=# 列出了合成器花费的时间(该名称指的是 TimeWarp,合成器的职责之一)。叠加层的成本包含在此数字中。
  • LCnt=# 列出合成器正在合成的独立图层的数量。应用的帧缓冲区总是至少有一层。附加层可以来自您的应用,也可以来自其他服务(如通知)。
  • LM# 列出合并在一起的图层数量,以节省合成器时间。这个数字要么是 0(没有发生合并),要么是 2+(层数减少到 1 层)。

合成图层记录

如果 VrApi 记录报告的图层数出乎意料,您可以使用以下方法获取实时记录,列出每帧绘制的合成图层:
  • 在终端中,运行:adb shell setprop debug.oculus.logLayers 1
  • 启动应用,然后在应用中找到一个点,在该点上对每帧绘制的合成图层进行剖析。
  • 在终端中,运行:adb logcat -s CompositorClient
完成上述步骤后,每渲染一帧,您都会在终端中看到如下输出:
LogLayers: Client 12 (com.Sample.UnityAppSpaceWarp:11432)
  Layer 0:
    Type QUAD
    Flags CLIP_TO_TEXTURE_RECT DIRECT_PROJECTION VIEW_FRUSTUM_CULLING
    Quad translation (0.4137005, -0.091204815, -0.9783828)
    Quad rotation (-0.997699, -9.291795e-10, 0.06780008, -6.314374e-11)
    Quad anchor translation (0, 0, 0)
    Quad anchor rotation (1, 0, 0, 0)
    Quad anchor id 0 version 0
    Quad size (0.4, 0.40000007)
    Corners not rounded
    Corner Rect size (0, 0)
    Corner Rect offset (0, 0)
    ApertureId 134bc4800000000c
    Placement 80
    ColorScale (1, 1, 1, 1)
    ColorOffset (0, 0, 0, 0)
    Src Blend Color BLEND_SRC_ALPHA
    Dst Blend Color BLEND_ONE_MINUS_SRC_ALPHA
    Src Blend Alpha BLEND_SRC_ALPHA
    Dst Blend Alpha BLEND_ONE_MINUS_SRC_ALPHA
    SwapChain[0] : TEXTURE2D : 333 : image 0x7024a248f8 (500 x 500) array index 0
    TextureRect[0] : (0.002, 0.002, 0.996, 0.996)
    SwapChain[1] : TEXTURE2D : 333 : image 0x7024a248f8 (500 x 500) array index 0
    TextureRect[1] : (0.002, 0.002, 0.996, 0.996)
  Layer 1:
    Type PROJECTION
    Flags CLIP_TO_TEXTURE_RECT APP_SPACE_WARP PREMULTIPLIED_WITH_VALID_ALPHA
    Content UNKNOWN id 0
    fov[0] (l:-54, r:40, b:-55, t:44)
    fov[1] (l:-40, r:54, b:-55, t:44)
    ApertureId 134bc4800000000c
    Placement 80
    ColorScale (1, 1, 1, 1)
    ColorOffset (0, 0, 0, 0)
    Src Blend Color BLEND_ONE
    Dst Blend Color BLEND_ONE_MINUS_SRC_ALPHA
    Src Blend Alpha BLEND_ONE
    Dst Blend Alpha BLEND_ONE_MINUS_SRC_ALPHA
    SwapChain[0] : TEXTURE2D_ARRAY : 309 : image 0x7024a24a48 (2016 x 1760) array index 0
    TextureRect[0] : (0, 0, 0.80505955, 1)
    SwapChain[1] : TEXTURE2D_ARRAY : 309 : image 0x7024a24a48 (2016 x 1760) array index 1
    TextureRect[1] : (0.19494048, 0, 0.80505955, 1)
LogLayers: Client 9 (com.oculus.ovrmonitormetricsservice:8465)
  Layer 0:
    Type QUAD
    Flags FIXED_TO_VIEW DISABLE_ALPHA_WRITE NO_AR_CLIP
    Quad translation (-1.4901161e-08, 0.34202012, -0.9396927)
    Quad rotation (1, 0, 0, 0)
    Quad anchor translation (0, 0, 0)
    Quad anchor rotation (1, 0, 0, 0)
    Quad anchor id 0 version 0
    Quad size (0.279375, 0.120000005)
    Corners not rounded
    Corner Rect size (0, 0)
    Corner Rect offset (0, 0)
    ApertureId a469a2e400000009
    Placement ffffffff
    ColorScale (1, 1, 1, 1)
    ColorOffset (0, 0, 0, 0)
    Src Blend Color BLEND_ONE
    Dst Blend Color BLEND_ONE_MINUS_SRC_ALPHA
    Src Blend Alpha BLEND_ONE
    Dst Blend Alpha BLEND_ONE_MINUS_SRC_ALPHA
    SwapChain[0] : TEXTURE2D : 241 : image 0x6f833cd4f8 (298 x 128) array index 0
    TextureRect[0] : (0, 0, 1, 1)
    SwapChain[1] : TEXTURE2D : 241 : image 0x6f833cd4f8 (298 x 128) array index 0
    TextureRect[1] : (0, 0, 1, 1)
输出结果会按照从前到后的绘制顺序列出叠加图层。这个示例输出表明:
  • 首先,com.Sample.UnityAppSpaceWarp 将渲染一个 500x500 的四边形。它对应的是场景中放置的一个 OVROverlayCanvas 元素,Max Texture Size 为 500。
  • 然后,com.Sample.UnityAppSpaceWarp 将渲染一个 2016x1760 eyebuffer (PROJECTION)。这是主图层,上面会显示您的应用主渲染通道。该图层也将使用应用空间翘曲专用路径 (APP_SPACE_WARP) 进行处理。
  • 最后,com.oculus.ovrmonitormetricsservice 将渲染一个 298 x 128 头部锁定 (FIXED_TO_VIEW) 纹理到一个四边形上。(这是 Metrics HUD)。

合成器图层属性记录

如果输入纹理的分辨率较低,合成器叠加仍然会显得模糊。您可以通过以下方法获取合成图层纹理分辨率的实时信息和更改建议:
  • 在已连接 adb 实例的终端中,运行:adb shell setprop debug.oculus.sysPropDebug 1
  • 轻触两次头戴设备的电源按钮,让头戴设备进入睡眠模式并重新唤醒它。这将重启部分服务,也是让此前属性生效的必要条件。
  • 运行:adb shell setprop debug.oculus.layerProperties 1
  • 启动应用,然后在应用中找到一个点,在该点上对每帧绘制的合成图层进行剖析。
  • 运行:adb logcat -s CompositorVR
完成上述步骤后,每隔一秒钟,您就会在终端中看到如下输出:
CLP: Eye:Right LayerType:Quad PanelVisiblity:Visible DevicePPD:20.67 LayerRenderedPPD:23.71 Texture Resolution:512.00x128.00 Recommended Texture Resolution:446.43x111.61 Recommend Apply:None
CLP: Eye:Left LayerType:Quad PanelVisiblity:Visible DevicePPD:20.67 LayerRenderedPPD:24.81 Texture Resolution:512.00x128.00 Recommended Texture Resolution:426.48x106.62 Recommend Apply:None
CLP: Eye:Right LayerType:Quad PanelVisiblity:Visible DevicePPD:20.67 LayerRenderedPPD:10.97 Texture Resolution:300.00x300.00 Recommended Texture Resolution:565.27x565.27 Recommend Apply:Sharpening
CLP: Eye:Left LayerType:Quad PanelVisiblity:Visible DevicePPD:20.67 LayerRenderedPPD:10.89 Texture Resolution:300.00x300.00 Recommended Texture Resolution:569.17x569.17 Recommend Apply:Sharpening
这个输出表明......
  • CLP:合成图层属性。这是一个任意字符串,用于在输出中搜索。
  • EyeLeft 或者 Right 的其中一个。表示输出时哪个 Eyebuffer 正在渲染。
  • DevicePPD:VR 头戴设备每度像素。Meta Quest 2 的 PPD 值为 20.67,表明其 1832x1920 每眼显示屏的 FOv 约为 89x93。
  • LayerRenderedPPD:合成图层的每度像素(该合成图层的输入纹理),如虚拟世界的渲染效果。
  • Texture Resolution:合成图层输入纹理的分辨率
  • Recommended Texture Resolution:合成图层输入纹理的理想分辨率。在此分辨率下,以当前形状和变换渲染合成图层时,输入纹理中的每个像素正好对应 VR 头戴设备中的一个渲染像素。
  • Recommend Apply:要么是 Sharpening,或 SuperSampling,要么是 None。关于这些算法的详细信息,请参阅合成图层过滤。虽然锐化和超采样可以分别改善过小和过大纹理的视觉质量,但如果可能的话,最好还是提供建议分辨率的输入纹理。

合成图层叠加

您可以通过以下命令激活一个内置叠加层,以便直观显示每个合成图层的实时位置。(这可以作为 Meta Quest 开发者中心的自定义命令,以便进行一键应用。)
adb shell setprop debug.oculus.sysPropDebug 1 && adb shell input keyevent "KEYCODE_POWER" && adb shell input keyevent "KEYCODE_POWER" && adb shell setprop debug.oculus.visualizeLayers 1
命令行为
setprop debug.oculus.sysPropDebug 1
为系统工具启用调试路径。
input keyevent "KEYCODE_POWER"
与按下您的 Meta Quest 头戴设备上的电源按钮效果相同。重复此操作做会重新唤醒头戴设备,使操作系统能够配置新的缓冲区来显示覆盖层。
setprop debug.oculus.visualizeLayers 1
启用一个系统叠加层,该图层会使用对比鲜明的颜色来渲染合成图层。
完成上述步骤后,您将在头戴设备中看到如下彩色叠加图: Colored Compositor layer overlays
在渲染到应用上的半透明度叠加层中,每个合成图层都会用不同的颜色标记其边界。本示例图片显示了一个包含 3 个图层的应用:橙色主图层、绿色底层和浅蓝色叠加层。每层使用的颜色是任意的,并不代表任何含义。
该工具最适合用于识别不常用的大型合成图层(如当前未显示的字幕或菜单),而这些图层可能会造成严重的性能损失。
当您完成调试后,运行以下命令来停止渲染运动矢量缓冲区。(也可在 Meta Quest 开发者中心将其设置为一个自定义命令。)
adb shell setprop debug.oculus.sysPropDebug 0 && adb shell setprop debug.oculus.visualizeLayers 0 && adb shell input keyevent "KEYCODE_POWER" && adb shell input keyevent "KEYCODE_POWER"