blob: 1155d3d462a366ae7f4c999e51ade42a0ec747c6 [file] [log] [blame]
//TODO: copyrights
#ifndef __COMMANDQUEUE_H__
#define __COMMANDQUEUE_H__
#include "object.h"
#include "opencl.h"
#include "Resource.hpp"
#include <mutex>
#include <map>
#include <list>
namespace Devices
{
class Context;
class DeviceInterface;
class Event;
/**
* \brief Command queue
*
* This class holds a list of events that will be pushed on a given device.
*
* More details are given on the \ref events page.
*/
class CommandQueue : public Object
{
public:
CommandQueue(Context *ctx,
DeviceInterface *device,
cl_command_queue_properties properties,
cl_int *errcode_ret);
~CommandQueue();
/**
* \brief Queue an event
* \param event event to be queued
* \return \c CL_SUCCESS if success, otherwise an error code
*/
cl_int queueEvent(Event *event);
/**
* \brief Information about the command queue
* \copydetails Coal::DeviceInterface::info
*/
cl_int info(cl_command_queue_info param_name,
size_t param_value_size,
void *param_value,
size_t *param_value_size_ret) const;
/**
* \brief Set properties of the command queue
* \note This function is deprecated and only there for OpenCL 1.0
* compatibility
* \param properties property to enable or disable
* \param enable true to enable the property, false to disable it
* \param old_properties old value of the properties, ignored if NULL
* \return \c CL_SUCCESS if all is good, an error code if \p properties is
* invalid
*/
cl_int setProperty(cl_command_queue_properties properties,
cl_bool enable,
cl_command_queue_properties *old_properties);
/**
* \brief Check the properties given
* \return \c CL_SUCCESS if they are valid, an error code otherwise
*/
cl_int checkProperties() const;
/**
* \brief Push events on the device
*
* This function implements a big part of what is described in
* \ref events .
*
* It is called by \c Coal::Event::setStatus() when an event is
* completed, or by \c queueEvent(). Its purpose is to explore the list
* of queued events (\c p_events) and to call
* \c Coal::DeviceInterface::pushEvent() for each event meeting its push
* conditions.
*
* \section conditions Conditions
*
* If the command queue has the \c CL_OUT_OF_ORDER_EXEC_MODE_ENABLE
* property disabled, an event can be pushed only if all the previous
* ones in the list are completed with success. This way, an event
* must be completed before any other can be pushed. This ensures
* in-order execution.
*
* If this property is enable, more complex heuristics are used.
*
* The event list \c p_events is explored from top to bottom. At each
* loop iteration, checks are performed to see if the event can be pushed.
*
* - When a \c Coal::BarrierEvent is encountered, no more events can be
* pushed, except if the \c Coal::BarrierEvent is the first in the list,
* as that means there are no other events that can be pushed, so the
* barrier can go away
* - All events that are already pushed or finished are skipped
* - The wait list of the event is then explored to ensure that all its
* dependencies are met.
* - Finally, if the events passes all the tests, it is either pushed on
* the device, or simply set to \c Coal::Event::Complete if it's a
* dummy event (see \c Coal::Event::isDummy()).
*/
void pushEventsOnDevice();
/**
* \brief Remove from the event list completed events
*
* This function is called periodically to clean the event list from
* completed events.
*
* It is needed to do that out of \c pushEventsOnDevice() as deleting
* event may \c dereference() this command queue, and also delete it. It
* would produce crashes.
*/
void cleanEvents();
/**
* \brief Flush the command queue
*
* Pushes all the events on the device, and then return. The event
* don't need to be completed after this call.
*/
void flush();
/**
* \brief Finish the command queue
*
* Pushes the events like \c flush() but also wait for them to be
* completed before returning.
*/
void finish();
/**
* \brief Return all the events in the command queue
* \note Retains all the events
* \param count number of events in the event queue
* \return events currently in the event queue
*/
Event **events(unsigned int &count);
private:
DeviceInterface *p_device;
cl_command_queue_properties p_properties;
std::list<Event *> p_events;
std::mutex p_event_list_mutex;
sw::Event *p_event_list_cond;
bool p_flushed;
};
/**
* \brief Base class for all events
*
* This class contains logic common to all the events.
*
* Beside handling OpenCL-specific stuff, \c Coal::Event objects do nothing
* implementation-wise. They do not compile kernels, copy data around, etc.
* They only contain static and immutable data that is then used by the devices
* to actually implement the event.
*/
class Event : public Object
{
public:
/**
* \brief Event type
*
* The allows objects using \c Coal::Event to know which event it is,
* and to cast it to the correct sub-class.
*/
enum Type
{
NDRangeKernel = CL_COMMAND_NDRANGE_KERNEL,
TaskKernel = CL_COMMAND_TASK,
NativeKernel = CL_COMMAND_NATIVE_KERNEL,
ReadBuffer = CL_COMMAND_READ_BUFFER,
WriteBuffer = CL_COMMAND_WRITE_BUFFER,
CopyBuffer = CL_COMMAND_COPY_BUFFER,
ReadImage = CL_COMMAND_READ_IMAGE,
WriteImage = CL_COMMAND_WRITE_IMAGE,
CopyImage = CL_COMMAND_COPY_IMAGE,
CopyImageToBuffer = CL_COMMAND_COPY_IMAGE_TO_BUFFER,
CopyBufferToImage = CL_COMMAND_COPY_BUFFER_TO_IMAGE,
MapBuffer = CL_COMMAND_MAP_BUFFER,
MapImage = CL_COMMAND_MAP_IMAGE,
UnmapMemObject = CL_COMMAND_UNMAP_MEM_OBJECT,
Marker = CL_COMMAND_MARKER,
AcquireGLObjects = CL_COMMAND_ACQUIRE_GL_OBJECTS,
ReleaseGLObjects = CL_COMMAND_RELEASE_GL_OBJECTS,
ReadBufferRect = CL_COMMAND_READ_BUFFER_RECT,
WriteBufferRect = CL_COMMAND_WRITE_BUFFER_RECT,
CopyBufferRect = CL_COMMAND_COPY_BUFFER_RECT,
User = CL_COMMAND_USER,
Barrier,
WaitForEvents
};
/**
* \brief Event status
*/
enum Status
{
Queued = CL_QUEUED, /*!< \brief Simply queued in a command queue */
Submitted = CL_SUBMITTED, /*!< \brief Submitted to a device */
Running = CL_RUNNING, /*!< \brief Running on the device */
Complete = CL_COMPLETE /*!< \brief Completed */
};
/**
* \brief Function that can be called when an event change status
*/
typedef void (CL_CALLBACK *event_callback)(cl_event, cl_int, void *);
/**
* Structure used internally by \c Coal::Event to store for each event
* status the callbacks to call with the corresponding \c user_data.
*/
struct CallbackData
{
event_callback callback; /*!< Function to call */
void *user_data; /*!< Pointer to pass as its third argument */
};
/**
* \brief Timing counters of an event
*/
enum Timing
{
Queue, /*!< Time when the event was queued */
Submit, /*!< Time when the event was submitted to the device */
Start, /*!< Time when its execution began on the device */
End, /*!< Time when its execution finished */
Max /*!< Number of items in this enum */
};
public:
/**
* \brief Constructor
* \param parent parent \c Coal::CommandQueue
* \param status \c Status the event has when it is created
* \param num_events_in_wait_list number of events to wait on
* \param event_wait_list list of events to wait on
* \param errcode_ret return value
*/
Event(CommandQueue *parent,
Status status,
cl_uint num_events_in_wait_list,
const Event **event_wait_list,
cl_int *errcode_ret);
void freeDeviceData(); /*!< \brief Call \c Coal::DeviceInterface::freeEventDeviceData() */
virtual ~Event(); /*!< \brief Destructor */
/**
* \brief Type of the event
* \return type of the event
*/
virtual Type type() const = 0;
/**
* \brief Dummy event
*
* A dummy event is an event that doesn't have to be pushed on a device,
* it is only a hint for \c Coal::CommandQueue
*
* \return true if the event is dummy
*/
bool isDummy() const;
/**
* \brief Set the event status
*
* This function calls the event callbacks, and
* \c Coal::CommandQueue::pushEventsOnDevice() if \p status is
* \c Complete .
*
* \param status new status of the event
*/
void setStatus(Status status);
/**
* \brief Set device-specific data
* \param data device-specific data
*/
void setDeviceData(void *data);
/**
* \brief Update timing info
*
* This function reads current system time and puts it in \c p_timing
*
* \param timing timing event having just finished
*/
void updateTiming(Timing timing);
/**
* \brief Status
* \return status of the event
*/
Status status() const;
/**
* \brief Wait for a specified status
*
* This function blocks until the event's status is set to \p status
* by another thread.
*
* \param status the status the event must have for the function to return
*/
void waitForStatus(Status status);
/**
* \brief Device-specific data
* \return data set using \c setDeviceData()
*/
void *deviceData();
/**
* \brief List of events on which this event depends on
* \param count number of events in the list
* \return list of the events
* \warning the data is not copied, it's a simple pointer to internal data
*/
const Event **waitEvents(cl_uint &count) const;
/**
* \brief Add a callback for this event
* \param command_exec_callback_type status the event must have in order
* to have the callback called
* \param callback callback function
* \param user_data user data given to the callback
*/
void setCallback(cl_int command_exec_callback_type,
event_callback callback,
void *user_data);
/**
* \brief Info about the event
* \copydetails Coal::DeviceInterface::info
*/
cl_int info(cl_event_info param_name,
size_t param_value_size,
void *param_value,
size_t *param_value_size_ret) const;
/**
* \brief Profiling info
* \copydetails Coal::DeviceInterface::info
*/
cl_int profilingInfo(cl_profiling_info param_name,
size_t param_value_size,
void *param_value,
size_t *param_value_size_ret) const;
private:
cl_uint p_num_events_in_wait_list;
const Event **p_event_wait_list;
sw::Event *p_state_change_cond;
std::mutex p_state_mutex;
Status p_status;
void *p_device_data;
std::multimap<Status, CallbackData> p_callbacks;
cl_uint p_timing[Max];
};
}
struct _cl_command_queue : public Devices::CommandQueue
{};
struct _cl_event : public Devices::Event
{};
#endif