渲染

本指南描述了不同渲染模式的工作原理,并探讨了它们的局限性。

概述 

Fuzio 在 BrowserView 中渲染 Web 内容的方式由渲染模式决定。Fuzio 提供两种渲染模式:HARDWARE_ACCELERATED(硬件加速)和 OFF_SCREEN(离屏)。

你需要在创建 Engine 实例时指定渲染模式,并且之后无法再更改。在同一个 Engine 中创建的所有浏览器都会使用相同的渲染模式。

Java
Kotlin
import static tech.fuzio.engine.RenderingMode.HARDWARE_ACCELERATED;
...
var engine = Engine.newInstance(HARDWARE_ACCELERATED);
import tech.fuzio.engine.RenderingMode.HARDWARE_ACCELERATED
...
val engine = Engine(HARDWARE_ACCELERATED)

选择合适的渲染模式会影响:

  • 渲染性能,
  • 整体资源消耗,
  • 以及 Fuzio 处理用户输入的方式。

当 GPU 可用时,Chromium 会在两种渲染模式下都使用硬件加速。当 GPU 不可用时,Chromium 会切换为基于 CPU 的软件渲染 —— 同样适用于两种渲染模式。

硬件加速模式 

在该渲染模式下,Chromium 会将 Web 内容直接渲染到一个特殊的渲染表面上,该表面由库放置在 BrowserView之上。该表面的类型取决于操作系统。

HARDWARE_ACCELERATED 渲染模式的架构

HARDWARE_ACCELERATED 渲染模式的架构。

在 Windows 和 Linux 上,Fuzio 会将一个原生 Chromium 窗口嵌入到 Java 应用中。这样 Chromium 就可以在自己的窗口中渲染内容,性能与独立浏览器相同。这个嵌入窗口对最终用户来说是“不可察觉”的:它没有 Chromium 的 UI 元素、没有任务栏图标,也不具备普通窗口的其他特征。

渲染模式之间没有视觉差异

嵌入窗口看起来与其他 UI 组件无异。

在 macOS 上,Fuzio 创建一个原生的 CARemoteLayer 表面,并在 Java 进程与 Chromium 进程之间共享。该表面会被嵌入到 Java 窗口中,Chromium 直接将内容渲染到该表面上。

在 macOS 上需要采用不同的实现方式,因为该操作系统只允许在同一进程内对原生窗口进行重新挂载(re-parent)。由于 Fuzio 会在单独的进程中启动 Chromium,因此 Chromium 窗口无法像在 Windows 和 Linux 上那样附加到 Java 窗口层级结构中。

输入处理 

在 Windows 和 Linux 上,输入由 Chromium 处理。由于用户直接与 Chromium 窗口交互,操作系统将键盘、无障碍功能和其他输入事件发送到那里,绕过了 Java 窗口。这确保了与 Google Chrome 完全相同的输入反应。

在 macOS 上,输入由 Java UI 工具包处理。详情请参见离屏模式中的“输入处理”部分。

你可以在 Java 代码中拦截鼠标、键盘和触摸事件。请参阅事件拦截指南了解如何实现。

局限性 

与其他 UI 组件的重叠问题 

无论在哪种操作系统上,用于渲染 Web 内容的表面始终位于 Java 窗口之上。因此,普通的 UI 组件无法显示在 BrowserView 之上,因为它们的区域会被渲染表面遮挡。

特别是在 Swing 中将 BrowserView 放入 JInternalFrameJLayeredPane,或在 JavaFX 中放入 StackPane 时,应避免使用 HARDWARE_ACCELERATED 渲染模式。

Fuzio overlapped by other components

两个 BrowserView 组件——处于不同模式——与 JPanel 重叠。

JavaFX 中的透明窗口 

在 Windows 上的 JavaFX 中,HARDWARE_ACCELERATED 渲染模式与 StageStyle.TRANSPARENT 样式不兼容。要显示透明的 Stage,JavaFX 会使用重叠窗口,但这种方式无法与嵌入的 Chromium 窗口一起工作。

离屏模式 

在该渲染模式下,Chromium 会使用 GPU 渲染内容,然后库会将像素复制到 Java 进程内存中的离屏缓冲区BrowserView 作为一个轻量级组件,从该缓冲区读取像素,并使用标准 UI 工具包的绘图接口(例如 Swing 中的 Graphics2D)进行显示。

OFF_SCREEN 渲染模式的架构

OFF_SCREEN 渲染模式的架构。

输入处理 

默认情况下,输入事件由 Java 窗口处理。当发生输入事件时,操作系统会将事件分发给 Java UI 工具包,随后传递给 BrowserView,再由 BrowserView 转发给 Chromium,最终送达网页。

局限性 

原生输入处理 

在事件处理的每一个阶段,事件都会从一种数据结构转换为另一种。由于不同子系统中的数据结构并不能完全匹配,部分数据可能会丢失或被误解释。这会导致在极少数情况下,Fuzio 中的用户交互产生的 JavaScript 事件与 Chromium 中相同交互产生的事件不一致。

我们引入了一个实验性功能,可以将输入事件直接从操作系统转发给 Chromium。这样一来,Fuzio 中生成的 JavaScript 事件将始终与 Chromium 中保持一致。

要启用该功能,请使用以下系统属性:

System.setProperty("fuzio.native.input.enabled", "true");

触摸事件 

Swing、JavaFX、SWT 和 Compose 对触摸的支持并不完整。因此,一些 Chromium 的手势(例如双指缩放)在 Fuzio 中无法使用 —— 在 Windows 和 Linux 的 OFF_SCREEN 模式下,以及在 macOS 的两种模式下都是如此。

性能 

HARDWARE_ACCELERATED 模式下,GPU 的使用情况会直接影响渲染性能,因为 Chromium 是直接将内容渲染到表面上的。该模式提供与独立版 Google Chrome 相同的渲染性能。在大多数平台上,它可以以 60 FPS 播放 4K 视频。

OFF_SCREEN 模式下,使用 GPU 可以改善整体资源消耗,但对渲染性能的影响不大。在该模式下,性能瓶颈在于跨进程复制像素,这是一个受 CPU 限制的操作。该模式在大多数情况下都能提供良好的渲染性能。对于对渲染性能要求极高的应用,建议使用 HARDWARE_ACCELERATED 模式。

无头模式 

当你不需要在 UI 中显示浏览器时,建议使用 HARDWARE_ACCELERATED 渲染模式。原因是即使浏览器没有显示,OFF_SCREEN 模式也可能会消耗额外的 CPU 和内存。

无论使用哪种渲染模式,Fuzio 在 BrowserView 之外都能保持完整功能。例如,你可以在任意渲染模式下对一个独立的 Browser 进行截图

Java
Kotlin
var engine = Engine.newInstance(HARDWARE_ACCELERATED);
var browser = engine.newBrowser();

browser.resize(1920, 1080);
browser.navigation().loadUrlAndWait("https://html5test.jiku.co");

// 获取网页的位图,尺寸为 1920x1080。
var bitmap = browser.bitmap();
import tech.fuzio.dsl.Engine
...
val engine = Engine(HARDWARE_ACCELERATED)
val browser = engine.newBrowser()

browser.resize(1920, 1080)
browser.navigation().loadUrlAndWait("https://html5test.jiku.co")

// 获取网页的位图,尺寸为 1920x1080。
val bitmap = browser.bitmap()
微信咨询

即库客服

微信公众号二维码

技术客服

微信公众号二维码