vello/pgpu-render/src/render.rs
Chad Brokaw 6c5a2cb4f4 macOS fixes
* Fix call to removed encoded_scene method in pgpu-render
* Add new ImageFormat::Surface variant to select a pixel format that matches the platform specific surface format. This makes gradients consistent across platforms.
2022-10-19 15:20:04 -04:00

228 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::{PixelFormat, RenderConfig};
use piet_gpu_hal::{QueryPool, Session};
use piet_scene::glyph::pinot::{types::Tag, FontDataRef};
use piet_scene::glyph::{GlyphContext, GlyphProvider};
use piet_scene::{Affine, Rect, Scene, SceneFragment};
/// 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 {
#[cfg(all(
not(target_arch = "wasm32"),
any(target_os = "ios", target_os = "macos")
))]
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,
}
}
#[cfg(all(
not(target_arch = "wasm32"),
any(target_os = "ios", target_os = "macos")
))]
pub fn render(
&mut self,
scene: &PgpuScene,
cmdbuf: &metal::CommandBufferRef,
target: &metal::TextureRef,
) -> u32 {
let is_color = target.pixel_format() != metal::MTLPixelFormat::R8Unorm;
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.0, 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);
cmd_buf.flush();
}
}
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(pub Scene);
impl PgpuScene {
pub fn new() -> Self {
Self(Scene::default())
}
pub fn builder(&mut self) -> PgpuSceneBuilder {
PgpuSceneBuilder {
builder: piet_scene::SceneBuilder::for_scene(&mut self.0),
transform: Affine::IDENTITY,
}
}
}
/// Encoded streams and resources describing a vector graphics scene fragment.
pub struct PgpuSceneFragment(pub SceneFragment);
impl PgpuSceneFragment {
pub fn new() -> Self {
Self(SceneFragment::default())
}
pub fn builder(&mut self) -> PgpuSceneBuilder {
PgpuSceneBuilder {
builder: piet_scene::SceneBuilder::for_fragment(&mut self.0),
transform: Affine::IDENTITY,
}
}
}
/// Builder for constructing an encoded scene.
pub struct PgpuSceneBuilder<'a> {
pub builder: piet_scene::SceneBuilder<'a>,
pub transform: Affine,
}
impl<'a> PgpuSceneBuilder<'a> {
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::Affine) {
self.builder.append(&glyph.fragment, Some(*transform));
}
pub fn finish(self) {
self.builder.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, None)?;
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: SceneFragment,
}
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())
}
}
}