summaryrefslogtreecommitdiff
path: root/boost/compute/program.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/compute/program.hpp')
-rw-r--r--boost/compute/program.hpp650
1 files changed, 650 insertions, 0 deletions
diff --git a/boost/compute/program.hpp b/boost/compute/program.hpp
new file mode 100644
index 0000000000..7573aa02e6
--- /dev/null
+++ b/boost/compute/program.hpp
@@ -0,0 +1,650 @@
+//---------------------------------------------------------------------------//
+// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
+//
+// Distributed under the Boost Software License, Version 1.0
+// See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt
+//
+// See http://boostorg.github.com/compute for more information.
+//---------------------------------------------------------------------------//
+
+#ifndef BOOST_COMPUTE_PROGRAM_HPP
+#define BOOST_COMPUTE_PROGRAM_HPP
+
+#include <string>
+#include <vector>
+#include <fstream>
+#include <streambuf>
+
+#ifdef BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION
+#include <iostream>
+#endif
+
+#include <boost/compute/config.hpp>
+#include <boost/compute/context.hpp>
+#include <boost/compute/exception.hpp>
+#include <boost/compute/detail/assert_cl_success.hpp>
+
+#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
+#include <sstream>
+#include <boost/optional.hpp>
+#include <boost/compute/platform.hpp>
+#include <boost/compute/detail/getenv.hpp>
+#include <boost/compute/detail/path.hpp>
+#include <boost/compute/detail/sha1.hpp>
+#endif
+
+namespace boost {
+namespace compute {
+
+class kernel;
+
+/// \class program
+/// \brief A compute program.
+///
+/// The program class represents an OpenCL program.
+///
+/// Program objects are created with one of the static \c create_with_*
+/// functions. For example, to create a program from a source string:
+///
+/// \snippet test/test_program.cpp create_with_source
+///
+/// And to create a program from a source file:
+/// \code
+/// boost::compute::program bar_program =
+/// boost::compute::program::create_with_source_file("/path/to/bar.cl", context);
+/// \endcode
+///
+/// Once a program object has been succesfully created, it can be compiled
+/// using the \c build() method:
+/// \code
+/// // build the program
+/// foo_program.build();
+/// \endcode
+///
+/// Once the program is built, \ref kernel objects can be created using the
+/// \c create_kernel() method by passing their name:
+/// \code
+/// // create a kernel from the compiled program
+/// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
+/// \endcode
+///
+/// \see kernel
+class program
+{
+public:
+ /// Creates a null program object.
+ program()
+ : m_program(0)
+ {
+ }
+
+ /// Creates a program object for \p program. If \p retain is \c true,
+ /// the reference count for \p program will be incremented.
+ explicit program(cl_program program, bool retain = true)
+ : m_program(program)
+ {
+ if(m_program && retain){
+ clRetainProgram(m_program);
+ }
+ }
+
+ /// Creates a new program object as a copy of \p other.
+ program(const program &other)
+ : m_program(other.m_program)
+ {
+ if(m_program){
+ clRetainProgram(m_program);
+ }
+ }
+
+ /// Copies the program object from \p other to \c *this.
+ program& operator=(const program &other)
+ {
+ if(this != &other){
+ if(m_program){
+ clReleaseProgram(m_program);
+ }
+
+ m_program = other.m_program;
+
+ if(m_program){
+ clRetainProgram(m_program);
+ }
+ }
+
+ return *this;
+ }
+
+ #ifndef BOOST_COMPUTE_NO_RVALUE_REFERENCES
+ /// Move-constructs a new program object from \p other.
+ program(program&& other) BOOST_NOEXCEPT
+ : m_program(other.m_program)
+ {
+ other.m_program = 0;
+ }
+
+ /// Move-assigns the program from \p other to \c *this.
+ program& operator=(program&& other) BOOST_NOEXCEPT
+ {
+ if(m_program){
+ clReleaseProgram(m_program);
+ }
+
+ m_program = other.m_program;
+ other.m_program = 0;
+
+ return *this;
+ }
+ #endif // BOOST_COMPUTE_NO_RVALUE_REFERENCES
+
+ /// Destroys the program object.
+ ~program()
+ {
+ if(m_program){
+ BOOST_COMPUTE_ASSERT_CL_SUCCESS(
+ clReleaseProgram(m_program)
+ );
+ }
+ }
+
+ /// Returns the underlying OpenCL program.
+ cl_program& get() const
+ {
+ return const_cast<cl_program &>(m_program);
+ }
+
+ /// Returns the source code for the program.
+ std::string source() const
+ {
+ return get_info<std::string>(CL_PROGRAM_SOURCE);
+ }
+
+ /// Returns the binary for the program.
+ std::vector<unsigned char> binary() const
+ {
+ size_t binary_size = get_info<size_t>(CL_PROGRAM_BINARY_SIZES);
+ std::vector<unsigned char> binary(binary_size);
+
+ unsigned char *binary_ptr = &binary[0];
+ cl_int error = clGetProgramInfo(m_program,
+ CL_PROGRAM_BINARIES,
+ sizeof(unsigned char **),
+ &binary_ptr,
+ 0);
+ if(error != CL_SUCCESS){
+ BOOST_THROW_EXCEPTION(opencl_error(error));
+ }
+
+ return binary;
+ }
+
+ std::vector<device> get_devices() const
+ {
+ std::vector<cl_device_id> device_ids =
+ get_info<std::vector<cl_device_id> >(CL_PROGRAM_DEVICES);
+
+ std::vector<device> devices;
+ for(size_t i = 0; i < device_ids.size(); i++){
+ devices.push_back(device(device_ids[i]));
+ }
+
+ return devices;
+ }
+
+ /// Returns the context for the program.
+ context get_context() const
+ {
+ return context(get_info<cl_context>(CL_PROGRAM_CONTEXT));
+ }
+
+ /// Returns information about the program.
+ ///
+ /// \see_opencl_ref{clGetProgramInfo}
+ template<class T>
+ T get_info(cl_program_info info) const
+ {
+ return detail::get_object_info<T>(clGetProgramInfo, m_program, info);
+ }
+
+ /// \overload
+ template<int Enum>
+ typename detail::get_object_info_type<program, Enum>::type
+ get_info() const;
+
+ /// Returns build information about the program.
+ ///
+ /// For example, this function can be used to retreive the options used
+ /// to build the program:
+ /// \code
+ /// std::string build_options =
+ /// program.get_build_info<std::string>(CL_PROGRAM_BUILD_OPTIONS);
+ /// \endcode
+ ///
+ /// \see_opencl_ref{clGetProgramInfo}
+ template<class T>
+ T get_build_info(cl_program_build_info info, const device &device) const
+ {
+ return detail::get_object_info<T>(clGetProgramBuildInfo, m_program, info, device.id());
+ }
+
+ /// Builds the program with \p options.
+ ///
+ /// If the program fails to compile, this function will throw an
+ /// opencl_error exception.
+ /// \code
+ /// try {
+ /// // attempt to compile to program
+ /// program.build();
+ /// }
+ /// catch(boost::compute::opencl_error &e){
+ /// // program failed to compile, print out the build log
+ /// std::cout << program.build_log() << std::endl;
+ /// }
+ /// \endcode
+ ///
+ /// \see_opencl_ref{clBuildProgram}
+ void build(const std::string &options = std::string())
+ {
+ const char *options_string = 0;
+
+ if(!options.empty()){
+ options_string = options.c_str();
+ }
+
+ cl_int ret = clBuildProgram(m_program, 0, 0, options_string, 0, 0);
+
+ #ifdef BOOST_COMPUTE_DEBUG_KERNEL_COMPILATION
+ if(ret != CL_SUCCESS){
+ // print the error, source code and build log
+ std::cerr << "Boost.Compute: "
+ << "kernel compilation failed (" << ret << ")\n"
+ << "--- source ---\n"
+ << source()
+ << "\n--- build log ---\n"
+ << build_log()
+ << std::endl;
+ }
+ #endif
+
+ if(ret != CL_SUCCESS){
+ BOOST_THROW_EXCEPTION(opencl_error(ret));
+ }
+ }
+
+ #if defined(CL_VERSION_1_2) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
+ /// Compiles the program with \p options.
+ ///
+ /// \opencl_version_warning{1,2}
+ ///
+ /// \see_opencl_ref{clCompileProgram}
+ void compile(const std::string &options = std::string())
+ {
+ const char *options_string = 0;
+
+ if(!options.empty()){
+ options_string = options.c_str();
+ }
+
+ cl_int ret = clCompileProgram(
+ m_program, 0, 0, options_string, 0, 0, 0, 0, 0
+ );
+
+ if(ret != CL_SUCCESS){
+ BOOST_THROW_EXCEPTION(opencl_error(ret));
+ }
+ }
+
+ /// Links the programs in \p programs with \p options in \p context.
+ ///
+ /// \opencl_version_warning{1,2}
+ ///
+ /// \see_opencl_ref{clLinkProgram}
+ static program link(const std::vector<program> &programs,
+ const context &context,
+ const std::string &options = std::string())
+ {
+ const char *options_string = 0;
+
+ if(!options.empty()){
+ options_string = options.c_str();
+ }
+
+ cl_int ret;
+ cl_program program_ = clLinkProgram(
+ context.get(),
+ 0,
+ 0,
+ options_string,
+ static_cast<uint_>(programs.size()),
+ reinterpret_cast<const cl_program*>(&programs[0]),
+ 0,
+ 0,
+ &ret
+ );
+
+ if(!program_){
+ BOOST_THROW_EXCEPTION(opencl_error(ret));
+ }
+
+ return program(program_, false);
+ }
+ #endif // CL_VERSION_1_2
+
+ /// Returns the build log.
+ std::string build_log() const
+ {
+ return get_build_info<std::string>(CL_PROGRAM_BUILD_LOG, get_devices().front());
+ }
+
+ /// Creates and returns a new kernel object for \p name.
+ ///
+ /// For example, to create the \c "foo" kernel (after the program has been
+ /// created and built):
+ /// \code
+ /// boost::compute::kernel foo_kernel = foo_program.create_kernel("foo");
+ /// \endcode
+ kernel create_kernel(const std::string &name) const;
+
+ /// Returns \c true if the program is the same at \p other.
+ bool operator==(const program &other) const
+ {
+ return m_program == other.m_program;
+ }
+
+ /// Returns \c true if the program is different from \p other.
+ bool operator!=(const program &other) const
+ {
+ return m_program != other.m_program;
+ }
+
+ /// \internal_
+ operator cl_program() const
+ {
+ return m_program;
+ }
+
+ /// Creates a new program with \p source in \p context.
+ ///
+ /// \see_opencl_ref{clCreateProgramWithSource}
+ static program create_with_source(const std::string &source,
+ const context &context)
+ {
+ const char *source_string = source.c_str();
+
+ cl_int error = 0;
+ cl_program program_ = clCreateProgramWithSource(context,
+ uint_(1),
+ &source_string,
+ 0,
+ &error);
+ if(!program_){
+ BOOST_THROW_EXCEPTION(opencl_error(error));
+ }
+
+ return program(program_, false);
+ }
+
+ /// Creates a new program with \p sources in \p context.
+ ///
+ /// \see_opencl_ref{clCreateProgramWithSource}
+ static program create_with_source(const std::vector<std::string> &sources,
+ const context &context)
+ {
+ std::vector<const char*> source_strings(sources.size());
+ for(size_t i = 0; i < sources.size(); i++){
+ source_strings[i] = sources[i].c_str();
+ }
+
+ cl_int error = 0;
+ cl_program program_ = clCreateProgramWithSource(context,
+ uint_(sources.size()),
+ &source_strings[0],
+ 0,
+ &error);
+ if(!program_){
+ BOOST_THROW_EXCEPTION(opencl_error(error));
+ }
+
+ return program(program_, false);
+ }
+
+ /// Creates a new program with \p file in \p context.
+ ///
+ /// \see_opencl_ref{clCreateProgramWithSource}
+ static program create_with_source_file(const std::string &file,
+ const context &context)
+ {
+ // open file stream
+ std::ifstream stream(file.c_str());
+
+ if(stream.fail()){
+ BOOST_THROW_EXCEPTION(std::ios_base::failure("failed to create stream."));
+ }
+
+ // read source
+ std::string source(
+ (std::istreambuf_iterator<char>(stream)),
+ std::istreambuf_iterator<char>()
+ );
+
+ // create program
+ return create_with_source(source, context);
+ }
+
+ /// Creates a new program with \p binary of \p binary_size in
+ /// \p context.
+ ///
+ /// \see_opencl_ref{clCreateProgramWithBinary}
+ static program create_with_binary(const unsigned char *binary,
+ size_t binary_size,
+ const context &context)
+ {
+ const cl_device_id device = context.get_device().id();
+
+ cl_int error = 0;
+ cl_int binary_status = 0;
+ cl_program program_ = clCreateProgramWithBinary(context,
+ uint_(1),
+ &device,
+ &binary_size,
+ &binary,
+ &binary_status,
+ &error);
+ if(!program_){
+ BOOST_THROW_EXCEPTION(opencl_error(error));
+ }
+ if(binary_status != CL_SUCCESS){
+ BOOST_THROW_EXCEPTION(opencl_error(binary_status));
+ }
+
+ return program(program_, false);
+ }
+
+ /// Creates a new program with \p binary in \p context.
+ ///
+ /// \see_opencl_ref{clCreateProgramWithBinary}
+ static program create_with_binary(const std::vector<unsigned char> &binary,
+ const context &context)
+ {
+ return create_with_binary(&binary[0], binary.size(), context);
+ }
+
+ /// Creates a new program with \p file in \p context.
+ ///
+ /// \see_opencl_ref{clCreateProgramWithBinary}
+ static program create_with_binary_file(const std::string &file,
+ const context &context)
+ {
+ // open file stream
+ std::ifstream stream(file.c_str(), std::ios::in | std::ios::binary);
+
+ // read binary
+ std::vector<unsigned char> binary(
+ (std::istreambuf_iterator<char>(stream)),
+ std::istreambuf_iterator<char>()
+ );
+
+ // create program
+ return create_with_binary(&binary[0], binary.size(), context);
+ }
+
+ #if defined(CL_VERSION_1_2) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
+ /// Creates a new program with the built-in kernels listed in
+ /// \p kernel_names for \p devices in \p context.
+ ///
+ /// \opencl_version_warning{1,2}
+ ///
+ /// \see_opencl_ref{clCreateProgramWithBuiltInKernels}
+ static program create_with_builtin_kernels(const context &context,
+ const std::vector<device> &devices,
+ const std::string &kernel_names)
+ {
+ cl_int error = 0;
+
+ cl_program program_ = clCreateProgramWithBuiltInKernels(
+ context.get(),
+ static_cast<uint_>(devices.size()),
+ reinterpret_cast<const cl_device_id *>(&devices[0]),
+ kernel_names.c_str(),
+ &error
+ );
+
+ if(!program_){
+ BOOST_THROW_EXCEPTION(opencl_error(error));
+ }
+
+ return program(program_, false);
+ }
+ #endif // CL_VERSION_1_2
+
+ /// Create a new program with \p source in \p context and builds it with \p options.
+ /**
+ * In case BOOST_COMPUTE_USE_OFFLINE_CACHE macro is defined,
+ * the compiled binary is stored for reuse in the offline cache located in
+ * $HOME/.boost_compute on UNIX-like systems and in %APPDATA%/boost_compute
+ * on Windows.
+ */
+ static program build_with_source(
+ const std::string &source,
+ const context &context,
+ const std::string &options = std::string()
+ )
+ {
+#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
+ // Get hash string for the kernel.
+ device d = context.get_device();
+ platform p = d.platform();
+
+ detail::sha1 hash;
+ hash.process( p.name() )
+ .process( p.version() )
+ .process( d.name() )
+ .process( options )
+ .process( source )
+ ;
+
+ // Try to get cached program binaries:
+ try {
+ boost::optional<program> prog = load_program_binary(hash, context);
+
+ if (prog) {
+ prog->build(options);
+ return *prog;
+ }
+ } catch (...) {
+ // Something bad happened. Fallback to normal compilation.
+ }
+
+ // Cache is apparently not available. Just compile the sources.
+#endif
+ const char *source_string = source.c_str();
+
+ cl_int error = 0;
+ cl_program program_ = clCreateProgramWithSource(context,
+ uint_(1),
+ &source_string,
+ 0,
+ &error);
+ if(!program_){
+ BOOST_THROW_EXCEPTION(opencl_error(error));
+ }
+
+ program prog(program_, false);
+ prog.build(options);
+
+#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
+ // Save program binaries for future reuse.
+ save_program_binary(hash, prog);
+#endif
+
+ return prog;
+ }
+
+private:
+#ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
+ // Saves program binaries for future reuse.
+ static void save_program_binary(const std::string &hash, const program &prog)
+ {
+ std::string fname = detail::program_binary_path(hash, true) + "kernel";
+ std::ofstream bfile(fname.c_str(), std::ios::binary);
+ if (!bfile) return;
+
+ std::vector<unsigned char> binary = prog.binary();
+
+ size_t binary_size = binary.size();
+ bfile.write((char*)&binary_size, sizeof(size_t));
+ bfile.write((char*)binary.data(), binary_size);
+ }
+
+ // Tries to read program binaries from file cache.
+ static boost::optional<program> load_program_binary(
+ const std::string &hash, const context &ctx
+ )
+ {
+ std::string fname = detail::program_binary_path(hash) + "kernel";
+ std::ifstream bfile(fname.c_str(), std::ios::binary);
+ if (!bfile) return boost::optional<program>();
+
+ size_t binary_size;
+ std::vector<unsigned char> binary;
+
+ bfile.read((char*)&binary_size, sizeof(size_t));
+
+ binary.resize(binary_size);
+ bfile.read((char*)binary.data(), binary_size);
+
+ return boost::optional<program>(
+ program::create_with_binary(
+ binary.data(), binary_size, ctx
+ )
+ );
+ }
+#endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
+
+private:
+ cl_program m_program;
+};
+
+/// \internal_ define get_info() specializations for program
+BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
+ ((cl_uint, CL_PROGRAM_REFERENCE_COUNT))
+ ((cl_context, CL_PROGRAM_CONTEXT))
+ ((cl_uint, CL_PROGRAM_NUM_DEVICES))
+ ((std::vector<cl_device_id>, CL_PROGRAM_DEVICES))
+ ((std::string, CL_PROGRAM_SOURCE))
+ ((std::vector<size_t>, CL_PROGRAM_BINARY_SIZES))
+ ((std::vector<unsigned char *>, CL_PROGRAM_BINARIES))
+)
+
+#ifdef CL_VERSION_1_2
+BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
+ ((size_t, CL_PROGRAM_NUM_KERNELS))
+ ((std::string, CL_PROGRAM_KERNEL_NAMES))
+)
+#endif // CL_VERSION_1_2
+
+} // end compute namespace
+} // end boost namespace
+
+#endif // BOOST_COMPUTE_PROGRAM_HPP