mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-10 20:51:29 +11:00
532b6ee808
First cut at a public C api that supports glyph rendering on Metal targets.
223 lines
6.8 KiB
Rust
223 lines
6.8 KiB
Rust
// Copyright 2022 The piet-gpu authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// Also licensed under MIT license, at your choice.
|
|
|
|
use piet_gpu::{EncodedSceneRef, PixelFormat, RenderConfig};
|
|
use piet_gpu_hal::{QueryPool, Session};
|
|
use piet_scene::glyph::pinot::{types::Tag, FontDataRef};
|
|
use piet_scene::geometry::{Affine, Rect};
|
|
use piet_scene::glyph::{GlyphContext, GlyphProvider};
|
|
use piet_scene::resource::ResourceContext;
|
|
use piet_scene::scene::{Fragment, Scene};
|
|
|
|
/// State and resources for rendering a scene.
|
|
pub struct PgpuRenderer {
|
|
session: Session,
|
|
pgpu_renderer: Option<piet_gpu::Renderer>,
|
|
query_pool: QueryPool,
|
|
width: u32,
|
|
height: u32,
|
|
is_color: bool,
|
|
}
|
|
|
|
impl PgpuRenderer {
|
|
pub fn new(device: &metal::DeviceRef, queue: &metal::CommandQueueRef) -> Self {
|
|
let piet_device = piet_gpu_hal::Device::new_from_raw_mtl(device, &queue);
|
|
let session = Session::new(piet_device);
|
|
let query_pool = session.create_query_pool(12).unwrap();
|
|
Self {
|
|
session,
|
|
pgpu_renderer: None,
|
|
query_pool,
|
|
width: 0,
|
|
height: 0,
|
|
is_color: false,
|
|
}
|
|
}
|
|
|
|
pub fn render(
|
|
&mut self,
|
|
scene: &PgpuScene,
|
|
cmdbuf: &metal::CommandBufferRef,
|
|
target: &metal::TextureRef,
|
|
) -> u32 {
|
|
let is_color = target.pixel_format() != metal::MTLPixelFormat::A8Unorm;
|
|
let width = target.width() as u32;
|
|
let height = target.height() as u32;
|
|
if self.pgpu_renderer.is_none()
|
|
|| self.width != width
|
|
|| self.height != height
|
|
|| self.is_color != is_color
|
|
{
|
|
self.width = width;
|
|
self.height = height;
|
|
self.is_color = is_color;
|
|
let format = if is_color {
|
|
PixelFormat::Rgba8
|
|
} else {
|
|
PixelFormat::A8
|
|
};
|
|
let config = RenderConfig::new(width as usize, height as usize).pixel_format(format);
|
|
unsafe {
|
|
self.pgpu_renderer =
|
|
piet_gpu::Renderer::new_from_config(&self.session, config, 1).ok();
|
|
}
|
|
}
|
|
unsafe {
|
|
let mut cmd_buf = self.session.cmd_buf_from_raw_mtl(cmdbuf);
|
|
let dst_image = self
|
|
.session
|
|
.image_from_raw_mtl(target, self.width, self.height);
|
|
if let Some(renderer) = &mut self.pgpu_renderer {
|
|
renderer.upload_scene(&scene.encoded_scene(), 0).unwrap();
|
|
renderer.record(&mut cmd_buf, &self.query_pool, 0);
|
|
// TODO later: we can bind the destination image and avoid the copy.
|
|
cmd_buf.blit_image(&renderer.image_dev, &dst_image);
|
|
}
|
|
}
|
|
0
|
|
}
|
|
|
|
pub fn release(&mut self, _id: u32) {
|
|
// TODO: worry about freeing resources / managing overlapping submits
|
|
}
|
|
}
|
|
|
|
/// Encoded streams and resources describing a vector graphics scene.
|
|
pub struct PgpuScene {
|
|
scene: Scene,
|
|
rcx: ResourceContext,
|
|
}
|
|
|
|
impl PgpuScene {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
scene: Scene::default(),
|
|
rcx: ResourceContext::new(),
|
|
}
|
|
}
|
|
|
|
pub fn build(&mut self) -> PgpuSceneBuilder {
|
|
self.rcx.advance();
|
|
PgpuSceneBuilder(piet_scene::scene::build_scene(
|
|
&mut self.scene,
|
|
&mut self.rcx,
|
|
))
|
|
}
|
|
|
|
fn encoded_scene<'a>(&'a self) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> {
|
|
let d = self.scene.data();
|
|
EncodedSceneRef {
|
|
transform_stream: &d.transform_stream,
|
|
tag_stream: &d.tag_stream,
|
|
pathseg_stream: &d.pathseg_stream,
|
|
linewidth_stream: &d.linewidth_stream,
|
|
drawtag_stream: &d.drawtag_stream,
|
|
drawdata_stream: &d.drawdata_stream,
|
|
n_path: d.n_path,
|
|
n_pathseg: d.n_pathseg,
|
|
n_clip: d.n_clip,
|
|
ramp_data: self.rcx.ramp_data(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Builder for constructing an encoded scene.
|
|
pub struct PgpuSceneBuilder<'a>(piet_scene::scene::Builder<'a>);
|
|
|
|
impl<'a> PgpuSceneBuilder<'a> {
|
|
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) {
|
|
self.0.push_transform(*transform);
|
|
self.0.append(&glyph.fragment);
|
|
self.0.pop_transform();
|
|
}
|
|
|
|
pub fn finish(self) {
|
|
self.0.finish();
|
|
}
|
|
}
|
|
|
|
/// Tag and value for a font variation axis.
|
|
#[derive(Copy, Clone)]
|
|
#[repr(C)]
|
|
pub struct PgpuFontVariation {
|
|
/// Tag that specifies the axis.
|
|
pub tag: u32,
|
|
/// Requested setting for the axis.
|
|
pub value: f32,
|
|
}
|
|
|
|
/// Context for loading and scaling glyphs.
|
|
pub struct PgpuGlyphContext(GlyphContext);
|
|
|
|
impl PgpuGlyphContext {
|
|
pub fn new() -> Self {
|
|
Self(GlyphContext::new())
|
|
}
|
|
|
|
pub fn new_provider<'a>(
|
|
&'a mut self,
|
|
font_data: &'a [u8],
|
|
font_index: u32,
|
|
font_id: u64,
|
|
ppem: f32,
|
|
hint: bool,
|
|
variations: &[PgpuFontVariation],
|
|
) -> Option<PgpuGlyphProvider> {
|
|
let font = FontDataRef::new(font_data).and_then(|f| f.get(font_index))?;
|
|
Some(PgpuGlyphProvider(
|
|
self.0.new_provider(
|
|
&font,
|
|
Some(font_id),
|
|
ppem,
|
|
hint,
|
|
variations
|
|
.iter()
|
|
.map(|variation| (Tag(variation.tag), variation.value)),
|
|
),
|
|
))
|
|
}
|
|
}
|
|
|
|
/// Context for loading a scaling glyphs from a specific font.
|
|
pub struct PgpuGlyphProvider<'a>(GlyphProvider<'a>);
|
|
|
|
impl<'a> PgpuGlyphProvider<'a> {
|
|
pub fn get(&mut self, gid: u16) -> Option<PgpuGlyph> {
|
|
let fragment = self.0.get(gid)?;
|
|
Some(PgpuGlyph { fragment })
|
|
}
|
|
|
|
pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option<PgpuGlyph> {
|
|
let fragment = self.0.get_color(palette_index, gid)?;
|
|
Some(PgpuGlyph { fragment })
|
|
}
|
|
}
|
|
|
|
/// Encoded (possibly color) outline for a glyph.
|
|
pub struct PgpuGlyph {
|
|
fragment: Fragment,
|
|
}
|
|
|
|
impl PgpuGlyph {
|
|
pub fn bbox(&self, transform: Option<Affine>) -> Rect {
|
|
if let Some(transform) = &transform {
|
|
Rect::from_points(self.fragment.points().iter().map(|p| p.transform(transform)))
|
|
} else {
|
|
Rect::from_points(self.fragment.points())
|
|
}
|
|
}
|
|
}
|