1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
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
8 // See http://boostorg.github.com/compute for more information.
9 //---------------------------------------------------------------------------//
11 #ifndef BOOST_COMPUTE_PROGRAM_HPP
12 #define BOOST_COMPUTE_PROGRAM_HPP
19 #ifdef BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION
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>
28 #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
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>
43 /// \brief A compute program.
45 /// The program class represents an OpenCL program.
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:
50 /// \snippet test/test_program.cpp create_with_source
52 /// And to create a program from a source file:
54 /// boost::compute::program bar_program =
55 /// boost::compute::program::create_with_source_file("/path/to/bar.cl", context);
58 /// Once a program object has been succesfully created, it can be compiled
59 /// using the \c build() method:
61 /// // build the program
62 /// foo_program.build();
65 /// Once the program is built, \ref kernel objects can be created using the
66 /// \c create_kernel() method by passing their name:
68 /// // create a kernel from the compiled program
69 /// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
76 /// Creates a null program object.
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)
87 if(m_program && retain){
88 clRetainProgram(m_program);
92 /// Creates a new program object as a copy of \p other.
93 program(const program &other)
94 : m_program(other.m_program)
97 clRetainProgram(m_program);
101 /// Copies the program object from \p other to \c *this.
102 program& operator=(const program &other)
106 clReleaseProgram(m_program);
109 m_program = other.m_program;
112 clRetainProgram(m_program);
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)
127 /// Move-assigns the program from \p other to \c *this.
128 program& operator=(program&& other) BOOST_NOEXCEPT
131 clReleaseProgram(m_program);
134 m_program = other.m_program;
139 #endif // BOOST_COMPUTE_NO_RVALUE_REFERENCES
141 /// Destroys the program object.
145 BOOST_COMPUTE_ASSERT_CL_SUCCESS(
146 clReleaseProgram(m_program)
151 /// Returns the underlying OpenCL program.
152 cl_program& get() const
154 return const_cast<cl_program &>(m_program);
157 /// Returns the source code for the program.
158 std::string source() const
160 return get_info<std::string>(CL_PROGRAM_SOURCE);
163 /// Returns the binary for the program.
164 std::vector<unsigned char> binary() const
166 size_t binary_size = get_info<size_t>(CL_PROGRAM_BINARY_SIZES);
167 std::vector<unsigned char> binary(binary_size);
169 unsigned char *binary_ptr = &binary[0];
170 cl_int error = clGetProgramInfo(m_program,
172 sizeof(unsigned char **),
175 if(error != CL_SUCCESS){
176 BOOST_THROW_EXCEPTION(opencl_error(error));
182 std::vector<device> get_devices() const
184 std::vector<cl_device_id> device_ids =
185 get_info<std::vector<cl_device_id> >(CL_PROGRAM_DEVICES);
187 std::vector<device> devices;
188 for(size_t i = 0; i < device_ids.size(); i++){
189 devices.push_back(device(device_ids[i]));
195 /// Returns the context for the program.
196 context get_context() const
198 return context(get_info<cl_context>(CL_PROGRAM_CONTEXT));
201 /// Returns information about the program.
203 /// \see_opencl_ref{clGetProgramInfo}
205 T get_info(cl_program_info info) const
207 return detail::get_object_info<T>(clGetProgramInfo, m_program, info);
212 typename detail::get_object_info_type<program, Enum>::type
215 /// Returns build information about the program.
217 /// For example, this function can be used to retreive the options used
218 /// to build the program:
220 /// std::string build_options =
221 /// program.get_build_info<std::string>(CL_PROGRAM_BUILD_OPTIONS);
224 /// \see_opencl_ref{clGetProgramInfo}
226 T get_build_info(cl_program_build_info info, const device &device) const
228 return detail::get_object_info<T>(clGetProgramBuildInfo, m_program, info, device.id());
231 /// Builds the program with \p options.
233 /// If the program fails to compile, this function will throw an
234 /// opencl_error exception.
237 /// // attempt to compile to program
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;
246 /// \see_opencl_ref{clBuildProgram}
247 void build(const std::string &options = std::string())
249 const char *options_string = 0;
251 if(!options.empty()){
252 options_string = options.c_str();
255 cl_int ret = clBuildProgram(m_program, 0, 0, options_string, 0, 0);
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"
264 << "\n--- build log ---\n"
270 if(ret != CL_SUCCESS){
271 BOOST_THROW_EXCEPTION(opencl_error(ret));
275 #if defined(CL_VERSION_1_2) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
276 /// Compiles the program with \p options.
278 /// \opencl_version_warning{1,2}
280 /// \see_opencl_ref{clCompileProgram}
281 void compile(const std::string &options = std::string())
283 const char *options_string = 0;
285 if(!options.empty()){
286 options_string = options.c_str();
289 cl_int ret = clCompileProgram(
290 m_program, 0, 0, options_string, 0, 0, 0, 0, 0
293 if(ret != CL_SUCCESS){
294 BOOST_THROW_EXCEPTION(opencl_error(ret));
298 /// Links the programs in \p programs with \p options in \p context.
300 /// \opencl_version_warning{1,2}
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())
307 const char *options_string = 0;
309 if(!options.empty()){
310 options_string = options.c_str();
314 cl_program program_ = clLinkProgram(
319 static_cast<uint_>(programs.size()),
320 reinterpret_cast<const cl_program*>(&programs[0]),
327 BOOST_THROW_EXCEPTION(opencl_error(ret));
330 return program(program_, false);
332 #endif // CL_VERSION_1_2
334 /// Returns the build log.
335 std::string build_log() const
337 return get_build_info<std::string>(CL_PROGRAM_BUILD_LOG, get_devices().front());
340 /// Creates and returns a new kernel object for \p name.
342 /// For example, to create the \c "foo" kernel (after the program has been
343 /// created and built):
345 /// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
347 kernel create_kernel(const std::string &name) const;
349 /// Returns \c true if the program is the same at \p other.
350 bool operator==(const program &other) const
352 return m_program == other.m_program;
355 /// Returns \c true if the program is different from \p other.
356 bool operator!=(const program &other) const
358 return m_program != other.m_program;
362 operator cl_program() const
367 /// Creates a new program with \p source in \p context.
369 /// \see_opencl_ref{clCreateProgramWithSource}
370 static program create_with_source(const std::string &source,
371 const context &context)
373 const char *source_string = source.c_str();
376 cl_program program_ = clCreateProgramWithSource(context,
382 BOOST_THROW_EXCEPTION(opencl_error(error));
385 return program(program_, false);
388 /// Creates a new program with \p sources in \p context.
390 /// \see_opencl_ref{clCreateProgramWithSource}
391 static program create_with_source(const std::vector<std::string> &sources,
392 const context &context)
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();
400 cl_program program_ = clCreateProgramWithSource(context,
401 uint_(sources.size()),
406 BOOST_THROW_EXCEPTION(opencl_error(error));
409 return program(program_, false);
412 /// Creates a new program with \p file in \p context.
414 /// \see_opencl_ref{clCreateProgramWithSource}
415 static program create_with_source_file(const std::string &file,
416 const context &context)
419 std::ifstream stream(file.c_str());
422 BOOST_THROW_EXCEPTION(std::ios_base::failure("failed to create stream."));
427 (std::istreambuf_iterator<char>(stream)),
428 std::istreambuf_iterator<char>()
432 return create_with_source(source, context);
435 /// Creates a new program with \p binary of \p binary_size in
438 /// \see_opencl_ref{clCreateProgramWithBinary}
439 static program create_with_binary(const unsigned char *binary,
441 const context &context)
443 const cl_device_id device = context.get_device().id();
446 cl_int binary_status = 0;
447 cl_program program_ = clCreateProgramWithBinary(context,
455 BOOST_THROW_EXCEPTION(opencl_error(error));
457 if(binary_status != CL_SUCCESS){
458 BOOST_THROW_EXCEPTION(opencl_error(binary_status));
461 return program(program_, false);
464 /// Creates a new program with \p binary in \p context.
466 /// \see_opencl_ref{clCreateProgramWithBinary}
467 static program create_with_binary(const std::vector<unsigned char> &binary,
468 const context &context)
470 return create_with_binary(&binary[0], binary.size(), context);
473 /// Creates a new program with \p file in \p context.
475 /// \see_opencl_ref{clCreateProgramWithBinary}
476 static program create_with_binary_file(const std::string &file,
477 const context &context)
480 std::ifstream stream(file.c_str(), std::ios::in | std::ios::binary);
483 std::vector<unsigned char> binary(
484 (std::istreambuf_iterator<char>(stream)),
485 std::istreambuf_iterator<char>()
489 return create_with_binary(&binary[0], binary.size(), context);
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.
496 /// \opencl_version_warning{1,2}
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)
505 cl_program program_ = clCreateProgramWithBuiltInKernels(
507 static_cast<uint_>(devices.size()),
508 reinterpret_cast<const cl_device_id *>(&devices[0]),
509 kernel_names.c_str(),
514 BOOST_THROW_EXCEPTION(opencl_error(error));
517 return program(program_, false);
519 #endif // CL_VERSION_1_2
521 /// Create a new program with \p source in \p context and builds it with \p options.
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
528 static program build_with_source(
529 const std::string &source,
530 const context &context,
531 const std::string &options = std::string()
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();
540 hash.process( p.name() )
541 .process( p.version() )
547 // Try to get cached program binaries:
549 boost::optional<program> prog = load_program_binary(hash, context);
552 prog->build(options);
556 // Something bad happened. Fallback to normal compilation.
559 // Cache is apparently not available. Just compile the sources.
561 const char *source_string = source.c_str();
564 cl_program program_ = clCreateProgramWithSource(context,
570 BOOST_THROW_EXCEPTION(opencl_error(error));
573 program prog(program_, false);
576 #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
577 // Save program binaries for future reuse.
578 save_program_binary(hash, prog);
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)
589 std::string fname = detail::program_binary_path(hash, true) + "kernel";
590 std::ofstream bfile(fname.c_str(), std::ios::binary);
593 std::vector<unsigned char> binary = prog.binary();
595 size_t binary_size = binary.size();
596 bfile.write((char*)&binary_size, sizeof(size_t));
597 bfile.write((char*)binary.data(), binary_size);
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
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>();
610 std::vector<unsigned char> binary;
612 bfile.read((char*)&binary_size, sizeof(size_t));
614 binary.resize(binary_size);
615 bfile.read((char*)binary.data(), binary_size);
617 return boost::optional<program>(
618 program::create_with_binary(
619 binary.data(), binary_size, ctx
623 #endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
626 cl_program m_program;
629 /// \internal_ define get_info() specializations for program
630 BOOST_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))
640 #ifdef CL_VERSION_1_2
641 BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
642 ((size_t, CL_PROGRAM_NUM_KERNELS))
643 ((std::string, CL_PROGRAM_KERNEL_NAMES))
645 #endif // CL_VERSION_1_2
647 } // end compute namespace
648 } // end boost namespace
650 #endif // BOOST_COMPUTE_PROGRAM_HPP