Skip to content

Commit

Permalink
Support setting AA method dynamically
Browse files Browse the repository at this point in the history
This replaces the static anti-aliasing setting with a dynamic option in
the form of two new settings (one used during pipeline creation and one
used during a render):

- The `FullShaders` collection now maintains up to 3 `fine` stage
  pipeline variants. This can be driven using a new optional
  `RendererOptions` field called `preferred_antialiasing_method`, which
  determines which fine stage pipeline variants should get  instantiated
  at start up.

- `RenderParams` now requires an `AaConfig` which selects which `fine`
  stage variant to use.

- Added a new key binding (`M`) to the `with_winit` example to
  dynamically cycle through all 3 AA methods.
  • Loading branch information
armansito committed Oct 31, 2023
1 parent 3fab38a commit 66bdcb1
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 77 deletions.
4 changes: 3 additions & 1 deletion examples/headless/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,11 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
let queue = &device_handle.queue;
let mut renderer = vello::Renderer::new(
device,
&RendererOptions {
RendererOptions {
surface_format: None,
timestamp_period: queue.get_timestamp_period(),
use_cpu: false,
preferred_antialiasing_method: Some(vello::AaConfig::Area),
},
)
.or_else(|_| bail!("Got non-Send/Sync error from creating renderer"))?;
Expand Down Expand Up @@ -140,6 +141,7 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
.unwrap_or(vello::peniko::Color::BLACK),
width,
height,
antialiasing_method: vello::AaConfig::Area,
};
let mut scene = Scene::new();
let mut builder = SceneBuilder::for_scene(&mut scene);
Expand Down
3 changes: 2 additions & 1 deletion examples/scenes/src/test_scenes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1210,10 +1210,11 @@ fn splash_screen(sb: &mut SceneBuilder, params: &mut SceneParams) {
" Space: reset transform",
" S: toggle stats",
" V: toggle vsync",
" M: cycle AA method",
" Q, E: rotate",
];
// Tweak to make it fit with tiger
let a = Affine::scale(0.12) * Affine::translate((-90.0, -50.0));
let a = Affine::scale(0.11) * Affine::translate((-90.0, -50.0));
for (i, s) in strings.iter().enumerate() {
let text_size = if i == 0 { 60.0 } else { 40.0 };
params.text.add(
Expand Down
2 changes: 2 additions & 0 deletions examples/with_bevy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl FromWorld for VelloRenderer {
&RendererOptions {
surface_format: None,
timestamp_period: queue.0.get_timestamp_period(),
preferred_antialiasing_method: Some(vello::AaConfig::Area),
},
)
.unwrap(),
Expand Down Expand Up @@ -65,6 +66,7 @@ fn render_scenes(
base_color: vello::peniko::Color::AQUAMARINE,
width: gpu_image.size.x as u32,
height: gpu_image.size.y as u32,
antialiasing_method: vello::AaConfig::Area,
};
renderer
.0
Expand Down
37 changes: 31 additions & 6 deletions examples/with_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use vello::util::RenderSurface;
use vello::{
kurbo::{Affine, Vec2},
util::RenderContext,
Renderer, Scene, SceneBuilder,
AaConfig, Renderer, Scene, SceneBuilder,
};
use vello::{BumpAllocators, RendererOptions, SceneFragment};

Expand Down Expand Up @@ -85,10 +85,11 @@ fn run(
renderers[id] = Some(
Renderer::new(
&render_cx.devices[id].device,
&RendererOptions {
RendererOptions {
surface_format: Some(render_state.surface.format),
timestamp_period: render_cx.devices[id].queue.get_timestamp_period(),
use_cpu: use_cpu,
preferred_antialiasing_method: None,
},
)
.expect("Could create renderer"),
Expand All @@ -111,6 +112,10 @@ fn run(
let mut scene_complexity: Option<BumpAllocators> = None;
let mut complexity_shown = false;
let mut vsync_on = true;

const AA_CONFIGS: [AaConfig; 3] = [AaConfig::Area, AaConfig::Msaa8, AaConfig::Msaa16];
let mut aa_config_ix = 0;

let mut frame_start_time = Instant::now();
let start = Instant::now();

Expand All @@ -129,6 +134,7 @@ fn run(
}
let mut profile_stored = None;
let mut prev_scene_ix = scene_ix - 1;
let mut prev_aa_config_ix = aa_config_ix;
let mut profile_taken = Instant::now();
// _event_loop is used on non-wasm platforms to create new windows
event_loop.run(move |event, _event_loop, control_flow| match event {
Expand Down Expand Up @@ -173,6 +179,9 @@ fn run(
Some(VirtualKeyCode::C) => {
stats.clear_min_and_max();
}
Some(VirtualKeyCode::M) => {
aa_config_ix = (aa_config_ix + 1) % AA_CONFIGS.len();
}
Some(VirtualKeyCode::P) => {
if let Some(renderer) = &renderers[render_state.surface.dev_id] {
if let Some(profile_result) = &renderer
Expand Down Expand Up @@ -325,12 +334,26 @@ fn run(
// Allow looping forever
scene_ix = scene_ix.rem_euclid(scenes.scenes.len() as i32);
let example_scene = &mut scenes.scenes[scene_ix as usize];
let mut reset_title = false;
if prev_scene_ix != scene_ix {
transform = Affine::IDENTITY;
prev_scene_ix = scene_ix;
render_state
.window
.set_title(&format!("Vello demo - {}", example_scene.config.name));
reset_title = true;
}
if prev_aa_config_ix != aa_config_ix {
prev_aa_config_ix = aa_config_ix;
reset_title = true;
}
if reset_title {
let aa_str = match AA_CONFIGS[aa_config_ix] {
AaConfig::Area => "Analytic Area",
AaConfig::Msaa16 => "16xMSAA",
AaConfig::Msaa8 => "8xMSAA",
};
render_state.window.set_title(&format!(
"Vello demo - {} - AA method: {}",
example_scene.config.name, aa_str
));
}
let mut builder = SceneBuilder::for_fragment(&mut fragment);
let mut scene_params = SceneParams {
Expand All @@ -356,6 +379,7 @@ fn run(
.unwrap_or(Color::BLACK),
width,
height,
antialiasing_method: AA_CONFIGS[aa_config_ix],
};
let mut builder = SceneBuilder::for_scene(&mut scene);
let mut transform = transform;
Expand Down Expand Up @@ -492,12 +516,13 @@ fn run(
eprintln!("Creating renderer {id}");
Renderer::new(
&render_cx.devices[id].device,
&RendererOptions {
RendererOptions {
surface_format: Some(render_state.surface.format),
timestamp_period: render_cx.devices[id]
.queue
.get_timestamp_period(),
use_cpu,
preferred_antialiasing_method: None,
},
)
.expect("Could create renderer")
Expand Down
43 changes: 25 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,17 @@ pub type Error = Box<dyn std::error::Error>;
pub type Result<T> = std::result::Result<T, Error>;

/// Possible configurations for antialiasing.
#[derive(PartialEq, Eq)]
#[allow(unused)]
enum AaConfig {
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AaConfig {
Area,
Msaa8,
Msaa16,
}

/// Configuration of antialiasing. Currently this is static, but could be switched to
/// a launch option or even finer-grained.
const ANTIALIASING: AaConfig = AaConfig::Msaa16;

/// Renders a scene into a texture or surface.
#[cfg(feature = "wgpu")]
pub struct Renderer {
options: RendererOptions,
engine: WgpuEngine,
shaders: FullShaders,
blit: Option<BlitPipeline>,
Expand All @@ -86,8 +82,6 @@ pub struct Renderer {
profiler: GpuProfiler,
#[cfg(feature = "wgpu-profiler")]
pub profile_result: Option<Vec<wgpu_profiler::GpuTimerScopeResult>>,
#[cfg(feature = "hot_reload")]
use_cpu: bool,
}

/// Parameters used in a single render that are configurable by the client.
Expand All @@ -99,43 +93,56 @@ pub struct RenderParams {
/// Dimensions of the rasterization target
pub width: u32,
pub height: u32,

/// The anti-aliasing algorithm. The selected algorithm must have been initialized while
/// constructing the `Renderer`.
pub antialiasing_method: AaConfig,
}

#[cfg(feature = "wgpu")]
pub struct RendererOptions {
/// The format of the texture used for surfaces with this renderer/device
/// If None, the renderer cannot be used with surfaces
pub surface_format: Option<TextureFormat>,

/// The timestamp period from [`wgpu::Queue::get_timestamp_period`]
/// Used when the wgpu-profiler feature is enabled
pub timestamp_period: f32,

/// If true, run all stages up to fine rasterization on the CPU.
/// TODO: Consider evolving this so that the CPU stages can be configured dynamically via
/// `RenderParams`.
pub use_cpu: bool,

/// The anti-aliasing specialization that should be used when creating the pipelines. `None`
/// initializes all variants.
pub preferred_antialiasing_method: Option<AaConfig>,
}

#[cfg(feature = "wgpu")]
impl Renderer {
/// Creates a new renderer for the specified device.
pub fn new(device: &Device, render_options: &RendererOptions) -> Result<Self> {
pub fn new(device: &Device, options: RendererOptions) -> Result<Self> {
let mut engine = WgpuEngine::new();
let mut shaders = shaders::full_shaders(device, &mut engine)?;
if render_options.use_cpu {
let mut shaders = shaders::full_shaders(device, &mut engine, &options)?;
if options.use_cpu {
shaders.install_cpu_shaders(&mut engine);
}
let blit = render_options
let blit = options
.surface_format
.map(|surface_format| BlitPipeline::new(device, surface_format));
let timestamp_period = options.timestamp_period;
Ok(Self {
options,
engine,
shaders,
blit,
target: None,
// Use 3 pending frames
#[cfg(feature = "wgpu-profiler")]
profiler: GpuProfiler::new(3, render_options.timestamp_period, device.features()),
profiler: GpuProfiler::new(3, timestamp_period, device.features()),
#[cfg(feature = "wgpu-profiler")]
profile_result: None,
#[cfg(feature = "hot_reload")]
use_cpu: render_options.use_cpu,
})
}

Expand Down Expand Up @@ -241,8 +248,8 @@ impl Renderer {
pub async fn reload_shaders(&mut self, device: &Device) -> Result<()> {
device.push_error_scope(wgpu::ErrorFilter::Validation);
let mut engine = WgpuEngine::new();
let mut shaders = shaders::full_shaders(device, &mut engine)?;
if self.use_cpu {
let mut shaders = shaders::full_shaders(device, &mut engine, &self.options)?;
if self.options.use_cpu {
shaders.install_cpu_shaders(&mut engine);
}
let error = device.pop_error_scope().await;
Expand Down
18 changes: 13 additions & 5 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{
engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy},
shaders::FullShaders,
AaConfig, RenderParams, Scene, ANTIALIASING,
AaConfig, RenderParams, Scene,
};
use vello_encoding::{Encoding, WorkgroupSize};

Expand All @@ -16,6 +16,8 @@ pub struct Render {

/// Resources produced by pipeline, needed for fine rasterization.
struct FineResources {
aa_config: AaConfig,

config_buf: ResourceProxy,
bump_buf: ResourceProxy,
tile_buf: ResourceProxy,
Expand Down Expand Up @@ -393,6 +395,7 @@ impl Render {
let out_image = ImageProxy::new(params.width, params.height, ImageFormat::Rgba8);
self.fine_wg_count = Some(wg_counts.fine);
self.fine_resources = Some(FineResources {
aa_config: params.antialiasing_method,
config_buf,
bump_buf,
tile_buf,
Expand All @@ -414,10 +417,10 @@ impl Render {
pub fn record_fine(&mut self, shaders: &FullShaders, recording: &mut Recording) {
let fine_wg_count = self.fine_wg_count.take().unwrap();
let fine = self.fine_resources.take().unwrap();
match ANTIALIASING {
match fine.aa_config {
AaConfig::Area => {
recording.dispatch(
shaders.fine,
shaders.fine_area.expect("unsupported AA mode: area"),
fine_wg_count,
[
fine.config_buf,
Expand All @@ -432,16 +435,21 @@ impl Render {
}
_ => {
if self.mask_buf.is_none() {
let mask_lut = match ANTIALIASING {
let mask_lut = match fine.aa_config {
AaConfig::Msaa16 => crate::mask::make_mask_lut_16(),
AaConfig::Msaa8 => crate::mask::make_mask_lut(),
_ => unreachable!(),
};
let buf = recording.upload("mask lut", mask_lut);
self.mask_buf = Some(buf.into());
}
let shader = match fine.aa_config {
AaConfig::Msaa16 => shaders.fine_msaa16.expect("unsupported AA mode: msaa16"),
AaConfig::Msaa8 => shaders.fine_msaa8.expect("unsupported AA mode: msaa8"),
_ => unreachable!(),
};
recording.dispatch(
shaders.fine,
shader,
fine_wg_count,
[
fine.config_buf,
Expand Down
Loading

0 comments on commit 66bdcb1

Please sign in to comment.