mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 20:31:29 +11:00
RenderParams struct for render-time options
The texture and surface render API now takes render-time parameters (such as clear color, target width/height) as a RenderParams struct. The examples have been updated to demonstrate this. The with_winit example now accepts a clear color as a command line option.
This commit is contained in:
parent
8eabc12a72
commit
05fa8c7c39
|
@ -126,6 +126,11 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
|
||||||
(Some(x), Some(y)) => (x.try_into()?, y.try_into()?),
|
(Some(x), Some(y)) => (x.try_into()?, y.try_into()?),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let params = vello::RenderParams {
|
||||||
|
clear_color: vello::peniko::Color::BLACK,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
let mut scene = Scene::new();
|
let mut scene = Scene::new();
|
||||||
let mut builder = SceneBuilder::for_scene(&mut scene);
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||||
builder.append(&fragment, Some(transform));
|
builder.append(&fragment, Some(transform));
|
||||||
|
@ -147,7 +152,7 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
|
||||||
});
|
});
|
||||||
let view = target.create_view(&wgpu::TextureViewDescriptor::default());
|
let view = target.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
renderer
|
renderer
|
||||||
.render_to_texture(&device, &queue, &scene, &view, width, height)
|
.render_to_texture(&device, &queue, &scene, &view, ¶ms)
|
||||||
.or_else(|_| bail!("Got non-Send/Sync error from rendering"))?;
|
.or_else(|_| bail!("Got non-Send/Sync error from rendering"))?;
|
||||||
// (width * 4).next_multiple_of(256)
|
// (width * 4).next_multiple_of(256)
|
||||||
let padded_byte_width = {
|
let padded_byte_width = {
|
||||||
|
|
|
@ -46,7 +46,11 @@ fn render_scenes(
|
||||||
) {
|
) {
|
||||||
for scene in &mut scenes {
|
for scene in &mut scenes {
|
||||||
let gpu_image = gpu_images.get(&scene.1).unwrap();
|
let gpu_image = gpu_images.get(&scene.1).unwrap();
|
||||||
|
let params = vello::RenderParams {
|
||||||
|
clear_color: vello::peniko::Color::AQUAMARINE,
|
||||||
|
width: gpu_image.size.x as u32,
|
||||||
|
height: gpu_image.size.y as u32,
|
||||||
|
};
|
||||||
renderer
|
renderer
|
||||||
.0
|
.0
|
||||||
.render_to_texture(
|
.render_to_texture(
|
||||||
|
@ -54,8 +58,7 @@ fn render_scenes(
|
||||||
&*queue,
|
&*queue,
|
||||||
&scene.0,
|
&scene.0,
|
||||||
&gpu_image.texture_view,
|
&gpu_image.texture_view,
|
||||||
gpu_image.size.x as u32,
|
¶ms,
|
||||||
gpu_image.size.y as u32,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,16 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{
|
||||||
|
builder::{PossibleValuesParser, TypedValueParser as _},
|
||||||
|
CommandFactory, Parser,
|
||||||
|
};
|
||||||
use scenes::{SceneParams, SceneSet, SimpleText};
|
use scenes::{SceneParams, SceneSet, SimpleText};
|
||||||
use vello::SceneFragment;
|
use vello::SceneFragment;
|
||||||
use vello::{
|
use vello::{
|
||||||
block_on_wgpu,
|
block_on_wgpu,
|
||||||
kurbo::{Affine, Vec2},
|
kurbo::{Affine, Vec2},
|
||||||
|
peniko::Color,
|
||||||
util::RenderContext,
|
util::RenderContext,
|
||||||
Renderer, Scene, SceneBuilder,
|
Renderer, Scene, SceneBuilder,
|
||||||
};
|
};
|
||||||
|
@ -49,10 +53,51 @@ struct Args {
|
||||||
/// Switch between scenes with left and right arrow keys
|
/// Switch between scenes with left and right arrow keys
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
scene: Option<i32>,
|
scene: Option<i32>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
default_value_t = ClearColor::Black,
|
||||||
|
value_parser = PossibleValuesParser::new(["black", "white", "aquamarine", "crimson"])
|
||||||
|
.map(|s| s.parse::<ClearColor>().unwrap())
|
||||||
|
)]
|
||||||
|
clear_color: ClearColor,
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
args: scenes::Arguments,
|
args: scenes::Arguments,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum ClearColor {
|
||||||
|
Black,
|
||||||
|
White,
|
||||||
|
Crimson,
|
||||||
|
Aquamarine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ClearColor {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
Self::Black => "black",
|
||||||
|
Self::White => "white",
|
||||||
|
Self::Crimson => "crimson",
|
||||||
|
Self::Aquamarine => "aquamarine",
|
||||||
|
};
|
||||||
|
s.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::str::FromStr for ClearColor {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"black" => Ok(Self::Black),
|
||||||
|
"white" => Ok(Self::White),
|
||||||
|
"crimson" => Ok(Self::Crimson),
|
||||||
|
"aquamarine" => Ok(Self::Aquamarine),
|
||||||
|
_ => Err(format!("invalid color: {s}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut scenes: SceneSet) {
|
async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut scenes: SceneSet) {
|
||||||
use winit::{event::*, event_loop::ControlFlow};
|
use winit::{event::*, event_loop::ControlFlow};
|
||||||
let mut render_cx = RenderContext::new().unwrap();
|
let mut render_cx = RenderContext::new().unwrap();
|
||||||
|
@ -75,6 +120,12 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
||||||
if let Some(set_scene) = args.scene {
|
if let Some(set_scene) = args.scene {
|
||||||
scene_ix = set_scene;
|
scene_ix = set_scene;
|
||||||
}
|
}
|
||||||
|
let clear_color = match args.clear_color {
|
||||||
|
ClearColor::Black => Color::BLACK,
|
||||||
|
ClearColor::White => Color::WHITE,
|
||||||
|
ClearColor::Crimson => Color::CRIMSON,
|
||||||
|
ClearColor::Aquamarine => Color::AQUAMARINE,
|
||||||
|
};
|
||||||
let mut prev_scene_ix = scene_ix - 1;
|
let mut prev_scene_ix = scene_ix - 1;
|
||||||
event_loop.run(move |event, _, control_flow| match event {
|
event_loop.run(move |event, _, control_flow| match event {
|
||||||
Event::WindowEvent {
|
Event::WindowEvent {
|
||||||
|
@ -155,6 +206,12 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
||||||
let height = surface.config.height;
|
let height = surface.config.height;
|
||||||
let device_handle = &render_cx.devices[surface.dev_id];
|
let device_handle = &render_cx.devices[surface.dev_id];
|
||||||
|
|
||||||
|
let render_params = vello::RenderParams {
|
||||||
|
clear_color,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
|
||||||
// Allow looping forever
|
// Allow looping forever
|
||||||
scene_ix = scene_ix.rem_euclid(scenes.scenes.len() as i32);
|
scene_ix = scene_ix.rem_euclid(scenes.scenes.len() as i32);
|
||||||
let example_scene = &mut scenes.scenes[scene_ix as usize];
|
let example_scene = &mut scenes.scenes[scene_ix as usize];
|
||||||
|
@ -164,16 +221,16 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
||||||
window.set_title(&format!("Vello demo - {}", example_scene.config.name));
|
window.set_title(&format!("Vello demo - {}", example_scene.config.name));
|
||||||
}
|
}
|
||||||
let mut builder = SceneBuilder::for_fragment(&mut fragment);
|
let mut builder = SceneBuilder::for_fragment(&mut fragment);
|
||||||
let mut params = SceneParams {
|
let mut scene_params = SceneParams {
|
||||||
time: start.elapsed().as_secs_f64(),
|
time: start.elapsed().as_secs_f64(),
|
||||||
text: &mut simple_text,
|
text: &mut simple_text,
|
||||||
resolution: None,
|
resolution: None,
|
||||||
};
|
};
|
||||||
(example_scene.function)(&mut builder, &mut params);
|
(example_scene.function)(&mut builder, &mut scene_params);
|
||||||
builder.finish();
|
builder.finish();
|
||||||
let mut builder = SceneBuilder::for_scene(&mut scene);
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||||
let mut transform = transform;
|
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 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);
|
let scale_factor = (factor.x / resolution.x).min(factor.y / resolution.y);
|
||||||
transform = transform * Affine::scale(scale_factor);
|
transform = transform * Affine::scale(scale_factor);
|
||||||
|
@ -193,8 +250,7 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
||||||
&device_handle.queue,
|
&device_handle.queue,
|
||||||
&scene,
|
&scene,
|
||||||
&surface_texture,
|
&surface_texture,
|
||||||
width,
|
&render_params,
|
||||||
height,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.expect("failed to render to surface");
|
.expect("failed to render to surface");
|
||||||
|
@ -208,8 +264,7 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args, mut s
|
||||||
&device_handle.queue,
|
&device_handle.queue,
|
||||||
&scene,
|
&scene,
|
||||||
&surface_texture,
|
&surface_texture,
|
||||||
width,
|
&render_params,
|
||||||
height,
|
|
||||||
)
|
)
|
||||||
.expect("failed to render to surface");
|
.expect("failed to render to surface");
|
||||||
surface_texture.present();
|
surface_texture.present();
|
||||||
|
|
35
src/lib.rs
35
src/lib.rs
|
@ -52,8 +52,19 @@ pub struct Renderer {
|
||||||
target: Option<TargetTexture>,
|
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 clear_color: peniko::Color,
|
||||||
|
|
||||||
|
/// Dimensions of the rasterization target
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
/// Creates a new renderer for the specified device.
|
/// Creates a new renderer for the specified device with default options.
|
||||||
pub fn new(device: &Device) -> Result<Self> {
|
pub fn new(device: &Device) -> Result<Self> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let shaders = shaders::full_shaders(device, &mut engine)?;
|
let shaders = shaders::full_shaders(device, &mut engine)?;
|
||||||
|
@ -77,10 +88,9 @@ impl Renderer {
|
||||||
queue: &Queue,
|
queue: &Queue,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
texture: &TextureView,
|
texture: &TextureView,
|
||||||
width: u32,
|
params: &RenderParams,
|
||||||
height: u32,
|
|
||||||
) -> Result<()> {
|
) -> 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(
|
let external_resources = [ExternalResource::Image(
|
||||||
*target.as_image().unwrap(),
|
*target.as_image().unwrap(),
|
||||||
texture,
|
texture,
|
||||||
|
@ -103,9 +113,12 @@ impl Renderer {
|
||||||
queue: &Queue,
|
queue: &Queue,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
surface: &SurfaceTexture,
|
surface: &SurfaceTexture,
|
||||||
|
params: &RenderParams,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let width = params.width;
|
||||||
|
let height = params.height;
|
||||||
let mut target = self
|
let mut target = self
|
||||||
.target
|
.target
|
||||||
.take()
|
.take()
|
||||||
|
@ -115,7 +128,7 @@ impl Renderer {
|
||||||
if target.width != width || target.height != height {
|
if target.width != width || target.height != height {
|
||||||
target = TargetTexture::new(device, width, 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 =
|
let mut encoder =
|
||||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
{
|
{
|
||||||
|
@ -177,12 +190,11 @@ impl Renderer {
|
||||||
queue: &Queue,
|
queue: &Queue,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
texture: &TextureView,
|
texture: &TextureView,
|
||||||
width: u32,
|
params: &RenderParams,
|
||||||
height: u32,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut render = Render::new();
|
let mut render = Render::new();
|
||||||
let encoding = scene.data();
|
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 target = render.out_image();
|
||||||
let bump_buf = render.bump_buf();
|
let bump_buf = render.bump_buf();
|
||||||
self.engine.run_recording(device, queue, &recording, &[])?;
|
self.engine.run_recording(device, queue, &recording, &[])?;
|
||||||
|
@ -216,9 +228,10 @@ impl Renderer {
|
||||||
queue: &Queue,
|
queue: &Queue,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
surface: &SurfaceTexture,
|
surface: &SurfaceTexture,
|
||||||
width: u32,
|
params: &RenderParams,
|
||||||
height: u32,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let width = params.width;
|
||||||
|
let height = params.height;
|
||||||
let mut target = self
|
let mut target = self
|
||||||
.target
|
.target
|
||||||
.take()
|
.take()
|
||||||
|
@ -228,7 +241,7 @@ impl Renderer {
|
||||||
if target.width != width || target.height != height {
|
if target.width != width || target.height != height {
|
||||||
target = TargetTexture::new(device, width, 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?;
|
.await?;
|
||||||
let mut encoder =
|
let mut encoder =
|
||||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy},
|
engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy},
|
||||||
peniko::Color,
|
peniko::Color,
|
||||||
shaders::{self, FullShaders, Shaders},
|
shaders::{self, FullShaders, Shaders},
|
||||||
Scene,
|
RenderParams, Scene,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// State for a render in progress.
|
/// State for a render in progress.
|
||||||
|
@ -22,8 +22,6 @@ pub struct Render {
|
||||||
ptcl_size: u32,
|
ptcl_size: u32,
|
||||||
width_in_tiles: u32,
|
width_in_tiles: u32,
|
||||||
height_in_tiles: u32,
|
height_in_tiles: u32,
|
||||||
/// The background color applied to the target
|
|
||||||
clear_color: Color,
|
|
||||||
fine: Option<FineResources>,
|
fine: Option<FineResources>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,10 +181,9 @@ fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) {
|
||||||
pub fn render_full(
|
pub fn render_full(
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
shaders: &FullShaders,
|
shaders: &FullShaders,
|
||||||
width: u32,
|
params: &RenderParams,
|
||||||
height: u32,
|
|
||||||
) -> (Recording, ResourceProxy) {
|
) -> (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.
|
/// Create a single recording with both coarse and fine render stages.
|
||||||
|
@ -196,11 +193,10 @@ pub fn render_full(
|
||||||
pub fn render_encoding_full(
|
pub fn render_encoding_full(
|
||||||
encoding: &Encoding,
|
encoding: &Encoding,
|
||||||
shaders: &FullShaders,
|
shaders: &FullShaders,
|
||||||
width: u32,
|
params: &RenderParams,
|
||||||
height: u32,
|
|
||||||
) -> (Recording, ResourceProxy) {
|
) -> (Recording, ResourceProxy) {
|
||||||
let mut render = Render::new();
|
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();
|
let out_image = render.out_image();
|
||||||
render.record_fine(shaders, &mut recording);
|
render.record_fine(shaders, &mut recording);
|
||||||
(recording, out_image.into())
|
(recording, out_image.into())
|
||||||
|
@ -220,7 +216,6 @@ impl Render {
|
||||||
ptcl_size: (1 << 25) / 4 as u32,
|
ptcl_size: (1 << 25) / 4 as u32,
|
||||||
width_in_tiles: 0,
|
width_in_tiles: 0,
|
||||||
height_in_tiles: 0,
|
height_in_tiles: 0,
|
||||||
clear_color: Color::BLACK,
|
|
||||||
fine: None,
|
fine: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,8 +228,7 @@ impl Render {
|
||||||
&mut self,
|
&mut self,
|
||||||
encoding: &Encoding,
|
encoding: &Encoding,
|
||||||
shaders: &FullShaders,
|
shaders: &FullShaders,
|
||||||
width: u32,
|
params: &RenderParams,
|
||||||
height: u32,
|
|
||||||
robust: bool,
|
robust: bool,
|
||||||
) -> Recording {
|
) -> Recording {
|
||||||
use crate::encoding::{resource::ResourceCache, PackedEncoding};
|
use crate::encoding::{resource::ResourceCache, PackedEncoding};
|
||||||
|
@ -261,16 +255,16 @@ impl Render {
|
||||||
let n_drawobj = n_paths;
|
let n_drawobj = n_paths;
|
||||||
let n_clip = encoding.n_clips;
|
let n_clip = encoding.n_clips;
|
||||||
|
|
||||||
let new_width = next_multiple_of(width, 16);
|
let new_width = next_multiple_of(params.width, 16);
|
||||||
let new_height = next_multiple_of(height, 16);
|
let new_height = next_multiple_of(params.height, 16);
|
||||||
|
|
||||||
let info_size = packed.layout.bin_data_start;
|
let info_size = packed.layout.bin_data_start;
|
||||||
let config = crate::encoding::Config {
|
let config = crate::encoding::Config {
|
||||||
width_in_tiles: new_width / 16,
|
width_in_tiles: new_width / 16,
|
||||||
height_in_tiles: new_height / 16,
|
height_in_tiles: new_height / 16,
|
||||||
target_width: width,
|
target_width: params.width,
|
||||||
target_height: height,
|
target_height: params.height,
|
||||||
clear_color: self.clear_color.to_premul_u32(),
|
clear_color: params.clear_color.to_premul_u32(),
|
||||||
binning_size: self.binning_info_size - info_size,
|
binning_size: self.binning_info_size - info_size,
|
||||||
tiles_size: self.tiles_size,
|
tiles_size: self.tiles_size,
|
||||||
segments_size: self.segments_size,
|
segments_size: self.segments_size,
|
||||||
|
@ -521,7 +515,7 @@ impl Render {
|
||||||
recording.free_resource(draw_monoid_buf);
|
recording.free_resource(draw_monoid_buf);
|
||||||
recording.free_resource(bin_header_buf);
|
recording.free_resource(bin_header_buf);
|
||||||
recording.free_resource(path_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.width_in_tiles = config.width_in_tiles;
|
||||||
self.height_in_tiles = config.height_in_tiles;
|
self.height_in_tiles = config.height_in_tiles;
|
||||||
self.fine = Some(FineResources {
|
self.fine = Some(FineResources {
|
||||||
|
|
Loading…
Reference in a new issue