use crate::recording::{BufferProxy, ImageFormat, ImageProxy, Recording, ResourceProxy};
use crate::shaders::FullShaders;
use crate::{AaConfig, RenderParams};
#[cfg(feature = "wgpu")]
use crate::Scene;
use vello_encoding::{Encoding, Resolver, WorkgroupSize, make_mask_lut, make_mask_lut_16};
pub struct Render {
fine_wg_count: Option<WorkgroupSize>,
fine_resources: Option<FineResources>,
mask_buf: Option<ResourceProxy>,
#[cfg(feature = "debug_layers")]
captured_buffers: Option<CapturedBuffers>,
}
#[cfg(feature = "debug_layers")]
impl Drop for Render {
fn drop(&mut self) {
if self.captured_buffers.is_some() {
unreachable!("Render captured buffers without freeing them");
}
}
}
struct FineResources {
aa_config: AaConfig,
config_buf: ResourceProxy,
bump_buf: ResourceProxy,
tile_buf: ResourceProxy,
segments_buf: ResourceProxy,
ptcl_buf: ResourceProxy,
gradient_image: ResourceProxy,
info_bin_data_buf: ResourceProxy,
image_atlas: ResourceProxy,
blend_spill_buf: ResourceProxy,
out_image: ImageProxy,
}
#[cfg(feature = "debug_layers")]
pub struct CapturedBuffers {
pub sizes: vello_encoding::BufferSizes,
pub path_bboxes: BufferProxy,
pub lines: BufferProxy,
}
#[cfg(feature = "debug_layers")]
impl CapturedBuffers {
pub fn release_buffers(self, recording: &mut Recording) {
recording.free_buffer(self.path_bboxes);
recording.free_buffer(self.lines);
}
}
#[cfg(feature = "wgpu")]
pub(crate) fn render_full(
scene: &Scene,
resolver: &mut Resolver,
shaders: &FullShaders,
params: &RenderParams,
) -> (Recording, ResourceProxy) {
render_encoding_full(scene.encoding(), resolver, shaders, params)
}
#[cfg(feature = "wgpu")]
pub(crate) fn render_encoding_full(
encoding: &Encoding,
resolver: &mut Resolver,
shaders: &FullShaders,
params: &RenderParams,
) -> (Recording, ResourceProxy) {
let mut render = Render::new();
let mut recording = render.render_encoding_coarse(encoding, resolver, shaders, params, false);
let out_image = render.out_image();
render.record_fine(shaders, &mut recording);
(recording, out_image.into())
}
impl Default for Render {
fn default() -> Self {
Self::new()
}
}
impl Render {
pub fn new() -> Self {
Self {
fine_wg_count: None,
fine_resources: None,
mask_buf: None,
#[cfg(feature = "debug_layers")]
captured_buffers: None,
}
}
pub fn render_encoding_coarse(
&mut self,
encoding: &Encoding,
resolver: &mut Resolver,
shaders: &FullShaders,
params: &RenderParams,
robust: bool,
) -> Recording {
use vello_encoding::RenderConfig;
let mut recording = Recording::default();
let mut packed = vec![];
let (layout, ramps, images) = resolver.resolve(encoding, &mut packed);
let gradient_image = if ramps.height == 0 {
ResourceProxy::new_image(1, 1, ImageFormat::Rgba8)
} else {
let data: &[u8] = bytemuck::cast_slice(ramps.data);
ResourceProxy::Image(recording.upload_image(
ramps.width,
ramps.height,
ImageFormat::Rgba8,
data,
))
};
let image_atlas = if images.images.is_empty() {
ImageProxy::new(1, 1, ImageFormat::Rgba8)
} else {
ImageProxy::new(images.width, images.height, ImageFormat::Rgba8)
};
for image in images.images {
recording.write_image(image_atlas, image.1, image.2, image.0.clone());
}
let cpu_config =
RenderConfig::new(&layout, params.width, params.height, ¶ms.base_color);
if (cpu_config.workgroup_counts.coarse.0
* cpu_config.workgroup_counts.coarse.1
* cpu_config.workgroup_counts.coarse.2)
> 256
{
log::warn!(
"Trying to paint too large image. {}x{}.\n\
See https://github.com/linebender/vello/issues/680 for details",
params.width,
params.height
);
}
let buffer_sizes = &cpu_config.buffer_sizes;
let wg_counts = &cpu_config.workgroup_counts;
if packed.is_empty() {
packed.resize(size_of::<u32>(), u8::MAX);
}
let scene_buf = ResourceProxy::Buffer(recording.upload("vello.scene", packed));
let config_buf = ResourceProxy::Buffer(
recording.upload_uniform("vello.config", bytemuck::bytes_of(&cpu_config.gpu)),
);
let info_bin_data_buf = ResourceProxy::new_buf(
buffer_sizes.bin_data.size_in_bytes() as u64,
"vello.info_bin_data_buf",
);
let tile_buf =
ResourceProxy::new_buf(buffer_sizes.tiles.size_in_bytes().into(), "vello.tile_buf");
let segments_buf = ResourceProxy::new_buf(
buffer_sizes.segments.size_in_bytes().into(),
"vello.segments_buf",
);
let ptcl_buf =
ResourceProxy::new_buf(buffer_sizes.ptcl.size_in_bytes().into(), "vello.ptcl_buf");
let reduced_buf = ResourceProxy::new_buf(
buffer_sizes.path_reduced.size_in_bytes().into(),
"vello.reduced_buf",
);
recording.dispatch(
shaders.pathtag_reduce,
wg_counts.path_reduce,
[config_buf, scene_buf, reduced_buf],
);
let mut pathtag_parent = reduced_buf;
let mut large_pathtag_bufs = None;
let use_large_path_scan = wg_counts.use_large_path_scan && !shaders.pathtag_is_cpu;
if use_large_path_scan {
let reduced2_buf = ResourceProxy::new_buf(
buffer_sizes.path_reduced2.size_in_bytes().into(),
"vello.reduced2_buf",
);
recording.dispatch(
shaders.pathtag_reduce2,
wg_counts.path_reduce2,
[reduced_buf, reduced2_buf],
);
let reduced_scan_buf = ResourceProxy::new_buf(
buffer_sizes.path_reduced_scan.size_in_bytes().into(),
"reduced_scan_buf",
);
recording.dispatch(
shaders.pathtag_scan1,
wg_counts.path_scan1,
[reduced_buf, reduced2_buf, reduced_scan_buf],
);
pathtag_parent = reduced_scan_buf;
large_pathtag_bufs = Some((reduced2_buf, reduced_scan_buf));
}
let tagmonoid_buf = ResourceProxy::new_buf(
buffer_sizes.path_monoids.size_in_bytes().into(),
"vello.tagmonoid_buf",
);
let pathtag_scan = if use_large_path_scan {
shaders.pathtag_scan_large
} else {
shaders.pathtag_scan
};
recording.dispatch(
pathtag_scan,
wg_counts.path_scan,
[config_buf, scene_buf, pathtag_parent, tagmonoid_buf],
);
recording.free_resource(reduced_buf);
if let Some((reduced2, reduced_scan)) = large_pathtag_bufs {
recording.free_resource(reduced2);
recording.free_resource(reduced_scan);
}
let path_bbox_buf = ResourceProxy::new_buf(
buffer_sizes.path_bboxes.size_in_bytes().into(),
"vello.path_bbox_buf",
);
recording.dispatch(
shaders.bbox_clear,
wg_counts.bbox_clear,
[config_buf, path_bbox_buf],
);
let bump_buf = BufferProxy::new(
buffer_sizes.bump_alloc.size_in_bytes().into(),
"vello.bump_buf",
);
recording.clear_all(bump_buf);
let bump_buf = ResourceProxy::Buffer(bump_buf);
let lines_buf =
ResourceProxy::new_buf(buffer_sizes.lines.size_in_bytes().into(), "vello.lines_buf");
recording.dispatch(
shaders.flatten,
wg_counts.flatten,
[
config_buf,
scene_buf,
tagmonoid_buf,
path_bbox_buf,
bump_buf,
lines_buf,
],
);
let draw_reduced_buf = ResourceProxy::new_buf(
buffer_sizes.draw_reduced.size_in_bytes().into(),
"vello.draw_reduced_buf",
);
recording.dispatch(
shaders.draw_reduce,
wg_counts.draw_reduce,
[config_buf, scene_buf, draw_reduced_buf],
);
let draw_monoid_buf = ResourceProxy::new_buf(
buffer_sizes.draw_monoids.size_in_bytes().into(),
"vello.draw_monoid_buf",
);
let clip_inp_buf = ResourceProxy::new_buf(
buffer_sizes.clip_inps.size_in_bytes().into(),
"vello.clip_inp_buf",
);
recording.dispatch(
shaders.draw_leaf,
wg_counts.draw_leaf,
[
config_buf,
scene_buf,
draw_reduced_buf,
path_bbox_buf,
draw_monoid_buf,
info_bin_data_buf,
clip_inp_buf,
],
);
recording.free_resource(draw_reduced_buf);
let clip_el_buf = ResourceProxy::new_buf(
buffer_sizes.clip_els.size_in_bytes().into(),
"vello.clip_el_buf",
);
let clip_bic_buf = ResourceProxy::new_buf(
buffer_sizes.clip_bics.size_in_bytes().into(),
"vello.clip_bic_buf",
);
if wg_counts.clip_reduce.0 > 0 {
recording.dispatch(
shaders.clip_reduce,
wg_counts.clip_reduce,
[clip_inp_buf, path_bbox_buf, clip_bic_buf, clip_el_buf],
);
}
let clip_bbox_buf = ResourceProxy::new_buf(
buffer_sizes.clip_bboxes.size_in_bytes().into(),
"vello.clip_bbox_buf",
);
if wg_counts.clip_leaf.0 > 0 {
recording.dispatch(
shaders.clip_leaf,
wg_counts.clip_leaf,
[
config_buf,
clip_inp_buf,
path_bbox_buf,
clip_bic_buf,
clip_el_buf,
draw_monoid_buf,
clip_bbox_buf,
],
);
}
recording.free_resource(clip_inp_buf);
recording.free_resource(clip_bic_buf);
recording.free_resource(clip_el_buf);
let draw_bbox_buf = ResourceProxy::new_buf(
buffer_sizes.draw_bboxes.size_in_bytes().into(),
"vello.draw_bbox_buf",
);
let bin_header_buf = ResourceProxy::new_buf(
buffer_sizes.bin_headers.size_in_bytes().into(),
"vello.bin_header_buf",
);
recording.dispatch(
shaders.binning,
wg_counts.binning,
[
config_buf,
draw_monoid_buf,
path_bbox_buf,
clip_bbox_buf,
draw_bbox_buf,
bump_buf,
info_bin_data_buf,
bin_header_buf,
],
);
recording.free_resource(draw_monoid_buf);
recording.free_resource(clip_bbox_buf);
let path_buf =
ResourceProxy::new_buf(buffer_sizes.paths.size_in_bytes().into(), "vello.path_buf");
recording.dispatch(
shaders.tile_alloc,
wg_counts.tile_alloc,
[
config_buf,
scene_buf,
draw_bbox_buf,
bump_buf,
path_buf,
tile_buf,
],
);
recording.free_resource(draw_bbox_buf);
recording.free_resource(tagmonoid_buf);
let indirect_count_buf = BufferProxy::new(
buffer_sizes.indirect_count.size_in_bytes().into(),
"vello.indirect_count",
);
recording.dispatch(
shaders.path_count_setup,
wg_counts.path_count_setup,
[bump_buf, indirect_count_buf.into()],
);
let seg_counts_buf = ResourceProxy::new_buf(
buffer_sizes.seg_counts.size_in_bytes().into(),
"vello.seg_counts_buf",
);
recording.dispatch_indirect(
shaders.path_count,
indirect_count_buf,
0,
[
config_buf,
bump_buf,
lines_buf,
path_buf,
tile_buf,
seg_counts_buf,
],
);
recording.dispatch(
shaders.backdrop,
wg_counts.backdrop,
[config_buf, bump_buf, path_buf, tile_buf],
);
recording.dispatch(
shaders.coarse,
wg_counts.coarse,
[
config_buf,
scene_buf,
draw_monoid_buf,
bin_header_buf,
info_bin_data_buf,
path_buf,
tile_buf,
bump_buf,
ptcl_buf,
],
);
recording.dispatch(
shaders.path_tiling_setup,
wg_counts.path_tiling_setup,
[bump_buf, indirect_count_buf.into(), ptcl_buf],
);
recording.dispatch_indirect(
shaders.path_tiling,
indirect_count_buf,
0,
[
bump_buf,
seg_counts_buf,
lines_buf,
path_buf,
tile_buf,
segments_buf,
],
);
recording.free_buffer(indirect_count_buf);
recording.free_resource(seg_counts_buf);
recording.free_resource(scene_buf);
recording.free_resource(draw_monoid_buf);
recording.free_resource(bin_header_buf);
recording.free_resource(path_buf);
let out_image = ImageProxy::new(params.width, params.height, ImageFormat::Rgba8);
let blend_spill_buf = BufferProxy::new(
buffer_sizes.blend_spill.size_in_bytes().into(),
"vello.blend_spill",
);
self.fine_wg_count = Some(wg_counts.fine);
self.fine_resources = Some(FineResources {
aa_config: params.antialiasing_method,
config_buf,
bump_buf,
tile_buf,
segments_buf,
ptcl_buf,
gradient_image,
info_bin_data_buf,
blend_spill_buf: ResourceProxy::Buffer(blend_spill_buf),
image_atlas: ResourceProxy::Image(image_atlas),
out_image,
});
if robust {
recording.download(*bump_buf.as_buf().unwrap());
}
recording.free_resource(bump_buf);
#[cfg(feature = "debug_layers")]
{
if robust {
let path_bboxes = *path_bbox_buf.as_buf().unwrap();
let lines = *lines_buf.as_buf().unwrap();
recording.download(lines);
self.captured_buffers = Some(CapturedBuffers {
sizes: cpu_config.buffer_sizes,
path_bboxes,
lines,
});
} else {
recording.free_resource(path_bbox_buf);
recording.free_resource(lines_buf);
}
}
#[cfg(not(feature = "debug_layers"))]
{
recording.free_resource(path_bbox_buf);
recording.free_resource(lines_buf);
}
recording
}
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 fine.aa_config {
AaConfig::Area => {
recording.dispatch(
shaders
.fine_area
.expect("shaders not configured to support AA mode: area"),
fine_wg_count,
[
fine.config_buf,
fine.segments_buf,
fine.ptcl_buf,
fine.info_bin_data_buf,
fine.blend_spill_buf,
ResourceProxy::Image(fine.out_image),
fine.gradient_image,
fine.image_atlas,
],
);
}
_ => {
if self.mask_buf.is_none() {
let mask_lut = match fine.aa_config {
AaConfig::Msaa16 => make_mask_lut_16(),
AaConfig::Msaa8 => make_mask_lut(),
_ => unreachable!(),
};
let buf = recording.upload("vello.mask_lut", mask_lut);
self.mask_buf = Some(buf.into());
}
let fine_shader = match fine.aa_config {
AaConfig::Msaa16 => shaders
.fine_msaa16
.expect("shaders not configured to support AA mode: msaa16"),
AaConfig::Msaa8 => shaders
.fine_msaa8
.expect("shaders not configured to support AA mode: msaa8"),
_ => unreachable!(),
};
recording.dispatch(
fine_shader,
fine_wg_count,
[
fine.config_buf,
fine.segments_buf,
fine.ptcl_buf,
fine.info_bin_data_buf,
fine.blend_spill_buf,
ResourceProxy::Image(fine.out_image),
fine.gradient_image,
fine.image_atlas,
self.mask_buf.unwrap(),
],
);
}
}
recording.free_resource(fine.config_buf);
recording.free_resource(fine.tile_buf);
recording.free_resource(fine.segments_buf);
recording.free_resource(fine.ptcl_buf);
recording.free_resource(fine.gradient_image);
recording.free_resource(fine.image_atlas);
recording.free_resource(fine.info_bin_data_buf);
recording.free_resource(fine.blend_spill_buf);
if let Some(mask_buf) = self.mask_buf.take() {
recording.free_resource(mask_buf);
}
}
pub fn out_image(&self) -> ImageProxy {
self.fine_resources.as_ref().unwrap().out_image
}
pub fn bump_buf(&self) -> BufferProxy {
*self
.fine_resources
.as_ref()
.unwrap()
.bump_buf
.as_buf()
.unwrap()
}
#[cfg(feature = "debug_layers")]
pub fn take_captured_buffers(&mut self) -> Option<CapturedBuffers> {
self.captured_buffers.take()
}
}