/** * \brief ocl::Image::copyTo Copies from this Image to the destination Image. * * The operation assumes that all data are valid and no synchronization is necessary (active Queue executes in-order). * The operation forces that all commands within the active Queue including this one are completed. * * \param src_origin is the 3D offset in bytes from which the Image is read. * \param region is the 3D region of the data. It is given with {image_width, image_height, image_depth}. * \param dest is the Image into which the data is going to be copied. * \param dest_origin is the 3D offset in bytes from which the destionation Image is read. */ void ocl::Image::copyTo(size_t *src_origin, const size_t *region, const Image & dest, size_t *dest_origin, const EventList & list ) const { TRUE_ASSERT(this->context() == dest.context(), "Context of this and dest must be equal"); TRUE_ASSERT(this->id() != dest.id(), "Images must not be equal this->id() " << this->id() << "; other.id " << dest.id()); OPENCL_SAFE_CALL( clEnqueueCopyImage(this->activeQueue().id(), this->id(), dest.id(), src_origin, dest_origin, region, list.size(), list.events().data(), NULL) ); OPENCL_SAFE_CALL( clFinish(this->activeQueue().id()) ); }
/*! \brief Erases comments within the string object containing kernel function. * * Note that this is a helper function and that * you do not have to call this function. */ void ocl::Program::eraseComments(std::string &kernels) const { size_t end_pos = 0, pos = 0; while(pos < kernels.length()){ pos = kernels.find("/*", pos,2); end_pos = kernels.find("*/", pos,2); if(pos >= kernels.length()) break; if(end_pos >= kernels.length()) break; TRUE_ASSERT(pos < end_pos, pos << " >= " << end_pos); // cout << "Erasing substring : " << kernels.substr(start_pos, end_pos-start_pos+2) << "-ENDEND" << endl; kernels.erase(pos, end_pos-pos+2); pos += 2; } pos = 0; while(pos < kernels.length()){ pos = kernels.find("//", pos,2); end_pos = kernels.find("\n", pos); std::string s("\n"); if(pos >= kernels.length()) break; if(end_pos >= kernels.length()) break; TRUE_ASSERT(pos < end_pos, pos << " >= " << end_pos); // cout << "Erasing substring : " << kernels.substr(start_pos, end_pos-start_pos) << "-ENDEND" << endl; kernels.erase(pos, end_pos-pos); pos++; } }
/** * \brief ocl::Image::create Creates cl_mem for this Image. * * Note that no Memory is allocated. Allocation takes place when data is transfered. * It is assumed that an active Queue exists. * * \param width Width of the image. * \param height Height of the image. * \param depth Depth of the image. * \param type Channeltype of the image. * \param order Channelorder of the image. */ void ocl::Image::create(size_t width, size_t height, size_t depth, ChannelType type, ChannelOrder order, Access access) { TRUE_ASSERT(this->_context != 0, "Context not valid - cannot create Image"); cl_mem_flags flags = access; cl_image_format format; format.image_channel_order = order; format.image_channel_data_type = type; cl_int status; #if defined(OPENCL_V1_0) || defined(OPENCL_V1_1) this->_id = clCreateImage3D(this->_context->id(), flags, &format, width, height, depth, 0, 0, NULL, &status); #else _cl_image_desc desc; desc.image_type = CL_MEM_OBJECT_IMAGE3D; desc.image_height = height; desc.image_width = width; desc.image_depth = depth; desc.image_array_size = 1; desc.image_row_pitch = 0; desc.image_slice_pitch = 0; desc.num_mip_levels = 0; desc.num_samples = 0; desc.buffer = NULL; this->_id = clCreateImage(this->_context->id(), flags, &format, &desc, NULL, &status); #endif OPENCL_SAFE_CALL(status); TRUE_ASSERT(this->_id != 0, "Could not create 3D image."); }
/*! \brief Sets the Types for the Kernel objects. * * Note that this Progam should not be built. */ void ocl::Program::setTypes(const utl::Types& types) { TRUE_ASSERT(!types.empty(), "Types should not be empty"); TRUE_ASSERT(!this->isBuilt(), "Program already built."); _types = types; }
/** * \brief ocl::Image::write Transfers data from host memory to this Image. * * You can be sure that the data is read. Be sure that the queue * and this Image are in the same context. * \param queue is a command queue on which the command is executed. * \param origin is the 3D offset in bytes from which the Image is read. * \param ptr_to_host_data must point to a memory location whith region bytes available. * \param region is the 3D region of the data. It is given with {image_width, image_height, image_depth}. */ void ocl::Image::write(const Queue& queue, size_t *origin, const void *ptr_to_host_data, const size_t *region, const EventList &list) const { TRUE_ASSERT(ptr_to_host_data != NULL, "data == 0"); TRUE_ASSERT(queue.context() == *this->context(), "Context of queue and this must be equal"); OPENCL_SAFE_CALL( clEnqueueWriteImage(queue.id(), this->id(), CL_TRUE, origin, region, 0, 0, ptr_to_host_data, list.size(), list.events().data(), NULL) ); OPENCL_SAFE_CALL( clFinish(queue.id()) ); }
/*! \brief Copies the Event. * * Note that no deep copy is performed. Both Event Objects * refer to the same OpenCL event. * * \param other Event from which the OpenCL Event and Context is taken from. */ ocl::Event::Event( const Event & other ) : _id(other._id), _ctxt(other._ctxt) { TRUE_ASSERT(_id != 0, "Event not valid (id == 0)"); TRUE_ASSERT(_ctxt != 0, "Event not valid (ctxt == 0)"); OPENCL_SAFE_CALL( clRetainEvent( _id ) ); }
/** * \brief ocl::Image::writeAsync Transfers data from host memory to this Image. * * Waits until the event list is completed. Be sure that the queue * and this Image are in the same context. * \param queue is a command queue on which the command is executed. * \param origin is the 3D offset in bytes from which the Image is read. * \param ptr_to_host_data must point to a memory location whith region bytes available. * \param region is the 3D region of the data. It is given with {image_width, image_height, image_depth}. * \param list contains all events for which this command has to wait. * \return an event which can be further put into an event list for synchronization. */ ocl::Event ocl::Image::writeAsync(const Queue &queue, size_t *origin, const void *ptr_to_host_data, const size_t *region, const EventList &list) const { TRUE_ASSERT(ptr_to_host_data != NULL, "data == 0"); TRUE_ASSERT(queue.context() == *this->context(), "Context of queue and this must be equal"); cl_event event_id; OPENCL_SAFE_CALL( clEnqueueWriteImage(queue.id(), this->id(), CL_FALSE, origin, region, 0, 0, ptr_to_host_data, list.size(), list.events().data(), &event_id) ); return ocl::Event(event_id, this->context()); }
/*! \brief Instantiates an Event returned by an command Queue instruction. * * Do not instantiate user events with this constructor. * * \param id is an OpenCL event id provided by the creating command Queue instruction. * \param ctxt is a valid Context provided which is the same as the command queue Context. */ ocl::Event::Event(cl_event id, ocl::Context* ctxt) : _id(id), _ctxt(ctxt) { TRUE_ASSERT(id != 0, "Event not valid."); TRUE_ASSERT(ctxt != 0, "Context not valid"); cl_context cl_ctxt = 0; OPENCL_SAFE_CALL( clGetEventInfo (this->id(), CL_EVENT_CONTEXT , sizeof(cl_ctxt), &cl_ctxt, NULL)); TRUE_ASSERT(_ctxt->id() == cl_ctxt, "Context must be the same"); }
/*! \brief Instantiates a user Event. * * You can add this event to track a command within a command Queue by adding this * Event into the EventList of the command. This * Event is created using the provided Context. */ ocl::Event::Event(ocl::Context& ctxt) : _id(0), _ctxt(&ctxt) { TRUE_ASSERT(this->_ctxt != 0, "Context not valid"); cl_int err; _id = clCreateUserEvent (_ctxt->id(), &err); OPENCL_SAFE_CALL(err); TRUE_ASSERT(_id != 0, "Could not create user event"); }
/** * \brief ocl::Image::copyToAsync Copies asynchronously from this Image to the destination Image. * * \param queue is a command queue on which the command is executed. * \param src_origin is the 3D offset in bytes from which the Image is read. * \param region is the 3D region of the data. It is given with {image_width, image_height, image_depth}. * \param dest is the Image into which the data is going to be copied. * \param dest_origin is the 3D offset in bytes from which the destionation Image is read. * \param list contains all events for which this command has to wait. * \return event which can be integrated into other EventList. */ ocl::Event ocl::Image::copyToAsync(const Queue &queue, size_t *src_origin, const size_t *region, const Image &dest, size_t *dest_origin, const EventList &list) { TRUE_ASSERT(this->context() == dest.context(), "Context of this and dest must be equal"); TRUE_ASSERT(queue.context() == *this->context(), "Context of queue and this must be equal"); cl_event event_id; OPENCL_SAFE_CALL( clEnqueueCopyImage(queue.id(), this->id(), dest.id(), src_origin, dest_origin, region, list.size(), list.events().data(), &event_id) ); return ocl::Event(event_id, this->context()); }
/** * \brief ocl::Image::copyToAsync Copies asynchronously from this Image to the destination Image. * * \param src_origin is the 3D offset in bytes from which the Image is read. * \param region is the 3D region of the data. It is given with {image_width, image_height, image_depth}. * \param dest is the Image into which the data is going to be copied. * \param dest_origin is the 3D offset in bytes from which the destionation Image is read. * \param list contains all events for which this command has to wait. * \return event which can be integrated into other EventList. */ ocl::Event ocl::Image::copyToAsync(size_t *src_origin, const size_t *region, const Image &dest, size_t *dest_origin, const EventList &list) { TRUE_ASSERT(this->context() == dest.context(), "Context of this and dest must be equal"); TRUE_ASSERT(this->id() != dest.id(), "Images must not be equal this->id() " << this->id() << "; other.id " << dest.id()); cl_event event_id; OPENCL_SAFE_CALL( clEnqueueCopyImage(this->activeQueue().id(), this->id(), dest.id(), src_origin, dest_origin, region, list.size(), list.events().data(), &event_id) ); return ocl::Event(event_id, this->context()); }
/*! \brief Instantiates a user Event. * * You can add this event to track a command within a command Queue by adding this * Event into the EventList of the command. * If there is an active Platform and an active Context * this Event is created. Otherwise do not forget * to provide a Context and to create this Event. */ ocl::Event::Event() : _id(0), _ctxt(0) { if(ocl::Platform::hasActivePlatform() && ocl::Platform::activePlatform()->hasActiveContext()){ _ctxt = ocl::Platform::activePlatform()->activeContext(); TRUE_ASSERT(_ctxt != 0, "No active context"); cl_int err; _id = clCreateUserEvent (_ctxt->id(), &err); OPENCL_SAFE_CALL(err); TRUE_ASSERT(_id != 0, "Could not create user event"); } }
/** * \brief ocl::Image::map Maps the Image into the host memory. * * No data transfer is performed. Note that in order to map data of the Image the active queue must be a cpu and must have been allocated * with the Image access mode AllocHost. You cannot modify the Image with OpenCL until unmap. * \param ptr is returned and contains the address of a pointer to the host memory. * \param origin is the 3D offset in bytes from which the image is read. * \param region is the 3D region in bytes to be mapped. * \param access specifies in what way the host_mem is used. * \param list contains all events for which this command has to wait. * \return event which can be integrated into other EventList */ ocl::Event ocl::Image::mapAsync(void **ptr, size_t *origin, const size_t *region, Memory::Access access, const EventList &list) const { TRUE_ASSERT(this->activeQueue().device().isCpu(), "Device " << this->activeQueue().device().name() << " is not a cpu!"); cl_int status; cl_event event_id; cl_map_flags flags = access; *ptr = clEnqueueMapImage(this->activeQueue().id(), this->id(), CL_TRUE, flags, origin, region, 0, 0, list.size(), list.events().data(), &event_id, &status); OPENCL_SAFE_CALL (status ) ; TRUE_ASSERT(ptr != NULL, "Could not map image!"); return ocl::Event(event_id, this->context()); }
/** * \brief ocl::Image::map Maps the Image into the host memory. * * No data transfer is performed. Note that in order to map data of the Image the active queue must be a cpu and must have been allocated * with the Image access mode AllocHost. You cannot modify the Image with OpenCL until unmap. * \param origin is the 3D offset in bytes from which the image is read. * \param region is the 3D region in bytes to be mapped. * \param access specifies in what way the host_mem is used. * \return a void pointer to the mapped host memory location. */ void * ocl::Image::map(size_t *origin, const size_t *region, Memory::Access access) const { TRUE_ASSERT(this->activeQueue().device().isCpu(), "Device " << this->activeQueue().device().name() << " is not a cpu!"); cl_int status; cl_map_flags flags = access; void *pointer = clEnqueueMapImage(this->activeQueue().id(), this->id(), CL_TRUE, flags, origin, region, 0, 0, 0, NULL, NULL, &status); OPENCL_SAFE_CALL (status ) ; TRUE_ASSERT(pointer != NULL, "Could not map image!"); OPENCL_SAFE_CALL( clFinish(this->activeQueue().id()) ); return pointer; }
/** * \brief ocl::Image::writeAsync Transfers data from host memory to this Image. * * Waits until the event list is completed. * \param origin is the 3D offset in bytes from which the Image is read. * \param ptr_to_host_data must point to a memory location whith region bytes available. * \param region is the 3D region of the data. It is given with {image_width, image_height, image_depth}. * \param list contains all events for which this command has to wait. * \return an event which can be further put into an event list for synchronization. */ ocl::Event ocl::Image::writeAsync(size_t *origin, const void *ptr_to_host_data, const size_t *region, const EventList &list) const { TRUE_ASSERT(ptr_to_host_data != NULL, "data == 0"); cl_event event_id; OPENCL_SAFE_CALL( clEnqueueWriteImage(this->activeQueue().id(), this->id(), CL_FALSE, origin, region, 0, 0, ptr_to_host_data, list.size(), list.events().data(), &event_id) ); return ocl::Event(event_id, this->context()); }
/*! \brief Set the Context of this Program. * * Note that you cannot change the context * once this Program has been built. */ void ocl::Program::setContext(ocl::Context &c) { TRUE_ASSERT(!this->isBuilt(), "Context already built"); _context = &c; _context->insert(this); }
/** * \brief ocl::Image::write Transfers data from host memory to this Image. * * You can be sure that the data is write. * \param ptr_to_host_data must point to a memory location whith region bytes available. * \param region is the 3D region of the data. It is given with {image_width, image_height, image_depth}. */ void ocl::Image::write(const void *ptr_to_host_data, const size_t *region, const EventList &list) const { TRUE_ASSERT(ptr_to_host_data != NULL, "data == 0"); std::vector<size_t> origin = {0, 0, 0}; OPENCL_SAFE_CALL( clEnqueueWriteImage(this->activeQueue().id(), this->id(), CL_TRUE, origin.data(), region, 0, 0, ptr_to_host_data, list.size(), list.events().data(), NULL) ); OPENCL_SAFE_CALL( clFinish(this->activeQueue().id()) ); }
ocl::Event& ocl::Event::operator =( ocl::Event const& other ) { if ( this == &other ) return *this; TRUE_ASSERT(other._id != 0, "Event not valid (id == 0)"); TRUE_ASSERT(other._ctxt != 0, "Event not valid (ctxt == 0)"); release(); _id = other._id; _ctxt = other._ctxt; clRetainEvent( _id ); return *this; }
/*! \brief Returns the reference count of this Event object.*/ size_t ocl::Event::reference_count() const { TRUE_ASSERT(_id != 0, "Cannot get reference count for this. Not yet created."); cl_uint info; OPENCL_SAFE_CALL( clGetEventInfo (_id, CL_EVENT_REFERENCE_COUNT, sizeof(info), &info, NULL)) ; return size_t(info); }
/*! \brief Destroys the Kernel specified by its function name. */ void ocl::Program::deleteKernel(const std::string &name) { iterator it = _kernels.find(name); TRUE_ASSERT(it != _kernels.end(), "Kernel " << name << " does not exist yet"); const Kernel *__k = it->second; delete __k; _kernels.erase(it); }
/*! \brief Reads kernel functions from an input stream into this Program. * * The input stream can contain multiple OpenCL kernel * functions. It deletes all comments and if the kernels * are templated, it substitutes the template parameter * with the provided types. For each Kernel function * a Kernel object is built and stored within a map. * The map stores the name of the kernel function and * the corresponding function. * Note that DEFINES are not supported yet. */ ocl::Program& ocl::Program::operator << (std::istream& stream) { TRUE_ASSERT(!stream.fail(), "Error while opening file."); std::stringstream buffer; stream >> buffer.rdbuf(); return (*this) << buffer.str(); }
/*! \brief Builds this Program. * * Do not forget to load Kernel objects into this * Program before executing this function. * This Program with all Kernel objects are built. Note that * compiling and linking in seperate stages are note supported * yet. Kernels built with this Program * can be executed on all Device objects within the Context * for which this Program is built. */ void ocl::Program::build() { TRUE_ASSERT(this->_context != 0, "Program has no Context"); TRUE_ASSERT(this->_id == 0, "Program already built"); TRUE_ASSERT(!_kernels.empty(), "No kernels loaded for the program"); std::stringstream stream; this->print(stream); const std::string &t = stream.str(); cl_int status; const char * file_char = t.c_str(); // stream.str().c_str(); _id = clCreateProgramWithSource(this->context().id(), 1, (const char**)&file_char, NULL, &status); OPENCL_SAFE_CALL(status); cl_int buildErr = clBuildProgram(_id, 0, NULL, _options().c_str(), NULL, NULL); checkBuild(buildErr); for(auto k : _kernels){ k.second->create(); } }
/** * \brief ocl::Image::create Creates an OpenCL Image from an OpenGL texture. * \param texture OpenGL-ID of the texture. * \param texture_target Target of the OpenGL texture. * \param miplevel MipMapping level. */ void ocl::Image::create(unsigned int texture, unsigned long texture_target, long miplevel) { cl_mem_flags flags = ocl::Image::ReadWrite; if (this->_context->devices().size() == 1 && this->_context->devices().front().type() == ocl::device_type::CPU){ flags |= ocl::Image::AllocHost; } cl_int status; #if defined(OPENCL_V1_0) || defined(OPENCL_V1_1) this->_id = clCreateFromGLTexture2D(this->_context->id(), flags, texture_target, miplevel, texture, &status); #else this->_id = clCreateFromGLTexture(this->_context->id(), flags, texture_target, miplevel, texture, &status); #endif OPENCL_SAFE_CALL(status); TRUE_ASSERT(this->_id != 0, "Could not create shared image."); }
/*! \brief Instantiates this Program for a given Context, predefined Types and CompileOption. * * This Program is not yet created, only initialized with the given Context, Types and CompileOptions. * In order to create it, load Kernel objects into this Program and call the appropriate create function. * Templated Kernel functions are then build for the given Types with the CompileOption. * The Kernel function name will be changed to kernel_<type>. * * \param context The Context for which this Program will be created. * \param types Types which consist of valid Type objects. * \param options defines a valid CompileOption for build process. */ ocl::Program::Program(ocl::Context& ctxt, const utl::Types &types, const ocl::CompileOption &options) : _id(NULL), _context(&ctxt), _types(types), _options(options) { TRUE_ASSERT(!_types.empty(), "no types selected."); _context->insert(this); }
/*! \brief Sets the Types for the Kernel objects. * * Note that this Progam should not be built. */ void ocl::Program::setTypes(utl::Types&& types) { TRUE_ASSERT(!types.empty(), "Types should not be empty"); TRUE_ASSERT(!this->isBuilt(), "Program already built."); _types = std::move(types); }
/*! \brief Sets the CompileOption for this Program. * * Note that this should not be built. */ void ocl::Program::setCompileOption(const ocl::CompileOption & o) { TRUE_ASSERT(!this->isBuilt(), "Program already built."); _options = o; }
/*! \brief Returns the context with which this Event was created.*/ ocl::Context& ocl::Event::context() const { TRUE_ASSERT(this->_ctxt != 0, "No Context for Event"); return *this->_ctxt; }
/*! \brief Returns the Context of this Program. */ ocl::Context& ocl::Program::context() const { TRUE_ASSERT(this->_context != 0, "Context not valid."); return *this->_context; }
/*! \brief Returns the Kernel from this Program by providing the Kernel's function name and its Type.*/ ocl::Kernel& ocl::Program::kernel(const std::string &name, const utl::Type &t) const { TRUE_ASSERT(_types.contains(t), "Type "<< t.name() <<" not found."); std::string n = name; n+= "_"; n+= t.name(); return this->kernel(n); }
/*! \brief Returns the Kernel from this Program by providing the Kernel's function name.*/ ocl::Kernel& ocl::Program::kernel(const std::string &name) const { const_iterator it = _kernels.find(name); TRUE_ASSERT(it != _kernels.end(), "Kernel " << name << " does not exist yet"); return *(it->second); }