make gradient ramps late bound

Removes ResourceContext type. This makes scenes and fragments nearly identical. Should they be merged?
This commit is contained in:
Chad Brokaw 2022-08-11 18:00:53 -04:00
parent 782e7d51d2
commit c0fcdbad58
12 changed files with 227 additions and 186 deletions

View file

@ -18,7 +18,7 @@ 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, ResourceContext, Scene, SceneFragment};
use piet_scene::{Affine, Rect, Scene, SceneFragment};
/// State and resources for rendering a scene.
pub struct PgpuRenderer {
@ -105,23 +105,17 @@ impl PgpuRenderer {
/// 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 builder(&mut self) -> PgpuSceneBuilder {
self.rcx.advance();
PgpuSceneBuilder(piet_scene::SceneBuilder::for_scene(
&mut self.scene,
&mut self.rcx,
))
PgpuSceneBuilder(piet_scene::SceneBuilder::for_scene(&mut self.scene))
}
}

View file

@ -17,7 +17,7 @@ use piet_gpu_hal::{
};
use piet_gpu::{samples, RenderDriver, Renderer, SimpleText};
use piet_scene::{ResourceContext, Scene, SceneBuilder};
use piet_scene::{Scene, SceneBuilder};
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
fn main() {
@ -115,13 +115,12 @@ impl GfxState {
}
let mut text = SimpleText::new();
let mut scene = Scene::default();
let mut rcx = ResourceContext::default();
let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx);
let mut builder = SceneBuilder::for_scene(&mut scene);
samples::render_anim_frame(&mut builder, self.current_frame);
//samples::render_tiger(&mut builder, false);
render_info(&mut text, &mut builder, &info_string);
builder.finish();
if let Err(e) = self.render_driver.upload_scene(&self.session, &scene, &rcx) {
if let Err(e) = self.render_driver.upload_scene(&self.session, &scene) {
println!("error in uploading: {}", e);
}
let (image_idx, acquisition_semaphore) = self.swapchain.next().unwrap();

View file

@ -7,7 +7,7 @@ use clap::{App, Arg};
use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session};
use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer};
use piet_scene::{ResourceContext, Scene, SceneBuilder};
use piet_scene::{Scene, SceneBuilder};
const WIDTH: usize = 2048;
const HEIGHT: usize = 1536;
@ -229,12 +229,10 @@ fn main() -> Result<(), Error> {
.get_matches();
let instance = Instance::new(InstanceFlags::default())?;
let mut scene = Scene::default();
let mut rcx = ResourceContext::default();
unsafe {
let device = instance.device()?;
let session = Session::new(device);
rcx.advance();
let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx);
let mut builder = SceneBuilder::for_scene(&mut scene);
if let Some(input) = matches.value_of("INPUT") {
let mut scale = matches
.value_of("scale")
@ -257,7 +255,7 @@ fn main() -> Result<(), Error> {
let renderer = Renderer::new(&session, WIDTH, HEIGHT, 1)?;
let mut render_driver = RenderDriver::new(&session, 1, renderer);
let start = std::time::Instant::now();
render_driver.upload_scene(&session, &scene, &rcx)?;
render_driver.upload_scene(&session, &scene)?;
let image_usage = BufferUsage::MAP_READ | BufferUsage::COPY_DST;
let image_buf = session.create_buffer((WIDTH * HEIGHT * 4) as u64, image_usage)?;

View file

@ -1,6 +1,6 @@
use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer, SimpleText};
use piet_gpu_hal::{Error, ImageLayout, Instance, InstanceFlags, Session};
use piet_scene::{ResourceContext, Scene, SceneBuilder};
use piet_scene::{Scene, SceneBuilder};
use clap::{App, Arg};
@ -60,7 +60,6 @@ fn main() -> Result<(), Error> {
let instance = Instance::new(InstanceFlags::default())?;
let mut info_string = "info".to_string();
let mut scene = Scene::default();
let mut rcx = ResourceContext::default();
let mut simple_text = piet_gpu::SimpleText::new();
unsafe {
let display_handle = window.raw_display_handle();
@ -117,17 +116,15 @@ fn main() -> Result<(), Error> {
}
if let Some(svg) = &svg {
rcx.advance();
let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx);
let mut builder = SceneBuilder::for_scene(&mut scene);
samples::render_svg(&mut builder, svg, false);
render_info(&mut simple_text, &mut builder, &info_string);
builder.finish();
if let Err(e) = render_driver.upload_scene(&session, &scene, &rcx) {
if let Err(e) = render_driver.upload_scene(&session, &scene) {
println!("error in uploading: {}", e);
}
} else {
rcx.advance();
let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx);
let mut builder = SceneBuilder::for_scene(&mut scene);
const N_SAMPLES: usize = 4;
match sample_index % N_SAMPLES {
@ -142,7 +139,7 @@ fn main() -> Result<(), Error> {
}
render_info(&mut simple_text, &mut builder, &info_string);
builder.finish();
if let Err(e) = render_driver.upload_scene(&session, &scene, &rcx) {
if let Err(e) = render_driver.upload_scene(&session, &scene) {
println!("error in uploading: {}", e);
}
}

View file

@ -1,4 +1,5 @@
mod pico_svg;
mod ramp;
mod render_driver;
pub mod samples;
mod simple_text;
@ -7,6 +8,7 @@ pub mod stages;
pub use piet_scene as scene;
use bytemuck::{Pod, Zeroable};
use scene::ResourcePatch;
use std::convert::TryInto;
pub use render_driver::RenderDriver;
@ -17,7 +19,7 @@ use piet_gpu_hal::{
DescriptorSet, Error, Image, ImageLayout, Pipeline, QueryPool, Session,
};
use piet_scene::{ResourceContext, Scene};
use piet_scene::Scene;
pub use pico_svg::PicoSvg;
use stages::{ClipBinding, ElementBinding, ElementCode, DRAW_PART_SIZE, PATHSEG_PART_SIZE};
@ -154,6 +156,9 @@ pub struct Renderer {
gradient_bufs: Vec<Buffer>,
gradients: Image,
ramps: ramp::RampCache,
drawdata_patches: Vec<(usize, u32)>,
}
impl RenderConfig {
@ -364,6 +369,9 @@ impl Renderer {
.build(&session, &k4_pipeline)?;
let scene_stats = Default::default();
let ramps = ramp::RampCache::default();
let drawdata_patches = vec![];
Ok(Renderer {
width,
height,
@ -403,26 +411,34 @@ impl Renderer {
_bg_image: bg_image,
gradient_bufs,
gradients,
ramps,
drawdata_patches,
})
}
pub fn upload_scene(
&mut self,
scene: &Scene,
rcx: &ResourceContext,
buf_ix: usize,
) -> Result<(), Error> {
pub fn upload_scene(&mut self, scene: &Scene, buf_ix: usize) -> Result<(), Error> {
self.drawdata_patches.clear();
self.scene_stats = SceneStats::from_scene(scene);
self.ramps.advance();
let data = scene.data();
let stop_data = &data.resources.stops;
for patch in &data.resources.patches {
match patch {
ResourcePatch::Ramp { offset, stops } => {
let ramp_id = self.ramps.add(&stop_data[stops.clone()]);
self.drawdata_patches.push((*offset, ramp_id));
}
}
}
unsafe {
self.upload_config(buf_ix)?;
{
let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?;
write_scene(scene, &mut mapped_scene);
write_scene(scene, &self.drawdata_patches, &mut mapped_scene);
}
// Upload gradient data.
let ramp_data = rcx.ramp_data();
let ramp_data = self.ramps.data();
if !ramp_data.is_empty() {
assert!(
self.gradient_bufs[buf_ix].size() as usize
@ -870,12 +886,28 @@ impl SceneStats {
}
}
fn write_scene(scene: &Scene, buf: &mut BufWrite) {
fn write_scene(scene: &Scene, drawdata_patches: &[(usize, u32)], buf: &mut BufWrite) {
let data = scene.data();
buf.extend_slice(&data.drawtag_stream);
let n_drawobj = data.drawtag_stream.len();
buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE);
buf.extend_slice(&data.drawdata_stream);
if !drawdata_patches.is_empty() {
let mut pos = 0;
for patch in drawdata_patches {
let offset = patch.0;
let value = patch.1;
if pos < offset {
buf.extend_slice(&data.drawdata_stream[pos..offset]);
}
buf.push(value);
pos = offset + 4;
}
if pos < data.drawdata_stream.len() {
buf.extend_slice(&data.drawdata_stream[pos..])
}
} else {
buf.extend_slice(&data.drawdata_stream);
}
buf.extend_slice(&data.transform_stream);
buf.extend_slice(&data.linewidth_stream);
buf.extend_slice(&data.tag_stream);

View file

@ -1,4 +1,5 @@
use crate::brush::{Color, GradientStop, GradientStops};
use piet_scene::{Color, GradientStop, GradientStops};
use std::collections::HashMap;
const N_SAMPLES: usize = 512;
@ -21,12 +22,6 @@ impl RampCache {
}
}
pub fn clear(&mut self) {
self.epoch = 0;
self.map.clear();
self.data.clear();
}
pub fn add(&mut self, stops: &[GradientStop]) -> u32 {
if let Some(entry) = self.map.get_mut(stops) {
entry.1 = self.epoch;

View file

@ -15,7 +15,7 @@
// Also licensed under MIT license, at your choice.
use piet_gpu_hal::{CmdBuf, Error, Image, QueryPool, Semaphore, Session, SubmittedCmdBuf};
use piet_scene::{ResourceContext, Scene};
use piet_scene::Scene;
use crate::{MemoryHeader, Renderer, SceneStats};
@ -86,15 +86,10 @@ impl RenderDriver {
}
}
pub fn upload_scene(
&mut self,
session: &Session,
scene: &Scene,
rcx: &ResourceContext,
) -> Result<(), Error> {
pub fn upload_scene(&mut self, session: &Session, scene: &Scene) -> Result<(), Error> {
let stats = SceneStats::from_scene(scene);
self.ensure_scene_buffers(session, &stats)?;
self.renderer.upload_scene(scene, rcx, self.buf_ix)
self.renderer.upload_scene(scene, self.buf_ix)
}
fn ensure_scene_buffers(&mut self, session: &Session, stats: &SceneStats) -> Result<(), Error> {

View file

@ -168,7 +168,10 @@ pub fn render_blend_grid(sb: &mut SceneBuilder) {
let i = ix % 4;
let j = ix / 4;
let transform = Affine::translate(i as f32 * 225., j as f32 * 225.);
render_blend_square(sb, blend.into(), transform);
let square = blend_square(blend.into());
sb.append(&square, Some(transform));
// sb.append(&square, Some(transform));
// render_blend_square(sb, blend.into(), transform);
}
}
@ -265,6 +268,101 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin
sb.pop_layer();
}
#[allow(unused)]
fn blend_square(blend: BlendMode) -> SceneFragment {
let mut fragment = SceneFragment::default();
let mut sb = SceneBuilder::for_fragment(&mut fragment);
// Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
let rect = Rect::from_origin_size(Point::new(0., 0.), 200., 200.);
let stops = &[
GradientStop {
color: Color::rgb8(0, 0, 0),
offset: 0.0,
},
GradientStop {
color: Color::rgb8(255, 255, 255),
offset: 1.0,
},
][..];
let linear = Brush::LinearGradient(LinearGradient {
start: Point::new(0.0, 0.0),
end: Point::new(200.0, 0.0),
stops: stops.into(),
extend: ExtendMode::Pad,
});
sb.fill(Fill::NonZero, &linear, None, rect.elements());
const GRADIENTS: &[(f32, f32, Color)] = &[
(150., 0., Color::rgb8(64, 240, 255)),
(175., 100., Color::rgb8(240, 96, 255)),
(125., 200., Color::rgb8(255, 192, 64)),
];
for (x, y, c) in GRADIENTS {
let mut color2 = c.clone();
color2.a = 0;
let stops = &[
GradientStop {
color: c.clone(),
offset: 0.0,
},
GradientStop {
color: color2,
offset: 1.0,
},
][..];
let rad = Brush::RadialGradient(RadialGradient {
center0: Point::new(*x, *y),
center1: Point::new(*x, *y),
radius0: 0.0,
radius1: 100.0,
stops: stops.into(),
extend: ExtendMode::Pad,
});
sb.fill(Fill::NonZero, &rad, None, rect.elements());
}
const COLORS: &[Color] = &[
Color::rgb8(0, 0, 255),
Color::rgb8(0, 255, 0),
Color::rgb8(255, 0, 0),
];
sb.push_layer(Mix::Normal.into(), rect.elements());
for (i, c) in COLORS.iter().enumerate() {
let stops = &[
GradientStop {
color: Color::rgb8(255, 255, 255),
offset: 0.0,
},
GradientStop {
color: c.clone(),
offset: 1.0,
},
][..];
let linear = Brush::LinearGradient(LinearGradient {
start: Point::new(0.0, 0.0),
end: Point::new(0.0, 200.0),
stops: stops.into(),
extend: ExtendMode::Pad,
});
sb.transform(Affine::IDENTITY);
sb.push_layer(blend, rect.elements());
// squash the ellipse
let a = Affine::translate(100., 100.)
* Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32)
* Affine::scale(1.0, 0.357)
* Affine::translate(-100., -100.);
sb.transform(a);
sb.fill(
Fill::NonZero,
&linear,
None,
make_ellipse(100., 100., 90., 90.),
);
sb.pop_layer();
}
sb.pop_layer();
sb.finish();
fragment
}
#[allow(unused)]
pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) {
sb.fill(

View file

@ -0,0 +1,31 @@
use crate::brush::GradientStop;
use core::ops::Range;
#[derive(Default)]
/// Collection of late bound resources for a scene or scene fragment.
pub struct ResourceBundle {
/// Sequence of resource patches.
pub patches: Vec<ResourcePatch>,
/// Cache of gradient stops, referenced by range from the patches.
pub stops: Vec<GradientStop>,
}
impl ResourceBundle {
/// Clears the resource set.
pub(crate) fn clear(&mut self) {
self.patches.clear();
self.stops.clear();
}
}
#[derive(Clone)]
/// Description of a late bound resource.
pub enum ResourcePatch {
/// Gradient ramp resource.
Ramp {
/// Byte offset to the ramp id in the draw data stream.
offset: usize,
/// Range of the gradient stops in the resource set.
stops: Range<usize>,
},
}

View file

@ -1,32 +0,0 @@
mod gradient;
use crate::brush::GradientStop;
use gradient::RampCache;
/// Context for caching resources across rendering operations.
#[derive(Default)]
pub struct ResourceContext {
ramps: RampCache,
}
impl ResourceContext {
pub fn new() -> Self {
Self::default()
}
pub fn advance(&mut self) {
self.ramps.advance();
}
pub fn clear(&mut self) {
self.ramps.clear();
}
pub fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 {
self.ramps.add(stops)
}
pub fn ramp_data(&self) -> &[u32] {
&self.ramps.data()
}
}

View file

@ -15,12 +15,8 @@
// Also licensed under MIT license, at your choice.
use super::style::{Fill, Stroke};
use super::{
Affine, BlendMode, FragmentResources, PathElement, ResourcePatch, Scene, SceneData,
SceneFragment,
};
use crate::brush::*;
use crate::resource::ResourceContext;
use super::{Affine, BlendMode, PathElement, Scene, SceneData, SceneFragment};
use crate::{brush::*, ResourcePatch};
use bytemuck::{Pod, Zeroable};
use core::borrow::Borrow;
use smallvec::SmallVec;
@ -28,37 +24,27 @@ use smallvec::SmallVec;
/// Builder for constructing a scene or scene fragment.
pub struct SceneBuilder<'a> {
scene: &'a mut SceneData,
resources: ResourceData<'a>,
layers: SmallVec<[BlendMode; 8]>,
}
impl<'a> SceneBuilder<'a> {
/// Creates a new builder for filling a scene. Any current content in the scene
/// will be cleared.
pub fn for_scene(scene: &'a mut Scene, rcx: &'a mut ResourceContext) -> Self {
Self::new(&mut scene.data, ResourceData::Scene(rcx))
pub fn for_scene(scene: &'a mut Scene) -> Self {
Self::new(&mut scene.data, false)
}
/// Creates a new builder for filling a scene fragment. Any current content in
/// the fragment will be cleared.
pub fn for_fragment(fragment: &'a mut SceneFragment) -> Self {
Self::new(
&mut fragment.data,
ResourceData::Fragment(&mut fragment.resources),
)
Self::new(&mut fragment.data, true)
}
/// Creates a new builder for constructing a scene.
fn new(scene: &'a mut SceneData, mut resources: ResourceData<'a>) -> Self {
let is_fragment = match resources {
ResourceData::Fragment(_) => true,
_ => false,
};
fn new(scene: &'a mut SceneData, is_fragment: bool) -> Self {
scene.reset(is_fragment);
resources.clear();
Self {
scene,
resources,
layers: Default::default(),
}
}
@ -161,7 +147,6 @@ impl<'a> SceneBuilder<'a> {
/// Appends a fragment to the scene.
pub fn append(&mut self, fragment: &SceneFragment, transform: Option<Affine>) {
let drawdata_base = self.scene.drawdata_stream.len();
let mut cur_transform = self.scene.transform_stream.last().copied();
if let Some(transform) = transform {
if cur_transform.is_none() {
@ -172,39 +157,6 @@ impl<'a> SceneBuilder<'a> {
self.encode_transform(Affine::IDENTITY);
}
self.scene.append(&fragment.data, &transform);
match &mut self.resources {
ResourceData::Scene(res) => {
for patch in &fragment.resources.patches {
match patch {
ResourcePatch::Ramp {
drawdata_offset,
stops,
} => {
let stops = &fragment.resources.stops[stops.clone()];
let ramp_id = res.add_ramp(stops);
let patch_base = *drawdata_offset + drawdata_base;
(&mut self.scene.drawdata_stream[patch_base..patch_base + 4])
.copy_from_slice(bytemuck::bytes_of(&ramp_id));
}
}
}
}
ResourceData::Fragment(res) => {
let stops_base = res.stops.len();
res.stops.extend_from_slice(&fragment.resources.stops);
res.patches.extend(fragment.resources.patches.iter().map(
|pending| match pending {
ResourcePatch::Ramp {
drawdata_offset,
stops,
} => ResourcePatch::Ramp {
drawdata_offset: drawdata_offset + drawdata_base,
stops: stops.start + stops_base..stops.end + stops_base,
},
},
));
}
}
// Prevent fragments from affecting transform state. Should we allow this?
if let Some(transform) = cur_transform {
self.transform(transform);
@ -325,19 +277,15 @@ impl<'a> SceneBuilder<'a> {
}
fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 {
match &mut self.resources {
ResourceData::Scene(res) => res.add_ramp(stops),
ResourceData::Fragment(res) => {
let stops_start = res.stops.len();
res.stops.extend_from_slice(stops);
let id = res.patches.len() as u32;
res.patches.push(ResourcePatch::Ramp {
drawdata_offset: self.scene.drawdata_stream.len(),
stops: stops_start..stops_start + stops.len(),
});
id
}
}
let offset = self.scene.drawdata_stream.len();
let resources = &mut self.scene.resources;
let stops_start = resources.stops.len();
resources.stops.extend_from_slice(stops);
resources.patches.push(ResourcePatch::Ramp {
offset,
stops: stops_start..stops_start + stops.len(),
});
0
}
/// Start a clip.
@ -366,22 +314,6 @@ impl<'a> SceneBuilder<'a> {
self.scene.n_clip += 1;
}
}
enum ResourceData<'a> {
Fragment(&'a mut FragmentResources),
Scene(&'a mut ResourceContext),
}
impl ResourceData<'_> {
fn clear(&mut self) {
match self {
Self::Fragment(res) => {
res.patches.clear();
res.stops.clear();
}
_ => {}
}
}
}
// Tags for draw objects. See shader/drawtag.h for the authoritative source.
const DRAWTAG_FILLCOLOR: u32 = 0x44;

View file

@ -22,11 +22,9 @@ pub use blend::{BlendMode, Compose, Mix};
pub use builder::SceneBuilder;
pub use style::*;
use super::brush::*;
use super::geometry::{Affine, Point};
use super::path::PathElement;
use core::ops::Range;
use super::resource::{ResourceBundle, ResourcePatch};
/// Raw data streams describing an encoded scene.
#[derive(Default)]
@ -40,6 +38,7 @@ pub struct SceneData {
pub n_path: u32,
pub n_pathseg: u32,
pub n_clip: u32,
pub resources: ResourceBundle,
}
impl SceneData {
@ -57,6 +56,7 @@ impl SceneData {
self.n_path = 0;
self.n_pathseg = 0;
self.n_clip = 0;
self.resources.clear();
if !is_fragment {
self.transform_stream
.push(Affine::new(&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0]));
@ -65,9 +65,11 @@ impl SceneData {
}
fn append(&mut self, other: &SceneData, transform: &Option<Affine>) {
let stops_base = self.resources.stops.len();
let drawdata_base = self.drawdata_stream.len();
if let Some(transform) = *transform {
self.transform_stream
.extend(other.transform_stream.iter().map(|x| *x * transform));
.extend(other.transform_stream.iter().map(|x| transform * *x));
} else {
self.transform_stream
.extend_from_slice(&other.transform_stream);
@ -82,6 +84,20 @@ impl SceneData {
self.n_path += other.n_path;
self.n_pathseg += other.n_pathseg;
self.n_clip += other.n_clip;
self.resources
.stops
.extend_from_slice(&other.resources.stops);
self.resources
.patches
.extend(other.resources.patches.iter().map(|patch| match patch {
ResourcePatch::Ramp { offset, stops } => {
let stops = stops.start + stops_base..stops.end + stops_base;
ResourcePatch::Ramp {
offset: drawdata_base + offset,
stops,
}
}
}));
}
}
@ -103,7 +119,6 @@ impl Scene {
#[derive(Default)]
pub struct SceneFragment {
data: SceneData,
resources: FragmentResources,
}
impl SceneFragment {
@ -122,16 +137,3 @@ impl SceneFragment {
}
}
}
#[derive(Default)]
struct FragmentResources {
patches: Vec<ResourcePatch>,
stops: Vec<GradientStop>,
}
enum ResourcePatch {
Ramp {
drawdata_offset: usize,
stops: Range<usize>,
},
}