/blackcl

highly opinionated OpenCL bindings for Go with some black magic

Primary LanguageGoMIT LicenseMIT

BlackCL

Black magic with OpenCL. These are highly opinionated OpenCL bindings for Go. It tries to make GPU computing easy, with some sugar abstraction, Go's concurency and channels.

//Do not create platforms/devices/contexts/queues/...
//Just get the GPU
d, err := blackcl.GetDefaultDevice()
if err != nil {
	panic("no opencl device")
}
defer d.Release()

//BlackCL has several kinds of device memory object: Bytes, Vector, Image
//allocate buffer on the device (16 elems of float32)
v, err := d.NewVector(16)
if err != nil {
	panic("could not allocate buffer")
}
defer v.Release()

//copy data to the vector (it's async)
data := []float32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
err = <-v.Copy(data)
if err != nil {
	panic("could not copy data to buffer")
}

//an complicated kernel
const kernelSource = `
__kernel void addOne(__global float* data) {
	const int i = get_global_id (0);
	data[i] += 1;
}
`

//Add program source to device, get kernel
d.AddProgram(kernelSource)
k := d.Kernel("addOne")
//run kernel (global work size 16 and local work size 1)
_, err = k.Global(16).Local(1).Run(false, nil, v)
if err != nil {
	panic("could not run kernel")
}

//Get data from vector
newData, err := v.Data()
if err != nil {
	panic("could not get data from buffer")
}

//prints out [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
fmt.Println(newData)

BlackCL also supports the image.Image interface, for image manipulation:

const invertColorKernel = `
__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;

__kernel void invert(__read_only image2d_t src, __write_only image2d_t dest) {
	const int2 pos = {get_global_id(0), get_global_id(1)};
	float4 pixel = read_imagef(src, sampler, pos);
	pixel.x = 1 - pixel.x;
	pixel.y = 1 - pixel.y;
	pixel.z = 1 - pixel.z;
	write_imagef(dest, pos, pixel);
}`

//read image file
imgFile, err := os.Open("test_data/opencl.png")
if err != nil {
	log.Fatal(err)
}
i, _, err := image.Decode(imgFile)
if err != nil {
	log.Fatal(err)
}
//create image buffer
img, err := d.NewImageFromImage(i)
if err != nil {
	log.Fatal(err)
}
defer img.Release()
//allocate an empty image for the result
invertedImg, err := d.NewImage(blackcl.ImageTypeRGBA, img.Bounds())
if err != nil {
	log.Fatal(err)
}
defer invertedImg.Release()
d.AddProgram(invertColorKernel)
//invert colors of the image
k := d.Kernel("invert")
// run kernel, and return an event
event, err := k.Global(img.Bounds().Dx(), img.Bounds().Dy()).Local(1, 1).Run(true, nil, img, invertedImg)
if err != nil {
	log.Fatal(err)
}
defer event.Release()
//wait for the kernel to finish. Not really necessary here, this just serves as example
event.Wait()
//get the inverted image data and save it to a file
inverted, err := invertedImg.Data()
if err != nil {
	log.Fatal(err)
}
f, err := os.Create("inverted.png")
if err != nil {
	log.Fatal(err)
}
png.Encode(f, inverted)