use criterion::{
black_box, criterion_group, criterion_main,
Criterion, BenchmarkId, Throughput, PlotConfiguration, AxisScale
};
use shdrlib::core::{
Instance, InstanceCreateInfo, Device, DeviceCreateInfo, QueueCreateInfo,
Buffer,
};
use shdrlib::ex::helpers::buffer::*;
use std::sync::Arc;
use ash::vk;
mod bench_utils;
use bench_utils::setup_device;
fn bench_core_buffer_creation(c: &mut Criterion) {
let device = setup_device().expect("Failed to setup device");
let mut group = c.benchmark_group("core_buffer_creation");
group.sample_size(100);
group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic));
let test_cases = vec![
(256, "uniform_buffer_small"), (1024, "uniform_buffer_typical"), (4096, "uniform_buffer_large"), (16384, "vertex_buffer_small"), (65536, "vertex_buffer_medium"), (262144, "vertex_buffer_large"), (1048576, "vertex_buffer_huge"), (4194304, "staging_buffer_4MB"), (16777216, "staging_buffer_16MB"), ];
for (size, name) in test_cases {
group.throughput(Throughput::Bytes(size as u64));
group.bench_with_input(
BenchmarkId::new("device_local", name),
&size,
|b, &size| {
b.iter(|| {
let buffer = Buffer::new(
black_box(&device),
black_box(size),
black_box(vk::BufferUsageFlags::VERTEX_BUFFER),
black_box(vk::MemoryPropertyFlags::DEVICE_LOCAL),
);
black_box(buffer)
})
}
);
group.bench_with_input(
BenchmarkId::new("host_visible", name),
&size,
|b, &size| {
b.iter(|| {
let buffer = Buffer::new(
black_box(&device),
black_box(size),
black_box(vk::BufferUsageFlags::TRANSFER_SRC),
black_box(vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT),
);
black_box(buffer)
})
}
);
}
group.finish();
}
fn bench_ex_buffer_helpers(c: &mut Criterion) {
let device = setup_device().expect("Failed to setup device");
let mut group = c.benchmark_group("ex_buffer_helpers");
group.sample_size(100);
#[repr(C)]
#[derive(Clone, Copy)]
struct Vertex {
pos: [f32; 3],
normal: [f32; 3],
uv: [f32; 2],
}
#[repr(C)]
struct SceneUniforms {
view_matrix: [[f32; 4]; 4],
proj_matrix: [[f32; 4]; 4],
light_pos: [f32; 4],
light_color: [f32; 4],
time: f32,
_padding: [f32; 3],
}
let small_vertices: Vec<Vertex> = (0..4).map(|i| Vertex {
pos: [i as f32, 0.0, 0.0],
normal: [0.0, 1.0, 0.0],
uv: [0.0, 0.0],
}).collect();
let medium_vertices: Vec<Vertex> = (0..1024).map(|i| Vertex {
pos: [i as f32, 0.0, 0.0],
normal: [0.0, 1.0, 0.0],
uv: [0.0, 0.0],
}).collect();
let large_vertices: Vec<Vertex> = (0..16384).map(|i| Vertex {
pos: [i as f32, 0.0, 0.0],
normal: [0.0, 1.0, 0.0],
uv: [0.0, 0.0],
}).collect();
group.bench_function("vertex_buffer_small_4_verts", |b| {
b.iter(|| {
let buffer = create_vertex_buffer(
black_box(&device),
black_box(&small_vertices),
);
black_box(buffer)
})
});
group.bench_function("vertex_buffer_medium_1k_verts", |b| {
b.iter(|| {
let buffer = create_vertex_buffer(
black_box(&device),
black_box(&medium_vertices),
);
black_box(buffer)
})
});
group.bench_function("vertex_buffer_large_16k_verts", |b| {
b.iter(|| {
let buffer = create_vertex_buffer(
black_box(&device),
black_box(&large_vertices),
);
black_box(buffer)
})
});
group.bench_function("uniform_buffer_scene_data", |b| {
let uniforms = SceneUniforms {
view_matrix: [[1.0; 4]; 4],
proj_matrix: [[1.0; 4]; 4],
light_pos: [0.0; 4],
light_color: [1.0; 4],
time: 0.0,
_padding: [0.0; 3],
};
b.iter(|| {
let buffer = create_uniform_buffer::<SceneUniforms>(
black_box(&device),
);
black_box(buffer)
})
});
let compute_data_small: Vec<u32> = (0..256).collect();
let compute_data_large: Vec<u32> = (0..65536).collect();
group.bench_function("storage_buffer_256_elements", |b| {
b.iter(|| {
let buffer = create_storage_buffer(
black_box(&device),
black_box((compute_data_small.len() * std::mem::size_of::<u32>()) as u64),
);
black_box(buffer)
})
});
group.bench_function("storage_buffer_64k_elements", |b| {
b.iter(|| {
let buffer = create_storage_buffer(
black_box(&device),
black_box((compute_data_large.len() * std::mem::size_of::<u32>()) as u64),
);
black_box(buffer)
})
});
let indices_u16: Vec<u16> = (0..4096).collect();
let indices_u32: Vec<u32> = (0..4096).collect();
group.bench_function("index_buffer_u16_4k_indices", |b| {
b.iter(|| {
let buffer = create_vertex_buffer( black_box(&device),
black_box(&indices_u16),
);
black_box(buffer)
})
});
group.bench_function("index_buffer_u32_4k_indices", |b| {
b.iter(|| {
let buffer = create_vertex_buffer(
black_box(&device),
black_box(&indices_u32),
);
black_box(buffer)
})
});
group.finish();
}
fn bench_buffer_operations(c: &mut Criterion) {
let device = setup_device().expect("Failed to setup device");
let mut group = c.benchmark_group("buffer_operations");
group.bench_function("buffer_handle_access", |b| {
let buffer = Buffer::new(
&device,
4096,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
).expect("Failed to create buffer");
b.iter(|| {
let handle = buffer.handle();
black_box(handle)
})
});
group.bench_function("device_arc_clone", |b| {
b.iter(|| {
let device_clone = device.clone();
black_box(device_clone)
})
});
group.finish();
}
fn bench_buffer_mapping(c: &mut Criterion) {
let device = setup_device().expect("Failed to setup device");
let mut group = c.benchmark_group("buffer_mapping");
group.sample_size(100);
let sizes = vec![
(4096, "uniform_4kb"),
(65536, "vertex_64kb"),
(1048576, "staging_1mb"),
(16777216, "staging_16mb"),
];
for (size, name) in sizes {
group.throughput(Throughput::Bytes(size as u64));
let buffer = Buffer::new(
&device,
size,
vk::BufferUsageFlags::TRANSFER_SRC,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
).expect("Failed to create buffer");
group.bench_with_input(
BenchmarkId::new("map_unmap_cycle", name),
&buffer,
|b, buffer| {
b.iter(|| {
let ptr = unsafe { buffer.map(&device) }.expect("Map failed");
black_box(ptr);
buffer.unmap(&device);
})
}
);
group.bench_with_input(
BenchmarkId::new("write_throughput", name),
&buffer,
|b, buffer| {
let data = vec![0u8; size as usize];
b.iter(|| {
let ptr = unsafe { buffer.map(&device) }.expect("Map failed");
unsafe {
std::ptr::copy_nonoverlapping(
data.as_ptr(),
ptr as *mut u8,
size as usize
);
}
buffer.unmap(&device);
black_box(());
})
}
);
group.bench_with_input(
BenchmarkId::new("read_throughput", name),
&buffer,
|b, buffer| {
let mut data = vec![0u8; size as usize];
b.iter(|| {
let ptr = unsafe { buffer.map(&device) }.expect("Map failed");
unsafe {
std::ptr::copy_nonoverlapping(
ptr as *const u8,
data.as_mut_ptr(),
size as usize
);
}
buffer.unmap(&device);
black_box(&data);
})
}
);
}
group.finish();
}
fn bench_batch_operations(c: &mut Criterion) {
let device = setup_device().expect("Failed to setup device");
let mut group = c.benchmark_group("batch_operations");
group.sample_size(50);
group.bench_function("per_frame_ubo_updates_100_objects", |b| {
#[repr(C)]
struct PerObjectUBO {
model_matrix: [[f32; 4]; 4],
}
let buffers: Vec<_> = (0..100)
.map(|_| {
Buffer::new(
&device,
std::mem::size_of::<PerObjectUBO>() as u64,
vk::BufferUsageFlags::UNIFORM_BUFFER,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
).expect("Failed to create buffer")
})
.collect();
let data = PerObjectUBO {
model_matrix: [[1.0; 4]; 4],
};
b.iter(|| {
for buffer in &buffers {
let ptr = unsafe { buffer.map(&device) }.expect("Map failed");
unsafe {
std::ptr::copy_nonoverlapping(
&data as *const PerObjectUBO as *const u8,
ptr as *mut u8,
std::mem::size_of::<PerObjectUBO>()
);
}
buffer.unmap(&device);
}
black_box(());
})
});
group.bench_function("streaming_upload_10_meshes_1mb_each", |b| {
let staging_buffers: Vec<_> = (0..10)
.map(|_| {
Buffer::new(
&device,
1048576,
vk::BufferUsageFlags::TRANSFER_SRC,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
).expect("Failed to create buffer")
})
.collect();
let data = vec![0xABu8; 1048576];
b.iter(|| {
for buffer in &staging_buffers {
let ptr = unsafe { buffer.map(&device) }.expect("Map failed");
unsafe {
std::ptr::copy_nonoverlapping(
data.as_ptr(),
ptr as *mut u8,
1048576
);
}
buffer.unmap(&device);
}
black_box(());
})
});
group.bench_function("mixed_buffer_creation_typical_frame", |b| {
b.iter(|| {
let _ubos: Vec<_> = (0..5)
.map(|_| {
Buffer::new(
&device,
256,
vk::BufferUsageFlags::UNIFORM_BUFFER,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)
})
.collect();
let _vbos: Vec<_> = (0..3)
.map(|_| {
Buffer::new(
&device,
16384,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)
})
.collect();
let _ssbo = Buffer::new(
&device,
65536,
vk::BufferUsageFlags::STORAGE_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
);
black_box(());
})
});
group.finish();
}
criterion_group!(
benches,
bench_core_buffer_creation,
bench_ex_buffer_helpers,
bench_buffer_operations,
bench_buffer_mapping,
bench_batch_operations
);
criterion_main!(benches);