vello/tests/src/clear.rs
Raph Levien 5a9b8d9243 Start applying compute pass to tests
Use compute pass for tests in tests subdir. This is also shaking out some issues that weren't apparent from just collatz.

In particular, we need more autorelease pools to prevent things from leaking. As of this commit, the "clear" test runs correctly but the others haven't yet been converted to the compute_pass format.
2022-04-20 13:45:42 -07:00

132 lines
4.4 KiB
Rust

// Copyright 2021 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
//! Utilities (and a benchmark) for clearing buffers with compute shaders.
use piet_gpu_hal::{include_shader, BindType, BufferUsage, ComputePass, DescriptorSet};
use piet_gpu_hal::{Buffer, Pipeline};
use crate::config::Config;
use crate::runner::Runner;
use crate::test_result::TestResult;
const WG_SIZE: u64 = 256;
/// The shader code for clearing buffers.
pub struct ClearCode {
pipeline: Pipeline,
}
/// The stage resources for clearing buffers.
pub struct ClearStage {
n_elements: u64,
config_buf: Buffer,
}
/// The binding for clearing buffers.
pub struct ClearBinding {
descriptor_set: DescriptorSet,
}
pub unsafe fn run_clear_test(runner: &mut Runner, config: &Config) -> TestResult {
let mut result = TestResult::new("clear buffers");
let n_elements: u64 = config.size.choose(1 << 12, 1 << 20, 1 << 24);
let out_buf = runner.buf_down(n_elements * 4, BufferUsage::empty());
let code = ClearCode::new(runner);
let stage = ClearStage::new_with_value(runner, n_elements, 0x42);
let binding = stage.bind(runner, &code, &out_buf.dev_buf);
let n_iter = config.n_iter;
let mut total_elapsed = 0.0;
for i in 0..n_iter {
let mut commands = runner.commands();
let mut pass = commands.compute_pass(0, 1);
stage.record(&mut pass, &code, &binding);
pass.end();
if i == 0 || config.verify_all {
commands.cmd_buf.memory_barrier();
commands.download(&out_buf);
}
total_elapsed += runner.submit(commands);
if i == 0 || config.verify_all {
let dst = out_buf.map_read(..);
if let Some(failure) = verify(dst.cast_slice()) {
result.fail(format!("failure at {}", failure));
}
}
}
result.timing(total_elapsed, n_elements * n_iter);
result
}
impl ClearCode {
pub unsafe fn new(runner: &mut Runner) -> ClearCode {
let code = include_shader!(&runner.session, "../shader/gen/clear");
let pipeline = runner
.session
.create_compute_pipeline(code, &[BindType::BufReadOnly, BindType::Buffer])
.unwrap();
ClearCode { pipeline }
}
}
impl ClearStage {
pub unsafe fn new_with_value(runner: &mut Runner, n_elements: u64, value: u32) -> ClearStage {
let config = [n_elements as u32, value];
let config_buf = runner
.session
.create_buffer_init(&config, BufferUsage::STORAGE)
.unwrap();
ClearStage {
n_elements,
config_buf,
}
}
pub unsafe fn bind(
&self,
runner: &mut Runner,
code: &ClearCode,
out_buf: &Buffer,
) -> ClearBinding {
let descriptor_set = runner
.session
.create_simple_descriptor_set(&code.pipeline, &[&self.config_buf, out_buf])
.unwrap();
ClearBinding { descriptor_set }
}
pub unsafe fn record(&self, pass: &mut ComputePass, code: &ClearCode, bindings: &ClearBinding) {
let n_workgroups = (self.n_elements + WG_SIZE - 1) / WG_SIZE;
// An issue: for clearing large buffers (>16M), we need to check the
// number of workgroups against the (dynamically detected) limit, and
// potentially issue multiple dispatches.
pass.dispatch(
&code.pipeline,
&bindings.descriptor_set,
(n_workgroups as u32, 1, 1),
(WG_SIZE as u32, 1, 1),
);
// One thing that's missing here is registering the buffers so
// they can be safely dropped by Rust code before the execution
// of the command buffer completes.
}
}
// Verify that the data is cleared.
fn verify(data: &[u32]) -> Option<usize> {
data.iter().position(|val| *val != 0x42)
}