//---------------------------------------------------------------------------// // Copyright (c) 2013 Kyle Lutz // // 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_EVENT_HPP #define BOOST_COMPUTE_EVENT_HPP #include #include #include #include #include #include #include namespace boost { namespace compute { /// \class event /// \brief An event corresponding to an operation on a compute device /// /// Event objects are used to track operations running on the device (such as /// kernel executions and memory transfers). Event objects are returned by the /// various \c enqueue_* methods of the command_queue class. /// /// Events can be used to synchronize operations between the host and the /// device. The \c wait() method will block execution on the host until the /// operation corresponding to the event on the device has completed. The /// status of the operation can also be polled with the \c status() method. /// /// Event objects can also be used for performance profiling. In order to use /// events for profiling, the command queue must be constructed with the /// \c CL_QUEUE_PROFILING_ENABLE flag. Then the \c duration() method can be /// used to retrieve the total duration of the operation on the device: /// \code /// std::cout << "time = " << e.duration().count() << "ms\n"; /// \endcode /// /// \see \ref future "future", wait_list class event { public: /// \internal_ enum execution_status { complete = CL_COMPLETE, running = CL_RUNNING, submitted = CL_SUBMITTED, queued = CL_QUEUED }; /// \internal_ enum command_type { ndrange_kernel = CL_COMMAND_NDRANGE_KERNEL, task = CL_COMMAND_TASK, native_kernel = CL_COMMAND_NATIVE_KERNEL, read_buffer = CL_COMMAND_READ_BUFFER, write_buffer = CL_COMMAND_WRITE_BUFFER, copy_buffer = CL_COMMAND_COPY_BUFFER, read_image = CL_COMMAND_READ_IMAGE, write_image = CL_COMMAND_WRITE_IMAGE, copy_image = CL_COMMAND_COPY_IMAGE, copy_image_to_buffer = CL_COMMAND_COPY_IMAGE_TO_BUFFER, copy_buffer_to_image = CL_COMMAND_COPY_BUFFER_TO_IMAGE, map_buffer = CL_COMMAND_MAP_BUFFER, map_image = CL_COMMAND_MAP_IMAGE, unmap_mem_object = CL_COMMAND_UNMAP_MEM_OBJECT, marker = CL_COMMAND_MARKER, aquire_gl_objects = CL_COMMAND_ACQUIRE_GL_OBJECTS, release_gl_object = CL_COMMAND_RELEASE_GL_OBJECTS #if defined(BOOST_COMPUTE_CL_VERSION_1_1) , read_buffer_rect = CL_COMMAND_READ_BUFFER_RECT, write_buffer_rect = CL_COMMAND_WRITE_BUFFER_RECT, copy_buffer_rect = CL_COMMAND_COPY_BUFFER_RECT #endif }; /// \internal_ enum profiling_info { profiling_command_queued = CL_PROFILING_COMMAND_QUEUED, profiling_command_submit = CL_PROFILING_COMMAND_SUBMIT, profiling_command_start = CL_PROFILING_COMMAND_START, profiling_command_end = CL_PROFILING_COMMAND_END }; /// Creates a null event object. event() : m_event(0) { } explicit event(cl_event event, bool retain = true) : m_event(event) { if(m_event && retain){ clRetainEvent(event); } } /// Makes a new event as a copy of \p other. event(const event &other) : m_event(other.m_event) { if(m_event){ clRetainEvent(m_event); } } /// Copies the event object from \p other to \c *this. event& operator=(const event &other) { if(this != &other){ if(m_event){ clReleaseEvent(m_event); } m_event = other.m_event; if(m_event){ clRetainEvent(m_event); } } return *this; } #ifndef BOOST_COMPUTE_NO_RVALUE_REFERENCES /// Move-constructs a new event object from \p other. event(event&& other) BOOST_NOEXCEPT : m_event(other.m_event) { other.m_event = 0; } /// Move-assigns the event from \p other to \c *this. event& operator=(event&& other) BOOST_NOEXCEPT { if(m_event){ clReleaseEvent(m_event); } m_event = other.m_event; other.m_event = 0; return *this; } #endif // BOOST_COMPUTE_NO_RVALUE_REFERENCES /// Destroys the event object. ~event() { if(m_event){ BOOST_COMPUTE_ASSERT_CL_SUCCESS( clReleaseEvent(m_event) ); } } /// Returns a reference to the underlying OpenCL event object. cl_event& get() const { return const_cast(m_event); } /// Returns the status of the event. cl_int status() const { return get_info(CL_EVENT_COMMAND_EXECUTION_STATUS); } /// Returns the command type for the event. cl_command_type get_command_type() const { return get_info(CL_EVENT_COMMAND_TYPE); } /// Returns information about the event. /// /// \see_opencl_ref{clGetEventInfo} template T get_info(cl_event_info info) const { return detail::get_object_info(clGetEventInfo, m_event, info); } /// \overload template typename detail::get_object_info_type::type get_info() const; /// Returns profiling information for the event. /// /// \see event::duration() /// /// \see_opencl_ref{clGetEventProfilingInfo} template T get_profiling_info(cl_profiling_info info) const { return detail::get_object_info(clGetEventProfilingInfo, m_event, info); } /// Blocks until the actions corresponding to the event have /// completed. void wait() const { cl_int ret = clWaitForEvents(1, &m_event); if(ret != CL_SUCCESS){ BOOST_THROW_EXCEPTION(opencl_error(ret)); } } #if defined(BOOST_COMPUTE_CL_VERSION_1_1) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED) /// Registers a function to be called when the event status changes to /// \p status (by default CL_COMPLETE). The callback is passed the OpenCL /// event object, the event status, and a pointer to arbitrary user data. /// /// \see_opencl_ref{clSetEventCallback} /// /// \opencl_version_warning{1,1} void set_callback(void (BOOST_COMPUTE_CL_CALLBACK *callback)( cl_event event, cl_int status, void *user_data ), cl_int status = CL_COMPLETE, void *user_data = 0) { cl_int ret = clSetEventCallback(m_event, status, callback, user_data); if(ret != CL_SUCCESS){ BOOST_THROW_EXCEPTION(opencl_error(ret)); } } /// Registers a generic function to be called when the event status /// changes to \p status (by default \c CL_COMPLETE). /// /// The function specified by \p callback must be invokable with zero /// arguments (e.g. \c callback()). /// /// \opencl_version_warning{1,1} template void set_callback(Function callback, cl_int status = CL_COMPLETE) { set_callback( event_callback_invoker, status, new boost::function(callback) ); } #endif // BOOST_COMPUTE_CL_VERSION_1_1 /// Returns the total duration of the event from \p start to \p end. /// /// For example, to print the number of milliseconds the event took to /// execute: /// \code /// std::cout << event.duration().count() << " ms" << std::endl; /// \endcode /// /// \see event::get_profiling_info() template Duration duration(cl_profiling_info start = CL_PROFILING_COMMAND_START, cl_profiling_info end = CL_PROFILING_COMMAND_END) const { const ulong_ nanoseconds = get_profiling_info(end) - get_profiling_info(start); return detail::make_duration_from_nanoseconds(Duration(), nanoseconds); } /// Returns \c true if the event is the same as \p other. bool operator==(const event &other) const { return m_event == other.m_event; } /// Returns \c true if the event is different from \p other. bool operator!=(const event &other) const { return m_event != other.m_event; } /// \internal_ operator cl_event() const { return m_event; } /// \internal_ (deprecated) cl_int get_status() const { return status(); } private: #ifdef BOOST_COMPUTE_CL_VERSION_1_1 /// \internal_ static void BOOST_COMPUTE_CL_CALLBACK event_callback_invoker(cl_event, cl_int, void *user_data) { boost::function *callback = static_cast *>(user_data); (*callback)(); delete callback; } #endif // BOOST_COMPUTE_CL_VERSION_1_1 protected: cl_event m_event; }; /// \internal_ define get_info() specializations for event BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(event, ((cl_command_queue, CL_EVENT_COMMAND_QUEUE)) ((cl_command_type, CL_EVENT_COMMAND_TYPE)) ((cl_int, CL_EVENT_COMMAND_EXECUTION_STATUS)) ((cl_uint, CL_EVENT_REFERENCE_COUNT)) ) #ifdef BOOST_COMPUTE_CL_VERSION_1_1 BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(event, ((cl_context, CL_EVENT_CONTEXT)) ) #endif } // end compute namespace } // end boost namespace #endif // BOOST_COMPUTE_EVENT_HPP