1 //---------------------------------------------------------------------------// 2 // Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com> 3 // 4 // Distributed under the Boost Software License, Version 1.0 5 // See accompanying file LICENSE_1_0.txt or copy at 6 // http://www.boost.org/LICENSE_1_0.txt 7 // 8 // See http://boostorg.github.com/compute for more information. 9 //---------------------------------------------------------------------------// 10 11 #ifndef BOOST_COMPUTE_SYSTEM_HPP 12 #define BOOST_COMPUTE_SYSTEM_HPP 13 14 #include <string> 15 #include <vector> 16 #include <cstdlib> 17 18 #include <boost/throw_exception.hpp> 19 20 #include <boost/compute/cl.hpp> 21 #include <boost/compute/device.hpp> 22 #include <boost/compute/context.hpp> 23 #include <boost/compute/platform.hpp> 24 #include <boost/compute/command_queue.hpp> 25 #include <boost/compute/detail/getenv.hpp> 26 #include <boost/compute/exception/no_device_found.hpp> 27 28 namespace boost { 29 namespace compute { 30 31 /// \class system 32 /// \brief Provides access to platforms and devices on the system. 33 /// 34 /// The system class contains a set of static functions which provide access to 35 /// the OpenCL platforms and compute devices on the host system. 36 /// 37 /// The default_device() convenience method automatically selects and returns 38 /// the "best" compute device for the system following a set of heuristics and 39 /// environment variables. This simplifies setup of the OpenCL enviornment. 40 /// 41 /// \see platform, device, context 42 class system 43 { 44 public: 45 /// Returns the default compute device for the system. 46 /// 47 /// The default device is selected based on a set of heuristics and can be 48 /// influenced using one of the following environment variables: 49 /// 50 /// \li \c BOOST_COMPUTE_DEFAULT_DEVICE - 51 /// name of the compute device (e.g. "GTX TITAN") 52 /// \li \c BOOST_COMPUTE_DEFAULT_DEVICE_TYPE 53 /// type of the compute device (e.g. "GPU" or "CPU") 54 /// \li \c BOOST_COMPUTE_DEFAULT_PLATFORM - 55 /// name of the platform (e.g. "NVIDIA CUDA") 56 /// \li \c BOOST_COMPUTE_DEFAULT_VENDOR - 57 /// name of the device vendor (e.g. "NVIDIA") 58 /// \li \c BOOST_COMPUTE_DEFAULT_ENFORCE - 59 /// If this is set to "1", then throw a no_device_found() exception 60 /// if any of the above environment variables is set, but a matching 61 /// device was not found. 62 /// 63 /// The default device is determined once on the first time this function 64 /// is called. Calling this function multiple times will always result in 65 /// the same device being returned. 66 /// 67 /// If no OpenCL device is found on the system, a no_device_found exception 68 /// is thrown. 69 /// 70 /// For example, to print the name of the default compute device on the 71 /// system: 72 /// \code 73 /// // get the default compute device 74 /// boost::compute::device device = boost::compute::system::default_device(); 75 /// 76 /// // print the name of the device 77 /// std::cout << "default device: " << device.name() << std::endl; 78 /// \endcode default_device()79 static device default_device() 80 { 81 static device default_device = find_default_device(); 82 83 return default_device; 84 } 85 86 /// Returns the device with \p name. 87 /// 88 /// \throws no_device_found if no device with \p name is found. find_device(const std::string & name)89 static device find_device(const std::string &name) 90 { 91 const std::vector<device> devices = system::devices(); 92 for(size_t i = 0; i < devices.size(); i++){ 93 const device& device = devices[i]; 94 95 if(device.name() == name){ 96 return device; 97 } 98 } 99 100 BOOST_THROW_EXCEPTION(no_device_found()); 101 } 102 103 /// Returns a vector containing all of the compute devices on 104 /// the system. 105 /// 106 /// For example, to print out the name of each OpenCL-capable device 107 /// available on the system: 108 /// \code 109 /// for(const auto &device : boost::compute::system::devices()){ 110 /// std::cout << device.name() << std::endl; 111 /// } 112 /// \endcode devices()113 static std::vector<device> devices() 114 { 115 std::vector<device> devices; 116 117 const std::vector<platform> platforms = system::platforms(); 118 for(size_t i = 0; i < platforms.size(); i++){ 119 const std::vector<device> platform_devices = platforms[i].devices(); 120 121 devices.insert( 122 devices.end(), platform_devices.begin(), platform_devices.end() 123 ); 124 } 125 126 return devices; 127 } 128 129 /// Returns the number of compute devices on the system. device_count()130 static size_t device_count() 131 { 132 size_t count = 0; 133 134 const std::vector<platform> platforms = system::platforms(); 135 for(size_t i = 0; i < platforms.size(); i++){ 136 count += platforms[i].device_count(); 137 } 138 139 return count; 140 } 141 142 /// Returns the default context for the system. 143 /// 144 /// The default context is created for the default device on the system 145 /// (as returned by default_device()). 146 /// 147 /// The default context is created once on the first time this function is 148 /// called. Calling this function multiple times will always result in the 149 /// same context object being returned. default_context()150 static context default_context() 151 { 152 static context default_context(default_device()); 153 154 return default_context; 155 } 156 157 /// Returns the default command queue for the system. default_queue()158 static command_queue& default_queue() 159 { 160 static command_queue queue(default_context(), default_device()); 161 162 return queue; 163 } 164 165 /// Blocks until all outstanding computations on the default 166 /// command queue are complete. 167 /// 168 /// This is equivalent to: 169 /// \code 170 /// system::default_queue().finish(); 171 /// \endcode finish()172 static void finish() 173 { 174 default_queue().finish(); 175 } 176 177 /// Returns a vector containing each of the OpenCL platforms on the system. 178 /// 179 /// For example, to print out the name of each OpenCL platform present on 180 /// the system: 181 /// \code 182 /// for(const auto &platform : boost::compute::system::platforms()){ 183 /// std::cout << platform.name() << std::endl; 184 /// } 185 /// \endcode platforms()186 static std::vector<platform> platforms() 187 { 188 cl_uint count = 0; 189 clGetPlatformIDs(0, 0, &count); 190 191 std::vector<platform> platforms; 192 if(count > 0) 193 { 194 std::vector<cl_platform_id> platform_ids(count); 195 clGetPlatformIDs(count, &platform_ids[0], 0); 196 197 for(size_t i = 0; i < platform_ids.size(); i++){ 198 platforms.push_back(platform(platform_ids[i])); 199 } 200 } 201 return platforms; 202 } 203 204 /// Returns the number of compute platforms on the system. platform_count()205 static size_t platform_count() 206 { 207 cl_uint count = 0; 208 clGetPlatformIDs(0, 0, &count); 209 return static_cast<size_t>(count); 210 } 211 212 private: 213 /// \internal_ find_default_device()214 static device find_default_device() 215 { 216 // get a list of all devices on the system 217 const std::vector<device> devices_ = devices(); 218 if(devices_.empty()){ 219 BOOST_THROW_EXCEPTION(no_device_found()); 220 } 221 222 // check for device from environment variable 223 const char *name = detail::getenv("BOOST_COMPUTE_DEFAULT_DEVICE"); 224 const char *type = detail::getenv("BOOST_COMPUTE_DEFAULT_DEVICE_TYPE"); 225 const char *platform = detail::getenv("BOOST_COMPUTE_DEFAULT_PLATFORM"); 226 const char *vendor = detail::getenv("BOOST_COMPUTE_DEFAULT_VENDOR"); 227 const char *enforce = detail::getenv("BOOST_COMPUTE_DEFAULT_ENFORCE"); 228 229 if(name || type || platform || vendor){ 230 for(size_t i = 0; i < devices_.size(); i++){ 231 const device& device = devices_[i]; 232 if (name && !matches(device.name(), name)) 233 continue; 234 235 if (type && matches(std::string("GPU"), type)) 236 if (!(device.type() & device::gpu)) 237 continue; 238 239 if (type && matches(std::string("CPU"), type)) 240 if (!(device.type() & device::cpu)) 241 continue; 242 243 if (platform && !matches(device.platform().name(), platform)) 244 continue; 245 246 if (vendor && !matches(device.vendor(), vendor)) 247 continue; 248 249 return device; 250 } 251 252 if(enforce && enforce[0] == '1') 253 BOOST_THROW_EXCEPTION(no_device_found()); 254 } 255 256 // find the first gpu device 257 for(size_t i = 0; i < devices_.size(); i++){ 258 const device& device = devices_[i]; 259 260 if(device.type() & device::gpu){ 261 return device; 262 } 263 } 264 265 // find the first cpu device 266 for(size_t i = 0; i < devices_.size(); i++){ 267 const device& device = devices_[i]; 268 269 if(device.type() & device::cpu){ 270 return device; 271 } 272 } 273 274 // return the first device found 275 return devices_[0]; 276 } 277 278 /// \internal_ matches(const std::string & str,const std::string & pattern)279 static bool matches(const std::string &str, const std::string &pattern) 280 { 281 return str.find(pattern) != std::string::npos; 282 } 283 }; 284 285 } // end compute namespace 286 } // end boost namespace 287 288 #endif // BOOST_COMPUTE_SYSTEM_HPP 289