mirror of
https://github.com/italicsjenga/vello.git
synced 2025-02-24 00:27:43 +11:00
Fixes the warnings remaining in the code, which should in turn let us check that in CI. Also checks the optional features, which are easy enough to break.
421 lines
14 KiB
Rust
421 lines
14 KiB
Rust
//! Take an encoded scene and create a graph to render it
|
|
|
|
use crate::{
|
|
engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy},
|
|
shaders::FullShaders,
|
|
RenderParams, Scene,
|
|
};
|
|
use vello_encoding::{Encoding, WorkgroupSize};
|
|
|
|
/// State for a render in progress.
|
|
pub struct Render {
|
|
fine_wg_count: Option<WorkgroupSize>,
|
|
fine_resources: Option<FineResources>,
|
|
}
|
|
|
|
/// Resources produced by pipeline, needed for fine rasterization.
|
|
struct FineResources {
|
|
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,
|
|
|
|
out_image: ImageProxy,
|
|
}
|
|
|
|
pub fn render_full(
|
|
scene: &Scene,
|
|
shaders: &FullShaders,
|
|
params: &RenderParams,
|
|
) -> (Recording, ResourceProxy) {
|
|
render_encoding_full(scene.data(), shaders, params)
|
|
}
|
|
|
|
/// Create a single recording with both coarse and fine render stages.
|
|
///
|
|
/// This function is not recommended when the scene can be complex, as it does not
|
|
/// implement robust dynamic memory.
|
|
pub fn render_encoding_full(
|
|
encoding: &Encoding,
|
|
shaders: &FullShaders,
|
|
params: &RenderParams,
|
|
) -> (Recording, ResourceProxy) {
|
|
let mut render = Render::new();
|
|
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())
|
|
}
|
|
|
|
impl Render {
|
|
pub fn new() -> Self {
|
|
Render {
|
|
fine_wg_count: None,
|
|
fine_resources: None,
|
|
}
|
|
}
|
|
|
|
/// Prepare a recording for the coarse rasterization phase.
|
|
///
|
|
/// The `robust` parameter controls whether we're preparing for readback
|
|
/// of the atomic bump buffer, for robust dynamic memory.
|
|
pub fn render_encoding_coarse(
|
|
&mut self,
|
|
encoding: &Encoding,
|
|
shaders: &FullShaders,
|
|
params: &RenderParams,
|
|
robust: bool,
|
|
) -> Recording {
|
|
use vello_encoding::{RenderConfig, Resolver};
|
|
|
|
let mut recording = Recording::default();
|
|
let mut resolver = Resolver::new();
|
|
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.width,
|
|
image.0.height,
|
|
image.0.data.data(),
|
|
);
|
|
}
|
|
|
|
let cpu_config =
|
|
RenderConfig::new(&layout, params.width, params.height, ¶ms.base_color);
|
|
let buffer_sizes = &cpu_config.buffer_sizes;
|
|
let wg_counts = &cpu_config.workgroup_counts;
|
|
|
|
let scene_buf = ResourceProxy::Buf(recording.upload("scene", packed));
|
|
let config_buf = ResourceProxy::Buf(
|
|
recording.upload_uniform("config", bytemuck::bytes_of(&cpu_config.gpu)),
|
|
);
|
|
let info_bin_data_buf = ResourceProxy::new_buf(
|
|
buffer_sizes.bin_data.size_in_bytes() as u64,
|
|
"info_bin_data_buf",
|
|
);
|
|
let tile_buf =
|
|
ResourceProxy::new_buf(buffer_sizes.tiles.size_in_bytes().into(), "tile_buf");
|
|
let segments_buf =
|
|
ResourceProxy::new_buf(buffer_sizes.segments.size_in_bytes().into(), "segments_buf");
|
|
let ptcl_buf = ResourceProxy::new_buf(buffer_sizes.ptcl.size_in_bytes().into(), "ptcl_buf");
|
|
let reduced_buf = ResourceProxy::new_buf(
|
|
buffer_sizes.path_reduced.size_in_bytes().into(),
|
|
"reduced_buf",
|
|
);
|
|
// TODO: really only need pathtag_wgs - 1
|
|
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;
|
|
if wg_counts.use_large_path_scan {
|
|
let reduced2_buf = ResourceProxy::new_buf(
|
|
buffer_sizes.path_reduced2.size_in_bytes().into(),
|
|
"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(),
|
|
"tagmonoid_buf",
|
|
);
|
|
let pathtag_scan = if wg_counts.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(),
|
|
"path_bbox_buf",
|
|
);
|
|
recording.dispatch(
|
|
shaders.bbox_clear,
|
|
wg_counts.bbox_clear,
|
|
[config_buf, path_bbox_buf],
|
|
);
|
|
let cubic_buf =
|
|
ResourceProxy::new_buf(buffer_sizes.cubics.size_in_bytes().into(), "cubic_buf");
|
|
recording.dispatch(
|
|
shaders.pathseg,
|
|
wg_counts.path_seg,
|
|
[
|
|
config_buf,
|
|
scene_buf,
|
|
tagmonoid_buf,
|
|
path_bbox_buf,
|
|
cubic_buf,
|
|
],
|
|
);
|
|
let draw_reduced_buf = ResourceProxy::new_buf(
|
|
buffer_sizes.draw_reduced.size_in_bytes().into(),
|
|
"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(),
|
|
"draw_monoid_buf",
|
|
);
|
|
let clip_inp_buf = ResourceProxy::new_buf(
|
|
buffer_sizes.clip_inps.size_in_bytes().into(),
|
|
"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(), "clip_el_buf");
|
|
let clip_bic_buf = ResourceProxy::new_buf(
|
|
buffer_sizes.clip_bics.size_in_bytes().into(),
|
|
"clip_bic_buf",
|
|
);
|
|
if wg_counts.clip_reduce.0 > 0 {
|
|
recording.dispatch(
|
|
shaders.clip_reduce,
|
|
wg_counts.clip_reduce,
|
|
[
|
|
config_buf,
|
|
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(),
|
|
"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(),
|
|
"draw_bbox_buf",
|
|
);
|
|
let bump_buf = BufProxy::new(buffer_sizes.bump_alloc.size_in_bytes().into(), "bump_buf");
|
|
let bin_header_buf = ResourceProxy::new_buf(
|
|
buffer_sizes.bin_headers.size_in_bytes().into(),
|
|
"bin_header_buf",
|
|
);
|
|
recording.clear_all(bump_buf);
|
|
let bump_buf = ResourceProxy::Buf(bump_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(path_bbox_buf);
|
|
recording.free_resource(clip_bbox_buf);
|
|
// Note: this only needs to be rounded up because of the workaround to store the tile_offset
|
|
// in storage rather than workgroup memory.
|
|
let path_buf =
|
|
ResourceProxy::new_buf(buffer_sizes.paths.size_in_bytes().into(), "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.dispatch(
|
|
shaders.path_coarse,
|
|
wg_counts.path_coarse,
|
|
[
|
|
config_buf,
|
|
scene_buf,
|
|
tagmonoid_buf,
|
|
cubic_buf,
|
|
path_buf,
|
|
bump_buf,
|
|
tile_buf,
|
|
segments_buf,
|
|
],
|
|
);
|
|
recording.free_resource(tagmonoid_buf);
|
|
recording.free_resource(cubic_buf);
|
|
recording.dispatch(
|
|
shaders.backdrop,
|
|
wg_counts.backdrop,
|
|
[config_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.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);
|
|
self.fine_wg_count = Some(wg_counts.fine);
|
|
self.fine_resources = Some(FineResources {
|
|
config_buf,
|
|
bump_buf,
|
|
tile_buf,
|
|
segments_buf,
|
|
ptcl_buf,
|
|
gradient_image,
|
|
info_bin_data_buf,
|
|
image_atlas: ResourceProxy::Image(image_atlas),
|
|
out_image,
|
|
});
|
|
if robust {
|
|
recording.download(*bump_buf.as_buf().unwrap());
|
|
}
|
|
recording.free_resource(bump_buf);
|
|
recording
|
|
}
|
|
|
|
/// Run fine rasterization assuming the coarse phase succeeded.
|
|
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();
|
|
recording.dispatch(
|
|
shaders.fine,
|
|
fine_wg_count,
|
|
[
|
|
fine.config_buf,
|
|
fine.tile_buf,
|
|
fine.segments_buf,
|
|
ResourceProxy::Image(fine.out_image),
|
|
fine.ptcl_buf,
|
|
fine.gradient_image,
|
|
fine.info_bin_data_buf,
|
|
fine.image_atlas,
|
|
],
|
|
);
|
|
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);
|
|
}
|
|
|
|
/// Get the output image.
|
|
///
|
|
/// This is going away, as the caller will add the output image to the bind
|
|
/// map.
|
|
pub fn out_image(&self) -> ImageProxy {
|
|
self.fine_resources.as_ref().unwrap().out_image
|
|
}
|
|
|
|
pub fn bump_buf(&self) -> BufProxy {
|
|
*self
|
|
.fine_resources
|
|
.as_ref()
|
|
.unwrap()
|
|
.bump_buf
|
|
.as_buf()
|
|
.unwrap()
|
|
}
|
|
}
|