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() { // First step: initialize GPU device. // Panic if case of wrong Vulkan API calls. let debug_messenger = crate::PanicIfErrorMessenger {}; // Use default CPU allocator. let allocation_callbacks = None; // Don't dump Vulkan API calls. let dump_api = false; // Create Vulkan API instance. let instance = crate::Instance::new(Some(&debug_messenger), allocation_callbacks, dump_api).unwrap(); // 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::>(); 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::(), ) .unwrap(); // Uniform buffer that contains parameter. let uniform_buffer = crate::Buffer::new( device.clone(), "Uniform buffer", crate::BufferType::Uniform, std::mem::size_of::(), ) .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_slice(&numbers, 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 = instance.compile_shader(SHADER_CODE, 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(); // 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 mut result = vec![0f32; numbers_count]; download_buffer.download_slice(&mut result, 0).unwrap(); // Check results. for i in 0..numbers_count { assert_eq!(result[i], numbers[i] + param); } }