Struct gpgpu::Shader [−][src]
pub struct Shader(_);
Expand description
Represents a shader.
It’s just a wrapper over wgpu::ShaderModule
.
Implementations
Initialises a Shader
from a SPIR-V file.
Initialises a Shader
from SPIR-V bytes with an optional name
.
Initialises a Shader
from a WGSL
file.
Examples found in repository
examples/image-compatibility/main.rs (line 7)
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
fn main() {
let fw = gpgpu::Framework::default();
let shader =
gpgpu::Shader::from_wgsl_file(&fw, "examples/image-compatibility/shader.wgsl").unwrap();
let dynamic_img = image::open("examples/image-compatibility/monke.jpg").unwrap(); // RGB8 image ...
let rgba = dynamic_img.into_rgba8(); // ... converted to RGBA8
let (width, height) = rgba.dimensions();
// GPU image creation
let input_img = gpgpu::GpuConstImage::from_image_buffer(&fw, &rgba); // Input
let output_img = gpgpu::GpuImage::<Rgba8Uint>::new(&fw, width, height); // Output
let desc = gpgpu::DescriptorSet::default()
.bind_const_image(&input_img)
.bind_image(&output_img);
let program = gpgpu::Program::new(&shader, "main").add_descriptor_set(desc);
gpgpu::Kernel::new(&fw, program).enqueue(width / 32, height / 32, 1); // Since the kernel workgroup size is (32,32,1) dims are divided
let output = output_img.read_to_image_buffer_blocking().unwrap();
output
.save("examples/image-compatibility/mirror-monke.png")
.unwrap();
}
More examples
examples/mirror-image/main.rs (line 6)
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
fn main() {
let fw = gpgpu::Framework::default();
let shader = gpgpu::Shader::from_wgsl_file(&fw, "examples/mirror-image/shader.wgsl").unwrap();
let dynamic_img = image::open("examples/mirror-image/monke.jpg").unwrap(); // RGB8 image ...
let rgba = dynamic_img.into_rgba8(); // ... converted to RGBA8
let (width, height) = rgba.dimensions();
// GPU image creation
let input_img = gpgpu::GpuConstImage::<Rgba8Uint>::new(&fw, width, height); // Input
let output_img = gpgpu::GpuImage::<Rgba8Uint>::new(&fw, width, height); // Output
// Write input image into the GPU
input_img.write(&rgba).unwrap();
let desc = gpgpu::DescriptorSet::default()
.bind_const_image(&input_img)
.bind_image(&output_img);
let program = gpgpu::Program::new(&shader, "main").add_descriptor_set(desc);
gpgpu::Kernel::new(&fw, program).enqueue(width / 32, height / 32, 1); // Since the kernel workgroup size is (32, 32, 1) dims are divided
let output_bytes = output_img.read_vec_blocking().unwrap();
image::save_buffer(
"examples/mirror-image/mirror-monke.png",
&output_bytes,
width,
height,
image::ColorType::Rgba8,
)
.unwrap();
}
examples/ndarray/main.rs (line 7)
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
fn main() {
let fw = gpgpu::Framework::default();
let shader = gpgpu::Shader::from_wgsl_file(&fw, "examples/ndarray/shader.wgsl").unwrap();
let dims = (3200, 3200); // X and Y dimensions. Must be multiple of the enqueuing dimensions
let array_src = ndarray::Array::<i32, _>::ones(dims) * 2;
let src_view = array_src.view();
let gpu_arrays_len = gpgpu::GpuUniformBuffer::from_slice(&fw, &[dims.0, dims.1]); // Send the ndarray dimensions
let gpu_array_a = gpgpu::GpuArray::from_array(&fw, src_view).unwrap(); // Array A
let gpu_array_b = gpgpu::GpuArray::from_array(&fw, src_view).unwrap(); // Array B
let gpu_array_c = gpgpu::GpuArray::from_array(&fw, ndarray::Array::zeros(dims).view()).unwrap(); // Array C: result storage
let desc_0 = gpgpu::DescriptorSet::default().bind_uniform_buffer(&gpu_arrays_len); // Descriptor set of arrays dimensions.
let desc_1 = gpgpu::DescriptorSet::default() // Descriptor set of all the arrays
.bind_array(&gpu_array_a, gpgpu::GpuBufferUsage::ReadOnly)
.bind_array(&gpu_array_b, gpgpu::GpuBufferUsage::ReadOnly)
.bind_array(&gpu_array_c, gpgpu::GpuBufferUsage::ReadWrite);
let program = gpgpu::Program::new(&shader, "main_fn_2")
.add_descriptor_set(desc_0)
.add_descriptor_set(desc_1);
gpgpu::Kernel::new(&fw, program)
// .enqueue((dims.0 * dims.1) as u32, 1, 1); // Kernel main_fn 1. Enqueuing in a single dimension
.enqueue(dims.0 as u32 / 32, dims.1 as u32 / 32, 1); // Kernel main_fn 2. Enqueuing in x and y dimensions (array dimensions are needed)
let array_output = gpu_array_c.read_blocking().unwrap();
for (cpu, gpu) in array_src.iter().zip(array_output) {
assert_eq!(2 * cpu, gpu)
}
}
examples/parallel-compute/main.rs (line 13)
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
let shader = Arc::new(
gpgpu::Shader::from_wgsl_file(&FW, "examples/parallel-compute/shader.wgsl").unwrap(),
);
let threading = 4; // Threading level
let size = 32000; // Must be multiple of 32
let cpu_data = (0..size).into_iter().collect::<Vec<u32>>();
let shader_input_buffer = Arc::new(gpgpu::GpuBuffer::from_slice(&FW, &cpu_data)); // Data shared across threads shader invocations
let mut handles = Vec::with_capacity(threading);
for _ in 0..threading {
let local_shader = shader.clone();
let local_shader_input_buffer = shader_input_buffer.clone();
// Threads spawn
let handle = std::thread::spawn(move || {
// Current thread GPU objects
let local_cpu_data = (0..size).into_iter().collect::<Vec<u32>>();
let local_input_buffer = gpgpu::GpuBuffer::from_slice(&FW, &local_cpu_data);
let local_output_buffer = gpgpu::GpuBuffer::<u32>::with_capacity(&FW, size as u64);
let desc = gpgpu::DescriptorSet::default()
.bind_buffer(&local_shader_input_buffer, gpgpu::GpuBufferUsage::ReadOnly)
.bind_buffer(&local_input_buffer, gpgpu::GpuBufferUsage::ReadOnly)
.bind_buffer(&local_output_buffer, gpgpu::GpuBufferUsage::ReadWrite);
let program = gpgpu::Program::new(&local_shader, "main").add_descriptor_set(desc);
gpgpu::Kernel::new(&FW, program).enqueue(size / 32, 1, 1);
local_output_buffer.read_vec_blocking().unwrap()
});
handles.push(handle);
}
// Join threads
for handle in handles {
let output = handle.join().unwrap();
for (idx, a) in cpu_data.iter().enumerate() {
let cpu_mult = a.pow(2);
let gpu_mult = output[idx];
assert_eq!(cpu_mult, gpu_mult);
}
}
}
examples/simple-compute/main.rs (line 7)
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
fn main() {
let fw = gpgpu::Framework::default(); // Framework initialization.
let shader = gpgpu::Shader::from_wgsl_file(&fw, "examples/simple-compute/shader.wgsl").unwrap(); // Shader loading.
let size = 10000; // Size of the vectors
let data_a = (0..size).into_iter().collect::<Vec<u32>>(); // Vector A data. 0, 1, 2, ..., 9999 (size - 1).
let data_b = (0..size).into_iter().rev().collect::<Vec<u32>>(); // Vector B data. 9999 (size - 1), 9998, ..., 0.
// Allocation of new vectors on the GPU
let gpu_vec_a = gpgpu::GpuBuffer::from_slice(&fw, &data_a); // Input vector A.
let gpu_vec_b = gpgpu::GpuBuffer::from_slice(&fw, &data_b); // Input vector B.
let gpu_vec_c = gpgpu::GpuBuffer::with_capacity(&fw, size as u64); // Output vector C. Empty.
// We have to tell the GPU how the data is sent. Take a look at the shader (mult.wgsl).
// The boolean indicates wether the vector is read-only or not.
let bindings = gpgpu::DescriptorSet::default() // Group 0
.bind_buffer(&gpu_vec_a, gpgpu::GpuBufferUsage::ReadOnly) // Binding 0
.bind_buffer(&gpu_vec_b, gpgpu::GpuBufferUsage::ReadOnly) // Binding 1
.bind_buffer(&gpu_vec_c, gpgpu::GpuBufferUsage::ReadWrite); // Binding 2. read_write in shader. No write-only yet.
// Match a shader entry point with its descriptor (the bindings).
// A program represents a function on a GPU with an already set of inputs and outputs following a layout (the variable `bindings` above).
let program = gpgpu::Program::new(&shader, "main").add_descriptor_set(bindings);
// Creation of a kernel. This represents the `program` function and its `enqueuing` parameters,
let kernel = gpgpu::Kernel::new(&fw, program);
// Execution of the kernel. It needs 3 dimmensions, x y and z.
// Since we are using single-dim vectors, only x is required.
kernel.enqueue(size as u32, 1, 1);
// After the kernel execution, we can read the results from the GPU.
let gpu_result = gpu_vec_c.read_vec_blocking().unwrap();
// We test that the results are correct.
for (idx, (a, b)) in data_a.into_iter().zip(data_b).enumerate() {
let cpu_mult = a * b;
let gpu_mult = gpu_result[idx];
assert_eq!(cpu_mult, gpu_mult);
}
}
examples/webcam/main.rs (line 51)
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
fn main() {
let fw = Framework::default();
// Camera initilization. Config may not work if not same cam as the Thinkpad T480 one.
// Change parameters accordingly
let mut camera = {
let camera_format = CameraFormat::new(
Resolution {
width_x: WIDTH as u32,
height_y: HEIGHT as u32,
},
nokhwa::FrameFormat::MJPEG,
30,
);
Camera::new(0, Some(camera_format)).unwrap()
};
// Window initialization
let mut window = Window::new(
"gpgpu webcam example",
WIDTH,
HEIGHT,
WindowOptions::default(),
)
.unwrap();
camera.open_stream().unwrap();
window.limit_update_rate(Some(std::time::Duration::from_secs_f32(1.0 / 60.0)));
// Since the same GPU resources could be used during the whole execution
// of the program, they are outside of the event loop
let gpu_input = GpuConstImage::<Rgba8UintNorm>::new(&fw, WIDTH as u32, HEIGHT as u32); // Cam frame texture
let buf_time = GpuUniformBuffer::<f32>::with_capacity(&fw, 1); // Elapsed time buffer (single element) for fancy shaders 😁
let gpu_output = GpuImage::<Rgba8UintNorm>::new(&fw, WIDTH as u32, HEIGHT as u32); // Shader output
let shader = gpgpu::Shader::from_wgsl_file(&fw, "examples/webcam/shader.wgsl").unwrap();
let desc = DescriptorSet::default()
.bind_const_image(&gpu_input)
.bind_image(&gpu_output)
.bind_uniform_buffer(&buf_time);
let program = gpgpu::Program::new(&shader, "main").add_descriptor_set(desc);
let kernel = gpgpu::Kernel::new(&fw, program);
let time = std::time::Instant::now();
let mut frame_buffer = vec![0u32; WIDTH * HEIGHT * 4];
let mut total = 0.0;
let mut count = 0;
while window.is_open() && !window.is_key_down(Key::Escape) {
let fps = std::time::Instant::now();
let cam_buf = camera.frame().unwrap(); // Obtain cam current frame
gpu_input.write_image_buffer(&cam_buf.convert()).unwrap(); // Upload cam frame into the cam frame texture
buf_time.write(&[time.elapsed().as_secs_f32()]).unwrap(); // Upload elapsed time into elapsed time buffer
kernel.enqueue(WIDTH as u32 / 32, HEIGHT as u32 / 31, 1);
gpu_output
.read_blocking(bytemuck::cast_slice_mut(&mut frame_buffer))
.unwrap();
// Write processed cam frame into window frame buffer
window
.update_with_buffer(&frame_buffer, WIDTH, HEIGHT)
.unwrap();
print_fps(fps.elapsed().as_secs_f32(), &mut total, &mut count);
}
}