1 个不稳定版本
0.1.0 | 2023 年 8 月 4 日 |
---|
#726 在 图形 API 中
125KB
2K SLoC
theo
为所有窗口和图形后端提供通用的 piet
渲染上下文。
窗口框架,如 winit
,默认不提供绘制到其中的方式。这个决定是有意为之的;它允许用户选择他们想要的图形后端,同时也使得维护窗口代码变得更加简单。对于游戏(winit
最初是为这类游戏设计的),通常使用像 wgpu
或 glow
这样的 3D 渲染上下文。然而,GUI 应用程序将需要一个 2D 向量图形上下文。
piet
是一种 2D 图形抽象,可以与许多不同的图形后端一起使用。然而,piet
的默认实现 piet-common
难以与其他窗口系统集成,除了不支持许多其他窗口系统支持的 druid-shell
。因此,theo
通过提供一个通用的、易于与窗口系统集成的 piet
渲染上下文,旨在弥合这一差距。
与通过类似 cairo
和 DirectX 的绘图 API 相比,theo
直接使用 GPU API 来渲染到窗口,这可以提供更好的性能和更大的灵活性,同时也确保了渲染逻辑的大部分安全性。这还减少了最终程序需要依赖的动态依赖项数量。
theo
优先考虑通用性和性能。默认情况下,theo
使用优化的GPU后端进行渲染。如果GPU不可用,theo
将回退到软件渲染。
使用示例
首先,用户必须创建一个Display
,它代表系统的根显示。从这里,用户应该创建Surface
,它代表绘图区域。最后,可以使用Surface
创建RenderContext
类型,用于绘图。
use piet::{RenderContext as _, kurbo::Circle};
use theo::{Display, Surface, RenderContext};
// Create a display using a display handle from your windowing framework.
// `my_display` is used as a stand-in for the root of your display system.
// It must implement `raw_window_handle::HasRawDisplayHandle`.
let mut display = unsafe {
Display::builder()
.build(&my_display)
.expect("failed to create display")
};
// Create a surface using a window handle from your windowing framework.
// `window` is used as a stand-in for a window in your display system.
// It must implement `raw_window_handle::HasRawWindowHandle`.
let surface_future = unsafe {
display.make_surface(
&window,
window.width(),
window.height()
)
};
// make_surface returns a future that needs to be polled.
let mut surface = surface_future.await.expect("failed to create surface");
// Set up drawing logic.
surface.on_draw(move || async move {
// Create the render context.
let mut ctx = RenderContext::new(
&mut display,
&mut surface,
window.width(),
window.height()
).expect("failed to create render context");
// Clear the screen and draw a circle.
ctx.clear(None, piet::Color::WHITE);
ctx.fill(
&Circle::new((200.0, 200.0), 50.0),
&piet::Color::RED
);
// Finish drawing.
ctx.finish().expect("failed to finish drawing");
// If you don't have any other windows to draw, make sure the windows are
// presented.
display.present().await;
});
有关如何使用绘图API的更多信息,请参阅piet
crate的文档。
后端
截至编写时,theo
支持以下后端
wgpu
后端(通过wgpu
功能启用),使用piet-wgpu
crate将渲染到窗口。此后端支持wgpu
支持的所有图形API,包括Vulkan、Metal和DirectX 11/12。glow
后端(通过gl
功能启用),使用piet-glow
crate将渲染到窗口。在桌面平台上使用glutin
创建OpenGL上下文,使用glow
与OpenGL API交互。此后端支持OpenGL 3.2及以上版本。- 软件光栅化后端。使用
tiny-skia
将渲染到位图,然后使用softbuffer
将位图复制到窗口。默认情况下启用此后端,并在没有其他后端可用时使用。
性能
由于theo
实现了大部分自己的渲染逻辑,如果使用不当,可能会导致性能严重下降,尤其是在软件光栅化后端。在某些情况下,在调试模式下而不是发布模式下编译theo
可能会将应用程序的帧率减半。如果您在使用theo
时遇到帧率低的问题,请确保您正在发布模式下编译它。
此外,渐变画刷经过优化,仅需要计算一次实际渐变。但是,这意味着如果您每次都重新实例化画刷,则每次都会重新计算渐变。即使在硬件加速后端,这也可能导致性能严重下降。解决方案是缓存您使用的画刷。例如,而不是这样做
let gradient = /* ... */;
surface.on_draw(|| {
let mut ctx = /* ... */;
ctx.fill(&Circle::new((200.0, 200.0), 50.0), &gradient);
})
这样做,确保缓存渐变画刷
let gradient = /* ... */;
let mut gradient_brush = None;
surface.on_draw(|| {
let mut ctx = /* ... */;
let gradient_brush = gradient_brush.get_or_insert_with(|| {
ctx.gradient_brush(gradient.clone()).unwrap()
});
ctx.fill(&Circle::new((200.0, 200.0), 50.0), gradient_brush);
})
theo
明确选择了线程不安全模型。不仅线程不安全代码的效率更高,而且这些API类型通常都是线程不安全的。
许可
theo
是自由软件:您可以根据以下任一条款重新分发和/或修改它
- 自由软件基金会发布的GNU Lesser General Public License的第3版,或(根据您的选择)任何更高版本。
- Mozilla Foundation发布的Mozilla Public License的第2版。
theo
的发布是为了希望它会有所帮助,但没有任何保证;甚至没有对适销性或特定目的适用性的暗示保证。有关详细信息,请参阅GNU Lesser General Public License或Mozilla Public License。
您应该已经收到了GNU Lesser General Public License和Mozilla Public License的副本,以及theo
。如果没有,请参阅https://gnu.ac.cn/licenses/。
依赖项
~15–51MB
~816K SLoC