Merge pull request #188 from dfrg/cpath

Expose path rendering in C API
This commit is contained in:
Chad Brokaw 2022-08-09 11:42:40 -04:00 committed by GitHub
commit 9dd4559b00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 362 additions and 21 deletions

View file

@ -6,6 +6,23 @@
#include <ostream> #include <ostream>
#include <new> #include <new>
enum class PgpuBrushKind {
Solid = 0,
};
enum class PgpuFill {
NonZero = 0,
EvenOdd = 1,
};
enum class PgpuPathVerb {
MoveTo = 0,
LineTo = 1,
QuadTo = 2,
CurveTo = 3,
Close = 4,
};
/// Encoded (possibly color) outline for a glyph. /// Encoded (possibly color) outline for a glyph.
struct PgpuGlyph; struct PgpuGlyph;
@ -24,6 +41,50 @@ struct PgpuScene;
/// Builder for constructing an encoded scene. /// Builder for constructing an encoded scene.
struct PgpuSceneBuilder; struct PgpuSceneBuilder;
/// Encoded streams and resources describing a vector graphics scene fragment.
struct PgpuSceneFragment;
/// Affine transformation matrix.
struct PgpuTransform {
float xx;
float yx;
float xy;
float yy;
float dx;
float dy;
};
struct PgpuColor {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
union PgpuBrushData {
PgpuColor solid;
};
struct PgpuBrush {
PgpuBrushKind kind;
PgpuBrushData data;
};
struct PgpuPoint {
float x;
float y;
};
struct PgpuPathElement {
PgpuPathVerb verb;
PgpuPoint points[3];
};
struct PgpuPathIter {
void *context;
bool (*next_element)(void*, PgpuPathElement*);
};
/// Tag and value for a font variation axis. /// Tag and value for a font variation axis.
struct PgpuFontVariation { struct PgpuFontVariation {
/// Tag that specifies the axis. /// Tag that specifies the axis.
@ -98,14 +159,45 @@ PgpuScene *pgpu_scene_new();
/// Destroys the piet-gpu scene. /// Destroys the piet-gpu scene.
void pgpu_scene_destroy(PgpuScene *scene); void pgpu_scene_destroy(PgpuScene *scene);
/// Creates a new, empty piet-gpu scene fragment.
PgpuSceneFragment *pgpu_scene_fragment_new();
/// Destroys the piet-gpu scene fragment.
void pgpu_scene_fragment_destroy(PgpuSceneFragment *fragment);
/// Creates a new builder for filling a piet-gpu scene. The specified scene /// Creates a new builder for filling a piet-gpu scene. The specified scene
/// should not be accessed while the builder is live. /// should not be accessed while the builder is live.
PgpuSceneBuilder *pgpu_scene_builder_new(PgpuScene *scene); PgpuSceneBuilder *pgpu_scene_builder_for_scene(PgpuScene *scene);
/// Creates a new builder for filling a piet-gpu scene fragment. The specified
/// scene fragment should not be accessed while the builder is live.
PgpuSceneBuilder *pgpu_scene_builder_for_fragment(PgpuSceneFragment *fragment);
/// Adds a glyph with the specified transform to the underlying scene. /// Adds a glyph with the specified transform to the underlying scene.
void pgpu_scene_builder_add_glyph(PgpuSceneBuilder *builder, void pgpu_scene_builder_add_glyph(PgpuSceneBuilder *builder,
const PgpuGlyph *glyph, const PgpuGlyph *glyph,
const float (*transform)[6]); const PgpuTransform *transform);
/// Sets the current absolute transform for the scene builder.
void pgpu_scene_builder_transform(PgpuSceneBuilder *builder, const PgpuTransform *transform);
/// Fills a path using the specified fill style and brush. If the brush
/// parameter is nullptr, a solid color white brush will be used. The
/// brush_transform may be nullptr.
void pgpu_scene_builder_fill_path(PgpuSceneBuilder *builder,
PgpuFill fill,
const PgpuBrush *brush,
const PgpuTransform *brush_transform,
PgpuPathIter *path);
/// Appends a scene fragment to the underlying scene or fragment. The
/// transform parameter represents an absolute transform to apply to
/// the fragment. If it is nullptr, the fragment will be appended to
/// the scene with an assumed identity transform regardless of the
/// current transform state.
void pgpu_scene_builder_append_fragment(PgpuSceneBuilder *builder,
const PgpuSceneFragment *fragment,
const PgpuTransform *transform);
/// Finalizes the scene builder, making the underlying scene ready for /// Finalizes the scene builder, making the underlying scene ready for
/// rendering. This takes ownership and consumes the builder. /// rendering. This takes ownership and consumes the builder.

View file

@ -26,6 +26,9 @@
mod render; mod render;
use piet_scene::brush::{Brush, Color};
use piet_scene::path::Element;
use piet_scene::scene::Fill;
use render::*; use render::*;
use std::ffi::c_void; use std::ffi::c_void;
use std::mem::transmute; use std::mem::transmute;
@ -98,13 +101,135 @@ pub unsafe extern "C" fn pgpu_scene_destroy(scene: *mut PgpuScene) {
Box::from_raw(scene); Box::from_raw(scene);
} }
/// Creates a new, empty piet-gpu scene fragment.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_fragment_new() -> *mut PgpuSceneFragment {
Box::into_raw(Box::new(PgpuSceneFragment::new()))
}
/// Destroys the piet-gpu scene fragment.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_fragment_destroy(fragment: *mut PgpuSceneFragment) {
Box::from_raw(fragment);
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum PgpuPathVerb {
MoveTo = 0,
LineTo = 1,
QuadTo = 2,
CurveTo = 3,
Close = 4,
}
#[derive(Copy, Clone, Default, Debug)]
#[repr(C)]
pub struct PgpuPoint {
pub x: f32,
pub y: f32,
}
/// Rectangle defined by minimum and maximum points.
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct PgpuRect {
pub x0: f32,
pub y0: f32,
pub x1: f32,
pub y1: f32,
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct PgpuPathElement {
pub verb: PgpuPathVerb,
pub points: [PgpuPoint; 3],
}
#[derive(Clone)]
#[repr(C)]
pub struct PgpuPathIter {
pub context: *mut c_void,
pub next_element: extern "C" fn(*mut c_void, *mut PgpuPathElement) -> bool,
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum PgpuFill {
NonZero = 0,
EvenOdd = 1,
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub enum PgpuBrushKind {
Solid = 0,
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct PgpuColor {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
#[repr(C)]
pub union PgpuBrushData {
pub solid: PgpuColor,
}
#[repr(C)]
pub struct PgpuBrush {
pub kind: PgpuBrushKind,
pub data: PgpuBrushData,
}
/// Affine transformation matrix.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct PgpuTransform {
pub xx: f32,
pub yx: f32,
pub xy: f32,
pub yy: f32,
pub dx: f32,
pub dy: f32,
}
impl From<PgpuTransform> for PgpuAffine {
fn from(xform: PgpuTransform) -> Self {
Self {
xx: xform.xx,
yx: xform.yx,
xy: xform.xy,
yy: xform.yy,
dx: xform.dx,
dy: xform.dy,
}
}
}
pub type PgpuAffine = piet_scene::geometry::Affine;
/// Creates a new builder for filling a piet-gpu scene. The specified scene /// Creates a new builder for filling a piet-gpu scene. The specified scene
/// should not be accessed while the builder is live. /// should not be accessed while the builder is live.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_new( pub unsafe extern "C" fn pgpu_scene_builder_for_scene(
scene: *mut PgpuScene, scene: *mut PgpuScene,
) -> *mut PgpuSceneBuilder<'static> { ) -> *mut PgpuSceneBuilder<'static> {
Box::into_raw(Box::new((*scene).build())) Box::into_raw(Box::new((*scene).builder()))
}
/// Creates a new builder for filling a piet-gpu scene fragment. The specified
/// scene fragment should not be accessed while the builder is live.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_for_fragment(
fragment: *mut PgpuSceneFragment,
) -> *mut PgpuSceneBuilder<'static> {
Box::into_raw(Box::new((*fragment).builder()))
} }
/// Adds a glyph with the specified transform to the underlying scene. /// Adds a glyph with the specified transform to the underlying scene.
@ -112,10 +237,103 @@ pub unsafe extern "C" fn pgpu_scene_builder_new(
pub unsafe extern "C" fn pgpu_scene_builder_add_glyph( pub unsafe extern "C" fn pgpu_scene_builder_add_glyph(
builder: *mut PgpuSceneBuilder<'static>, builder: *mut PgpuSceneBuilder<'static>,
glyph: *const PgpuGlyph, glyph: *const PgpuGlyph,
transform: &[f32; 6], transform: *const PgpuTransform,
) { ) {
let transform = piet_scene::geometry::Affine::new(transform); (*builder).add_glyph(&*glyph, &(*transform).into());
(*builder).add_glyph(&*glyph, &transform); }
impl Iterator for PgpuPathIter {
type Item = piet_scene::path::Element;
fn next(&mut self) -> Option<Self::Item> {
let mut el = PgpuPathElement {
verb: PgpuPathVerb::MoveTo,
points: [PgpuPoint::default(); 3],
};
if (self.next_element)(self.context, &mut el as _) {
let p = &el.points;
Some(match el.verb {
PgpuPathVerb::MoveTo => Element::MoveTo((p[0].x, p[0].y).into()),
PgpuPathVerb::LineTo => Element::LineTo((p[0].x, p[0].y).into()),
PgpuPathVerb::QuadTo => {
Element::QuadTo((p[0].x, p[0].y).into(), (p[1].x, p[1].y).into())
}
PgpuPathVerb::CurveTo => Element::CurveTo(
(p[0].x, p[0].y).into(),
(p[1].x, p[1].y).into(),
(p[2].x, p[2].y).into(),
),
PgpuPathVerb::Close => Element::Close,
})
} else {
None
}
}
}
/// Sets the current absolute transform for the scene builder.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_transform(
builder: *mut PgpuSceneBuilder<'static>,
transform: *const PgpuTransform,
) {
if !transform.is_null() {
(*builder).0.transform((*transform).into())
}
}
/// Fills a path using the specified fill style and brush. If the brush
/// parameter is nullptr, a solid color white brush will be used. The
/// brush_transform may be nullptr.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_fill_path(
builder: *mut PgpuSceneBuilder<'static>,
fill: PgpuFill,
brush: *const PgpuBrush,
brush_transform: *const PgpuTransform,
path: *mut PgpuPathIter,
) {
let fill = match fill {
PgpuFill::NonZero => Fill::NonZero,
PgpuFill::EvenOdd => Fill::EvenOdd,
};
let brush = if brush.is_null() {
Brush::Solid(Color::rgb8(255, 255, 255))
} else {
match (*brush).kind {
PgpuBrushKind::Solid => {
let color = &(*brush).data.solid;
Brush::Solid(Color::rgba8(color.r, color.g, color.b, color.a))
}
}
};
let brush_transform = if brush_transform.is_null() {
None
} else {
Some((*brush_transform).into())
};
(*builder)
.0
.fill(fill, &brush, brush_transform, (*path).clone());
}
/// Appends a scene fragment to the underlying scene or fragment. The
/// transform parameter represents an absolute transform to apply to
/// the fragment. If it is nullptr, the fragment will be appended to
/// the scene with an assumed identity transform regardless of the
/// current transform state.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_append_fragment(
builder: *mut PgpuSceneBuilder<'static>,
fragment: *const PgpuSceneFragment,
transform: *const PgpuTransform,
) {
let transform = if transform.is_null() {
None
} else {
Some((*transform).into())
};
(*builder).0.append(&(*fragment).0, transform);
} }
/// Finalizes the scene builder, making the underlying scene ready for /// Finalizes the scene builder, making the underlying scene ready for
@ -220,16 +438,6 @@ pub unsafe extern "C" fn pgpu_glyph_provider_destroy(provider: *mut PgpuGlyphPro
Box::from_raw(provider); Box::from_raw(provider);
} }
/// Rectangle defined by minimum and maximum points.
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct PgpuRect {
pub x0: f32,
pub y0: f32,
pub x1: f32,
pub y1: f32,
}
/// Computes the bounding box for the glyph after applying the specified /// Computes the bounding box for the glyph after applying the specified
/// transform. /// transform.
#[no_mangle] #[no_mangle]

