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