├── .gitignore ├── README.md ├── src ├── api │ ├── gl │ │ ├── mod.rs │ │ ├── buffer.rs │ │ ├── program.rs │ │ ├── sync_event.rs │ │ ├── profile_event.rs │ │ ├── instance.rs │ │ ├── image.rs │ │ ├── device.rs │ │ └── queue.rs │ └── cl │ │ ├── mod.rs │ │ ├── buffer.rs │ │ ├── sync_event.rs │ │ ├── program.rs │ │ ├── profile_event.rs │ │ ├── image.rs │ │ ├── instance.rs │ │ ├── device.rs │ │ ├── queue.rs │ │ └── ffi.rs ├── error.rs ├── lib.rs ├── program.rs ├── sync_event.rs ├── profile_event.rs ├── instance.rs ├── buffer.rs ├── device.rs ├── image.rs └── queue.rs ├── Cargo.toml ├── LICENSE-MIT ├── examples ├── matrix-multiply.rs └── generate-cave.rs └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `compute-shader` 2 | 3 | ## Description 4 | 5 | A simple cross-platform interface to a subset of GPU compute functionality in Rust. 6 | 7 | Supports OpenCL 1.2+ and OpenGL 4.3+. 8 | 9 | See `examples/matrix-multiply.rs` and `examples/generate-cave.rs` for examples of use. 10 | 11 | ## License 12 | 13 | Dual-licensed under MIT and Apache 2.0 licenses. 14 | 15 | -------------------------------------------------------------------------------- /src/api/gl/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | pub mod buffer; 12 | pub mod device; 13 | pub mod image; 14 | pub mod instance; 15 | pub mod profile_event; 16 | pub mod program; 17 | pub mod queue; 18 | pub mod sync_event; 19 | 20 | -------------------------------------------------------------------------------- /src/api/cl/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | pub mod buffer; 12 | pub mod device; 13 | pub mod image; 14 | pub mod instance; 15 | pub mod profile_event; 16 | pub mod program; 17 | pub mod queue; 18 | pub mod sync_event; 19 | 20 | mod ffi; 21 | 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "compute-shader" 3 | version = "0.1.0" 4 | authors = ["Patrick Walton "] 5 | license = "MIT / Apache-2.0" 6 | 7 | [dependencies] 8 | euclid = "0.10" 9 | gl = "0.6" 10 | libc = "0.2" 11 | 12 | [dev-dependencies] 13 | byteorder = "1" 14 | rand = "0.3" 15 | 16 | [dev-dependencies.glfw] 17 | git = "https://github.com/bjz/glfw-rs.git" 18 | 19 | [dev-dependencies.lord-drawquaad] 20 | git = "https://github.com/pcwalton/lord-drawquaad.git" 21 | 22 | [target.'cfg(target_os = "macos")'.dependencies] 23 | core-foundation = "0.2" 24 | 25 | [target.'cfg(target_os = "macos")'.dependencies.io-surface] 26 | git = "https://github.com/pcwalton/rust-io-surface.git" 27 | branch = "more-formats" 28 | 29 | -------------------------------------------------------------------------------- /src/api/cl/buffer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::ffi::{self, cl_mem}; 12 | use buffer::{Buffer, BufferFunctions}; 13 | 14 | pub static BUFFER_FUNCTIONS: BufferFunctions = BufferFunctions { 15 | destroy: destroy, 16 | }; 17 | 18 | unsafe fn destroy(this: &Buffer) { 19 | ffi::clReleaseMemObject(this.data() as cl_mem); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/api/gl/buffer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use buffer::{Buffer, BufferFunctions}; 12 | use gl::types::GLuint; 13 | use gl; 14 | 15 | pub static BUFFER_FUNCTIONS: BufferFunctions = BufferFunctions { 16 | destroy: destroy, 17 | }; 18 | 19 | unsafe fn destroy(this: &Buffer) { 20 | let mut buffer = this.data() as GLuint; 21 | gl::DeleteBuffers(1, &mut buffer) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/api/gl/program.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use gl::types::GLuint; 12 | use gl; 13 | use program::{Program, ProgramFunctions}; 14 | 15 | pub static PROGRAM_FUNCTIONS: ProgramFunctions = ProgramFunctions { 16 | destroy: destroy, 17 | }; 18 | 19 | unsafe fn destroy(this: &Program) { 20 | let mut shader = 0; 21 | gl::GetAttachedShaders(this.data() as GLuint, 1, &mut 0, &mut shader); 22 | gl::UseProgram(0); 23 | gl::DeleteProgram(this.data() as GLuint); 24 | gl::DeleteShader(shader); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Errors. 12 | 13 | /// The universal error type. 14 | #[derive(Debug)] 15 | pub enum Error { 16 | /// A miscellaneous error occurred. 17 | Failed, 18 | /// Compilation of the shader failed. 19 | /// 20 | /// The string represents the error message that the driver reported. 21 | CompileFailed(String), 22 | /// Shader linking failed. 23 | /// 24 | /// The string represents the error message that the driver reported. 25 | LinkFailed(String), 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! A cross-platform interface to a subset of GPU compute functionality. 12 | 13 | extern crate euclid; 14 | extern crate gl; 15 | extern crate libc; 16 | 17 | #[cfg(target_os = "macos")] 18 | extern crate core_foundation; 19 | #[cfg(target_os = "macos")] 20 | extern crate io_surface; 21 | 22 | mod api { 23 | #[cfg(target_os = "macos")] 24 | pub mod cl; 25 | #[cfg_attr(target_os = "macos", allow(dead_code))] 26 | pub mod gl; 27 | } 28 | 29 | pub mod buffer; 30 | pub mod device; 31 | pub mod error; 32 | pub mod image; 33 | pub mod instance; 34 | pub mod profile_event; 35 | pub mod program; 36 | pub mod queue; 37 | pub mod sync_event; 38 | 39 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/api/gl/sync_event.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use error::Error; 12 | use gl::types::{GLsync, GLuint64}; 13 | use gl; 14 | use sync_event::{SyncEvent, SyncEventFunctions}; 15 | 16 | const TIMEOUT: GLuint64 = 1_000_000_000_000; 17 | 18 | pub static SYNC_EVENT_FUNCTIONS: SyncEventFunctions = SyncEventFunctions { 19 | destroy: destroy, 20 | wait: wait, 21 | }; 22 | 23 | unsafe fn destroy(event: &SyncEvent) { 24 | let data = event.data() as GLsync; 25 | gl::DeleteSync(data); 26 | } 27 | 28 | fn wait(event: &SyncEvent) -> Result<(), Error> { 29 | unsafe { 30 | gl::ClientWaitSync(event.data() as GLsync, gl::SYNC_FLUSH_COMMANDS_BIT, TIMEOUT); 31 | Ok(()) 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/api/cl/sync_event.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::ffi::{self, CL_SUCCESS, cl_event}; 12 | use error::Error; 13 | use sync_event::{SyncEvent, SyncEventFunctions}; 14 | 15 | pub static SYNC_EVENT_FUNCTIONS: SyncEventFunctions = SyncEventFunctions { 16 | destroy: destroy, 17 | wait: wait, 18 | }; 19 | 20 | unsafe fn destroy(this: &SyncEvent) { 21 | ffi::clReleaseEvent(this.data() as cl_event); 22 | } 23 | 24 | fn wait(this: &SyncEvent) -> Result<(), Error> { 25 | unsafe { 26 | let event = this.data() as cl_event; 27 | if ffi::clWaitForEvents(1, &event) == CL_SUCCESS { 28 | Ok(()) 29 | } else { 30 | Err(Error::Failed) 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/api/gl/profile_event.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use error::Error; 12 | use gl::types::GLuint; 13 | use gl; 14 | use profile_event::{ProfileEvent, ProfileEventFunctions}; 15 | 16 | pub static PROFILE_EVENT_FUNCTIONS: ProfileEventFunctions = ProfileEventFunctions { 17 | destroy: destroy, 18 | time_elapsed: time_elapsed, 19 | }; 20 | 21 | unsafe fn destroy(event: &ProfileEvent) { 22 | let mut data = event.data() as GLuint; 23 | gl::DeleteQueries(1, &mut data); 24 | } 25 | 26 | fn time_elapsed(event: &ProfileEvent) -> Result { 27 | unsafe { 28 | let mut result = 0; 29 | gl::GetQueryObjectui64v(event.data() as GLuint, gl::QUERY_RESULT, &mut result); 30 | Ok(result) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/api/cl/program.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::ffi::{self, CL_KERNEL_PROGRAM, cl_kernel, cl_program}; 12 | use program::{Program, ProgramFunctions}; 13 | use std::mem; 14 | use std::os::raw::c_void; 15 | use std::ptr; 16 | 17 | pub static PROGRAM_FUNCTIONS: ProgramFunctions = ProgramFunctions { 18 | destroy: destroy, 19 | }; 20 | 21 | unsafe fn destroy(this: &Program) { 22 | let mut program = ptr::null_mut(); 23 | ffi::clGetKernelInfo(this.data() as cl_kernel, 24 | CL_KERNEL_PROGRAM, 25 | mem::size_of::(), 26 | &mut program as *mut cl_program as *mut c_void, 27 | ptr::null_mut()); 28 | 29 | ffi::clReleaseKernel(this.data() as cl_kernel); 30 | ffi::clReleaseProgram(program); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/api/gl/instance.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::gl::device::DEVICE_FUNCTIONS; 12 | use device::Device; 13 | use error::Error; 14 | use instance::{Instance, InstanceFunctions, ShadingLanguage}; 15 | 16 | pub static INSTANCE_FUNCTIONS: InstanceFunctions = InstanceFunctions { 17 | destroy: destroy, 18 | shading_language: shading_language, 19 | open_device: open_device, 20 | }; 21 | 22 | pub fn create() -> Result { 23 | unsafe { 24 | Ok(Instance::from_raw_data(0, &INSTANCE_FUNCTIONS)) 25 | } 26 | } 27 | 28 | unsafe fn destroy(_: &Instance) {} 29 | 30 | fn shading_language(_: &Instance) -> ShadingLanguage { 31 | ShadingLanguage::Glsl 32 | } 33 | 34 | fn open_device(_: &Instance) -> Result { 35 | unsafe { 36 | Ok(Device::from_raw_data(0, &DEVICE_FUNCTIONS)) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/program.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Programs to be run on the GPU. 12 | 13 | /// A program to be run on the GPU. 14 | pub struct Program { 15 | data: usize, 16 | functions: &'static ProgramFunctions, 17 | } 18 | 19 | #[doc(hidden)] 20 | pub struct ProgramFunctions { 21 | pub destroy: unsafe extern "Rust" fn(this: &Program), 22 | } 23 | 24 | impl Drop for Program { 25 | fn drop(&mut self) { 26 | unsafe { 27 | (self.functions.destroy)(self) 28 | } 29 | } 30 | } 31 | 32 | impl Program { 33 | #[doc(hidden)] 34 | #[inline] 35 | pub unsafe fn from_raw_data(data: usize, functions: &'static ProgramFunctions) -> Program { 36 | Program { 37 | data: data, 38 | functions: functions, 39 | } 40 | } 41 | 42 | #[doc(hidden)] 43 | #[inline] 44 | pub fn data(&self) -> usize { 45 | self.data 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/sync_event.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Events (a.k.a. fences) that can be waited on. 12 | 13 | use error::Error; 14 | 15 | /// An event (a.k.a. fence) that can be waited on. 16 | pub struct SyncEvent { 17 | data: usize, 18 | functions: &'static SyncEventFunctions, 19 | } 20 | 21 | #[doc(hidden)] 22 | pub struct SyncEventFunctions { 23 | pub destroy: unsafe extern "Rust" fn(this: &SyncEvent), 24 | pub wait: extern "Rust" fn(this: &SyncEvent) -> Result<(), Error>, 25 | } 26 | 27 | impl Drop for SyncEvent { 28 | fn drop(&mut self) { 29 | unsafe { 30 | (self.functions.destroy)(self) 31 | } 32 | } 33 | } 34 | 35 | impl SyncEvent { 36 | #[doc(hidden)] 37 | #[inline] 38 | pub unsafe fn from_raw_data(data: usize, functions: &'static SyncEventFunctions) -> SyncEvent { 39 | SyncEvent { 40 | data: data, 41 | functions: functions, 42 | } 43 | } 44 | 45 | #[doc(hidden)] 46 | #[inline] 47 | pub fn data(&self) -> usize { 48 | self.data 49 | } 50 | 51 | /// Blocks the CPU until this event has occurred. 52 | #[inline] 53 | pub fn wait(&self) -> Result<(), Error> { 54 | (self.functions.wait)(self) 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/profile_event.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Objects that can be used to query how long GPU operations took. 12 | 13 | use error::Error; 14 | 15 | /// An object that can be used to query how long GPU operations took. 16 | pub struct ProfileEvent { 17 | data: usize, 18 | functions: &'static ProfileEventFunctions, 19 | } 20 | 21 | #[doc(hidden)] 22 | pub struct ProfileEventFunctions { 23 | pub destroy: unsafe extern "Rust" fn(this: &ProfileEvent), 24 | pub time_elapsed: extern "Rust" fn(this: &ProfileEvent) -> Result, 25 | } 26 | 27 | impl Drop for ProfileEvent { 28 | fn drop(&mut self) { 29 | unsafe { 30 | (self.functions.destroy)(self) 31 | } 32 | } 33 | } 34 | 35 | impl ProfileEvent { 36 | #[doc(hidden)] 37 | #[inline] 38 | pub unsafe fn from_raw_data(data: usize, functions: &'static ProfileEventFunctions) 39 | -> ProfileEvent { 40 | ProfileEvent { 41 | data: data, 42 | functions: functions, 43 | } 44 | } 45 | 46 | #[doc(hidden)] 47 | #[inline] 48 | pub fn data(&self) -> usize { 49 | self.data 50 | } 51 | 52 | /// Returns the time that this operation took in nanoseconds. 53 | /// 54 | /// If the operation has not yet completed, this function blocks until it completes. 55 | #[inline] 56 | pub fn time_elapsed(&self) -> Result { 57 | (self.functions.time_elapsed)(self) 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/api/cl/profile_event.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::ffi::{self, CL_PROFILING_COMMAND_END, CL_PROFILING_COMMAND_START, CL_SUCCESS}; 12 | use api::cl::ffi::{cl_event, cl_ulong}; 13 | use error::Error; 14 | use profile_event::{ProfileEvent, ProfileEventFunctions}; 15 | use std::mem; 16 | use std::os::raw::c_void; 17 | use std::ptr; 18 | 19 | pub static PROFILE_EVENT_FUNCTIONS: ProfileEventFunctions = ProfileEventFunctions { 20 | destroy: destroy, 21 | time_elapsed: time_elapsed, 22 | }; 23 | 24 | unsafe fn destroy(this: &ProfileEvent) { 25 | ffi::clReleaseEvent(this.data() as cl_event); 26 | } 27 | 28 | fn time_elapsed(this: &ProfileEvent) -> Result { 29 | unsafe { 30 | if ffi::clWaitForEvents(1, &this.data() as *const usize as *const cl_event) != CL_SUCCESS { 31 | return Err(Error::Failed) 32 | } 33 | 34 | let mut start_time = 0; 35 | if ffi::clGetEventProfilingInfo(this.data() as cl_event, 36 | CL_PROFILING_COMMAND_START, 37 | mem::size_of::(), 38 | &mut start_time as *mut u64 as *mut c_void, 39 | ptr::null_mut()) != CL_SUCCESS { 40 | return Err(Error::Failed) 41 | } 42 | 43 | let mut end_time = 0; 44 | if ffi::clGetEventProfilingInfo(this.data() as cl_event, 45 | CL_PROFILING_COMMAND_END, 46 | mem::size_of::(), 47 | &mut end_time as *mut u64 as *mut c_void, 48 | ptr::null_mut()) != CL_SUCCESS { 49 | return Err(Error::Failed) 50 | } 51 | 52 | Ok(end_time - start_time) 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/instance.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Instances of the `compute-shader` library. 12 | 13 | use api; 14 | use device::Device; 15 | use error::Error; 16 | 17 | /// An instance of the `compute-shader` library. 18 | /// 19 | /// This wraps the underlying platform-specific API (currently OpenCL 1.2+ or OpenGL 4.3+). 20 | pub struct Instance { 21 | data: usize, 22 | functions: &'static InstanceFunctions, 23 | } 24 | 25 | #[doc(hidden)] 26 | pub struct InstanceFunctions { 27 | pub destroy: unsafe extern "Rust" fn(this: &Instance), 28 | pub shading_language: extern "Rust" fn(this: &Instance) -> ShadingLanguage, 29 | pub open_device: extern "Rust" fn(this: &Instance) -> Result, 30 | } 31 | 32 | /// The shading language supported by this instance. 33 | #[derive(Clone, Copy, PartialEq, Debug)] 34 | pub enum ShadingLanguage { 35 | /// OpenCL 1.2+. 36 | Cl, 37 | /// OpenGL Shading Language 4.3+. 38 | Glsl, 39 | } 40 | 41 | impl Drop for Instance { 42 | fn drop(&mut self) { 43 | unsafe { 44 | (self.functions.destroy)(self) 45 | } 46 | } 47 | } 48 | 49 | impl Instance { 50 | #[doc(hidden)] 51 | #[inline] 52 | pub unsafe fn from_raw_data(data: usize, functions: &'static InstanceFunctions) -> Instance { 53 | Instance { 54 | data: data, 55 | functions: functions, 56 | } 57 | } 58 | 59 | #[doc(hidden)] 60 | #[inline] 61 | pub fn data(&self) -> usize { 62 | self.data 63 | } 64 | 65 | /// Returns the shading language accepted by `Device::create_program()`. 66 | #[inline] 67 | pub fn shading_language(&self) -> ShadingLanguage { 68 | (self.functions.shading_language)(self) 69 | } 70 | 71 | /// Opens a handle to the GPU. 72 | #[inline] 73 | pub fn open_device(&self) -> Result { 74 | (self.functions.open_device)(self) 75 | } 76 | 77 | /// Initializes the library and returns a new instance. 78 | #[cfg(target_os = "macos")] 79 | pub fn new() -> Result { 80 | api::cl::instance::create() 81 | } 82 | 83 | /// Initializes the library and returns a new instance. 84 | #[cfg(not(target_os = "macos"))] 85 | pub fn new() -> Result { 86 | api::gl::instance::create() 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Data buffers on the GPU. 12 | 13 | use std::mem; 14 | use std::slice; 15 | 16 | /// A data buffer on the GPU. 17 | pub struct Buffer { 18 | data: usize, 19 | functions: &'static BufferFunctions, 20 | } 21 | 22 | #[doc(hidden)] 23 | pub struct BufferFunctions { 24 | pub destroy: unsafe extern "Rust" fn(this: &Buffer), 25 | } 26 | 27 | /// Memory protection from the GPU side. (The CPU is always free to perform whatever reads and 28 | /// writes it wants.) 29 | /// 30 | /// Do not rely on the driver to enforce this. 31 | #[derive(Clone, Copy, PartialEq, Debug)] 32 | pub enum Protection { 33 | /// The GPU may only read from this buffer. 34 | ReadOnly = 0, 35 | /// The GPU may only write to the buffer. 36 | /// 37 | /// Note that atomic writes count as reads too, so use `ReadWrite` instead if you're using 38 | /// them. 39 | WriteOnly = 1, 40 | /// The GPU may read and write to the buffer. 41 | ReadWrite = 2, 42 | } 43 | 44 | /// Where the initial data for a buffer comes from. 45 | pub enum BufferData<'a> { 46 | /// The data is undefined data of the given size. 47 | Uninitialized(usize), 48 | /// The data is initialized with the given contents. 49 | HostAllocated(HostAllocatedData<'a>), 50 | } 51 | 52 | /// CPU-side data to initialize a buffer store with. 53 | #[derive(Clone, Copy)] 54 | pub struct HostAllocatedData<'a>(&'a [u8]); 55 | 56 | impl Drop for Buffer { 57 | fn drop(&mut self) { 58 | unsafe { 59 | (self.functions.destroy)(self) 60 | } 61 | } 62 | } 63 | 64 | impl Buffer { 65 | #[doc(hidden)] 66 | #[inline] 67 | pub unsafe fn from_raw_data(data: usize, functions: &'static BufferFunctions) -> Buffer { 68 | Buffer { 69 | data: data, 70 | functions: functions, 71 | } 72 | } 73 | 74 | #[doc(hidden)] 75 | #[inline] 76 | pub fn data(&self) -> usize { 77 | self.data 78 | } 79 | } 80 | 81 | impl<'a> HostAllocatedData<'a> { 82 | /// Returns a raw pointer to the data. 83 | #[inline] 84 | pub fn as_ptr(&self) -> *const u8 { 85 | let slice = self.0; 86 | slice.as_ptr() 87 | } 88 | 89 | /// Returns the size of the data. 90 | #[inline] 91 | pub fn size(&self) -> usize { 92 | let slice = self.0; 93 | slice.len() 94 | } 95 | 96 | /// Constructs a `HostAllocatedData` from the given slice. 97 | #[inline] 98 | pub fn new<'b, T>(slice: &'b [T]) -> HostAllocatedData<'b> { 99 | unsafe { 100 | HostAllocatedData(slice::from_raw_parts(slice.as_ptr() as *const u8, 101 | slice.len() * mem::size_of::())) 102 | } 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/device.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! A GPU that supports compute. 12 | 13 | use buffer::{Buffer, BufferData, Protection}; 14 | use error::Error; 15 | use euclid::Size2D; 16 | use image::{Format, Image}; 17 | use program::Program; 18 | use queue::Queue; 19 | 20 | /// A GPU that supports compute. 21 | pub struct Device { 22 | data: usize, 23 | functions: &'static DeviceFunctions, 24 | } 25 | 26 | #[doc(hidden)] 27 | pub struct DeviceFunctions { 28 | pub destroy: unsafe extern "Rust" fn(this: &Device), 29 | pub create_queue: extern "Rust" fn(this: &Device) -> Result, 30 | pub create_program: extern "Rust" fn(this: &Device, source: &str) -> Result, 31 | pub create_buffer: extern "Rust" fn(this: &Device, protection: Protection, data: BufferData) 32 | -> Result, 33 | pub create_image: extern "Rust" fn(this: &Device, 34 | format: Format, 35 | protection: Protection, 36 | size: &Size2D) 37 | -> Result, 38 | } 39 | 40 | impl Drop for Device { 41 | fn drop(&mut self) { 42 | unsafe { 43 | (self.functions.destroy)(self) 44 | } 45 | } 46 | } 47 | 48 | impl Device { 49 | #[doc(hidden)] 50 | #[inline] 51 | pub unsafe fn from_raw_data(data: usize, functions: &'static DeviceFunctions) -> Device { 52 | Device { 53 | data: data, 54 | functions: functions, 55 | } 56 | } 57 | 58 | #[doc(hidden)] 59 | #[inline] 60 | pub fn data(&self) -> usize { 61 | self.data 62 | } 63 | 64 | /// Creates a new command queue on which jobs can be submitted. 65 | #[inline] 66 | pub fn create_queue(&self) -> Result { 67 | (self.functions.create_queue)(self) 68 | } 69 | 70 | /// Creates, compiles, and links a new compute program to execute on the GPU with the given 71 | /// source. 72 | /// 73 | /// The supplied source must conform to the result of `Instance::shading_language()`. 74 | #[inline] 75 | pub fn create_program(&self, source: &str) -> Result { 76 | (self.functions.create_program)(self, source) 77 | } 78 | 79 | /// Creates a new block of GPU memory with the given GPU-side protection, initialized with the 80 | /// supplied data. 81 | #[inline] 82 | pub fn create_buffer(&self, protection: Protection, data: BufferData) 83 | -> Result { 84 | (self.functions.create_buffer)(self, protection, data) 85 | } 86 | 87 | /// Creates a new image of the given format, GPU-side protection, and size. 88 | /// 89 | /// The initial contents of the image are undefined. 90 | #[inline] 91 | pub fn create_image(&self, format: Format, protection: Protection, size: &Size2D) 92 | -> Result { 93 | (self.functions.create_image)(self, format, protection, size) 94 | } 95 | } 96 | 97 | -------------------------------------------------------------------------------- /src/api/gl/image.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use error::Error; 12 | use gl::types::GLuint; 13 | use gl; 14 | use image::{ExternalImage, Format, Image, ImageFunctions}; 15 | 16 | pub static IMAGE_FUNCTIONS: ImageFunctions = ImageFunctions { 17 | destroy: destroy, 18 | bind_to: bind_to, 19 | width: width, 20 | height: height, 21 | format: format, 22 | }; 23 | 24 | unsafe fn destroy(this: &Image) { 25 | let mut texture = this.data()[0] as GLuint; 26 | gl::DeleteTextures(1, &mut texture); 27 | } 28 | 29 | fn bind_to(this: &Image, external_texture: &ExternalImage) -> Result<(), Error> { 30 | unsafe { 31 | match *external_texture { 32 | ExternalImage::GlTexture(texture) => { 33 | let mut format = 0; 34 | gl::ActiveTexture(gl::TEXTURE0); 35 | gl::BindTexture(gl::TEXTURE_RECTANGLE, this.data()[0] as GLuint); 36 | gl::GetTexLevelParameteriv(gl::TEXTURE_RECTANGLE, 37 | 0, 38 | gl::TEXTURE_INTERNAL_FORMAT, 39 | &mut format); 40 | 41 | gl::TextureView(texture, 42 | gl::TEXTURE_RECTANGLE, 43 | this.data()[0] as GLuint, 44 | format as GLuint, 45 | 0, 46 | 1, 47 | 0, 48 | 1) 49 | } 50 | } 51 | Ok(()) 52 | } 53 | } 54 | 55 | fn width(this: &Image) -> Result { 56 | unsafe { 57 | let mut width = 0; 58 | gl::ActiveTexture(gl::TEXTURE0); 59 | gl::BindTexture(gl::TEXTURE_RECTANGLE, this.data()[0] as GLuint); 60 | gl::GetTexLevelParameteriv(gl::TEXTURE_RECTANGLE, 0, gl::TEXTURE_WIDTH, &mut width); 61 | Ok(width as u32) 62 | } 63 | } 64 | 65 | fn height(this: &Image) -> Result { 66 | unsafe { 67 | let mut height = 0; 68 | gl::ActiveTexture(gl::TEXTURE0); 69 | gl::BindTexture(gl::TEXTURE_RECTANGLE, this.data()[0] as GLuint); 70 | gl::GetTexLevelParameteriv(gl::TEXTURE_RECTANGLE, 0, gl::TEXTURE_HEIGHT, &mut height); 71 | Ok(height as u32) 72 | } 73 | } 74 | 75 | fn format(this: &Image) -> Result { 76 | unsafe { 77 | let mut internal_format = 0; 78 | gl::ActiveTexture(gl::TEXTURE0); 79 | gl::BindTexture(gl::TEXTURE_RECTANGLE, this.data()[0] as GLuint); 80 | gl::GetTexLevelParameteriv(gl::TEXTURE_RECTANGLE, 81 | 0, 82 | gl::TEXTURE_INTERNAL_FORMAT, 83 | &mut internal_format); 84 | 85 | // This must match the definition of `Format::gl_internal_format()`. 86 | match internal_format as GLuint { 87 | gl::R8 => Ok(Format::R8), 88 | gl::RGBA8 => Ok(Format::RGBA8), 89 | gl::R32F => Ok(Format::R32F), 90 | _ => Err(Error::Failed), 91 | } 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /src/api/cl/image.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::ffi::{self, CL_FLOAT, CL_IMAGE_FORMAT, CL_IMAGE_HEIGHT, CL_IMAGE_WIDTH, CL_R}; 12 | use api::cl::ffi::{CL_RGBA, CL_SUCCESS, CL_UNORM_INT8, cl_image_format, cl_mem}; 13 | use error::Error; 14 | use gl; 15 | use image::{ExternalImage, Format, Image, ImageFunctions}; 16 | use std::mem; 17 | use std::os::raw::c_void; 18 | use std::ptr; 19 | 20 | #[cfg(target_os = "macos")] 21 | use core_foundation::base::TCFType; 22 | #[cfg(target_os = "macos")] 23 | use io_surface::{IOSurface, IOSurfaceRef}; 24 | 25 | pub static IMAGE_FUNCTIONS: ImageFunctions = ImageFunctions { 26 | destroy: destroy, 27 | bind_to: bind_to, 28 | width: width, 29 | height: height, 30 | format: format, 31 | }; 32 | 33 | #[cfg(target_os = "macos")] 34 | unsafe fn destroy(this: &Image) { 35 | // Release the `IOSurfaceRef` by wrapping it with no reference count change and letting that 36 | // wrapper drop. 37 | let io_surface = mem::transmute::(this.data()[1]); 38 | IOSurface::wrap_under_create_rule(io_surface); 39 | 40 | ffi::clReleaseMemObject(this.data()[0] as cl_mem); 41 | } 42 | 43 | #[cfg(target_os = "macos")] 44 | fn bind_to(this: &Image, external_image: &ExternalImage) -> Result<(), Error> { 45 | unsafe { 46 | match *external_image { 47 | ExternalImage::GlTexture(texture) => { 48 | let (width, height) = (try!(width(this)), try!(height(this))); 49 | let format = try!(format(this)); 50 | 51 | // FIXME(pcwalton): Fail more gracefully than panicking! (Really an `io-surface-rs` 52 | // bug.) 53 | gl::ActiveTexture(gl::TEXTURE0); 54 | gl::BindTexture(gl::TEXTURE_RECTANGLE, texture); 55 | let io_surface = mem::transmute::(this.data()[1]); 56 | let io_surface = IOSurface::wrap_under_get_rule(io_surface); 57 | io_surface.bind_to_gl_texture(width as i32, 58 | height as i32, 59 | format.gl_internal_format(), 60 | format.gl_format(), 61 | format.gl_type()); 62 | Ok(()) 63 | } 64 | } 65 | } 66 | } 67 | 68 | fn width(this: &Image) -> Result { 69 | unsafe { 70 | let mut width = 0usize; 71 | if ffi::clGetImageInfo(this.data()[0] as cl_mem, 72 | CL_IMAGE_WIDTH, 73 | mem::size_of::(), 74 | &mut width as *mut usize as *mut c_void, 75 | ptr::null_mut()) == CL_SUCCESS { 76 | Ok(width as u32) 77 | } else { 78 | Err(Error::Failed) 79 | } 80 | } 81 | } 82 | 83 | fn height(this: &Image) -> Result { 84 | unsafe { 85 | let mut height = 0usize; 86 | if ffi::clGetImageInfo(this.data()[0] as cl_mem, 87 | CL_IMAGE_HEIGHT, 88 | mem::size_of::(), 89 | &mut height as *mut usize as *mut c_void, 90 | ptr::null_mut()) == CL_SUCCESS { 91 | Ok(height as u32) 92 | } else { 93 | Err(Error::Failed) 94 | } 95 | } 96 | } 97 | 98 | fn format(this: &Image) -> Result { 99 | unsafe { 100 | let mut image_format = cl_image_format { 101 | image_channel_order: 0, 102 | image_channel_data_type: 0, 103 | }; 104 | if ffi::clGetImageInfo(this.data()[0] as cl_mem, 105 | CL_IMAGE_FORMAT, 106 | mem::size_of::(), 107 | &mut image_format as *mut cl_image_format as *mut c_void, 108 | ptr::null_mut()) != CL_SUCCESS { 109 | return Err(Error::Failed) 110 | } 111 | 112 | match (image_format.image_channel_order, image_format.image_channel_data_type) { 113 | (CL_R, CL_UNORM_INT8) => Ok(Format::R8), 114 | (CL_R, CL_FLOAT) => Ok(Format::R32F), 115 | (CL_RGBA, CL_UNORM_INT8) => Ok(Format::RGBA8), 116 | _ => unreachable!(), 117 | } 118 | } 119 | } 120 | 121 | -------------------------------------------------------------------------------- /src/api/gl/device.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::gl::buffer::BUFFER_FUNCTIONS; 12 | use api::gl::image::IMAGE_FUNCTIONS; 13 | use api::gl::program::PROGRAM_FUNCTIONS; 14 | use api::gl::queue::QUEUE_FUNCTIONS; 15 | use buffer::{Buffer, BufferData, Protection}; 16 | use device::{Device, DeviceFunctions}; 17 | use error::Error; 18 | use euclid::Size2D; 19 | use gl::types::GLint; 20 | use gl; 21 | use image::{Format, Image}; 22 | use program::Program; 23 | use queue::Queue; 24 | use std::os::raw::c_void; 25 | use std::ptr; 26 | 27 | pub static DEVICE_FUNCTIONS: DeviceFunctions = DeviceFunctions { 28 | destroy: destroy, 29 | create_queue: create_queue, 30 | create_program: create_program, 31 | create_buffer: create_buffer, 32 | create_image: create_image, 33 | }; 34 | 35 | unsafe fn destroy(_: &Device) {} 36 | 37 | fn create_queue(_: &Device) -> Result { 38 | unsafe { 39 | Ok(Queue::from_raw_data(0, &QUEUE_FUNCTIONS)) 40 | } 41 | } 42 | 43 | fn create_program(_: &Device, source: &str) -> Result { 44 | unsafe { 45 | let shader = gl::CreateShader(gl::COMPUTE_SHADER); 46 | let mut source_bytes = source.as_ptr() as *const i8; 47 | let source_length = source.len() as i32; 48 | gl::ShaderSource(shader, 1, &mut source_bytes, &source_length); 49 | gl::CompileShader(shader); 50 | 51 | let mut compile_status = 0; 52 | gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status); 53 | if compile_status != gl::TRUE as GLint { 54 | let mut info_log_length = 0; 55 | gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut info_log_length); 56 | let mut info_log_buffer = vec![0; info_log_length as usize + 1]; 57 | gl::GetShaderInfoLog(shader, 58 | info_log_length, 59 | ptr::null_mut(), 60 | info_log_buffer.as_mut_ptr() as *mut i8); 61 | let info_log = String::from_utf8(info_log_buffer).unwrap_or("".to_owned()); 62 | return Err(Error::CompileFailed(info_log)) 63 | } 64 | 65 | let program = gl::CreateProgram(); 66 | gl::AttachShader(program, shader); 67 | gl::LinkProgram(program); 68 | 69 | let mut link_status = 0; 70 | gl::GetProgramiv(program, gl::LINK_STATUS, &mut link_status); 71 | if link_status != gl::TRUE as GLint { 72 | let mut info_log_length = 0; 73 | gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut info_log_length); 74 | let mut info_log_buffer = vec![0; info_log_length as usize + 1]; 75 | gl::GetProgramInfoLog(program, 76 | info_log_length, 77 | ptr::null_mut(), 78 | info_log_buffer.as_mut_ptr() as *mut i8); 79 | let info_log = String::from_utf8(info_log_buffer).unwrap_or("".to_owned()); 80 | return Err(Error::LinkFailed(info_log)) 81 | } 82 | 83 | Ok(Program::from_raw_data(program as usize, &PROGRAM_FUNCTIONS)) 84 | } 85 | } 86 | 87 | fn create_buffer(_: &Device, _: Protection, mut data: BufferData) -> Result { 88 | unsafe { 89 | let mut buffer = 0; 90 | gl::GenBuffers(1, &mut buffer); 91 | gl::BindBuffer(gl::COPY_WRITE_BUFFER, buffer); 92 | 93 | match data { 94 | BufferData::HostAllocated(ref mut host_buffer) => { 95 | gl::BufferData(gl::COPY_WRITE_BUFFER, 96 | host_buffer.size() as isize, 97 | host_buffer.as_ptr() as *const c_void, 98 | gl::DYNAMIC_DRAW) 99 | } 100 | BufferData::Uninitialized(size) => { 101 | gl::BufferData(gl::COPY_WRITE_BUFFER, size as isize, ptr::null(), gl::DYNAMIC_DRAW) 102 | } 103 | } 104 | 105 | Ok(Buffer::from_raw_data(buffer as usize, &BUFFER_FUNCTIONS)) 106 | } 107 | } 108 | 109 | fn create_image(_: &Device, format: Format, protection: Protection, size: &Size2D) 110 | -> Result { 111 | unsafe { 112 | let mut texture = 0; 113 | gl::GenTextures(1, &mut texture); 114 | gl::BindTexture(gl::TEXTURE_RECTANGLE, texture); 115 | 116 | let gl_format = format.gl_internal_format(); 117 | gl::TexStorage2D(gl::TEXTURE_RECTANGLE, 118 | 1, 119 | gl_format, 120 | size.width as i32, 121 | size.height as i32); 122 | 123 | Ok(Image::from_raw_data([texture as usize, protection as usize], &IMAGE_FUNCTIONS)) 124 | } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /src/api/cl/instance.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::device::DEVICE_FUNCTIONS; 12 | use api::cl::ffi::{self, CL_DEVICE_TYPE_GPU, CL_DEVICE_NAME, CL_SUCCESS, cl_device_id}; 13 | use device::Device; 14 | use error::Error; 15 | use gl; 16 | use instance::{Instance, InstanceFunctions, ShadingLanguage}; 17 | use libc; 18 | use std::cmp; 19 | use std::os::raw::{c_char, c_void}; 20 | use std::ptr; 21 | use std::slice; 22 | 23 | pub static INSTANCE_FUNCTIONS: InstanceFunctions = InstanceFunctions { 24 | destroy: destroy, 25 | shading_language: shading_language, 26 | open_device: open_device, 27 | }; 28 | 29 | pub fn create() -> Result { 30 | unsafe { 31 | Ok(Instance::from_raw_data(0, &INSTANCE_FUNCTIONS)) 32 | } 33 | } 34 | 35 | unsafe fn destroy(_: &Instance) {} 36 | 37 | fn shading_language(_: &Instance) -> ShadingLanguage { 38 | ShadingLanguage::Cl 39 | } 40 | 41 | fn open_device(_: &Instance) -> Result { 42 | unsafe { 43 | let mut num_devices = 0; 44 | if ffi::clGetDeviceIDs(ptr::null_mut(), 45 | CL_DEVICE_TYPE_GPU, 46 | 0, 47 | ptr::null_mut(), 48 | &mut num_devices) != CL_SUCCESS || num_devices == 0 { 49 | return Err(Error::Failed) 50 | } 51 | 52 | let mut device_ids: Vec = vec![ptr::null_mut(); num_devices as usize]; 53 | if ffi::clGetDeviceIDs(ptr::null_mut(), 54 | CL_DEVICE_TYPE_GPU, 55 | num_devices, 56 | device_ids.as_mut_ptr(), 57 | ptr::null_mut()) != CL_SUCCESS { 58 | return Err(Error::Failed) 59 | } 60 | 61 | // Make sure the OpenCL vendor matches the current OpenGL vendor. Otherwise, on dual-GPU 62 | // systems, we might end up with multiple GPUs in use! 63 | // 64 | // We choose the OpenCL device for which the name has the longest substring in common with 65 | // the OpenGL renderer. 66 | // 67 | // FIXME(pcwalton): This is a really hacky approach. I apologize. 68 | let gl_renderer = to_slice(gl::GetString(gl::RENDERER)); 69 | let mut best_device_id = None; 70 | for &device_id in &device_ids { 71 | let mut name_len = 0; 72 | if ffi::clGetDeviceInfo(device_id, 73 | CL_DEVICE_NAME, 74 | 0, 75 | ptr::null_mut(), 76 | &mut name_len) != CL_SUCCESS { 77 | return Err(Error::Failed) 78 | } 79 | 80 | let mut name: Vec = vec![0; name_len]; 81 | if ffi::clGetDeviceInfo(device_id, 82 | CL_DEVICE_NAME, 83 | name_len, 84 | name.as_mut_ptr() as *mut c_void, 85 | ptr::null_mut()) != CL_SUCCESS { 86 | return Err(Error::Failed) 87 | } 88 | 89 | // Strip the trailing null. 90 | name.pop(); 91 | 92 | let score = longest_common_substring(gl_renderer, &name); 93 | best_device_id = match best_device_id { 94 | Some((best_score, _)) if score > best_score => Some((score, device_id)), 95 | Some(_) => best_device_id, 96 | None => Some((score, device_id)), 97 | } 98 | } 99 | 100 | let device_id = best_device_id.unwrap().1; 101 | let context = ffi::clCreateContext(ptr::null_mut(), 102 | 1, 103 | &device_id, 104 | None, 105 | ptr::null_mut(), 106 | ptr::null_mut()); 107 | if context.is_null() { 108 | return Err(Error::Failed) 109 | } 110 | 111 | Ok(Device::from_raw_data(context as usize, &DEVICE_FUNCTIONS)) 112 | } 113 | } 114 | 115 | unsafe fn to_slice<'a>(p: *const u8) -> &'a [u8] { 116 | slice::from_raw_parts(p, libc::strlen(p as *const c_char)) 117 | } 118 | 119 | // Dynamic programming algorithm: https://en.wikipedia.org/wiki/Longest_common_substring_problem 120 | fn longest_common_substring(a: &[u8], b: &[u8]) -> usize { 121 | let width = b.len() + 1; 122 | let mut longest_length = 0; 123 | let mut lengths = vec![0; (a.len() + 1) * width]; 124 | for (i, &ac) in a.iter().enumerate() { 125 | for (j, &bc) in b.iter().enumerate() { 126 | if ac == bc { 127 | let this_length = lengths[i * width + j] + 1; 128 | lengths[(i + 1) * width + j + 1] = this_length; 129 | longest_length = cmp::max(longest_length, this_length) 130 | } 131 | } 132 | } 133 | longest_length 134 | } 135 | 136 | -------------------------------------------------------------------------------- /src/image.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Images, which are essentially read-write textures. 12 | 13 | use error::Error; 14 | use gl::types::GLuint; 15 | use gl; 16 | 17 | /// An image, which is essentially a read-write texture. 18 | pub struct Image { 19 | data: [usize; 2], 20 | functions: &'static ImageFunctions, 21 | } 22 | 23 | #[doc(hidden)] 24 | pub struct ImageFunctions { 25 | pub destroy: unsafe extern "Rust" fn(this: &Image), 26 | pub bind_to: extern "Rust" fn(this: &Image, external_image: &ExternalImage) 27 | -> Result<(), Error>, 28 | pub width: extern "Rust" fn(this: &Image) -> Result, 29 | pub height: extern "Rust" fn(this: &Image) -> Result, 30 | pub format: extern "Rust" fn(this: &Image) -> Result, 31 | } 32 | 33 | /// An external resource that can be made to refer to this image. 34 | pub enum ExternalImage { 35 | /// The name of an OpenGL texture. 36 | GlTexture(u32), 37 | } 38 | 39 | /// The image format of a texture. 40 | /// 41 | /// TODO(pcwalton): Support more. 42 | #[derive(Clone, Copy, PartialEq, Debug)] 43 | pub enum Format { 44 | /// 8-bit unsigned normalized single-channel. 45 | R8, 46 | /// 8-bit unsigned normalized 4-channel. 47 | RGBA8, 48 | /// 32-bit single-channel floating-point. 49 | R32F, 50 | } 51 | 52 | /// A color. 53 | #[derive(Clone, Copy, PartialEq, Debug)] 54 | pub enum Color { 55 | /// A 4-channel color. 56 | UInt(u32, u32, u32, u32), 57 | } 58 | 59 | impl Drop for Image { 60 | fn drop(&mut self) { 61 | unsafe { 62 | (self.functions.destroy)(self) 63 | } 64 | } 65 | } 66 | 67 | impl Image { 68 | #[doc(hidden)] 69 | #[inline] 70 | pub unsafe fn from_raw_data(data: [usize; 2], functions: &'static ImageFunctions) -> Image { 71 | Image { 72 | data: data, 73 | functions: functions, 74 | } 75 | } 76 | 77 | #[doc(hidden)] 78 | #[inline] 79 | pub fn data(&self) -> [usize; 2] { 80 | self.data 81 | } 82 | 83 | /// Makes `external_image` reflect the contents of this image. 84 | /// 85 | /// This is useful in order to render an image created using a compute shader with OpenGL, for 86 | /// example. 87 | #[inline] 88 | pub fn bind_to(&self, external_image: &ExternalImage) -> Result<(), Error> { 89 | (self.functions.bind_to)(self, external_image) 90 | } 91 | 92 | /// Returns the width of this image in pixels. 93 | #[inline] 94 | pub fn width(&self) -> Result { 95 | (self.functions.width)(self) 96 | } 97 | 98 | /// Returns the height of this image in pixels. 99 | #[inline] 100 | pub fn height(&self) -> Result { 101 | (self.functions.height)(self) 102 | } 103 | 104 | /// Returns the format of this image. 105 | #[inline] 106 | pub fn format(&self) -> Result { 107 | (self.functions.format)(self) 108 | } 109 | } 110 | 111 | impl Format { 112 | /// Returns the value that should be passed as the `format` parameter to `glTexImage2D()` to 113 | /// create a texture matching this image format. 114 | #[inline] 115 | #[cfg(target_os = "macos")] 116 | pub fn gl_format(self) -> GLuint { 117 | match self { 118 | Format::R8 | Format::R32F => gl::RED, 119 | Format::RGBA8 => gl::BGRA, 120 | } 121 | } 122 | 123 | /// Returns the value that should be passed as the `format` parameter to `glTexImage2D()` to 124 | /// create a texture matching this image format. 125 | #[inline] 126 | #[cfg(not(target_os = "macos"))] 127 | pub fn gl_format(self) -> GLuint { 128 | match self { 129 | Format::R8 | Format::R32F => gl::RED, 130 | Format::RGBA8 => gl::RGBA, 131 | } 132 | } 133 | 134 | /// Returns the value that should be passed as the `type` parameter to `glTexImage2D()` to 135 | /// create a texture matching this image format. 136 | #[inline] 137 | #[cfg(target_os = "macos")] 138 | pub fn gl_type(self) -> GLuint { 139 | match self { 140 | Format::R8 => gl::UNSIGNED_BYTE, 141 | Format::RGBA8 => gl::UNSIGNED_INT_8_8_8_8_REV, 142 | Format::R32F => gl::FLOAT, 143 | } 144 | } 145 | 146 | /// Returns the value that should be passed as the `type` parameter to `glTexImage2D()` to 147 | /// create a texture matching this image format. 148 | #[inline] 149 | #[cfg(not(target_os = "macos"))] 150 | pub fn gl_type(self) -> GLuint { 151 | match self { 152 | Format::R8 | Format::RGBA8 => gl::UNSIGNED_BYTE, 153 | Format::R32F => gl::FLOAT, 154 | } 155 | } 156 | 157 | /// Returns the OpenGL image format corresponding to this image format. 158 | #[inline] 159 | pub fn gl_internal_format(self) -> GLuint { 160 | match self { 161 | Format::R8 => gl::R8, 162 | Format::RGBA8 => gl::RGBA8, 163 | Format::R32F => gl::R32F, 164 | } 165 | } 166 | } 167 | 168 | -------------------------------------------------------------------------------- /examples/matrix-multiply.rs: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | extern crate byteorder; 5 | extern crate compute_shader; 6 | extern crate gl; 7 | extern crate glfw; 8 | extern crate rand; 9 | 10 | use byteorder::{NativeEndian, ReadBytesExt}; 11 | use compute_shader::buffer::{BufferData, HostAllocatedData, Protection}; 12 | use compute_shader::instance::{Instance, ShadingLanguage}; 13 | use compute_shader::queue::Uniform; 14 | use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode}; 15 | use rand::Rng; 16 | use std::env; 17 | use std::io::Cursor; 18 | use std::mem; 19 | use std::os::raw::c_void; 20 | 21 | const DEFAULT_MATRIX_LENGTH: usize = 512; 22 | 23 | // Naïve matrix multiplication, just as a demo. 24 | // 25 | // With an unsupported GPU on Linux (e.g. VM), try: 26 | // 27 | // $ LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=softpipe ./matrix-multiply 16 28 | // 29 | // (Because the shader code is totally naïve, this will run slower on the GPU than on the CPU. 30 | // Don't use the shader code for anything.) 31 | pub fn main() { 32 | let matrix_length = env::args().nth(1) 33 | .and_then(|arg| arg.parse().ok()) 34 | .unwrap_or(DEFAULT_MATRIX_LENGTH); 35 | 36 | let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); 37 | glfw.window_hint(WindowHint::ContextVersion(3, 3)); 38 | glfw.window_hint(WindowHint::OpenGlForwardCompat(true)); 39 | glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core)); 40 | glfw.window_hint(WindowHint::Visible(false)); 41 | let context = glfw.create_window(320, 240, "matrix-multiply", WindowMode::Windowed); 42 | 43 | let mut window = context.expect("Couldn't create a window!").0; 44 | window.make_current(); 45 | gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void); 46 | 47 | let instance = Instance::new().unwrap(); 48 | let device = instance.open_device().unwrap(); 49 | 50 | let source = match instance.shading_language() { 51 | ShadingLanguage::Cl => CL_SHADER, 52 | ShadingLanguage::Glsl => GL_SHADER, 53 | }; 54 | let program = device.create_program(source).unwrap(); 55 | 56 | let mut thread_rng = rand::thread_rng(); 57 | 58 | let input: Vec = thread_rng.gen_iter().take(matrix_length * matrix_length).collect(); 59 | println!("Input:"); 60 | print(&input, matrix_length); 61 | 62 | let input = BufferData::HostAllocated(HostAllocatedData::new(&input)); 63 | let input = device.create_buffer(Protection::ReadOnly, input).unwrap(); 64 | let output = BufferData::Uninitialized(matrix_length * matrix_length * mem::size_of::()); 65 | let output = device.create_buffer(Protection::WriteOnly, output).unwrap(); 66 | 67 | let queue = device.create_queue().unwrap(); 68 | let groups = [matrix_length as u32, matrix_length as u32]; 69 | let uniforms = [ 70 | (0, Uniform::Buffer(&output)), 71 | (1, Uniform::Buffer(&input)), 72 | (2, Uniform::U32(matrix_length as u32)), 73 | ]; 74 | queue.submit_compute(&program, &groups, &uniforms, &[]).unwrap(); 75 | let event = queue.submit_sync_event().unwrap(); 76 | 77 | let mut result_bytes = vec![0; matrix_length * matrix_length * mem::size_of::()]; 78 | queue.submit_read_buffer(&mut result_bytes, &output, 0, &[event]).unwrap(); 79 | queue.submit_sync_event().unwrap().wait().unwrap(); 80 | 81 | let mut result = Vec::with_capacity(matrix_length * matrix_length); 82 | let mut result_cursor = Cursor::new(result_bytes); 83 | while let Ok(value) = result_cursor.read_f32::() { 84 | result.push(value) 85 | } 86 | 87 | println!("\nResult:"); 88 | print(&result, matrix_length); 89 | } 90 | 91 | fn print(matrix: &[f32], matrix_length: usize) { 92 | for row in 0..matrix_length { 93 | for column in 0..matrix_length { 94 | print!("{} ", matrix[row * matrix_length + column]) 95 | } 96 | println!(""); 97 | } 98 | } 99 | 100 | static CL_SHADER: &'static str = r#" 101 | __kernel void matrix_multiply(__global __write_only float *gOutput, 102 | __global __read_only float *gInput, 103 | uint kLength) { 104 | uint destColumn = get_global_id(0), destRow = get_global_id(1); 105 | float value = 0.0f; 106 | for (uint i = 0; i < kLength; i++) 107 | value += gInput[i * kLength + destColumn] * gInput[destRow * kLength + i]; 108 | gOutput[destRow * kLength + destColumn] = value; 109 | } 110 | "#; 111 | 112 | static GL_SHADER: &'static str = r#" 113 | #version 330 114 | #extension GL_ARB_compute_shader : require 115 | #extension GL_ARB_explicit_uniform_location : require 116 | #extension GL_ARB_shader_storage_buffer_object : require 117 | 118 | layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 119 | 120 | layout(std430, binding = 0) buffer ssbOutput { 121 | float gOutput[]; 122 | }; 123 | layout(std430, binding = 1) buffer ssbInput { 124 | float gInput[]; 125 | }; 126 | layout(location = 2) uniform uint uLength; 127 | 128 | void main() { 129 | uint destColumn = gl_GlobalInvocationID[0], destRow = gl_GlobalInvocationID[1]; 130 | float value = 0.0f; 131 | for (uint i = 0u; i < uLength; i++) 132 | value += gInput[i * uLength + destColumn] * gInput[destRow * uLength + i]; 133 | gOutput[destRow * uLength + destColumn] = value; 134 | } 135 | "#; 136 | 137 | -------------------------------------------------------------------------------- /src/api/gl/queue.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::gl::profile_event::PROFILE_EVENT_FUNCTIONS; 12 | use api::gl::sync_event::SYNC_EVENT_FUNCTIONS; 13 | use buffer::{Buffer, Protection}; 14 | use error::Error; 15 | use gl::types::{GLint, GLuint}; 16 | use gl; 17 | use image::{Color, Image}; 18 | use profile_event::ProfileEvent; 19 | use program::Program; 20 | use queue::{Queue, QueueFunctions, Uniform}; 21 | use std::os::raw::c_void; 22 | use sync_event::SyncEvent; 23 | 24 | pub static QUEUE_FUNCTIONS: QueueFunctions = QueueFunctions { 25 | destroy: destroy, 26 | flush: flush, 27 | finish: finish, 28 | submit_compute: submit_compute, 29 | submit_clear: submit_clear, 30 | submit_read_buffer: submit_read_buffer, 31 | submit_sync_event: submit_sync_event, 32 | }; 33 | 34 | unsafe fn destroy(_: &Queue) {} 35 | 36 | fn flush(_: &Queue) -> Result<(), Error> { 37 | unsafe { 38 | gl::Flush(); 39 | Ok(()) 40 | } 41 | } 42 | 43 | fn finish(_: &Queue) -> Result<(), Error> { 44 | unsafe { 45 | gl::Finish(); 46 | Ok(()) 47 | } 48 | } 49 | 50 | fn submit_compute(_: &Queue, 51 | program: &Program, 52 | num_groups: &[u32], 53 | uniforms: &[(u32, Uniform)], 54 | _: &[SyncEvent]) 55 | -> Result { 56 | unsafe { 57 | gl::UseProgram(program.data() as GLuint); 58 | 59 | for &(uniform_index, ref uniform) in uniforms { 60 | match *uniform { 61 | Uniform::Buffer(buffer) => { 62 | gl::MemoryBarrier(gl::SHADER_STORAGE_BARRIER_BIT); 63 | 64 | let mut buffer_size = 0; 65 | gl::BindBuffer(gl::COPY_READ_BUFFER, buffer.data() as u32); 66 | gl::GetBufferParameteriv(gl::COPY_READ_BUFFER, 67 | gl::BUFFER_SIZE, 68 | &mut buffer_size); 69 | 70 | gl::BindBufferBase(gl::SHADER_STORAGE_BUFFER, 71 | uniform_index, 72 | buffer.data() as GLuint); 73 | } 74 | Uniform::Image(image) => { 75 | gl::MemoryBarrier(gl::TEXTURE_FETCH_BARRIER_BIT | 76 | gl::SHADER_IMAGE_ACCESS_BARRIER_BIT); 77 | 78 | let access = match image.data()[1] { 79 | p if p == Protection::ReadOnly as usize => gl::READ_ONLY, 80 | p if p == Protection::WriteOnly as usize => gl::WRITE_ONLY, 81 | _ => gl::READ_WRITE, 82 | }; 83 | 84 | let mut internal_format = 0; 85 | gl::ActiveTexture(gl::TEXTURE0); 86 | gl::BindTexture(gl::TEXTURE_RECTANGLE, image.data()[0] as GLuint); 87 | gl::GetTexLevelParameteriv(gl::TEXTURE_RECTANGLE, 88 | 0, 89 | gl::TEXTURE_INTERNAL_FORMAT, 90 | &mut internal_format); 91 | gl::BindTexture(gl::TEXTURE_RECTANGLE, 0); 92 | 93 | gl::BindImageTexture(uniform_index, 94 | image.data()[0] as GLuint, 95 | 0, 96 | gl::FALSE, 97 | 0, 98 | access, 99 | internal_format as GLuint); 100 | } 101 | Uniform::U32(value) => gl::Uniform1ui(uniform_index as GLint, value), 102 | Uniform::UVec4(values) => { 103 | gl::Uniform4ui(uniform_index as GLint, 104 | values[0], 105 | values[1], 106 | values[2], 107 | values[3]) 108 | } 109 | } 110 | } 111 | 112 | let mut query = 0; 113 | gl::GenQueries(1, &mut query); 114 | gl::BeginQuery(gl::TIME_ELAPSED, query); 115 | 116 | gl::DispatchCompute(*num_groups.get(0).unwrap_or(&1), 117 | *num_groups.get(1).unwrap_or(&1), 118 | *num_groups.get(2).unwrap_or(&1)); 119 | 120 | gl::EndQuery(gl::TIME_ELAPSED); 121 | 122 | Ok(ProfileEvent::from_raw_data(query as usize, &PROFILE_EVENT_FUNCTIONS)) 123 | } 124 | } 125 | 126 | fn submit_clear(_: &Queue, image: &Image, color: &Color, _: &[SyncEvent]) 127 | -> Result { 128 | unsafe { 129 | let color = match *color { 130 | Color::UInt(r, _, _, _) => r as u8, 131 | }; 132 | 133 | let mut query = 0; 134 | gl::GenQueries(1, &mut query); 135 | gl::BeginQuery(gl::TIME_ELAPSED, query); 136 | 137 | gl::ClearTexImage(image.data()[0] as GLuint, 138 | 0, 139 | gl::RED, 140 | gl::UNSIGNED_BYTE, 141 | &color as *const u8 as *const c_void); 142 | 143 | gl::EndQuery(gl::TIME_ELAPSED); 144 | 145 | Ok(ProfileEvent::from_raw_data(query as usize, &PROFILE_EVENT_FUNCTIONS)) 146 | } 147 | } 148 | 149 | fn submit_read_buffer(_: &Queue, dest: &mut [u8], buffer: &Buffer, start: usize, _: &[SyncEvent]) 150 | -> Result { 151 | unsafe { 152 | let mut query = 0; 153 | gl::GenQueries(1, &mut query); 154 | gl::BeginQuery(gl::TIME_ELAPSED, query); 155 | 156 | gl::BindBuffer(gl::COPY_READ_BUFFER, buffer.data() as GLuint); 157 | gl::GetBufferSubData(gl::COPY_READ_BUFFER, 158 | start as isize, 159 | dest.len() as isize, 160 | dest.as_mut_ptr() as *mut c_void); 161 | 162 | gl::EndQuery(gl::TIME_ELAPSED); 163 | 164 | Ok(ProfileEvent::from_raw_data(query as usize, &PROFILE_EVENT_FUNCTIONS)) 165 | } 166 | } 167 | 168 | fn submit_sync_event(_: &Queue) -> Result { 169 | unsafe { 170 | let fence = gl::FenceSync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); 171 | Ok(SyncEvent::from_raw_data(fence as usize, &SYNC_EVENT_FUNCTIONS)) 172 | } 173 | } 174 | 175 | -------------------------------------------------------------------------------- /src/queue.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Queues on which compute jobs can be submitted. 12 | 13 | use buffer::Buffer; 14 | use error::Error; 15 | use image::{Color, Image}; 16 | use profile_event::ProfileEvent; 17 | use program::Program; 18 | use sync_event::SyncEvent; 19 | 20 | /// A queue on which compute jobs can be submitted. 21 | pub struct Queue { 22 | data: usize, 23 | functions: &'static QueueFunctions, 24 | } 25 | 26 | #[doc(hidden)] 27 | pub struct QueueFunctions { 28 | pub destroy: unsafe extern "Rust" fn(this: &Queue), 29 | pub flush: extern "Rust" fn(this: &Queue) -> Result<(), Error>, 30 | pub finish: extern "Rust" fn(this: &Queue) -> Result<(), Error>, 31 | pub submit_compute: extern "Rust" fn(this: &Queue, 32 | program: &Program, 33 | num_groups: &[u32], 34 | uniforms: &[(u32, Uniform)], 35 | events: &[SyncEvent]) 36 | -> Result, 37 | pub submit_clear: extern "Rust" fn(this: &Queue, 38 | image: &Image, 39 | color: &Color, 40 | events: &[SyncEvent]) 41 | -> Result, 42 | pub submit_read_buffer: extern "Rust" fn(this: &Queue, 43 | dest: &mut [u8], 44 | buffer: &Buffer, 45 | start: usize, 46 | events: &[SyncEvent]) 47 | -> Result, 48 | pub submit_sync_event: extern "Rust" fn(this: &Queue) -> Result, 49 | } 50 | 51 | /// An argument to a program. 52 | pub enum Uniform<'a> { 53 | /// A reference to a GPU-side memory buffer. 54 | Buffer(&'a Buffer), 55 | /// A reference to an image on the GPU. 56 | Image(&'a Image), 57 | /// A 32-bit unsigned integer value. 58 | U32(u32), 59 | /// A vector of 4 32-bit unsigned integers. 60 | UVec4([u32; 4]), 61 | } 62 | 63 | impl Drop for Queue { 64 | fn drop(&mut self) { 65 | unsafe { 66 | (self.functions.destroy)(self) 67 | } 68 | } 69 | } 70 | 71 | impl Queue { 72 | #[doc(hidden)] 73 | #[inline] 74 | pub unsafe fn from_raw_data(data: usize, functions: &'static QueueFunctions) -> Queue { 75 | Queue { 76 | data: data, 77 | functions: functions, 78 | } 79 | } 80 | 81 | #[doc(hidden)] 82 | #[inline] 83 | pub fn data(&self) -> usize { 84 | self.data 85 | } 86 | 87 | /// Submits all queued commands to the GPU. 88 | /// 89 | /// This does *not* wait for the commands to finish. It does, however, guarantee that they will 90 | /// complete (or fail) in finite time. 91 | #[inline] 92 | pub fn flush(&self) -> Result<(), Error> { 93 | (self.functions.flush)(self) 94 | } 95 | 96 | /// Submits commands to the GPU and waits for them to complete. 97 | #[inline] 98 | pub fn finish(&self) -> Result<(), Error> { 99 | (self.functions.finish)(self) 100 | } 101 | 102 | /// Instructs the GPU to execute the given program. 103 | /// 104 | /// * `program` specifies the program. 105 | /// 106 | /// * `num_groups` specifies the number of workgroups in each dimension. It must be an array of 107 | /// between 1 and 3 nonzero values. 108 | /// 109 | /// * `uniforms` specifies the values of arguments used to invoke the program. The first 110 | /// element in the tuple is the argument index; the second is the actual value of the 111 | /// argument (see `Uniform`). 112 | /// 113 | /// * `events` is a list of sync events that must complete before execution of this program can 114 | /// begin. 115 | /// 116 | /// Returns a profiling event that can be used to time the execution of this program. 117 | #[inline] 118 | pub fn submit_compute(&self, 119 | program: &Program, 120 | num_groups: &[u32], 121 | uniforms: &[(u32, Uniform)], 122 | events: &[SyncEvent]) 123 | -> Result { 124 | (self.functions.submit_compute)(self, program, num_groups, uniforms, events) 125 | } 126 | 127 | /// Instructs the GPU to clear the given image to a solid color. 128 | /// 129 | /// * `image` specifies the image to clear. 130 | /// 131 | /// * `color` specifies the color to fill with. 132 | /// 133 | /// * `events` is a list of sync events that must complete before this operation can begin. 134 | /// 135 | /// Returns a profiling event that can be used to query how long it took to clear the image. 136 | #[inline] 137 | pub fn submit_clear(&self, image: &Image, color: &Color, events: &[SyncEvent]) 138 | -> Result { 139 | (self.functions.submit_clear)(self, image, color, events) 140 | } 141 | 142 | /// Reads a buffer from the GPU to main memory. 143 | /// 144 | /// * `dest` specifies the buffer to read to. 145 | /// 146 | /// * `buffer` specifies the buffer to read from. 147 | /// 148 | /// * `start` specifies the position in the buffer to begin reading from. 149 | /// 150 | /// * `events` is a list of sync events that must complete before this operation can begin. 151 | /// 152 | /// Returns a profiling event that can be used to query how long this operation took. 153 | /// 154 | /// This operation blocks until completion. 155 | #[inline] 156 | pub fn submit_read_buffer(&self, 157 | dest: &mut [u8], 158 | buffer: &Buffer, 159 | start: usize, 160 | events: &[SyncEvent]) 161 | -> Result { 162 | (self.functions.submit_read_buffer)(self, dest, buffer, start, events) 163 | } 164 | 165 | /// Returns a sync event that can be used to wait until the GPU has finished executing all 166 | /// commands up to the point at which this is called. 167 | #[inline] 168 | pub fn submit_sync_event(&self) -> Result { 169 | (self.functions.submit_sync_event)(self) 170 | } 171 | } 172 | 173 | -------------------------------------------------------------------------------- /src/api/cl/device.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::buffer::BUFFER_FUNCTIONS; 12 | use api::cl::ffi::{self, CL_CONTEXT_DEVICES, CL_FLOAT, CL_MEM_COPY_HOST_PTR, CL_MEM_READ_ONLY}; 13 | use api::cl::ffi::{CL_MEM_READ_WRITE, CL_MEM_WRITE_ONLY, CL_PROGRAM_BUILD_LOG}; 14 | use api::cl::ffi::{CL_QUEUE_PROFILING_ENABLE, CL_R, CL_RGBA, CL_SUCCESS, CL_UNORM_INT8}; 15 | use api::cl::ffi::{cl_context, cl_device_id, cl_image_format, cl_mem_flags}; 16 | use api::cl::image::IMAGE_FUNCTIONS; 17 | use api::cl::program::PROGRAM_FUNCTIONS; 18 | use api::cl::queue::QUEUE_FUNCTIONS; 19 | use buffer::{Buffer, BufferData, Protection}; 20 | use device::{Device, DeviceFunctions}; 21 | use error::Error; 22 | use euclid::Size2D; 23 | use image::{Format, Image}; 24 | use program::Program; 25 | use queue::Queue; 26 | use std::mem; 27 | use std::os::raw::c_void; 28 | use std::ptr; 29 | 30 | #[cfg(target_os = "macos")] 31 | use core_foundation::base::TCFType; 32 | #[cfg(target_os = "macos")] 33 | use core_foundation::dictionary::CFDictionary; 34 | #[cfg(target_os = "macos")] 35 | use core_foundation::number::CFNumber; 36 | #[cfg(target_os = "macos")] 37 | use core_foundation::string::CFString; 38 | #[cfg(target_os = "macos")] 39 | use io_surface::{self, kIOSurfaceBytesPerElement, kIOSurfaceHeight, kIOSurfaceWidth}; 40 | 41 | pub static DEVICE_FUNCTIONS: DeviceFunctions = DeviceFunctions { 42 | destroy: destroy, 43 | create_queue: create_queue, 44 | create_program: create_program, 45 | create_buffer: create_buffer, 46 | create_image: create_image, 47 | }; 48 | 49 | unsafe fn destroy(this: &Device) { 50 | ffi::clReleaseContext(this.data() as cl_context); 51 | } 52 | 53 | fn create_queue(this: &Device) -> Result { 54 | unsafe { 55 | let mut device_id: cl_device_id = ptr::null_mut(); 56 | if ffi::clGetContextInfo(this.data() as cl_context, 57 | CL_CONTEXT_DEVICES, 58 | mem::size_of::(), 59 | &mut device_id as *mut cl_device_id as *mut c_void, 60 | ptr::null_mut()) != CL_SUCCESS { 61 | return Err(Error::Failed) 62 | } 63 | 64 | let queue = ffi::clCreateCommandQueue(this.data() as cl_context, 65 | device_id, 66 | CL_QUEUE_PROFILING_ENABLE, 67 | ptr::null_mut()); 68 | if queue != ptr::null_mut() { 69 | Ok(Queue::from_raw_data(queue as usize, &QUEUE_FUNCTIONS)) 70 | } else { 71 | Err(Error::Failed) 72 | } 73 | } 74 | } 75 | 76 | fn create_program(this: &Device, source: &str) -> Result { 77 | unsafe { 78 | let mut strings = source.as_ptr() as *const i8; 79 | let lengths = source.len(); 80 | let program = ffi::clCreateProgramWithSource(this.data() as cl_context, 81 | 1, 82 | &mut strings, 83 | &lengths, 84 | ptr::null_mut()); 85 | if program == ptr::null_mut() { 86 | return Err(Error::Failed) 87 | } 88 | 89 | let mut device_id: cl_device_id = ptr::null_mut(); 90 | if ffi::clGetContextInfo(this.data() as cl_context, 91 | CL_CONTEXT_DEVICES, 92 | mem::size_of::(), 93 | &mut device_id as *mut cl_device_id as *mut c_void, 94 | ptr::null_mut()) != CL_SUCCESS { 95 | return Err(Error::Failed) 96 | } 97 | 98 | let null = 0; 99 | 100 | if ffi::clBuildProgram(program, 101 | 1, 102 | &device_id, 103 | &null, 104 | None, 105 | ptr::null_mut()) != CL_SUCCESS { 106 | let mut build_log = vec![0; 65536]; 107 | let mut build_log_size = build_log.len(); 108 | ffi::clGetProgramBuildInfo(program, 109 | device_id, 110 | CL_PROGRAM_BUILD_LOG, 111 | build_log.len() - 1, 112 | build_log.as_mut_ptr() as *mut c_void, 113 | &mut build_log_size); 114 | build_log.truncate(build_log_size); 115 | 116 | return Err(Error::CompileFailed(String::from_utf8(build_log).unwrap_or("".to_owned()))) 117 | } 118 | 119 | let mut kernel = ptr::null_mut(); 120 | if ffi::clCreateKernelsInProgram(program, 1, &mut kernel, ptr::null_mut()) != CL_SUCCESS { 121 | return Err(Error::Failed) 122 | } 123 | 124 | Ok(Program::from_raw_data(kernel as usize, &PROGRAM_FUNCTIONS)) 125 | } 126 | } 127 | 128 | fn create_buffer(this: &Device, protection: Protection, mut data: BufferData) 129 | -> Result { 130 | unsafe { 131 | let mut mem_flags = protection_to_mem_flags(protection); 132 | let (size, host_ptr); 133 | match data { 134 | BufferData::HostAllocated(ref mut buffer) => { 135 | mem_flags |= CL_MEM_COPY_HOST_PTR; 136 | 137 | size = buffer.size(); 138 | host_ptr = buffer.as_ptr() 139 | } 140 | BufferData::Uninitialized(in_size) => { 141 | size = in_size; 142 | host_ptr = ptr::null_mut() 143 | } 144 | } 145 | 146 | let buffer = ffi::clCreateBuffer(this.data() as cl_context, 147 | mem_flags, 148 | size, 149 | host_ptr as *mut c_void, 150 | ptr::null_mut()); 151 | if !buffer.is_null() { 152 | Ok(Buffer::from_raw_data(buffer as usize, &BUFFER_FUNCTIONS)) 153 | } else { 154 | Err(Error::Failed) 155 | } 156 | } 157 | } 158 | 159 | #[cfg(target_os = "macos")] 160 | fn create_image(this: &Device, format: Format, protection: Protection, size: &Size2D) 161 | -> Result { 162 | unsafe { 163 | let bytes_per_element = match format { 164 | Format::R8 => 1, 165 | Format::RGBA8 | Format::R32F => 4, 166 | }; 167 | 168 | let properties = CFDictionary::from_CFType_pairs(&[ 169 | (CFString::wrap_under_get_rule(kIOSurfaceWidth), 170 | CFNumber::from_i64(size.width as i64)), 171 | (CFString::wrap_under_get_rule(kIOSurfaceHeight), 172 | CFNumber::from_i64(size.height as i64)), 173 | (CFString::wrap_under_get_rule(kIOSurfaceBytesPerElement), 174 | CFNumber::from_i32(bytes_per_element)), 175 | ]); 176 | let surface = io_surface::new(&properties); 177 | 178 | let protection = protection_to_mem_flags(protection); 179 | 180 | let image_format = match format { 181 | Format::R8 => { 182 | cl_image_format { 183 | image_channel_order: CL_R, 184 | image_channel_data_type: CL_UNORM_INT8, 185 | } 186 | } 187 | Format::RGBA8 => { 188 | cl_image_format { 189 | image_channel_order: CL_RGBA, 190 | image_channel_data_type: CL_UNORM_INT8, 191 | } 192 | } 193 | Format::R32F => { 194 | cl_image_format { 195 | image_channel_order: CL_R, 196 | image_channel_data_type: CL_FLOAT, 197 | } 198 | } 199 | }; 200 | 201 | let mut error = CL_SUCCESS; 202 | 203 | let image = ffi::clCreateImageFromIOSurface2DAPPLE(this.data() as cl_context, 204 | protection, 205 | &image_format, 206 | size.width as usize, 207 | size.height as usize, 208 | surface.as_concrete_TypeRef(), 209 | &mut error); 210 | 211 | if error != CL_SUCCESS || image.is_null() { 212 | return Err(Error::Failed) 213 | } 214 | 215 | let surface_ref = surface.as_concrete_TypeRef(); 216 | mem::forget(surface); 217 | 218 | Ok(Image::from_raw_data([image as usize, surface_ref as usize], &IMAGE_FUNCTIONS)) 219 | } 220 | } 221 | 222 | fn protection_to_mem_flags(protection: Protection) -> cl_mem_flags { 223 | match protection { 224 | Protection::ReadOnly => CL_MEM_READ_ONLY, 225 | Protection::WriteOnly => CL_MEM_WRITE_ONLY, 226 | Protection::ReadWrite => CL_MEM_READ_WRITE, 227 | } 228 | } 229 | 230 | -------------------------------------------------------------------------------- /examples/generate-cave.rs: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | extern crate compute_shader; 5 | extern crate euclid; 6 | extern crate gl; 7 | extern crate glfw; 8 | extern crate lord_drawquaad; 9 | extern crate rand; 10 | 11 | use compute_shader::buffer::{BufferData, Protection}; 12 | use compute_shader::image::{ExternalImage, Format}; 13 | use compute_shader::instance::{Instance, ShadingLanguage}; 14 | use compute_shader::queue::Uniform; 15 | use euclid::Size2D; 16 | use gl::types::GLint; 17 | use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent}; 18 | use glfw::{WindowHint, WindowMode}; 19 | use rand::Rng; 20 | use std::mem; 21 | use std::os::raw::c_void; 22 | 23 | const WIDTH: u32 = 800; 24 | const HEIGHT: u32 = 600; 25 | 26 | const ITERATIONS: u32 = 8; 27 | 28 | #[derive(Clone, Copy, Debug)] 29 | struct Vertex { 30 | position: [f32; 2], 31 | tex_coord: [i32; 2], 32 | } 33 | 34 | // A simple cave generator. 35 | // 36 | // See: http://bit.ly/2hWytfH 37 | pub fn main() { 38 | let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); 39 | glfw.window_hint(WindowHint::ContextVersion(3, 3)); 40 | glfw.window_hint(WindowHint::OpenGlForwardCompat(true)); 41 | glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core)); 42 | let context = glfw.create_window(WIDTH, HEIGHT, "generate-cave", WindowMode::Windowed); 43 | 44 | let (mut window, events) = context.expect("Couldn't create a window!"); 45 | window.make_current(); 46 | gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void); 47 | 48 | let instance = Instance::new().unwrap(); 49 | let device = instance.open_device().unwrap(); 50 | 51 | let source = match instance.shading_language() { 52 | ShadingLanguage::Cl => CL_SHADER, 53 | ShadingLanguage::Glsl => GL_SHADER, 54 | }; 55 | let program = device.create_program(source).unwrap(); 56 | 57 | let draw_context = lord_drawquaad::Context::new(); 58 | 59 | let buffer_data = BufferData::Uninitialized(WIDTH as usize * HEIGHT as usize * 60 | mem::size_of::()); 61 | let buffer = device.create_buffer(Protection::ReadWrite, buffer_data).unwrap(); 62 | let dest = device.create_image(Format::R8, Protection::ReadWrite, &Size2D::new(WIDTH, HEIGHT)) 63 | .unwrap(); 64 | let seed: u32 = rand::thread_rng().next_u32(); 65 | 66 | let mut texture = 0; 67 | unsafe { 68 | gl::GenTextures(1, &mut texture); 69 | dest.bind_to(&ExternalImage::GlTexture(texture)).unwrap(); 70 | 71 | gl::BindTexture(gl::TEXTURE_RECTANGLE, texture); 72 | gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); 73 | gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); 74 | gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint); 75 | gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint); 76 | } 77 | 78 | let groups = [WIDTH, HEIGHT]; 79 | let uniforms = [ 80 | (0, Uniform::Image(&dest)), 81 | (1, Uniform::Buffer(&buffer)), 82 | (2, Uniform::U32(seed)), 83 | (3, Uniform::U32(ITERATIONS)), 84 | ]; 85 | let queue = device.create_queue().unwrap(); 86 | queue.submit_compute(&program, &groups, &uniforms, &[]).unwrap(); 87 | queue.submit_sync_event().unwrap().wait().unwrap(); 88 | 89 | unsafe { 90 | gl::MemoryBarrier(gl::TEXTURE_FETCH_BARRIER_BIT | gl::SHADER_IMAGE_ACCESS_BARRIER_BIT); 91 | 92 | gl::ClearColor(1.0, 1.0, 1.0, 1.0); 93 | gl::Clear(gl::COLOR_BUFFER_BIT); 94 | } 95 | 96 | draw_context.draw(texture); 97 | window.swap_buffers(); 98 | 99 | while !window.should_close() { 100 | glfw.poll_events(); 101 | for (_, event) in glfw::flush_messages(&events) { 102 | match event { 103 | WindowEvent::Key(Key::Escape, _, Action::Press, _) => { 104 | window.set_should_close(true) 105 | } 106 | _ => {} 107 | } 108 | } 109 | } 110 | } 111 | 112 | static CL_SHADER: &'static str = r#" 113 | // Xorshift32 114 | uint rand(uint state) { 115 | state ^= state << 13; 116 | state ^= state >> 17; 117 | state ^= state << 5; 118 | return state; 119 | } 120 | 121 | uint offset(int2 pos, int2 dimensions) { 122 | return (uint)pos.y * dimensions.x + (uint)pos.x; 123 | } 124 | 125 | uchar value(bool on) { 126 | return on ? 255 : 0; 127 | } 128 | 129 | uint countNeighbors(__global uchar *buffer, int2 p, int2 dimensions) { 130 | uint neighbors = 0; 131 | for (int y = p.y - 1; y <= p.y + 1; y++) { 132 | if (y >= 0 && y < dimensions.y) { 133 | for (int x = p.x - 1; x <= p.x + 1; x++) { 134 | if (x >= 0 && x < dimensions.x && (y != p.y || x != p.x)) { 135 | if (buffer[offset((int2)(x, y), dimensions)] != 0) 136 | neighbors++; 137 | } 138 | } 139 | } 140 | } 141 | return neighbors; 142 | } 143 | 144 | __kernel void generate_caves(__write_only image2d_t gImage, 145 | __global uchar *buffer, 146 | uint kSeed, 147 | uint kIterations) { 148 | // Based on xxHash 149 | uint state = kSeed; 150 | state *= get_global_id(0); 151 | state ^= state >> 13; 152 | state *= get_global_id(1); 153 | state ^= state >> 16; 154 | 155 | // Initial state 156 | int2 dimensions = get_image_dim(gImage); 157 | int2 home = (int2)((int)get_global_id(0), (int)get_global_id(1)); 158 | bool on = rand(state) < 0x73333333; 159 | buffer[offset(home, dimensions)] = value(on); 160 | 161 | for (uint i = 0; i < kIterations; i++) { 162 | barrier(CLK_GLOBAL_MEM_FENCE); 163 | uint neighbors = countNeighbors(buffer, home, dimensions); 164 | 165 | // Verbosity to work around an LLVM bug. 166 | if (on && neighbors < 3) 167 | on = false; 168 | else if (neighbors > 4) 169 | on = true; 170 | 171 | barrier(CLK_GLOBAL_MEM_FENCE); 172 | buffer[offset(home, dimensions)] = value(on); 173 | } 174 | 175 | uint4 color = (uint4)((uint)value(on), (uint)value(on), (uint)value(on), (uint)value(on)); 176 | write_imageui(gImage, home, color); 177 | } 178 | "#; 179 | 180 | static GL_SHADER: &'static str = r#" 181 | #version 330 182 | #extension GL_ARB_compute_shader : require 183 | #extension GL_ARB_explicit_uniform_location : require 184 | #extension GL_ARB_shader_storage_buffer_object : require 185 | #extension GL_ARB_shader_image_load_store : require 186 | #extension GL_ARB_shader_image_size : require 187 | #extension GL_ARB_shading_language_420pack : require 188 | 189 | layout(r8, binding = 0) uniform restrict writeonly image2D uImage; 190 | 191 | layout(std430, binding = 1) buffer ssbBuffer { 192 | float gBuffer[480000]; 193 | }; 194 | 195 | layout(location = 2) uniform uint uSeed; 196 | layout(location = 3) uniform uint uIterations; 197 | 198 | layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 199 | 200 | // Xorshift32 201 | uint rand(uint state) { 202 | state ^= state << 13u; 203 | state ^= state >> 17u; 204 | state ^= state << 5u; 205 | return state; 206 | } 207 | 208 | uint offset(ivec2 pos, ivec2 dimensions) { 209 | return uint(pos.y) * uint(dimensions.x) + uint(pos.x); 210 | } 211 | 212 | float value(bool on) { 213 | return on ? 1.0 : 0.0; 214 | } 215 | 216 | uint countNeighbors(ivec2 p, ivec2 dimensions) { 217 | uint neighbors = 0u; 218 | for (int y = p.y - 1; y <= p.y + 1; y++) { 219 | if (y >= 0 && y < dimensions.y) { 220 | for (int x = p.x - 1; x <= p.x + 1; x++) { 221 | if (x >= 0 && x < dimensions.x && (y != p.y || x != p.x)) { 222 | if (gBuffer[offset(ivec2(x, y), dimensions)] != 0.0) 223 | neighbors++; 224 | } 225 | } 226 | } 227 | } 228 | return neighbors; 229 | } 230 | 231 | void main() { 232 | // Based on xxHash 233 | uint state = uSeed; 234 | state *= uint(gl_GlobalInvocationID.x); 235 | state ^= state >> 13u; 236 | state *= uint(gl_GlobalInvocationID.y); 237 | state ^= state >> 16u; 238 | 239 | // Initial state 240 | ivec2 dimensions = imageSize(uImage); 241 | ivec2 home = ivec2(gl_GlobalInvocationID.xy); 242 | bool inBounds = home.x < dimensions.x && home.y < dimensions.y; 243 | bool on = rand(state) < 0x73333333u; 244 | if (inBounds) 245 | gBuffer[offset(home, dimensions)] = value(on); 246 | 247 | for (uint i = 0u; i < uIterations; i++) { 248 | barrier(); 249 | uint neighbors = inBounds ? countNeighbors(home, dimensions) : 0u; 250 | 251 | if (on && neighbors < 3u) 252 | on = false; 253 | else if (neighbors > 4u) 254 | on = true; 255 | 256 | barrier(); 257 | if (inBounds) 258 | gBuffer[offset(home, dimensions)] = value(on); 259 | } 260 | 261 | vec4 color = vec4(value(on)); 262 | if (inBounds) 263 | imageStore(uImage, home, color); 264 | } 265 | "#; 266 | 267 | -------------------------------------------------------------------------------- /src/api/cl/queue.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use api::cl::ffi::{self, CL_IMAGE_DEPTH, CL_IMAGE_HEIGHT, CL_IMAGE_WIDTH, CL_SUCCESS, CL_TRUE}; 12 | use api::cl::ffi::{cl_command_queue, cl_event, cl_kernel, cl_mem}; 13 | use api::cl::profile_event::PROFILE_EVENT_FUNCTIONS; 14 | use api::cl::sync_event::SYNC_EVENT_FUNCTIONS; 15 | use buffer::Buffer; 16 | use error::Error; 17 | use image::{Color, Image}; 18 | use profile_event::ProfileEvent; 19 | use program::Program; 20 | use queue::{Queue, QueueFunctions, Uniform}; 21 | use std::mem; 22 | use std::os::raw::c_void; 23 | use std::ptr; 24 | use sync_event::SyncEvent; 25 | 26 | pub static QUEUE_FUNCTIONS: QueueFunctions = QueueFunctions { 27 | destroy: destroy, 28 | flush: flush, 29 | finish: finish, 30 | submit_compute: submit_compute, 31 | submit_clear: submit_clear, 32 | submit_read_buffer: submit_read_buffer, 33 | submit_sync_event: submit_sync_event, 34 | }; 35 | 36 | unsafe fn destroy(this: &Queue) { 37 | ffi::clReleaseCommandQueue(this.data() as cl_command_queue); 38 | } 39 | 40 | fn flush(this: &Queue) -> Result<(), Error> { 41 | unsafe { 42 | if ffi::clFlush(this.data() as cl_command_queue) == CL_SUCCESS { 43 | Ok(()) 44 | } else { 45 | Err(Error::Failed) 46 | } 47 | } 48 | } 49 | 50 | fn finish(this: &Queue) -> Result<(), Error> { 51 | unsafe { 52 | if ffi::clFinish(this.data() as cl_command_queue) == CL_SUCCESS { 53 | Ok(()) 54 | } else { 55 | Err(Error::Failed) 56 | } 57 | } 58 | } 59 | 60 | fn submit_compute(this: &Queue, 61 | program: &Program, 62 | num_groups: &[u32], 63 | uniforms: &[(u32, Uniform)], 64 | events: &[SyncEvent]) 65 | -> Result { 66 | unsafe { 67 | for &(uniform_index, ref uniform) in uniforms { 68 | let err = match *uniform { 69 | Uniform::Buffer(buffer) => { 70 | let data = buffer.data(); 71 | ffi::clSetKernelArg(program.data() as cl_kernel, 72 | uniform_index, 73 | mem::size_of::(), 74 | &data as *const usize as *const c_void) 75 | } 76 | Uniform::Image(image) => { 77 | let data = image.data()[0]; 78 | ffi::clSetKernelArg(program.data() as cl_kernel, 79 | uniform_index, 80 | mem::size_of::(), 81 | &data as *const usize as *const c_void) 82 | } 83 | Uniform::U32(ref value) => { 84 | ffi::clSetKernelArg(program.data() as cl_kernel, 85 | uniform_index, 86 | mem::size_of::(), 87 | value as *const u32 as *const c_void) 88 | } 89 | Uniform::UVec4(ref value) => { 90 | ffi::clSetKernelArg(program.data() as cl_kernel, 91 | uniform_index, 92 | mem::size_of::<[u32; 4]>(), 93 | value as *const [u32; 4] as *const c_void) 94 | } 95 | }; 96 | if err != CL_SUCCESS { 97 | return Err(Error::Failed) 98 | } 99 | } 100 | 101 | let mut global_work_size = [0; 3]; 102 | for (dimension, &group_size) in num_groups.iter().enumerate() { 103 | global_work_size[dimension] = group_size as usize 104 | } 105 | 106 | let event_wait_list: Vec<_> = events.iter() 107 | .map(|event| event.data() as cl_event) 108 | .collect(); 109 | let event_wait_list_ptr = if event_wait_list.is_empty() { 110 | ptr::null() 111 | } else { 112 | event_wait_list.as_ptr() 113 | }; 114 | 115 | let mut event = ptr::null_mut(); 116 | 117 | if ffi::clEnqueueNDRangeKernel(this.data() as cl_command_queue, 118 | program.data() as cl_kernel, 119 | num_groups.len() as u32, 120 | ptr::null(), 121 | global_work_size.as_mut_ptr(), 122 | ptr::null(), 123 | event_wait_list.len() as u32, 124 | event_wait_list_ptr, 125 | &mut event) != CL_SUCCESS { 126 | return Err(Error::Failed) 127 | } 128 | 129 | Ok(ProfileEvent::from_raw_data(event as usize, &PROFILE_EVENT_FUNCTIONS)) 130 | } 131 | } 132 | 133 | fn submit_clear(this: &Queue, image: &Image, color: &Color, events: &[SyncEvent]) 134 | -> Result { 135 | unsafe { 136 | let colors = match *color { 137 | Color::UInt(r, g, b, a) => [r, g, b, a], 138 | }; 139 | 140 | let origin = [0, 0, 0]; 141 | 142 | let mut size = [0, 0, 0]; 143 | ffi::clGetImageInfo(image.data()[0] as cl_mem, 144 | CL_IMAGE_WIDTH, 145 | mem::size_of::(), 146 | &mut size[0] as *mut usize as *mut c_void, 147 | ptr::null_mut()); 148 | ffi::clGetImageInfo(image.data()[0] as cl_mem, 149 | CL_IMAGE_HEIGHT, 150 | mem::size_of::(), 151 | &mut size[1] as *mut usize as *mut c_void, 152 | ptr::null_mut()); 153 | ffi::clGetImageInfo(image.data()[0] as cl_mem, 154 | CL_IMAGE_DEPTH, 155 | mem::size_of::(), 156 | &mut size[2] as *mut usize as *mut c_void, 157 | ptr::null_mut()); 158 | for length in &mut size { 159 | if *length == 0 { 160 | *length = 1 161 | } 162 | } 163 | 164 | let event_wait_list: Vec<_> = events.iter() 165 | .map(|event| event.data() as cl_event) 166 | .collect(); 167 | let event_wait_list_ptr = if event_wait_list.is_empty() { 168 | ptr::null() 169 | } else { 170 | event_wait_list.as_ptr() 171 | }; 172 | 173 | let mut event = ptr::null_mut(); 174 | 175 | if ffi::clEnqueueFillImage(this.data() as cl_command_queue, 176 | image.data()[0] as cl_mem, 177 | colors.as_ptr() as *const c_void, 178 | origin.as_ptr(), 179 | size.as_mut_ptr(), 180 | event_wait_list.len() as u32, 181 | event_wait_list_ptr, 182 | &mut event) == CL_SUCCESS { 183 | Ok(ProfileEvent::from_raw_data(event as usize, &PROFILE_EVENT_FUNCTIONS)) 184 | } else { 185 | Err(Error::Failed) 186 | } 187 | } 188 | } 189 | 190 | fn submit_read_buffer(this: &Queue, 191 | dest: &mut [u8], 192 | buffer: &Buffer, 193 | start: usize, 194 | events: &[SyncEvent]) 195 | -> Result { 196 | unsafe { 197 | let event_wait_list: Vec<_> = events.iter() 198 | .map(|event| event.data() as cl_event) 199 | .collect(); 200 | let event_wait_list_ptr = if event_wait_list.is_empty() { 201 | ptr::null() 202 | } else { 203 | event_wait_list.as_ptr() 204 | }; 205 | 206 | let mut event = ptr::null_mut(); 207 | 208 | if ffi::clEnqueueReadBuffer(this.data() as cl_command_queue, 209 | buffer.data() as cl_mem, 210 | CL_TRUE, 211 | start, 212 | dest.len(), 213 | dest.as_mut_ptr() as *mut c_void, 214 | event_wait_list.len() as u32, 215 | event_wait_list_ptr, 216 | &mut event) == CL_SUCCESS { 217 | Ok(ProfileEvent::from_raw_data(event as usize, &PROFILE_EVENT_FUNCTIONS)) 218 | } else { 219 | Err(Error::Failed) 220 | } 221 | } 222 | } 223 | 224 | fn submit_sync_event(this: &Queue) -> Result { 225 | unsafe { 226 | let mut event = ptr::null_mut(); 227 | if ffi::clEnqueueMarker(this.data() as cl_command_queue, &mut event) == CL_SUCCESS { 228 | Ok(SyncEvent::from_raw_data(event as usize, &SYNC_EVENT_FUNCTIONS)) 229 | } else { 230 | Err(Error::Failed) 231 | } 232 | } 233 | } 234 | 235 | -------------------------------------------------------------------------------- /src/api/cl/ffi.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen and then manually modified */ 2 | 3 | #![allow(non_camel_case_types)] 4 | 5 | #[cfg(target_os = "macos")] 6 | use io_surface::IOSurfaceRef; 7 | use std::os::raw::c_void; 8 | 9 | pub type int32_t = i32; 10 | pub type uint32_t = u32; 11 | pub type uint64_t = u64; 12 | pub type intptr_t = isize; 13 | pub type cl_int = int32_t; 14 | pub type cl_uint = uint32_t; 15 | pub type cl_ulong = uint64_t; 16 | pub type size_t = usize; 17 | pub enum _cl_platform_id { } 18 | pub type cl_platform_id = *mut _cl_platform_id; 19 | pub enum _cl_device_id { } 20 | pub type cl_device_id = *mut _cl_device_id; 21 | pub enum _cl_context { } 22 | pub type cl_context = *mut _cl_context; 23 | pub enum _cl_command_queue { } 24 | pub type cl_command_queue = *mut _cl_command_queue; 25 | pub enum _cl_mem { } 26 | pub type cl_mem = *mut _cl_mem; 27 | pub enum _cl_program { } 28 | pub type cl_program = *mut _cl_program; 29 | pub enum _cl_kernel { } 30 | pub type cl_kernel = *mut _cl_kernel; 31 | pub enum _cl_event { } 32 | pub type cl_event = *mut _cl_event; 33 | pub type cl_bool = cl_uint; 34 | pub type cl_bitfield = cl_ulong; 35 | pub type cl_device_type = cl_bitfield; 36 | pub type cl_command_queue_properties = cl_bitfield; 37 | pub type cl_context_properties = intptr_t; 38 | pub type cl_context_info = cl_uint; 39 | pub type cl_program_build_info = cl_uint; 40 | pub type cl_device_info = cl_uint; 41 | pub type cl_kernel_info = cl_uint; 42 | pub type cl_channel_order = cl_uint; 43 | pub type cl_channel_type = cl_uint; 44 | pub type cl_mem_flags = cl_bitfield; 45 | pub type cl_image_info = cl_uint; 46 | pub type cl_profiling_info = cl_uint; 47 | 48 | #[repr(C)] 49 | #[derive(Copy, Clone)] 50 | #[derive(Debug)] 51 | pub struct _cl_image_format { 52 | pub image_channel_order: cl_channel_order, 53 | pub image_channel_data_type: cl_channel_type, 54 | } 55 | impl ::std::default::Default for _cl_image_format { 56 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 57 | } 58 | pub type cl_image_format = _cl_image_format; 59 | 60 | #[link(name = "OpenCL", kind = "framework")] 61 | extern "C" { 62 | pub fn clGetDeviceIDs(arg1: cl_platform_id, arg2: cl_device_type, 63 | arg3: cl_uint, arg4: *mut cl_device_id, 64 | arg5: *mut cl_uint) -> cl_int; 65 | pub fn clGetDeviceInfo(arg1: cl_device_id, arg2: cl_device_info, 66 | arg3: size_t, arg4: *mut c_void, 67 | arg5: *mut size_t) -> cl_int; 68 | pub fn clCreateContext(arg1: *const cl_context_properties, arg2: cl_uint, 69 | arg3: *const cl_device_id, 70 | arg4: 71 | ::std::option::Option, 79 | arg5: *mut ::std::os::raw::c_void, 80 | arg6: *mut cl_int) -> cl_context; 81 | pub fn clReleaseContext(arg1: cl_context) -> cl_int; 82 | pub fn clGetContextInfo(arg1: cl_context, arg2: cl_context_info, 83 | arg3: size_t, arg4: *mut ::std::os::raw::c_void, 84 | arg5: *mut size_t) -> cl_int; 85 | pub fn clCreateCommandQueue(arg1: cl_context, arg2: cl_device_id, 86 | arg3: cl_command_queue_properties, 87 | arg4: *mut cl_int) -> cl_command_queue; 88 | pub fn clReleaseCommandQueue(arg1: cl_command_queue) -> cl_int; 89 | pub fn clCreateBuffer(arg1: cl_context, arg2: cl_mem_flags, arg3: size_t, 90 | arg4: *mut ::std::os::raw::c_void, 91 | arg5: *mut cl_int) -> cl_mem; 92 | pub fn clReleaseMemObject(arg1: cl_mem) -> cl_int; 93 | pub fn clGetImageInfo(arg1: cl_mem, arg2: cl_image_info, arg3: size_t, 94 | arg4: *mut ::std::os::raw::c_void, 95 | arg5: *mut size_t) -> cl_int; 96 | pub fn clCreateProgramWithSource(arg1: cl_context, arg2: cl_uint, 97 | arg3: *mut *const ::std::os::raw::c_char, 98 | arg4: *const size_t, arg5: *mut cl_int) 99 | -> cl_program; 100 | pub fn clReleaseProgram(arg1: cl_program) -> cl_int; 101 | pub fn clBuildProgram(arg1: cl_program, arg2: cl_uint, 102 | arg3: *const cl_device_id, 103 | arg4: *const ::std::os::raw::c_char, 104 | arg5: 105 | ::std::option::Option, 109 | arg6: *mut ::std::os::raw::c_void) -> cl_int; 110 | pub fn clGetProgramBuildInfo(program: cl_program, 111 | device: cl_device_id, 112 | param_name: cl_program_build_info, 113 | param_value_size: size_t, 114 | param_value: *mut ::std::os::raw::c_void, 115 | param_value_size_ret: *mut size_t) 116 | -> cl_int; 117 | pub fn clCreateKernelsInProgram(arg1: cl_program, arg2: cl_uint, 118 | arg3: *mut cl_kernel, arg4: *mut cl_uint) 119 | -> cl_int; 120 | pub fn clReleaseKernel(arg1: cl_kernel) -> cl_int; 121 | pub fn clSetKernelArg(arg1: cl_kernel, arg2: cl_uint, arg3: size_t, 122 | arg4: *const ::std::os::raw::c_void) -> cl_int; 123 | pub fn clGetKernelInfo(kernel: cl_kernel, 124 | param_name: cl_kernel_info, 125 | param_value_size: size_t, 126 | param_value: *mut ::std::os::raw::c_void, 127 | param_value_size_ret: *mut size_t) 128 | -> cl_int; 129 | pub fn clWaitForEvents(arg1: cl_uint, arg2: *const cl_event) -> cl_int; 130 | pub fn clReleaseEvent(arg1: cl_event) -> cl_int; 131 | pub fn clGetEventProfilingInfo(arg1: cl_event, arg2: cl_profiling_info, 132 | arg3: size_t, 133 | arg4: *mut ::std::os::raw::c_void, 134 | arg5: *mut size_t) -> cl_int; 135 | pub fn clFlush(arg1: cl_command_queue) -> cl_int; 136 | pub fn clFinish(arg1: cl_command_queue) -> cl_int; 137 | pub fn clEnqueueReadBuffer(arg1: cl_command_queue, arg2: cl_mem, 138 | arg3: cl_bool, arg4: size_t, arg5: size_t, 139 | arg6: *mut ::std::os::raw::c_void, 140 | arg7: cl_uint, arg8: *const cl_event, 141 | arg9: *mut cl_event) -> cl_int; 142 | pub fn clEnqueueFillImage(arg1: cl_command_queue, arg2: cl_mem, 143 | arg3: *const ::std::os::raw::c_void, 144 | arg4: *const size_t, arg5: *const size_t, 145 | arg6: cl_uint, arg7: *const cl_event, 146 | arg8: *mut cl_event) -> cl_int; 147 | pub fn clEnqueueNDRangeKernel(arg1: cl_command_queue, arg2: cl_kernel, 148 | arg3: cl_uint, arg4: *const size_t, 149 | arg5: *const size_t, arg6: *const size_t, 150 | arg7: cl_uint, arg8: *const cl_event, 151 | arg9: *mut cl_event) -> cl_int; 152 | pub fn clEnqueueMarker(arg1: cl_command_queue, arg2: *mut cl_event) -> cl_int; 153 | 154 | #[cfg(target_os = "macos")] 155 | pub fn clCreateImageFromIOSurface2DAPPLE(context: cl_context, 156 | flags: cl_mem_flags, 157 | image_format: *const cl_image_format, 158 | image_width: size_t, 159 | image_height: size_t, 160 | iosurface: IOSurfaceRef, 161 | errcode_ret: *mut cl_int) 162 | -> cl_mem; 163 | } 164 | 165 | pub const CL_SUCCESS: cl_int = 0; 166 | 167 | pub const CL_TRUE: cl_bool = 1; 168 | 169 | pub const CL_DEVICE_TYPE_GPU: cl_device_type = 1 << 2; 170 | 171 | pub const CL_QUEUE_PROFILING_ENABLE: cl_command_queue_properties = 1 << 1; 172 | 173 | pub const CL_DEVICE_NAME: cl_device_info = 0x102b; 174 | 175 | pub const CL_CONTEXT_DEVICES: cl_context_info = 0x1081; 176 | 177 | pub const CL_R: cl_channel_order = 0x10b0; 178 | pub const CL_RGBA: cl_channel_order = 0x10b5; 179 | 180 | pub const CL_UNORM_INT8: cl_channel_type = 0x10d2; 181 | pub const CL_FLOAT: cl_channel_type = 0x10de; 182 | 183 | pub const CL_MEM_READ_WRITE: cl_mem_flags = 1 << 0; 184 | pub const CL_MEM_WRITE_ONLY: cl_mem_flags = 1 << 1; 185 | pub const CL_MEM_READ_ONLY: cl_mem_flags = 1 << 2; 186 | pub const CL_MEM_COPY_HOST_PTR: cl_mem_flags = 1 << 5; 187 | 188 | pub const CL_IMAGE_FORMAT: cl_image_info = 0x1110; 189 | pub const CL_IMAGE_WIDTH: cl_image_info = 0x1114; 190 | pub const CL_IMAGE_HEIGHT: cl_image_info = 0x1115; 191 | pub const CL_IMAGE_DEPTH: cl_image_info = 0x1116; 192 | 193 | pub const CL_PROGRAM_BUILD_LOG: cl_program_build_info = 0x1183; 194 | 195 | pub const CL_KERNEL_PROGRAM: cl_kernel_info = 0x1194; 196 | 197 | pub const CL_PROFILING_COMMAND_START: cl_profiling_info = 0x1282; 198 | pub const CL_PROFILING_COMMAND_END: cl_profiling_info = 0x1283; 199 | 200 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------