mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 20:31:29 +11:00
Merge pull request #287 from armansito/pr-clear-color
Add a base-color uniform to the (full) fine rasterization stage
This commit is contained in:
commit
5d915113e8
|
@ -92,15 +92,16 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
|
|||
let mut builder = SceneBuilder::for_fragment(&mut fragment);
|
||||
let example_scene = &mut scenes.scenes[index];
|
||||
let mut text = SimpleText::new();
|
||||
let mut params = SceneParams {
|
||||
let mut scene_params = SceneParams {
|
||||
time: args.time.unwrap_or(0.),
|
||||
text: &mut text,
|
||||
resolution: None,
|
||||
base_color: None,
|
||||
};
|
||||
(example_scene.function)(&mut builder, &mut params);
|
||||
(example_scene.function)(&mut builder, &mut scene_params);
|
||||
builder.finish();
|
||||
let mut transform = Affine::IDENTITY;
|
||||
let (width, height) = if let Some(resolution) = params.resolution {
|
||||
let (width, height) = if let Some(resolution) = scene_params.resolution {
|
||||
let ratio = resolution.x / resolution.y;
|
||||
let (new_width, new_height) = match (args.x_resolution, args.y_resolution) {
|
||||
(None, None) => (resolution.x.ceil() as u32, resolution.y.ceil() as u32),
|
||||
|
@ -126,6 +127,15 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
|
|||
(Some(x), Some(y)) => (x.try_into()?, y.try_into()?),
|
||||
}
|
||||
};
|
||||
let render_params = vello::RenderParams {
|
||||
base_color: args
|
||||
.args
|
||||
.base_color
|
||||
.or(scene_params.base_color)
|
||||
.unwrap_or(vello::peniko::Color::BLACK),
|
||||
width,
|
||||
height,
|
||||
};
|
||||
let mut scene = Scene::new();
|
||||
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||
builder.append(&fragment, Some(transform));
|
||||
|
@ -147,7 +157,7 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
|
|||
});
|
||||
let view = target.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
renderer
|
||||
.render_to_texture(&device, &queue, &scene, &view, width, height)
|
||||
.render_to_texture(&device, &queue, &scene, &view, &render_params)
|
||||
.or_else(|_| bail!("Got non-Send/Sync error from rendering"))?;
|
||||
// (width * 4).next_multiple_of(256)
|
||||
let padded_byte_width = {
|
||||
|
|
|
@ -5,7 +5,7 @@ mod svg;
|
|||
mod test_scenes;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::{Args, Subcommand};
|
||||
use download::Download;
|
||||
pub use simple_text::SimpleText;
|
||||
|
@ -13,12 +13,13 @@ pub use simple_text::SimpleText;
|
|||
pub use svg::{default_scene, scene_from_files};
|
||||
pub use test_scenes::test_scenes;
|
||||
|
||||
use vello::{kurbo::Vec2, SceneBuilder};
|
||||
use vello::{kurbo::Vec2, peniko::Color, SceneBuilder};
|
||||
|
||||
pub struct SceneParams<'a> {
|
||||
pub time: f64,
|
||||
pub text: &'a mut SimpleText,
|
||||
pub resolution: Option<Vec2>,
|
||||
pub base_color: Option<vello::peniko::Color>,
|
||||
}
|
||||
|
||||
pub struct SceneConfig {
|
||||
|
@ -46,6 +47,12 @@ pub struct Arguments {
|
|||
#[arg(help_heading = "Scene Selection", global(false))]
|
||||
/// The svg files paths to render
|
||||
svgs: Option<Vec<PathBuf>>,
|
||||
#[arg(help_heading = "Render Parameters")]
|
||||
#[arg(long, global(false), value_parser = parse_color)]
|
||||
/// The base color applied as the blend background to the rasterizer.
|
||||
/// Format is CSS style hexidecimal (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) or
|
||||
/// an SVG color name such as "aliceblue"
|
||||
pub base_color: Option<Color>,
|
||||
#[clap(subcommand)]
|
||||
command: Option<Command>,
|
||||
}
|
||||
|
@ -88,3 +95,7 @@ impl Command {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_color(s: &str) -> Result<Color> {
|
||||
Color::parse(s).ok_or(anyhow!("'{s}' is not a valid color"))
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ pub fn test_scenes() -> SceneSet {
|
|||
scene!(blend_grid),
|
||||
scene!(conflation_artifacts),
|
||||
scene!(labyrinth),
|
||||
scene!(base_color_test: animated),
|
||||
];
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
scenes.push(ExampleScene {
|
||||
|
@ -582,6 +583,21 @@ fn labyrinth(sb: &mut SceneBuilder, _: &mut SceneParams) {
|
|||
)
|
||||
}
|
||||
|
||||
fn base_color_test(sb: &mut SceneBuilder, params: &mut SceneParams) {
|
||||
// Cycle through the hue value every 5 seconds (t % 5) * 360/5
|
||||
let color = Color::hlc((params.time % 5.0) * 72.0, 80.0, 80.0);
|
||||
params.base_color = Some(color);
|
||||
|
||||
// Blend a white square over it.
|
||||
sb.fill(
|
||||
Fill::NonZero,
|
||||
Affine::IDENTITY,
|
||||
Color::rgba8(255, 255, 255, 128),
|
||||
None,
|
||||
&Rect::new(50.0, 50.0, 500.0, 500.0),
|
||||
);
|
||||
}
|
||||
|
||||
fn around_center(xform: Affine, center: Point) -> Affine {
|
||||
Affine::translate(center.to_vec2()) * xform * Affine::translate(-center.to_vec2())
|
||||
}
|
||||
|
|
|
@ -46,7 +46,11 @@ fn render_scenes(
|
|||
) {
|
||||
for scene in &mut scenes {
|
||||
let gpu_image = gpu_images.get(&scene.1).unwrap();
|
||||
|
||||
let params = vello::RenderParams {
|
||||
base_color: vello::peniko::Color::AQUAMARINE,
|
||||
width: gpu_image.size.x as u32,
|
||||
height: gpu_image.size.y as u32,
|
||||
};
|
||||
renderer
|
||||
.0
|
||||
.render_to_texture(
|
||||
|
@ -54,8 +58,7 @@ fn render_scenes(
|
|||
&*queue,
|
||||
&scene.0,
|
||||
&gpu_image.texture_view,
|
||||
gpu_image.size.x as u32,
|
||||
gpu_image.size.y as u32,
|
||||
¶ms,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ use vello::SceneFragment;
|
|||
use vello::{
|
||||
block_on_wgpu,
|
||||
kurbo::{Affine, Vec2},
|
||||
peniko::Color,
|
||||
util::RenderContext,
|
||||
Renderer, Scene, SceneBuilder,
|
||||
};
|
||||
|
@ -164,16 +165,29 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
|||
window.set_title(&format!("Vello demo - {}", example_scene.config.name));
|
||||
}
|
||||
let mut builder = SceneBuilder::for_fragment(&mut fragment);
|
||||
let mut params = SceneParams {
|
||||
let mut scene_params = SceneParams {
|
||||
time: start.elapsed().as_secs_f64(),
|
||||
text: &mut simple_text,
|
||||
resolution: None,
|
||||
base_color: None,
|
||||
};
|
||||
(example_scene.function)(&mut builder, &mut params);
|
||||
(example_scene.function)(&mut builder, &mut scene_params);
|
||||
builder.finish();
|
||||
|
||||
// If the user specifies a base color in the CLI we use that. Otherwise we use any
|
||||
// color specified by the scene. The default is black.
|
||||
let render_params = vello::RenderParams {
|
||||
base_color: args
|
||||
.args
|
||||
.base_color
|
||||
.or(scene_params.base_color)
|
||||
.unwrap_or(Color::BLACK),
|
||||
width,
|
||||
height,
|
||||
};
|
||||
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||
let mut transform = transform;
|
||||
if let Some(resolution) = params.resolution {
|
||||
if let Some(resolution) = scene_params.resolution {
|
||||
let factor = Vec2::new(surface.config.width as f64, surface.config.height as f64);
|
||||
let scale_factor = (factor.x / resolution.x).min(factor.y / resolution.y);
|
||||
transform = transform * Affine::scale(scale_factor);
|
||||
|
@ -193,8 +207,7 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
|||
&device_handle.queue,
|
||||
&scene,
|
||||
&surface_texture,
|
||||
width,
|
||||
height,
|
||||
&render_params,
|
||||
),
|
||||
)
|
||||
.expect("failed to render to surface");
|
||||
|
@ -208,8 +221,7 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
|||
&device_handle.queue,
|
||||
&scene,
|
||||
&surface_texture,
|
||||
width,
|
||||
height,
|
||||
&render_params,
|
||||
)
|
||||
.expect("failed to render to surface");
|
||||
surface_texture.present();
|
||||
|
|
|
@ -187,6 +187,9 @@ fn main(
|
|||
let xy = vec2(f32(global_id.x * PIXELS_PER_THREAD), f32(global_id.y));
|
||||
#ifdef full
|
||||
var rgba: array<vec4<f32>, PIXELS_PER_THREAD>;
|
||||
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
||||
rgba[i] = unpack4x8unorm(config.base_color).wzyx;
|
||||
}
|
||||
var blend_stack: array<array<u32, PIXELS_PER_THREAD>, BLEND_STACK_SPLIT>;
|
||||
var clip_depth = 0u;
|
||||
var area: array<f32, PIXELS_PER_THREAD>;
|
||||
|
|
|
@ -7,6 +7,11 @@ struct Config {
|
|||
target_width: u32,
|
||||
target_height: u32,
|
||||
|
||||
// The initial color applied to the pixels in a tile during the fine stage.
|
||||
// This is only used in the full pipeline. The format is packed RGBA8 in MSB
|
||||
// order.
|
||||
base_color: u32,
|
||||
|
||||
n_drawobj: u32,
|
||||
n_path: u32,
|
||||
n_clip: u32,
|
||||
|
|
|
@ -48,7 +48,8 @@ pub struct Layout {
|
|||
pub linewidth_base: u32,
|
||||
}
|
||||
|
||||
/// Scene configuration.
|
||||
/// Scene configuration. This data structure must be kept in sync with the definition in
|
||||
/// shaders/shared/config.wgsl.
|
||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Config {
|
||||
|
@ -60,6 +61,8 @@ pub struct Config {
|
|||
pub target_width: u32,
|
||||
/// Height of the target in pixels.
|
||||
pub target_height: u32,
|
||||
/// The base background color applied to the target before any blends.
|
||||
pub base_color: u32,
|
||||
/// Layout of packed scene data.
|
||||
pub layout: Layout,
|
||||
/// Size of binning buffer allocation (in u32s).
|
||||
|
|
35
src/lib.rs
35
src/lib.rs
|
@ -52,6 +52,17 @@ pub struct Renderer {
|
|||
target: Option<TargetTexture>,
|
||||
}
|
||||
|
||||
/// Parameters used in a single render that are configurable by the client.
|
||||
pub struct RenderParams {
|
||||
/// The background color applied to the target. This value is only applicable to the full
|
||||
/// pipeline.
|
||||
pub base_color: peniko::Color,
|
||||
|
||||
/// Dimensions of the rasterization target
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
/// Creates a new renderer for the specified device.
|
||||
pub fn new(device: &Device) -> Result<Self> {
|
||||
|
@ -77,10 +88,9 @@ impl Renderer {
|
|||
queue: &Queue,
|
||||
scene: &Scene,
|
||||
texture: &TextureView,
|
||||
width: u32,
|
||||
height: u32,
|
||||
params: &RenderParams,
|
||||
) -> Result<()> {
|
||||
let (recording, target) = render::render_full(scene, &self.shaders, width, height);
|
||||
let (recording, target) = render::render_full(scene, &self.shaders, params);
|
||||
let external_resources = [ExternalResource::Image(
|
||||
*target.as_image().unwrap(),
|
||||
texture,
|
||||
|
@ -103,9 +113,10 @@ impl Renderer {
|
|||
queue: &Queue,
|
||||
scene: &Scene,
|
||||
surface: &SurfaceTexture,
|
||||
width: u32,
|
||||
height: u32,
|
||||
params: &RenderParams,
|
||||
) -> Result<()> {
|
||||
let width = params.width;
|
||||
let height = params.height;
|
||||
let mut target = self
|
||||
.target
|
||||
.take()
|
||||
|
@ -115,7 +126,7 @@ impl Renderer {
|
|||
if target.width != width || target.height != height {
|
||||
target = TargetTexture::new(device, width, height);
|
||||
}
|
||||
self.render_to_texture(device, queue, scene, &target.view, width, height)?;
|
||||
self.render_to_texture(device, queue, scene, &target.view, ¶ms)?;
|
||||
let mut encoder =
|
||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
{
|
||||
|
@ -177,12 +188,11 @@ impl Renderer {
|
|||
queue: &Queue,
|
||||
scene: &Scene,
|
||||
texture: &TextureView,
|
||||
width: u32,
|
||||
height: u32,
|
||||
params: &RenderParams,
|
||||
) -> Result<()> {
|
||||
let mut render = Render::new();
|
||||
let encoding = scene.data();
|
||||
let recording = render.render_encoding_coarse(encoding, &self.shaders, width, height, true);
|
||||
let recording = render.render_encoding_coarse(encoding, &self.shaders, params, true);
|
||||
let target = render.out_image();
|
||||
let bump_buf = render.bump_buf();
|
||||
self.engine.run_recording(device, queue, &recording, &[])?;
|
||||
|
@ -216,9 +226,10 @@ impl Renderer {
|
|||
queue: &Queue,
|
||||
scene: &Scene,
|
||||
surface: &SurfaceTexture,
|
||||
width: u32,
|
||||
height: u32,
|
||||
params: &RenderParams,
|
||||
) -> Result<()> {
|
||||
let width = params.width;
|
||||
let height = params.height;
|
||||
let mut target = self
|
||||
.target
|
||||
.take()
|
||||
|
@ -228,7 +239,7 @@ impl Renderer {
|
|||
if target.width != width || target.height != height {
|
||||
target = TargetTexture::new(device, width, height);
|
||||
}
|
||||
self.render_to_texture_async(device, queue, scene, &target.view, width, height)
|
||||
self.render_to_texture_async(device, queue, scene, &target.view, params)
|
||||
.await?;
|
||||
let mut encoder =
|
||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
|
|
|
@ -5,8 +5,9 @@ use bytemuck::{Pod, Zeroable};
|
|||
use crate::{
|
||||
encoding::Encoding,
|
||||
engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy},
|
||||
peniko::Color,
|
||||
shaders::{self, FullShaders, Shaders},
|
||||
Scene,
|
||||
RenderParams, Scene,
|
||||
};
|
||||
|
||||
/// State for a render in progress.
|
||||
|
@ -54,6 +55,8 @@ const BIN_HEADER_SIZE: u64 = 8;
|
|||
const TILE_SIZE: u64 = 8;
|
||||
const SEGMENT_SIZE: u64 = 24;
|
||||
|
||||
// This data structure must be kept in sync with encoding::Config and the definition in
|
||||
// shaders/shared/config.wgsl.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
||||
struct Config {
|
||||
|
@ -61,6 +64,7 @@ struct Config {
|
|||
height_in_tiles: u32,
|
||||
target_width: u32,
|
||||
target_height: u32,
|
||||
base_color: u32,
|
||||
n_drawobj: u32,
|
||||
n_path: u32,
|
||||
n_clip: u32,
|
||||
|
@ -179,10 +183,9 @@ fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) {
|
|||
pub fn render_full(
|
||||
scene: &Scene,
|
||||
shaders: &FullShaders,
|
||||
width: u32,
|
||||
height: u32,
|
||||
params: &RenderParams,
|
||||
) -> (Recording, ResourceProxy) {
|
||||
render_encoding_full(scene.data(), shaders, width, height)
|
||||
render_encoding_full(scene.data(), shaders, params)
|
||||
}
|
||||
|
||||
/// Create a single recording with both coarse and fine render stages.
|
||||
|
@ -192,11 +195,10 @@ pub fn render_full(
|
|||
pub fn render_encoding_full(
|
||||
encoding: &Encoding,
|
||||
shaders: &FullShaders,
|
||||
width: u32,
|
||||
height: u32,
|
||||
params: &RenderParams,
|
||||
) -> (Recording, ResourceProxy) {
|
||||
let mut render = Render::new();
|
||||
let mut recording = render.render_encoding_coarse(encoding, shaders, width, height, false);
|
||||
let mut recording = render.render_encoding_coarse(encoding, shaders, params, false);
|
||||
let out_image = render.out_image();
|
||||
render.record_fine(shaders, &mut recording);
|
||||
(recording, out_image.into())
|
||||
|
@ -228,8 +230,7 @@ impl Render {
|
|||
&mut self,
|
||||
encoding: &Encoding,
|
||||
shaders: &FullShaders,
|
||||
width: u32,
|
||||
height: u32,
|
||||
params: &RenderParams,
|
||||
robust: bool,
|
||||
) -> Recording {
|
||||
use crate::encoding::{resource::ResourceCache, PackedEncoding};
|
||||
|
@ -256,15 +257,16 @@ impl Render {
|
|||
let n_drawobj = n_paths;
|
||||
let n_clip = encoding.n_clips;
|
||||
|
||||
let new_width = next_multiple_of(width, 16);
|
||||
let new_height = next_multiple_of(height, 16);
|
||||
let new_width = next_multiple_of(params.width, 16);
|
||||
let new_height = next_multiple_of(params.height, 16);
|
||||
|
||||
let info_size = packed.layout.bin_data_start;
|
||||
let config = crate::encoding::Config {
|
||||
width_in_tiles: new_width / 16,
|
||||
height_in_tiles: new_height / 16,
|
||||
target_width: width,
|
||||
target_height: height,
|
||||
target_width: params.width,
|
||||
target_height: params.height,
|
||||
base_color: params.base_color.to_premul_u32(),
|
||||
binning_size: self.binning_info_size - info_size,
|
||||
tiles_size: self.tiles_size,
|
||||
segments_size: self.segments_size,
|
||||
|
@ -515,7 +517,7 @@ impl Render {
|
|||
recording.free_resource(draw_monoid_buf);
|
||||
recording.free_resource(bin_header_buf);
|
||||
recording.free_resource(path_buf);
|
||||
let out_image = ImageProxy::new(width, height, ImageFormat::Rgba8);
|
||||
let out_image = ImageProxy::new(params.width, params.height, ImageFormat::Rgba8);
|
||||
self.width_in_tiles = config.width_in_tiles;
|
||||
self.height_in_tiles = config.height_in_tiles;
|
||||
self.fine = Some(FineResources {
|
||||
|
|
Loading…
Reference in a new issue