0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-15 22:41:12 +01:00
construct/include/ircd/cl.h

220 lines
5.2 KiB
C++

// The Construct
//
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2021 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_CL_H
/// OpenCL Interface
namespace ircd::cl
{
IRCD_EXCEPTION(ircd::error, error)
IRCD_EXCEPTION(error, opencl_error)
struct init;
struct exec;
struct kern;
struct code;
struct data;
struct work;
extern const info::versions version_api;
extern info::versions version_abi;
extern conf::item<bool> enable;
extern log::log log;
string_view reflect_error(const int code) noexcept;
void flush();
void sync();
}
/// cl_event wrapping
struct ircd::cl::work
:instance_list<cl::work>
{
void *handle {nullptr};
ctx::ctx *context {ctx::current};
static void init(), fini() noexcept;
public:
std::array<uint64_t, 4> profile() const;
work(void *const &handle); // note: RetainEvent()
work() = default;
work(work &&) noexcept;
work(const work &) = delete;
work &operator=(work &&) noexcept;
work &operator=(const work &) = delete;
~work() noexcept;
};
/// cl_mem wrapping
struct ircd::cl::data
{
void *handle {nullptr};
public:
data(const size_t, const mutable_buffer &, const bool wonly = false); // device rw
data(const size_t, const const_buffer &); // device ro
data(const mutable_buffer &, const bool wonly = false); // host rw
data(const const_buffer &); // host ro
data(const data &) = delete;
data() = default;
data(data &&) noexcept;
data &operator=(const data &) = delete;
data &operator=(data &&) noexcept;
~data() noexcept;
};
/// cl_program wrapping
struct ircd::cl::code
{
void *handle {nullptr};
public:
void build(const string_view &opts = {});
code(const vector_view<const string_view> &srcs, const string_view &opts = {});
code(const string_view &src, const string_view &opts = {});
code() = default;
code(code &&) noexcept;
code &operator=(const code &) = delete;
code &operator=(code &&) noexcept;
~code() noexcept;
};
/// cl_kernel wrapping
struct ircd::cl::kern
{
struct range;
void *handle {nullptr};
public:
void arg(const int, data &);
template<class... argv> kern(code &, const string_view &name, argv&&...);
kern(code &, const string_view &name);
kern() = default;
kern(kern &&) noexcept;
kern &operator=(const kern &) = delete;
kern &operator=(kern &&) noexcept;
~kern() noexcept;
};
/// NDRangeKernel dimension range selector
struct ircd::cl::kern::range
{
std::array<size_t, 5>
offset { 0, 0, 0, 0, 0 },
global { 0, 0, 0, 0, 0 },
local { 0, 0, 0, 0, 0 };
};
/// Construction enqueues the task; destruction waits for completion.
///
/// clEnqueue* construction with resulting cl_event wrapping. Instances
/// represent the full lifecycle of work creation, submission and completion.
///
/// Our interface is tied directly to ircd::ctx for intuitive control flow and
/// interaction with the device. By default, all constructions are dependent
/// on the last construction made on the same ircd::ctx, providing sequential
/// consistency for each ircd::ctx, and independence between different ctxs.
/// Each instance destructs only when complete, otherwise the ircd::ctx will
/// block in the destructor.
struct ircd::cl::exec
:work
{
struct opts;
static const opts opts_default;
// Read data from the device into buffer.
exec(data &, const mutable_buffer &, const opts & = opts_default);
// Write data to the device from buffer.
exec(data &, const const_buffer &, const opts & = opts_default);
// Execute a kernel on a range.
exec(kern &, const kern::range &, const opts & = opts_default);
// Execute a barrier.
exec(const opts &);
};
/// Options for an exec.
struct ircd::cl::exec::opts
{
/// Specify a list of dependencies. When provided, this list overrides the
/// default sequential behavior; thus can be used to start new dependency
/// chains for some task concurrency on the same ircd::ctx. Providing a
/// single reference to the last exec on the same stack is equivalent to
/// the default.
vector_view<cl::exec> deps;
/// For operations that have an optional blocking behavior;
/// otherwise ignored.
bool blocking {false};
};
struct ircd::cl::init
{
init();
~init() noexcept;
};
#ifndef IRCD_USE_OPENCL
inline ircd::cl::init::init() {}
inline ircd::cl::init::~init() noexcept {}
#endif
inline
ircd::cl::work::work(work &&other)
noexcept
:handle{std::move(other.handle)}
,context{std::move(other.context)}
{
other.handle = nullptr;
other.context = nullptr;
}
inline ircd::cl::work &
ircd::cl::work::operator=(work &&other)
noexcept
{
this->~work();
handle = std::move(other.handle);
context = std::move(other.context);
other.handle = nullptr;
other.context = nullptr;
return *this;
}
template<class... argv>
inline
ircd::cl::kern::kern(code &c,
const string_view &name,
argv&&... a)
:kern{c, name}
{
constexpr uint argc
{
sizeof...(a)
};
data *const datas[argc]
{
std::addressof(a)...
};
for(uint i(0); i < argc; ++i)
this->arg(i, *datas[i]);
}