]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/compute/include/boost/compute/program.hpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / compute / include / boost / compute / program.hpp
CommitLineData
7c673cae
FG
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_PROGRAM_HPP
12#define BOOST_COMPUTE_PROGRAM_HPP
13
14#include <string>
15#include <vector>
16#include <fstream>
17#include <streambuf>
18
19#ifdef BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION
20#include <iostream>
21#endif
22
23#include <boost/compute/config.hpp>
24#include <boost/compute/context.hpp>
25#include <boost/compute/exception.hpp>
26#include <boost/compute/detail/assert_cl_success.hpp>
27
28#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
29#include <sstream>
30#include <boost/optional.hpp>
31#include <boost/compute/platform.hpp>
32#include <boost/compute/detail/getenv.hpp>
33#include <boost/compute/detail/path.hpp>
34#include <boost/compute/detail/sha1.hpp>
35#endif
36
37namespace boost {
38namespace compute {
39
40class kernel;
41
42/// \class program
43/// \brief A compute program.
44///
45/// The program class represents an OpenCL program.
46///
47/// Program objects are created with one of the static \c create_with_*
48/// functions. For example, to create a program from a source string:
49///
50/// \snippet test/test_program.cpp create_with_source
51///
52/// And to create a program from a source file:
53/// \code
54/// boost::compute::program bar_program =
55/// boost::compute::program::create_with_source_file("/path/to/bar.cl", context);
56/// \endcode
57///
58/// Once a program object has been succesfully created, it can be compiled
59/// using the \c build() method:
60/// \code
61/// // build the program
62/// foo_program.build();
63/// \endcode
64///
65/// Once the program is built, \ref kernel objects can be created using the
66/// \c create_kernel() method by passing their name:
67/// \code
68/// // create a kernel from the compiled program
69/// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
70/// \endcode
71///
72/// \see kernel
73class program
74{
75public:
76 /// Creates a null program object.
77 program()
78 : m_program(0)
79 {
80 }
81
82 /// Creates a program object for \p program. If \p retain is \c true,
83 /// the reference count for \p program will be incremented.
84 explicit program(cl_program program, bool retain = true)
85 : m_program(program)
86 {
87 if(m_program && retain){
88 clRetainProgram(m_program);
89 }
90 }
91
92 /// Creates a new program object as a copy of \p other.
93 program(const program &other)
94 : m_program(other.m_program)
95 {
96 if(m_program){
97 clRetainProgram(m_program);
98 }
99 }
100
101 /// Copies the program object from \p other to \c *this.
102 program& operator=(const program &other)
103 {
104 if(this != &other){
105 if(m_program){
106 clReleaseProgram(m_program);
107 }
108
109 m_program = other.m_program;
110
111 if(m_program){
112 clRetainProgram(m_program);
113 }
114 }
115
116 return *this;
117 }
118
119 #ifndef BOOST_COMPUTE_NO_RVALUE_REFERENCES
120 /// Move-constructs a new program object from \p other.
121 program(program&& other) BOOST_NOEXCEPT
122 : m_program(other.m_program)
123 {
124 other.m_program = 0;
125 }
126
127 /// Move-assigns the program from \p other to \c *this.
128 program& operator=(program&& other) BOOST_NOEXCEPT
129 {
130 if(m_program){
131 clReleaseProgram(m_program);
132 }
133
134 m_program = other.m_program;
135 other.m_program = 0;
136
137 return *this;
138 }
139 #endif // BOOST_COMPUTE_NO_RVALUE_REFERENCES
140
141 /// Destroys the program object.
142 ~program()
143 {
144 if(m_program){
145 BOOST_COMPUTE_ASSERT_CL_SUCCESS(
146 clReleaseProgram(m_program)
147 );
148 }
149 }
150
151 /// Returns the underlying OpenCL program.
152 cl_program& get() const
153 {
154 return const_cast<cl_program &>(m_program);
155 }
156
157 /// Returns the source code for the program.
158 std::string source() const
159 {
160 return get_info<std::string>(CL_PROGRAM_SOURCE);
161 }
162
163 /// Returns the binary for the program.
164 std::vector<unsigned char> binary() const
165 {
166 size_t binary_size = get_info<size_t>(CL_PROGRAM_BINARY_SIZES);
167 std::vector<unsigned char> binary(binary_size);
168
169 unsigned char *binary_ptr = &binary[0];
170 cl_int error = clGetProgramInfo(m_program,
171 CL_PROGRAM_BINARIES,
172 sizeof(unsigned char **),
173 &binary_ptr,
174 0);
175 if(error != CL_SUCCESS){
176 BOOST_THROW_EXCEPTION(opencl_error(error));
177 }
178
179 return binary;
180 }
181
182 std::vector<device> get_devices() const
183 {
184 std::vector<cl_device_id> device_ids =
185 get_info<std::vector<cl_device_id> >(CL_PROGRAM_DEVICES);
186
187 std::vector<device> devices;
188 for(size_t i = 0; i < device_ids.size(); i++){
189 devices.push_back(device(device_ids[i]));
190 }
191
192 return devices;
193 }
194
195 /// Returns the context for the program.
196 context get_context() const
197 {
198 return context(get_info<cl_context>(CL_PROGRAM_CONTEXT));
199 }
200
201 /// Returns information about the program.
202 ///
203 /// \see_opencl_ref{clGetProgramInfo}
204 template<class T>
205 T get_info(cl_program_info info) const
206 {
207 return detail::get_object_info<T>(clGetProgramInfo, m_program, info);
208 }
209
210 /// \overload
211 template<int Enum>
212 typename detail::get_object_info_type<program, Enum>::type
213 get_info() const;
214
215 /// Returns build information about the program.
216 ///
217 /// For example, this function can be used to retreive the options used
218 /// to build the program:
219 /// \code
220 /// std::string build_options =
221 /// program.get_build_info<std::string>(CL_PROGRAM_BUILD_OPTIONS);
222 /// \endcode
223 ///
224 /// \see_opencl_ref{clGetProgramInfo}
225 template<class T>
226 T get_build_info(cl_program_build_info info, const device &device) const
227 {
228 return detail::get_object_info<T>(clGetProgramBuildInfo, m_program, info, device.id());
229 }
230
231 /// Builds the program with \p options.
232 ///
233 /// If the program fails to compile, this function will throw an
234 /// opencl_error exception.
235 /// \code
236 /// try {
237 /// // attempt to compile to program
238 /// program.build();
239 /// }
240 /// catch(boost::compute::opencl_error &e){
241 /// // program failed to compile, print out the build log
242 /// std::cout << program.build_log() << std::endl;
243 /// }
244 /// \endcode
245 ///
246 /// \see_opencl_ref{clBuildProgram}
247 void build(const std::string &options = std::string())
248 {
249 const char *options_string = 0;
250
251 if(!options.empty()){
252 options_string = options.c_str();
253 }
254
255 cl_int ret = clBuildProgram(m_program, 0, 0, options_string, 0, 0);
256
257 #ifdef BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION
258 if(ret != CL_SUCCESS){
259 // print the error, source code and build log
260 std::cerr << "Boost.Compute: "
261 << "kernel compilation failed (" << ret << ")\n"
262 << "--- source ---\n"
263 << source()
264 << "\n--- build log ---\n"
265 << build_log()
266 << std::endl;
267 }
268 #endif
269
270 if(ret != CL_SUCCESS){
271 BOOST_THROW_EXCEPTION(opencl_error(ret));
272 }
273 }
274
275 #if defined(CL_VERSION_1_2) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
276 /// Compiles the program with \p options.
277 ///
278 /// \opencl_version_warning{1,2}
279 ///
280 /// \see_opencl_ref{clCompileProgram}
281 void compile(const std::string &options = std::string())
282 {
283 const char *options_string = 0;
284
285 if(!options.empty()){
286 options_string = options.c_str();
287 }
288
289 cl_int ret = clCompileProgram(
290 m_program, 0, 0, options_string, 0, 0, 0, 0, 0
291 );
292
293 if(ret != CL_SUCCESS){
294 BOOST_THROW_EXCEPTION(opencl_error(ret));
295 }
296 }
297
298 /// Links the programs in \p programs with \p options in \p context.
299 ///
300 /// \opencl_version_warning{1,2}
301 ///
302 /// \see_opencl_ref{clLinkProgram}
303 static program link(const std::vector<program> &programs,
304 const context &context,
305 const std::string &options = std::string())
306 {
307 const char *options_string = 0;
308
309 if(!options.empty()){
310 options_string = options.c_str();
311 }
312
313 cl_int ret;
314 cl_program program_ = clLinkProgram(
315 context.get(),
316 0,
317 0,
318 options_string,
319 static_cast<uint_>(programs.size()),
320 reinterpret_cast<const cl_program*>(&programs[0]),
321 0,
322 0,
323 &ret
324 );
325
326 if(!program_){
327 BOOST_THROW_EXCEPTION(opencl_error(ret));
328 }
329
330 return program(program_, false);
331 }
332 #endif // CL_VERSION_1_2
333
334 /// Returns the build log.
335 std::string build_log() const
336 {
337 return get_build_info<std::string>(CL_PROGRAM_BUILD_LOG, get_devices().front());
338 }
339
340 /// Creates and returns a new kernel object for \p name.
341 ///
342 /// For example, to create the \c "foo" kernel (after the program has been
343 /// created and built):
344 /// \code
345 /// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
346 /// \endcode
347 kernel create_kernel(const std::string &name) const;
348
349 /// Returns \c true if the program is the same at \p other.
350 bool operator==(const program &other) const
351 {
352 return m_program == other.m_program;
353 }
354
355 /// Returns \c true if the program is different from \p other.
356 bool operator!=(const program &other) const
357 {
358 return m_program != other.m_program;
359 }
360
361 /// \internal_
362 operator cl_program() const
363 {
364 return m_program;
365 }
366
367 /// Creates a new program with \p source in \p context.
368 ///
369 /// \see_opencl_ref{clCreateProgramWithSource}
370 static program create_with_source(const std::string &source,
371 const context &context)
372 {
373 const char *source_string = source.c_str();
374
375 cl_int error = 0;
376 cl_program program_ = clCreateProgramWithSource(context,
377 uint_(1),
378 &source_string,
379 0,
380 &error);
381 if(!program_){
382 BOOST_THROW_EXCEPTION(opencl_error(error));
383 }
384
385 return program(program_, false);
386 }
387
388 /// Creates a new program with \p sources in \p context.
389 ///
390 /// \see_opencl_ref{clCreateProgramWithSource}
391 static program create_with_source(const std::vector<std::string> &sources,
392 const context &context)
393 {
394 std::vector<const char*> source_strings(sources.size());
395 for(size_t i = 0; i < sources.size(); i++){
396 source_strings[i] = sources[i].c_str();
397 }
398
399 cl_int error = 0;
400 cl_program program_ = clCreateProgramWithSource(context,
401 uint_(sources.size()),
402 &source_strings[0],
403 0,
404 &error);
405 if(!program_){
406 BOOST_THROW_EXCEPTION(opencl_error(error));
407 }
408
409 return program(program_, false);
410 }
411
412 /// Creates a new program with \p file in \p context.
413 ///
414 /// \see_opencl_ref{clCreateProgramWithSource}
415 static program create_with_source_file(const std::string &file,
416 const context &context)
417 {
418 // open file stream
419 std::ifstream stream(file.c_str());
420
421 if(stream.fail()){
422 BOOST_THROW_EXCEPTION(std::ios_base::failure("failed to create stream."));
423 }
424
425 // read source
426 std::string source(
427 (std::istreambuf_iterator<char>(stream)),
428 std::istreambuf_iterator<char>()
429 );
430
431 // create program
432 return create_with_source(source, context);
433 }
434
435 /// Creates a new program with \p binary of \p binary_size in
436 /// \p context.
437 ///
438 /// \see_opencl_ref{clCreateProgramWithBinary}
439 static program create_with_binary(const unsigned char *binary,
440 size_t binary_size,
441 const context &context)
442 {
443 const cl_device_id device = context.get_device().id();
444
445 cl_int error = 0;
446 cl_int binary_status = 0;
447 cl_program program_ = clCreateProgramWithBinary(context,
448 uint_(1),
449 &device,
450 &binary_size,
451 &binary,
452 &binary_status,
453 &error);
454 if(!program_){
455 BOOST_THROW_EXCEPTION(opencl_error(error));
456 }
457 if(binary_status != CL_SUCCESS){
458 BOOST_THROW_EXCEPTION(opencl_error(binary_status));
459 }
460
461 return program(program_, false);
462 }
463
464 /// Creates a new program with \p binary in \p context.
465 ///
466 /// \see_opencl_ref{clCreateProgramWithBinary}
467 static program create_with_binary(const std::vector<unsigned char> &binary,
468 const context &context)
469 {
470 return create_with_binary(&binary[0], binary.size(), context);
471 }
472
473 /// Creates a new program with \p file in \p context.
474 ///
475 /// \see_opencl_ref{clCreateProgramWithBinary}
476 static program create_with_binary_file(const std::string &file,
477 const context &context)
478 {
479 // open file stream
480 std::ifstream stream(file.c_str(), std::ios::in | std::ios::binary);
481
482 // read binary
483 std::vector<unsigned char> binary(
484 (std::istreambuf_iterator<char>(stream)),
485 std::istreambuf_iterator<char>()
486 );
487
488 // create program
489 return create_with_binary(&binary[0], binary.size(), context);
490 }
491
492 #if defined(CL_VERSION_1_2) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
493 /// Creates a new program with the built-in kernels listed in
494 /// \p kernel_names for \p devices in \p context.
495 ///
496 /// \opencl_version_warning{1,2}
497 ///
498 /// \see_opencl_ref{clCreateProgramWithBuiltInKernels}
499 static program create_with_builtin_kernels(const context &context,
500 const std::vector<device> &devices,
501 const std::string &kernel_names)
502 {
503 cl_int error = 0;
504
505 cl_program program_ = clCreateProgramWithBuiltInKernels(
506 context.get(),
507 static_cast<uint_>(devices.size()),
508 reinterpret_cast<const cl_device_id *>(&devices[0]),
509 kernel_names.c_str(),
510 &error
511 );
512
513 if(!program_){
514 BOOST_THROW_EXCEPTION(opencl_error(error));
515 }
516
517 return program(program_, false);
518 }
519 #endif // CL_VERSION_1_2
520
521 /// Create a new program with \p source in \p context and builds it with \p options.
522 /**
523 * In case BOOST_COMPUTE_USE_OFFLINE_CACHE macro is defined,
524 * the compiled binary is stored for reuse in the offline cache located in
525 * $HOME/.boost_compute on UNIX-like systems and in %APPDATA%/boost_compute
526 * on Windows.
527 */
528 static program build_with_source(
529 const std::string &source,
530 const context &context,
531 const std::string &options = std::string()
532 )
533 {
534#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
535 // Get hash string for the kernel.
536 device d = context.get_device();
537 platform p = d.platform();
538
539 detail::sha1 hash;
540 hash.process( p.name() )
541 .process( p.version() )
542 .process( d.name() )
543 .process( options )
544 .process( source )
545 ;
546
547 // Try to get cached program binaries:
548 try {
549 boost::optional<program> prog = load_program_binary(hash, context);
550
551 if (prog) {
552 prog->build(options);
553 return *prog;
554 }
555 } catch (...) {
556 // Something bad happened. Fallback to normal compilation.
557 }
558
559 // Cache is apparently not available. Just compile the sources.
560#endif
561 const char *source_string = source.c_str();
562
563 cl_int error = 0;
564 cl_program program_ = clCreateProgramWithSource(context,
565 uint_(1),
566 &source_string,
567 0,
568 &error);
569 if(!program_){
570 BOOST_THROW_EXCEPTION(opencl_error(error));
571 }
572
573 program prog(program_, false);
574 prog.build(options);
575
576#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
577 // Save program binaries for future reuse.
578 save_program_binary(hash, prog);
579#endif
580
581 return prog;
582 }
583
584private:
585#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
586 // Saves program binaries for future reuse.
587 static void save_program_binary(const std::string &hash, const program &prog)
588 {
589 std::string fname = detail::program_binary_path(hash, true) + "kernel";
590 std::ofstream bfile(fname.c_str(), std::ios::binary);
591 if (!bfile) return;
592
593 std::vector<unsigned char> binary = prog.binary();
594
595 size_t binary_size = binary.size();
596 bfile.write((char*)&binary_size, sizeof(size_t));
597 bfile.write((char*)binary.data(), binary_size);
598 }
599
600 // Tries to read program binaries from file cache.
601 static boost::optional<program> load_program_binary(
602 const std::string &hash, const context &ctx
603 )
604 {
605 std::string fname = detail::program_binary_path(hash) + "kernel";
606 std::ifstream bfile(fname.c_str(), std::ios::binary);
607 if (!bfile) return boost::optional<program>();
608
609 size_t binary_size;
610 std::vector<unsigned char> binary;
611
612 bfile.read((char*)&binary_size, sizeof(size_t));
613
614 binary.resize(binary_size);
615 bfile.read((char*)binary.data(), binary_size);
616
617 return boost::optional<program>(
618 program::create_with_binary(
619 binary.data(), binary_size, ctx
620 )
621 );
622 }
623#endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
624
625private:
626 cl_program m_program;
627};
628
629/// \internal_ define get_info() specializations for program
630BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
631 ((cl_uint, CL_PROGRAM_REFERENCE_COUNT))
632 ((cl_context, CL_PROGRAM_CONTEXT))
633 ((cl_uint, CL_PROGRAM_NUM_DEVICES))
634 ((std::vector<cl_device_id>, CL_PROGRAM_DEVICES))
635 ((std::string, CL_PROGRAM_SOURCE))
636 ((std::vector<size_t>, CL_PROGRAM_BINARY_SIZES))
637 ((std::vector<unsigned char *>, CL_PROGRAM_BINARIES))
638)
639
640#ifdef CL_VERSION_1_2
641BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
642 ((size_t, CL_PROGRAM_NUM_KERNELS))
643 ((std::string, CL_PROGRAM_KERNEL_NAMES))
644)
645#endif // CL_VERSION_1_2
646
647} // end compute namespace
648} // end boost namespace
649
650#endif // BOOST_COMPUTE_PROGRAM_HPP