123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799 |
- //---------------------------------------------------------------------------//
- // 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/exception/program_build_failure.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 successfully 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;
- }
- #if defined(BOOST_COMPUTE_CL_VERSION_2_1) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
- /// Returns the SPIR-V binary for the program.
- std::vector<unsigned char> il_binary() const
- {
- return get_info<std::vector<unsigned char> >(CL_PROGRAM_IL);
- }
- #endif // BOOST_COMPUTE_CL_VERSION_2_1
- 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(program_build_failure(ret, build_log()));
- }
- }
- #if defined(BOOST_COMPUTE_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 std::vector<std::pair<std::string, program> > &headers =
- std::vector<std::pair<std::string, program> >())
- {
- const char *options_string = 0;
- if(!options.empty()){
- options_string = options.c_str();
- }
- cl_int ret;
- if (headers.empty())
- {
- ret = clCompileProgram(
- m_program, 0, 0, options_string, 0, 0, 0, 0, 0
- );
- }
- else
- {
- std::vector<const char*> header_names(headers.size());
- std::vector<cl_program> header_programs(headers.size());
- for (size_t i = 0; i < headers.size(); ++i)
- {
- header_names[i] = headers[i].first.c_str();
- header_programs[i] = headers[i].second.m_program;
- }
- ret = clCompileProgram(
- m_program,
- 0,
- 0,
- options_string,
- static_cast<cl_uint>(headers.size()),
- header_programs.data(),
- header_names.data(),
- 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 // BOOST_COMPUTE_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)
- {
- // create program
- return create_with_source(read_source_file(file), context);
- }
- /// Creates a new program with \p files in \p context.
- ///
- /// \see_opencl_ref{clCreateProgramWithSource}
- static program create_with_source_file(const std::vector<std::string> &files,
- const context &context)
- {
- std::vector<std::string> sources(files.size());
- for(size_t i = 0; i < files.size(); ++i) {
- // open file stream
- std::ifstream stream(files[i].c_str());
- if(stream.fail()){
- BOOST_THROW_EXCEPTION(std::ios_base::failure("failed to create stream."));
- }
- // read source
- sources[i] = std::string(
- (std::istreambuf_iterator<char>(stream)),
- std::istreambuf_iterator<char>()
- );
- }
- // create program
- return create_with_source(sources, 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(BOOST_COMPUTE_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 // BOOST_COMPUTE_CL_VERSION_1_2
- #if defined(BOOST_COMPUTE_CL_VERSION_2_1) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
- /// Creates a new program with \p il_binary (SPIR-V binary)
- /// of \p il_size size in \p context.
- ///
- /// \opencl_version_warning{2,1}
- ///
- /// \see_opencl21_ref{clCreateProgramWithIL}
- static program create_with_il(const void * il_binary,
- const size_t il_size,
- const context &context)
- {
- cl_int error = 0;
- cl_program program_ = clCreateProgramWithIL(
- context.get(), il_binary, il_size, &error
- );
- if(!program_){
- BOOST_THROW_EXCEPTION(opencl_error(error));
- }
- return program(program_, false);
- }
- /// Creates a new program with \p il_binary (SPIR-V binary)
- /// in \p context.
- ///
- /// \opencl_version_warning{2,1}
- ///
- /// \see_opencl_ref{clCreateProgramWithIL}
- static program create_with_il(const std::vector<unsigned char> &il_binary,
- const context &context)
- {
- return create_with_il(&il_binary[0], il_binary.size(), context);
- }
- /// Creates a new program in \p context using SPIR-V
- /// binary \p file.
- ///
- /// \opencl_version_warning{2,1}
- ///
- /// \see_opencl_ref{clCreateProgramWithIL}
- static program create_with_il_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> il(
- (std::istreambuf_iterator<char>(stream)),
- std::istreambuf_iterator<char>()
- );
- // create program
- return create_with_il(&il[0], il.size(), context);
- }
- #endif // BOOST_COMPUTE_CL_VERSION_2_1
- /// 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 )
- ;
- std::string hash_string = hash;
- // Try to get cached program binaries:
- try {
- boost::optional<program> prog = load_program_binary(hash_string, 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_string, prog);
- #endif
- return prog;
- }
- /// Create a new program with \p file 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_file(
- const std::string &file,
- const context &context,
- const std::string &options = std::string()
- )
- {
- return build_with_source(read_source_file(file), context, options);
- }
- 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
- static std::string read_source_file(const std::string &file)
- {
- // 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
- return std::string(
- (std::istreambuf_iterator<char>(stream)),
- std::istreambuf_iterator<char>()
- );
- }
- 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 BOOST_COMPUTE_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 // BOOST_COMPUTE_CL_VERSION_1_2
- #ifdef BOOST_COMPUTE_CL_VERSION_2_1
- BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(program,
- ((std::vector<unsigned char>, CL_PROGRAM_IL))
- )
- #endif // BOOST_COMPUTE_CL_VERSION_2_1
- } // end compute namespace
- } // end boost namespace
- #endif // BOOST_COMPUTE_PROGRAM_HPP
|