platform_ptr platform::create(cl_platform_id platform_id,
                              unsigned start_id) {
  vector<unsigned> device_types = {CL_DEVICE_TYPE_GPU,
                                   CL_DEVICE_TYPE_ACCELERATOR,
                                   CL_DEVICE_TYPE_CPU};
  vector<cl_device_id> ids;
  for (cl_device_type device_type : device_types) {
    auto known = ids.size();
    cl_uint discoverd;
    auto err = clGetDeviceIDs(platform_id, device_type, 0, nullptr, &discoverd);
    if (err == CL_DEVICE_NOT_FOUND) {
      continue; // no devices of the type found
    } else if (err != CL_SUCCESS) {
      throwcl("clGetDeviceIDs", err);
    }
    ids.resize(known + discoverd);
    v2callcl(CAF_CLF(clGetDeviceIDs), platform_id, device_type,
             discoverd, (ids.data() + known));
  }
  vector<detail::raw_device_ptr> devices;
  devices.resize(ids.size());
  auto lift = [](cl_device_id ptr) {
    return detail::raw_device_ptr{ptr, false};
  };
  transform(ids.begin(), ids.end(), devices.begin(), lift);
  detail::raw_context_ptr context;
  context.reset(v2get(CAF_CLF(clCreateContext), nullptr,
                      static_cast<unsigned>(ids.size()),
                      ids.data(), pfn_notify, nullptr),
                false);
  vector<device_ptr> device_information;
  for (auto& device_id : devices) {
    device_information.push_back(device::create(context, device_id,
                                                start_id++));
  }
  if (device_information.empty())
    CAF_RAISE_ERROR("no devices for the platform found");
  auto name = platform_info(platform_id, CL_PLATFORM_NAME);
  auto vendor = platform_info(platform_id, CL_PLATFORM_VENDOR);
  auto version = platform_info(platform_id, CL_PLATFORM_VERSION);
  return make_counted<platform>(platform_id, move(context), move(name),
                                move(vendor), move(version),
                                move(device_information));
}
void opencl_metainfo::initialize() {
  // get number of available platforms
  auto num_platforms = v1get<cl_uint>(CAF_CLF(clGetPlatformIDs));
  // get platform ids
  std::vector<cl_platform_id> platforms(num_platforms);
  v2callcl(CAF_CLF(clGetPlatformIDs), num_platforms, platforms.data());
  if (platforms.empty()) {
    throw std::runtime_error("no OpenCL platform found");
  }
  // support multiple platforms -> "for (auto platform : platforms)"?
  auto platform = platforms.front();
  // detect how many devices we got
  cl_uint num_devs = 0;
  cl_device_type dev_type = CL_DEVICE_TYPE_GPU;
  // try get some GPU devices and try falling back to CPU devices on error
  try {
    num_devs = v1get<cl_uint>(CAF_CLF(clGetDeviceIDs), platform, dev_type);
  }
  catch (std::runtime_error&) {
    dev_type = CL_DEVICE_TYPE_CPU;
    num_devs = v1get<cl_uint>(CAF_CLF(clGetDeviceIDs), platform, dev_type);
  }
  // get available devices
  std::vector<cl_device_id> ds(num_devs);
  v2callcl(CAF_CLF(clGetDeviceIDs), platform, dev_type, num_devs, ds.data());
  std::vector<device_ptr> devices(num_devs);
  // lift raw pointer as returned by OpenCL to C++ smart pointers
  auto lift = [](cl_device_id ptr) { return device_ptr{ptr, false}; };
  std::transform(ds.begin(), ds.end(), devices.begin(), lift);
  // create a context
  context_.reset(v2get(CAF_CLF(clCreateContext), nullptr, num_devs, ds.data(),
                        pfn_notify, nullptr),
                  false);
  for (auto& device : devices) {
    CAF_LOG_DEBUG("creating command queue for device(s)");
    command_queue_ptr cmd_queue;
    try {
      cmd_queue.reset(v2get(CAF_CLF(clCreateCommandQueue),
                            context_.get(), device.get(),
                            unsigned{CL_QUEUE_PROFILING_ENABLE}),
                      false);
    }
    catch (std::runtime_error&) {
      CAF_LOG_DEBUG("unable to create command queue for device");
    }
    if (cmd_queue) {
      auto max_wgs = v3get<size_t>(CAF_CLF(clGetDeviceInfo), device.get(),
                      unsigned{CL_DEVICE_MAX_WORK_GROUP_SIZE});
      auto max_wid = v3get<cl_uint>(CAF_CLF(clGetDeviceInfo), device.get(),
                      unsigned{CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS});
      dim_vec max_wi_per_dim(max_wid);
      v2callcl(CAF_CLF(clGetDeviceInfo), device.get(),
             unsigned{CL_DEVICE_MAX_WORK_ITEM_SIZES},
             sizeof(size_t) * max_wid,
             max_wi_per_dim.data());
      devices_.push_back(device_info{std::move(device), std::move(cmd_queue),
                                      max_wgs, max_wid, max_wi_per_dim});
    }
  }
  if (devices_.empty()) {
    std::string errstr = "could not create a command queue for any device";
    CAF_LOG_ERROR(errstr);
    throw std::runtime_error(std::move(errstr));
  }
}