2021-11-11 09:56:00 +11:00
|
|
|
// 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.
|
|
|
|
|
2022-04-21 03:21:49 +10:00
|
|
|
use piet_gpu_hal::{include_shader, BindType, BufferUsage, ComputePass, DescriptorSet};
|
2021-11-11 09:56:00 +11:00
|
|
|
use piet_gpu_hal::{Buffer, Pipeline};
|
|
|
|
|
|
|
|
use crate::config::Config;
|
2022-04-21 03:21:49 +10:00
|
|
|
use crate::runner::Runner;
|
2021-11-11 09:56:00 +11:00
|
|
|
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);
|
2021-11-21 02:14:23 +11:00
|
|
|
let out_buf = runner.buf_down(n_elements * 4, BufferUsage::empty());
|
2021-11-11 09:56:00 +11:00
|
|
|
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);
|
2021-11-12 02:26:32 +11:00
|
|
|
let n_iter = config.n_iter;
|
2021-11-11 09:56:00 +11:00
|
|
|
let mut total_elapsed = 0.0;
|
|
|
|
for i in 0..n_iter {
|
|
|
|
let mut commands = runner.commands();
|
2022-04-21 03:21:49 +10:00
|
|
|
let mut pass = commands.compute_pass(0, 1);
|
|
|
|
stage.record(&mut pass, &code, &binding);
|
|
|
|
pass.end();
|
2021-11-24 02:28:50 +11:00
|
|
|
if i == 0 || config.verify_all {
|
2021-11-11 09:56:00 +11:00
|
|
|
commands.cmd_buf.memory_barrier();
|
|
|
|
commands.download(&out_buf);
|
|
|
|
}
|
|
|
|
total_elapsed += runner.submit(commands);
|
2021-11-24 02:28:50 +11:00
|
|
|
if i == 0 || config.verify_all {
|
2021-11-26 08:12:25 +11:00
|
|
|
let dst = out_buf.map_read(..);
|
|
|
|
if let Some(failure) = verify(dst.cast_slice()) {
|
2021-11-11 09:56:00 +11:00
|
|
|
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 {
|
2021-11-12 11:17:04 +11:00
|
|
|
let code = include_shader!(&runner.session, "../shader/gen/clear");
|
2021-11-11 09:56:00 +11:00
|
|
|
let pipeline = runner
|
|
|
|
.session
|
2021-11-12 02:26:32 +11:00
|
|
|
.create_compute_pipeline(code, &[BindType::BufReadOnly, BindType::Buffer])
|
2021-11-11 09:56:00 +11:00
|
|
|
.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 }
|
|
|
|
}
|
|
|
|
|
2022-04-21 03:21:49 +10:00
|
|
|
pub unsafe fn record(&self, pass: &mut ComputePass, code: &ClearCode, bindings: &ClearBinding) {
|
2021-11-11 09:56:00 +11:00
|
|
|
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.
|
2022-04-21 03:21:49 +10:00
|
|
|
pass.dispatch(
|
2021-11-11 09:56:00 +11:00
|
|
|
&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)
|
|
|
|
}
|