#![recursion_limit="1024"]
extern crate gl_struct;
extern crate glfw;
extern crate gl;
extern crate rand;
extern crate rayon;
use gl_struct::*;
use rayon::slice::ParallelSliceMut;
use std::env;
glsl! {$
pub mod SmallBitonic {
@Compute
#version 450
layout(local_size_x = 8, local_size_y = 1, local_size_z = 1) in;
layout(std430) buffer Values {
float values[];
};
void main() {
for(uint order=0; order<4; order++){
for(uint suborder=order; ; suborder--){
bool flip = suborder==order && order > 0;
uint swap_offset = 1 << suborder;
uint mask = 0xFFFFFFFF << suborder;
uint id = ((gl_GlobalInvocationID.x & mask) << 1) | (gl_GlobalInvocationID.x & ~mask);
uint offset;
if(!flip){
offset = swap_offset;
} else {
offset = (swap_offset<<1) - ((gl_GlobalInvocationID.x & ~mask)<<1) - 1;
}
if(values[id] > values[id+offset]){
float tmp = values[id];
values[id] = values[id+offset];
values[id+offset] = tmp;
}
barrier();
if(suborder==0) break;
}
}
}
}
pub mod BitonicSwaps {
@Compute
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
uniform uint order;
uniform bool flip = false;
layout(std430) buffer Values {
float values[];
};
void main() {
const uint swap_offset = 1 << order;
const uint mask = 0xFFFFFFFF << order;
uint id = ((gl_GlobalInvocationID.x & mask) << 1) | (gl_GlobalInvocationID.x & ~mask);
uint offset;
if(!flip){
offset = swap_offset;
} else {
offset = (swap_offset<<1) - ((gl_GlobalInvocationID.x & ~mask)<<1) - 1;
}
if(values[id] > values[id+offset]){
float tmp = values[id];
values[id] = values[id+offset];
values[id+offset] = tmp;
}
}
}
}
glsl! {$
pub mod XtremeBubbleSort {
@Compute
#version 450
uniform bool offset;
layout(local_size_x = 1024, local_size_y = 1, local_size_z = 1) in;
layout(std430) buffer Values {
float values[];
};
void main() {
uint ptr = gl_GlobalInvocationID.x;
bool odd = (ptr & 1) == 1;
for(uint i=0; i<gl_WorkGroupSize.x; i++) {
if(!odd) {
if(values[ptr] > values[ptr+1]) {
float tmp1 = values[ptr];
float tmp2 = values[ptr+1];
values[ptr] = tmp2;
values[ptr+1] = tmp1;
}
}
barrier();
if(odd) {
if(values[ptr] > values[ptr+1]) {
float tmp1 = values[ptr];
float tmp2 = values[ptr+1];
values[ptr] = tmp2;
values[ptr+1] = tmp1;
}
}
barrier();
}
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let mut gpu = false;
let mut cpu = false;
let mut par_cpu = false;
let mut order = 0;
let mut i = 0;
while i < args.len() {
if args[i] == "--gpu" { gpu = true; }
else if args[i] == "--cpu" { cpu = true; }
else if args[i] == "--par_cpu" { par_cpu = true; }
else if args[i] == "--order" || args[i] == "--o" {
i+=1;
if i < args.len() {
order = args[i].parse::<u32>().unwrap();
}
}
i+=1;
}
let num = 1 << order;
println!("Generating {} random numbers", num);
let rand_start = ::std::time::Instant::now();
let mut points = vec![rand::random::<f32>(); num];
println!("List generated in {:?}", ::std::time::Instant::now() - rand_start);
if gpu {
let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
let mut window = glfw.create_window(640, 480, "Sorting!", glfw::WindowMode::Windowed).unwrap().0;
glfw::Context::make_current(&mut window);
window.set_key_polling(false);
let gl_provider = unsafe {
GLProvider::load(|s| ::std::mem::transmute(glfw.get_proc_address_raw(s)))
};
let _context = Context::init(&gl_provider);
let mut computer = BitonicSwaps::init(&gl_provider).unwrap();
let mut small_bitonic = SmallBitonic::init(&gl_provider).unwrap();
println!("uploading to gpu");
let upload_start = ::std::time::Instant::now();
let mut list: Buffer<[f32], _> = Buffer::readonly_from(&gl_provider, points.clone().into_boxed_slice());
unsafe { gl::Finish(); }
println!("Data uploaded in {:?}", ::std::time::Instant::now() - upload_start);
println!("Starting GPU sorting");
let start = ::std::time::Instant::now();
fn bitonic_sort<A:ReadAccess>(n: u32, c:&mut BitonicSwaps::Program, c2:&mut SmallBitonic::Program, buf: &mut Buffer<[f32], A>) {
if n==0 {
return;
} else if n==4 {
c2.compute((buf.len()>>4) as u32, 1, 1, buf);
} else {
bitonic_sort(n-1, c, c2, buf);
*c.order = n-1;
c.flip.set(true);
c.compute((buf.len()>>1) as u32, 1, 1, buf);
c.flip.set(false);
for m in (0..n-1).rev() {
*c.order = m;
c.compute((buf.len()>>1) as u32, 1, 1, buf);
}
}
}
bitonic_sort(order, &mut computer, &mut small_bitonic, &mut list);
unsafe { gl::Finish();}
println!("Finished GPU sorting in {:?}", ::std::time::Instant::now() - start);
println!("Reading GPU buffer");
let download_start = ::std::time::Instant::now();
let _res = list.read_into_box();
println!("Finished Reading in {:?}", ::std::time::Instant::now()-download_start);
}
if cpu {
println!("Starting CPU sorting");
let mut start = ::std::time::Instant::now();
if !par_cpu {
points.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
} else {
let mut new_p = points.clone();
start = ::std::time::Instant::now();
new_p.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
}
println!("Finished CPU sorting in {:?}", ::std::time::Instant::now()-start);
}
if par_cpu {
println!("Starting Parallel CPU sorting");
let start = ::std::time::Instant::now();
points.par_sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
println!("Finished Parallel CPU sorting in {:?}", ::std::time::Instant::now()-start);
}
}