| 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>#endifnamespace 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 kernelclass 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 programBOOST_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_2BOOST_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_1BOOST_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
 |