mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-03 16:40:54 -05:00
175 lines
5.4 KiB
Rust
175 lines
5.4 KiB
Rust
static SHADER_CODE: &str = "
|
|
#version 450
|
|
|
|
layout(set = 0, binding = 0) buffer Numbers {
|
|
float data[];
|
|
} numbers;
|
|
|
|
layout(set = 0, binding = 1) uniform Param {
|
|
float param;
|
|
} param;
|
|
|
|
void main() {
|
|
uint index = gl_GlobalInvocationID.x;
|
|
numbers.data[index] += param.param;
|
|
}
|
|
";
|
|
|
|
// Basic GPU test.
|
|
// It takes list of numbers and adds parameter to each number.
|
|
#[test]
|
|
fn basic_gpu_test() {
|
|
// Get Vulkan API instance.
|
|
let instance = crate::GPU_TEST_INSTANCE.clone();
|
|
// Choose any GPU hardware to use.
|
|
let physical_device = &instance.physical_devices()[0];
|
|
// Create GPU device.
|
|
let device = crate::Device::new(instance.clone(), physical_device).unwrap();
|
|
|
|
// Create gpu context that records command to GPU and runs them.
|
|
let mut context = crate::Context::new(device.clone()).unwrap();
|
|
let context_timeout = std::time::Duration::from_secs(30);
|
|
|
|
// Second step: create GPU resources.
|
|
|
|
// Generate input numbers.
|
|
let numbers_count = 512;
|
|
let numbers = (0..numbers_count).map(|x| x as f32).collect::<Vec<_>>();
|
|
let param = 5f32;
|
|
|
|
// Create GPU buffers.
|
|
// Storage buffer that contains numbers.
|
|
let storage_buffer = crate::Buffer::new(
|
|
device.clone(),
|
|
"Storage buffer",
|
|
crate::BufferType::Storage,
|
|
numbers.len() * std::mem::size_of::<f32>(),
|
|
)
|
|
.unwrap();
|
|
// Uniform buffer that contains parameter.
|
|
let uniform_buffer = crate::Buffer::new(
|
|
device.clone(),
|
|
"Uniform buffer",
|
|
crate::BufferType::Uniform,
|
|
std::mem::size_of::<f32>(),
|
|
)
|
|
.unwrap();
|
|
|
|
// Upload data to the GPU.
|
|
// We cannot just call `memcpy` because GPU don't see RAM memory.
|
|
// To upload data we need to create intermediate buffer
|
|
// which is visible to both CPU and GPU.
|
|
// We will copy data to this buffer and then copy it to the GPU buffer.
|
|
// Use one buffer for both data and parameter.
|
|
let upload_buffer = crate::Buffer::new(
|
|
device.clone(),
|
|
"Upload buffer",
|
|
crate::BufferType::CpuToGpu, // Mark buffer as buffer to copy from CPU to GPU.
|
|
storage_buffer.size() + uniform_buffer.size(),
|
|
)
|
|
.unwrap();
|
|
// Copy numbers.
|
|
upload_buffer.upload(numbers.as_slice(), 0).unwrap();
|
|
// Copy parameter to the end.
|
|
upload_buffer.upload(¶m, storage_buffer.size()).unwrap();
|
|
|
|
// Upload from intermediate buffer to GPU buffers.
|
|
context
|
|
.copy_gpu_buffer(
|
|
upload_buffer.clone(),
|
|
storage_buffer.clone(),
|
|
0,
|
|
0,
|
|
storage_buffer.size(),
|
|
)
|
|
.unwrap();
|
|
context
|
|
.copy_gpu_buffer(
|
|
upload_buffer.clone(),
|
|
uniform_buffer.clone(),
|
|
storage_buffer.size(),
|
|
0,
|
|
uniform_buffer.size(),
|
|
)
|
|
.unwrap();
|
|
// run copy commands.
|
|
context.run().unwrap();
|
|
context.wait_finish(context_timeout).unwrap();
|
|
|
|
// Third step: create computation pipeline.
|
|
|
|
// Compile shader code to SPIR-V.
|
|
let spirv = crate::GPU_TEST_INSTANCE
|
|
.compile_shader(SHADER_CODE, "shader.glsl", None, None)
|
|
.unwrap();
|
|
// Create shader.
|
|
let shader = crate::Shader::new(device.clone(), &spirv).unwrap();
|
|
|
|
// Create linking to the shader.
|
|
let descriptor_set_layout = crate::DescriptorSetLayout::builder()
|
|
.add_storage_buffer(0)
|
|
.add_uniform_buffer(1)
|
|
.build(device.clone())
|
|
.unwrap();
|
|
let descriptor_set = crate::DescriptorSet::builder(descriptor_set_layout.clone())
|
|
.add_storage_buffer(0, storage_buffer.clone())
|
|
.add_uniform_buffer(1, uniform_buffer.clone())
|
|
.build()
|
|
.unwrap();
|
|
|
|
// Create computation pipeline.
|
|
let pipeline = crate::Pipeline::builder()
|
|
.add_descriptor_set_layout(0, descriptor_set_layout)
|
|
.add_shader(shader)
|
|
.build(device.clone())
|
|
.unwrap();
|
|
|
|
// Fourth step: run computation.
|
|
let descriptor_sets = [descriptor_set.clone()];
|
|
// Bind pipeline and descriptor sets.
|
|
context.bind_pipeline(pipeline, &descriptor_sets).unwrap();
|
|
// Run computeation command. Threads count is the inputs count.
|
|
context.dispatch(numbers_count, 1, 1).unwrap();
|
|
// Add barrier to ensure that all writes to the storage buffer are visible to the next command.
|
|
context
|
|
.barrier_buffers(std::slice::from_ref(&storage_buffer))
|
|
.unwrap();
|
|
// Run GPU and wait finish.
|
|
context.run().unwrap();
|
|
context.wait_finish(context_timeout).unwrap();
|
|
|
|
// Fifth step: download and check results.
|
|
|
|
// Like upload, we need to create intermediate buffer to download data from GPU.
|
|
let download_buffer = crate::Buffer::new(
|
|
device.clone(),
|
|
"Download buffer",
|
|
crate::BufferType::GpuToCpu, // Mark buffer as buffer to copy from GPU to CPU.
|
|
storage_buffer.size(),
|
|
)
|
|
.unwrap();
|
|
// Copy data from GPU to intermediate buffer.
|
|
context
|
|
.copy_gpu_buffer(
|
|
storage_buffer.clone(),
|
|
download_buffer.clone(),
|
|
0,
|
|
0,
|
|
storage_buffer.size(),
|
|
)
|
|
.unwrap();
|
|
// Run copy command.
|
|
context.run().unwrap();
|
|
context.wait_finish(context_timeout).unwrap();
|
|
|
|
// Download data from intermediate buffer.
|
|
let result = download_buffer
|
|
.download_vec::<f32>(0, numbers_count)
|
|
.unwrap();
|
|
|
|
// Check results.
|
|
for i in 0..numbers_count {
|
|
assert_eq!(result[i], numbers[i] + param);
|
|
}
|
|
}
|