View file

@ -61,7 +61,7 @@ impl PgpuRenderer {
cmdbuf: &metal::CommandBufferRef, cmdbuf: &metal::CommandBufferRef,
target: &metal::TextureRef, target: &metal::TextureRef,
) -> u32 { ) -> u32 {
let is_color = target.pixel_format() != metal::MTLPixelFormat::A8Unorm; let is_color = target.pixel_format() != metal::MTLPixelFormat::R8Unorm;
let width = target.width() as u32; let width = target.width() as u32;
let height = target.height() as u32; let height = target.height() as u32;
if self.pgpu_renderer.is_none() if self.pgpu_renderer.is_none()
@ -93,6 +93,7 @@ impl PgpuRenderer {
renderer.record(&mut cmd_buf, &self.query_pool, 0); renderer.record(&mut cmd_buf, &self.query_pool, 0);
// TODO later: we can bind the destination image and avoid the copy. // TODO later: we can bind the destination image and avoid the copy.
cmd_buf.blit_image(&renderer.image_dev, &dst_image); cmd_buf.blit_image(&renderer.image_dev, &dst_image);
cmd_buf.flush();
} }
} }
0 0
@ -117,7 +118,7 @@ impl PgpuScene {
} }
} }
pub fn build(&mut self) -> PgpuSceneBuilder { pub fn builder(&mut self) -> PgpuSceneBuilder {
self.rcx.advance(); self.rcx.advance();
PgpuSceneBuilder(piet_scene::scene::build_scene( PgpuSceneBuilder(piet_scene::scene::build_scene(
&mut self.scene, &mut self.scene,
@ -142,8 +143,21 @@ impl PgpuScene {
} }
} }
/// Encoded streams and resources describing a vector graphics scene fragment.
pub struct PgpuSceneFragment(pub Fragment);
impl PgpuSceneFragment {
pub fn new() -> Self {
Self(Fragment::default())
}
pub fn builder(&mut self) -> PgpuSceneBuilder {
PgpuSceneBuilder(piet_scene::scene::build_fragment(&mut self.0))
}
}
/// Builder for constructing an encoded scene. /// Builder for constructing an encoded scene.
pub struct PgpuSceneBuilder<'a>(piet_scene::scene::Builder<'a>); pub struct PgpuSceneBuilder<'a>(pub piet_scene::scene::Builder<'a>);
impl<'a> PgpuSceneBuilder<'a> { impl<'a> PgpuSceneBuilder<'a> {
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) { pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) {

View file

@ -205,6 +205,9 @@ pub trait CmdBuf<D: Device> {
/// State: ready -> finished /// State: ready -> finished
unsafe fn finish(&mut self); unsafe fn finish(&mut self);
/// Commits any open command encoder.
unsafe fn flush(&mut self);
/// Return true if the command buffer is suitable for reuse. /// Return true if the command buffer is suitable for reuse.
unsafe fn reset(&mut self) -> bool; unsafe fn reset(&mut self) -> bool;

View file

@ -631,6 +631,8 @@ impl crate::backend::CmdBuf<Dx12Device> for CmdBuf {
self.needs_reset = true; self.needs_reset = true;
} }
unsafe fn flush(&mut self) {}
unsafe fn reset(&mut self) -> bool { unsafe fn reset(&mut self) -> bool {
self.allocator.reset().is_ok() && self.c.reset(&self.allocator, None).is_ok() self.allocator.reset().is_ok() && self.c.reset(&self.allocator, None).is_ok()
} }

View file

@ -509,6 +509,11 @@ impl CmdBuf {
self.cmd_buf().finish(); self.cmd_buf().finish();
} }
/// Commits any open command encoder.
pub unsafe fn flush(&mut self) {
self.cmd_buf().flush();
}
/// Begin a compute pass. /// Begin a compute pass.
pub unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor) -> ComputePass { pub unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor) -> ComputePass {
self.cmd_buf().begin_compute_pass(desc); self.cmd_buf().begin_compute_pass(desc);

View file

@ -590,6 +590,10 @@ impl crate::backend::CmdBuf<MtlDevice> for CmdBuf {
self.flush_encoder(); self.flush_encoder();
} }
unsafe fn flush(&mut self) {
self.flush_encoder();
}
unsafe fn reset(&mut self) -> bool { unsafe fn reset(&mut self) -> bool {
false false
} }

View file

@ -675,6 +675,14 @@ impl CmdBuf {
} }
} }
pub unsafe fn flush(&mut self) {
mux_match! { self;
CmdBuf::Vk(c) => c.flush(),
CmdBuf::Dx12(c) => c.flush(),
CmdBuf::Mtl(c) => c.flush(),
}
}
pub unsafe fn finish(&mut self) { pub unsafe fn finish(&mut self) {
mux_match! { self; mux_match! { self;
CmdBuf::Vk(c) => c.finish(), CmdBuf::Vk(c) => c.finish(),

View file

@ -967,6 +967,8 @@ impl crate::backend::CmdBuf<VkDevice> for CmdBuf {
self.device.device.end_command_buffer(self.cmd_buf).unwrap(); self.device.device.end_command_buffer(self.cmd_buf).unwrap();
} }
unsafe fn flush(&mut self) {}
unsafe fn reset(&mut self) -> bool { unsafe fn reset(&mut self) -> bool {
true true
} }

View file

@ -243,7 +243,10 @@ impl Renderer {
.unwrap() .unwrap()
}) })
.collect(); .collect();
let memory_buf_dev = session.create_buffer(16 * 1024 * 1024, usage_mem_dev)?; let target_dependent_size =
(width / TILE_W) as u64 * (height / TILE_H) as u64 * PTCL_INITIAL_ALLOC as u64;
let memory_buf_dev =
session.create_buffer(target_dependent_size + 8 * 1024 * 1024, usage_mem_dev)?;
let memory_buf_readback = let memory_buf_readback =
session.create_buffer(std::mem::size_of::<MemoryHeader>() as u64, usage_readback)?; session.create_buffer(std::mem::size_of::<MemoryHeader>() as u64, usage_readback)?;
let blend_buf = session.create_buffer(16 * 1024 * 1024, usage_blend)?; let blend_buf = session.create_buffer(16 * 1024 * 1024, usage_blend)?;