blob: 0d69f2d1798a58b4c58bbb321b2a8805ebaf923c [file] [edit]
// Copyright 2018 The clvk authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <array>
#include <memory>
#include "config.hpp"
#include "event.hpp"
#include "init.hpp"
#include "kernel.hpp"
#include "objects.hpp"
#include "printf.hpp"
#include "queue_controller.hpp"
#include "tracing.hpp"
struct cvk_command;
struct cvk_command_queue;
struct cvk_command_batch;
struct cvk_queue_controller;
using cvk_command_queue_holder = refcounted_holder<cvk_command_queue>;
struct cvk_command_group {
std::deque<cvk_command*> commands;
cl_int execute_cmds();
void execute_cmds_in_executor();
};
struct cvk_executor_thread {
cvk_executor_thread()
: m_thread(nullptr), m_shutdown(false), m_running(false) {
m_thread =
std::make_unique<std::thread>(&cvk_executor_thread::executor, this);
}
void send_group(std::unique_ptr<cvk_command_group>&& group) {
m_lock.lock();
m_groups.push_back(std::move(group));
m_cv.notify_one();
m_running = true;
m_lock.unlock();
}
bool is_idle() {
std::unique_lock<std::mutex> lock(m_lock);
return !m_running;
}
void shutdown() {
// Tell the executor to shutdown
m_lock.lock();
m_shutdown = true;
m_cv.notify_one();
m_lock.unlock();
// Wait for executor to shutdown
if (m_thread != nullptr) {
m_thread->join();
}
}
cvk_command_group extract_cmds_required_by(bool only_non_batch_cmds,
cl_uint num_events,
_cl_event* const* event_list);
private:
void executor();
std::mutex m_lock;
std::condition_variable m_cv;
std::unique_ptr<std::thread> m_thread;
bool m_shutdown;
std::deque<std::unique_ptr<cvk_command_group>> m_groups;
bool m_running;
};
struct cvk_command_pool {
cvk_command_pool(cvk_device* device, uint32_t queue_family)
: m_device(device), m_queue_family(queue_family),
m_command_pool(VK_NULL_HANDLE) {}
~cvk_command_pool() {
if (m_command_pool != VK_NULL_HANDLE) {
vkDestroyCommandPool(m_device->vulkan_device(), m_command_pool,
nullptr);
}
}
CHECK_RETURN VkResult init() {
VkCommandPoolCreateFlags flags = 0;
if (m_device->is_driver_behavior_enabled(
cvk_device::use_reset_command_buffer_bit)) {
flags |= VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
}
// Create command pool
VkCommandPoolCreateInfo createInfo = {
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, nullptr, flags,
m_queue_family};
return vkCreateCommandPool(m_device->vulkan_device(), &createInfo,
nullptr, &m_command_pool);
}
VkResult allocate_command_buffer(VkCommandBuffer* buf);
void free_command_buffer(VkCommandBuffer buf);
void lock() { m_lock.lock(); }
void unlock() { m_lock.unlock(); }
private:
cvk_device* m_device;
uint32_t m_queue_family;
VkCommandPool m_command_pool;
std::mutex m_lock;
};
struct cvk_command_queue : public _cl_command_queue,
api_object<object_magic::command_queue> {
cvk_command_queue(cvk_context* ctx, cvk_device* dev,
cl_command_queue_properties props,
std::vector<cl_queue_properties>&& properties_array);
CHECK_RETURN cl_int init();
virtual ~cvk_command_queue();
bool has_property(cl_command_queue_properties prop) const {
return (m_properties & prop) == prop;
}
CHECK_RETURN cl_int enqueue_command_with_deps(cvk_command* cmd,
cl_uint num_dep_events,
_cl_event* const* dep_events,
_cl_event** event);
CHECK_RETURN cl_int enqueue_command_with_deps(cvk_command* cmd,
bool blocking,
cl_uint num_dep_events,
_cl_event* const* dep_events,
_cl_event** event);
CHECK_RETURN static cl_int wait_for_events(cl_uint num_events,
const cl_event* event_list);
CHECK_RETURN cl_int flush_no_lock();
CHECK_RETURN cl_int flush();
CHECK_RETURN cl_int finish();
bool profiling_on_device() const {
return m_device->has_timer_support() ||
config.queue_profiling_use_timestamp_queries;
}
CHECK_RETURN bool allocate_command_buffer(VkCommandBuffer* cmdbuf) {
return m_command_pool.allocate_command_buffer(cmdbuf) == VK_SUCCESS;
}
void free_command_buffer(VkCommandBuffer cmdbuf) {
return m_command_pool.free_command_buffer(cmdbuf);
}
cvk_buffer* get_or_create_printf_buffer() {
CVK_ASSERT(m_context != nullptr);
if (!m_printf_buffer) {
cl_int status;
m_printf_buffer = cvk_buffer::create(
context(), 0, m_context->get_printf_buffersize(), nullptr,
&status);
CVK_ASSERT(status == CL_SUCCESS);
}
return m_printf_buffer.get();
}
cvk_buffer* get_printf_buffer() {
if (!m_printf_buffer) {
return nullptr;
}
return m_printf_buffer.get();
}
cl_int reset_printf_buffer() {
if (m_printf_buffer && m_printf_buffer->map_write_only()) {
memset(m_printf_buffer->host_va(), 0, 4);
m_printf_buffer->unmap_to_write(0, 4);
return CL_SUCCESS;
}
cvk_error_fn("Could not reset printf buffer");
return CL_OUT_OF_RESOURCES;
}
void command_pool_lock() { m_command_pool.lock(); }
void command_pool_unlock() { m_command_pool.unlock(); }
cvk_vulkan_queue_wrapper& vulkan_queue() { return m_vulkan_queue; }
cvk_device* device() const { return m_device; }
cl_command_queue_properties properties() const { return m_properties; }
const std::vector<cl_queue_properties>& properties_array() const {
return m_properties_array;
}
void batch_enqueued() {
uint64_t batches = m_nb_batch_in_flight.fetch_add(1);
TRACE_CNT(batch_in_flight_counter, batches + 1);
}
void batch_completed() {
uint64_t batches = m_nb_batch_in_flight.fetch_sub(1);
TRACE_CNT(batch_in_flight_counter, batches - 1);
}
void group_sent() {
uint64_t group = m_nb_group_in_flight.fetch_add(1);
TRACE_CNT(group_in_flight_counter, group + 1);
}
void group_completed() {
uint64_t group = m_nb_group_in_flight.fetch_sub(1);
TRACE_CNT(group_in_flight_counter, group - 1);
}
cl_int execute_cmds_required_by(cl_uint num_events,
_cl_event* const* event_list);
cl_int execute_cmds_required_by_no_lock(cl_uint num_events,
_cl_event* const* event_list,
std::unique_lock<std::mutex>& lock);
void detach_from_context();
TRACE_TRACK_FCT(queue_track,
"clvk-queue_" + std::to_string((uintptr_t)this))
private:
CHECK_RETURN cl_int satisfy_data_dependencies(cvk_command* cmd);
void enqueue_command(cvk_command* cmd);
CHECK_RETURN cl_int enqueue_command_with_retry(cvk_command*,
_cl_event** event);
CHECK_RETURN cl_int enqueue_command(cvk_command* cmd, _cl_event** event);
CHECK_RETURN cl_int end_current_command_batch(bool from_flush = false);
void executor();
cvk_device* m_device;
cl_command_queue_properties m_properties;
std::vector<cl_queue_properties> m_properties_array;
cvk_executor_thread* m_executor;
cvk_event_holder m_finish_event;
std::mutex m_lock;
std::deque<std::unique_ptr<cvk_command_group>> m_groups;
cvk_command_batch* m_command_batch;
cvk_vulkan_queue_wrapper& m_vulkan_queue;
cvk_command_pool m_command_pool;
cl_uint m_max_cmd_batch_size;
cl_uint m_max_first_cmd_batch_size;
cl_uint m_max_cmd_group_size;
cl_uint m_max_first_cmd_group_size;
std::atomic<uint64_t> m_nb_batch_in_flight;
std::atomic<uint64_t> m_nb_group_in_flight;
TRACE_CNT_VAR(batch_in_flight_counter);
TRACE_CNT_VAR(group_in_flight_counter);
TRACE_TRACK_VAR(queue_track);
std::unique_ptr<cvk_buffer> m_printf_buffer;
std::vector<std::unique_ptr<cvk_queue_controller>> m_controllers;
friend struct cvk_queue_controller;
friend struct cvk_queue_controller_batch_parameters;
};
static inline cvk_command_queue* icd_downcast(cl_command_queue queue) {
return static_cast<cvk_command_queue*>(queue);
}
struct cvk_command_pool_lock_holder {
cvk_command_pool_lock_holder(cvk_command_queue* queue) : m_queue(queue) {
m_queue->command_pool_lock();
}
~cvk_command_pool_lock_holder() { m_queue->command_pool_unlock(); }
private:
cvk_command_queue* m_queue;
};
struct cvk_executor_thread_pool {
~cvk_executor_thread_pool() {
// Shutdown all executors
for (auto& exec_state : m_executors) {
auto exec = exec_state.first;
exec->shutdown();
delete exec;
}
}
cvk_executor_thread* get_executor() {
std::unique_lock<std::mutex> lock(m_lock);
// Try to find a free executor
for (auto& exec_state : m_executors) {
if (exec_state.second == executor_state::free) {
exec_state.second = executor_state::bound;
auto exec = exec_state.first;
if (!exec->is_idle()) {
continue;
}
return exec;
}
}
// No free executor found in the pool, create a new one
cvk_executor_thread* exec = new cvk_executor_thread();
m_executors[exec] = executor_state::bound;
return exec;
}
void return_executor(cvk_executor_thread* exec) {
// No need to drain all commands here.
// We will make sure the thread is idle before giving it to another
// queue.
//
// Also trying to wait_idle here can produce a deadlock.
// When a queue is released before the execution of all commands, the
// queue will be destroyed in `cvk_executor_thread::executor` when
// releasing the holder on the queue. But at that point, the executor
// will have the lock. When calling wait_idle here, we will never be
// able to take the lock, generating the deadlock.
std::unique_lock<std::mutex> lock(m_lock);
m_executors[exec] = executor_state::free;
}
private:
enum class executor_state
{
free,
bound,
};
std::mutex m_lock;
std::unordered_map<cvk_executor_thread*, executor_state> m_executors;
};
struct cvk_command_buffer {
cvk_command_buffer(cvk_command_queue* queue)
: m_queue(queue), m_command_buffer(VK_NULL_HANDLE) {}
~cvk_command_buffer() {
if (m_command_buffer != VK_NULL_HANDLE) {
m_queue->free_command_buffer(m_command_buffer);
}
}
CHECK_RETURN bool begin();
CHECK_RETURN bool end() {
auto res = vkEndCommandBuffer(m_command_buffer);
return res == VK_SUCCESS;
}
CHECK_RETURN bool submit_and_wait();
operator VkCommandBuffer() { return m_command_buffer; }
protected:
cvk_command_queue_holder m_queue;
VkCommandBuffer m_command_buffer;
};
#define CLVK_COMMAND_BATCH 0x5000
#define CLVK_COMMAND_IMAGE_INIT 0x5001
struct cvk_command {
cvk_command(cl_command_type type, cvk_command_queue* queue)
: m_type(type), m_queue(queue),
m_event(new cvk_event_command(m_queue->context(), this, queue)) {}
virtual ~cvk_command() { m_event->release(); }
void set_dependencies(cl_uint num_event_deps,
_cl_event* const* event_deps) {
CVK_ASSERT(m_event_deps.size() == 0);
for (cl_uint i = 0; i < num_event_deps; i++) {
auto evt = icd_downcast(event_deps[i]);
evt->retain();
cvk_debug_fn("adding dep on event %p", evt);
m_event_deps.push_back(evt);
}
}
void set_dependencies(const std::vector<cvk_event*>& deps) {
CVK_ASSERT(m_event_deps.size() == 0);
m_event_deps = deps;
}
virtual bool can_be_batched() const { return false; }
virtual bool is_built_before_enqueue() const { return true; }
// Data movement commands are only created as part of the centralised
// memory object asynchronous data consistency management code when
// another non-data movement command has reported the memory object. They
// never have data movement requirements of their own.
virtual bool is_data_movement() const { return false; }
void add_dependency(cvk_event* dep) {
dep->retain();
m_event_deps.push_back(dep);
}
CHECK_RETURN cl_int execute() {
// First wait for dependencies
cl_int status = CL_COMPLETE;
for (auto& ev : m_event_deps) {
if (ev->wait() != CL_COMPLETE) {
status = CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST;
}
ev->release();
}
// Then execute the action if no dependencies failed
if (status != CL_COMPLETE) {
cvk_error_fn("one or more dependencies have failed for cmd %p (%s)",
this, cl_command_type_to_string(m_type));
} else {
set_event_status(CL_RUNNING);
TRACE_BEGIN_CMD(m_type, "queue", (uintptr_t) & (*m_queue),
"command", (uintptr_t)this);
status = do_action();
TRACE_END();
}
// When executing batch with many commands, "set_event_status" can take
// a while. Trace it to be able to understand it easily.
TRACE_BEGIN("set_event_status");
set_event_status(status);
TRACE_END();
return status;
}
cvk_event_command* event() const { return m_event; }
cl_command_type type() const { return m_type; }
cvk_command_queue* queue() const { return m_queue; }
const std::vector<cvk_event*>& dependencies() const { return m_event_deps; }
virtual const std::vector<cvk_mem*> memory_objects() const {
CVK_ASSERT(false && "Should never be called");
return {};
}
virtual void set_event_status(cl_int status) {
m_event->set_status(status);
}
CHECK_RETURN virtual cl_int set_profiling_info(cl_profiling_info pinfo) {
m_event->set_profiling_info_from_monotonic_clock(pinfo);
return CL_SUCCESS;
}
protected:
cl_command_type m_type;
cvk_command_queue_holder m_queue;
cvk_event_command* m_event;
private:
CHECK_RETURN virtual cl_int do_action() = 0;
std::vector<cvk_event*> m_event_deps;
};
struct cvk_command_buffer_base : public cvk_command {
cvk_command_buffer_base(cvk_command_queue* queue, cl_command_type type,
cvk_buffer* buffer)
: cvk_command(type, queue), m_buffer(buffer) {}
protected:
cvk_buffer_holder m_buffer;
};
struct cvk_command_buffer_base_region : public cvk_command_buffer_base {
cvk_command_buffer_base_region(cvk_command_queue* queue,
cl_command_type type, cvk_buffer* buffer,
size_t offset, size_t size)
: cvk_command_buffer_base(queue, type, buffer), m_offset(offset),
m_size(size) {}
protected:
size_t m_offset;
size_t m_size;
};
struct cvk_command_buffer_host_copy final
: public cvk_command_buffer_base_region {
cvk_command_buffer_host_copy(cvk_command_queue* q, cl_command_type type,
cvk_buffer* buffer, const void* ptr,
size_t offset, size_t size)
: cvk_command_buffer_base_region(q, type, buffer, offset, size),
m_ptr(const_cast<void*>(ptr)) {}
const std::vector<cvk_mem*> memory_objects() const override {
return {m_buffer};
}
private:
CHECK_RETURN cl_int do_action() override final;
void* m_ptr;
};
struct cvk_rectangle_copier {
cvk_rectangle_copier(const size_t* a_origin, const size_t* b_origin,
const size_t* region, size_t a_row_pitch,
size_t a_slice_pitch, size_t b_row_pitch,
size_t b_slice_pitch, size_t elem_size)
: m_elem_size(elem_size) {
m_a_row_pitch = initialize_pitch(a_row_pitch, region[0]);
m_b_row_pitch = initialize_pitch(b_row_pitch, region[0]);
m_a_slice_pitch =
initialize_pitch(a_slice_pitch, m_a_row_pitch * region[1]);
m_b_slice_pitch =
initialize_pitch(b_slice_pitch, m_b_row_pitch * region[1]);
m_a_origin[0] = a_origin[0];
m_a_origin[1] = a_origin[1];
m_a_origin[2] = a_origin[2];
m_b_origin[0] = b_origin[0];
m_b_origin[1] = b_origin[1];
m_b_origin[2] = b_origin[2];
m_region[0] = region[0];
m_region[1] = region[1];
m_region[2] = region[2];
cvk_debug_fn("region = {%zu,%zu,%zu}", m_region[0], m_region[1],
m_region[2]);
}
enum class direction
{
A_TO_B,
B_TO_A,
};
void do_copy(direction dir, void* src_base, void* dst_base);
private:
size_t initialize_pitch(size_t default_pitch, size_t region_pitch) {
return default_pitch == 0 ? region_pitch : default_pitch;
}
std::array<size_t, 3> m_a_origin;
size_t m_a_row_pitch;
size_t m_a_slice_pitch;
std::array<size_t, 3> m_b_origin;
size_t m_b_row_pitch;
size_t m_b_slice_pitch;
std::array<size_t, 3> m_region;
size_t m_elem_size;
};
struct cvk_command_copy_host_buffer_rect final : public cvk_command {
cvk_command_copy_host_buffer_rect(
cvk_command_queue* queue, cl_command_type type, cvk_buffer* buffer,
void* hostptr, const size_t* host_origin, const size_t* buffer_origin,
const size_t* region, size_t host_row_pitch, size_t host_slice_pitch,
size_t buffer_row_pitch, size_t buffer_slice_pitch,
size_t elem_size = 1)
: cvk_command(type, queue),
m_copier(buffer_origin, host_origin, region, buffer_row_pitch,
buffer_slice_pitch, host_row_pitch, host_slice_pitch,
elem_size),
m_buffer(buffer), m_hostptr(hostptr) {}
const std::vector<cvk_mem*> memory_objects() const override final {
return {m_buffer};
}
private:
CHECK_RETURN cl_int do_action() override final;
cvk_rectangle_copier m_copier;
cvk_buffer_holder m_buffer;
void* m_hostptr;
};
struct cvk_command_copy_buffer final : public cvk_command {
cvk_command_copy_buffer(cvk_command_queue* q, cl_command_type type,
cvk_buffer* src, cvk_buffer* dst, size_t src_offset,
size_t dst_offset, size_t size)
: cvk_command(type, q), m_src_buffer(src), m_dst_buffer(dst),
m_src_offset(src_offset), m_dst_offset(dst_offset), m_size(size) {}
const std::vector<cvk_mem*> memory_objects() const override {
return {m_src_buffer, m_dst_buffer};
}
private:
CHECK_RETURN cl_int do_action() override final;
cvk_buffer_holder m_src_buffer;
cvk_buffer_holder m_dst_buffer;
size_t m_src_offset;
size_t m_dst_offset;
size_t m_size;
};
struct cvk_command_copy_buffer_rect final : public cvk_command {
cvk_command_copy_buffer_rect(cvk_command_queue* queue,
cvk_buffer* src_buffer, cvk_buffer* dst_buffer,
const size_t* src_origin,
const size_t* dst_origin, const size_t* region,
size_t src_row_pitch, size_t src_slice_pitch,
size_t dst_row_pitch, size_t dst_slice_pitch)
: cvk_command(CL_COMMAND_COPY_BUFFER_RECT, queue),
m_copier(src_origin, dst_origin, region, src_row_pitch,
src_slice_pitch, dst_row_pitch, dst_slice_pitch, 1),
m_src_buffer(src_buffer), m_dst_buffer(dst_buffer) {}
const std::vector<cvk_mem*> memory_objects() const override {
return {m_src_buffer, m_dst_buffer};
}
private:
CHECK_RETURN cl_int do_action() override final;
cvk_rectangle_copier m_copier;
cvk_buffer_holder m_src_buffer;
cvk_buffer_holder m_dst_buffer;
};
struct cvk_command_fill_buffer final : public cvk_command_buffer_base_region {
cvk_command_fill_buffer(cvk_command_queue* q, cvk_buffer* buffer,
size_t offset, size_t size, const void* pattern,
size_t pattern_size, cl_command_type type)
: cvk_command_buffer_base_region(q, type, buffer, offset, size),
m_pattern_size(pattern_size) {
memcpy(m_pattern.data(), pattern, pattern_size);
}
const std::vector<cvk_mem*> memory_objects() const override {
return {m_buffer};
}
private:
CHECK_RETURN cl_int do_action() override final;
static constexpr int MAX_PATTERN_SIZE = 128;
std::array<char, MAX_PATTERN_SIZE> m_pattern;
size_t m_pattern_size;
};
struct cvk_command_batchable : public cvk_command {
cvk_command_batchable(cl_command_type type, cvk_command_queue* queue)
: cvk_command(type, queue), m_query_pool(VK_NULL_HANDLE) {}
virtual ~cvk_command_batchable() {
if (m_query_pool != VK_NULL_HANDLE) {
auto vkdev = m_queue->device()->vulkan_device();
vkDestroyQueryPool(vkdev, m_query_pool, nullptr);
}
}
bool can_be_batched() const override;
bool is_built_before_enqueue() const override final { return false; }
CHECK_RETURN cl_int get_timestamp_query_results(cl_ulong* start,
cl_ulong* end);
CHECK_RETURN cl_int build();
CHECK_RETURN cl_int build(cvk_command_buffer& cmdbuf);
CHECK_RETURN virtual cl_int
build_batchable_inner(cvk_command_buffer& cmdbuf) = 0;
CHECK_RETURN cl_int set_profiling_info_end() {
// If it has already been set, don't override it
if (m_event->get_profiling_info(CL_PROFILING_COMMAND_END) != 0) {
return CL_SUCCESS;
}
cl_ulong start_dev, end_dev;
cl_int perr = get_timestamp_query_results(&start_dev, &end_dev);
if (perr != CL_COMPLETE) {
return perr;
}
cl_ulong start_host, end_host;
perr = m_queue->device()->device_timer_to_host(start_dev, start_host);
if (perr != CL_SUCCESS) {
return perr;
}
perr = m_queue->device()->device_timer_to_host(end_dev, end_host);
if (perr != CL_SUCCESS) {
return perr;
}
m_event->set_profiling_info(CL_PROFILING_COMMAND_START, start_host);
m_event->set_profiling_info(CL_PROFILING_COMMAND_END, end_host);
return CL_SUCCESS;
}
CHECK_RETURN cl_int
set_profiling_info(cl_profiling_info pinfo) override final {
if (!m_queue->profiling_on_device()) {
return cvk_command::set_profiling_info(pinfo);
}
if (pinfo == CL_PROFILING_COMMAND_QUEUED ||
pinfo == CL_PROFILING_COMMAND_SUBMIT) {
return cvk_command::set_profiling_info(pinfo);
} else if (pinfo == CL_PROFILING_COMMAND_START) {
return m_queue->device()->update_device_host_timer();
} else {
CVK_ASSERT(pinfo == CL_PROFILING_COMMAND_END);
return set_profiling_info_end();
}
}
private:
CHECK_RETURN virtual cl_int do_post_action() { return CL_SUCCESS; }
CHECK_RETURN cl_int do_action() override;
std::unique_ptr<cvk_command_buffer> m_command_buffer;
VkQueryPool m_query_pool;
static const int NUM_POOL_QUERIES_PER_COMMAND = 2;
static const int POOL_QUERY_CMD_START = 0;
static const int POOL_QUERY_CMD_END = 1;
};
struct cvk_ndrange {
cvk_ndrange() : offset({0}), gws({1, 1, 1}), lws({1, 1, 1}) {}
cvk_ndrange(cl_uint work_dim, const size_t* global_work_offset,
const size_t* global_work_size, const size_t* local_work_size)
: cvk_ndrange() {
for (cl_uint dim = 0; dim < work_dim; dim++) {
if (global_work_offset != nullptr) {
offset[dim] = global_work_offset[dim];
}
gws[dim] = global_work_size[dim];
if (local_work_size != nullptr) {
lws[dim] = local_work_size[dim];
}
}
}
bool is_uniform() const {
for (cl_uint i = 0; i < 3; i++) {
if (gws[i] % lws[i] != 0) {
return false;
}
}
return true;
}
std::array<uint32_t, 3> offset;
std::array<uint32_t, 3> gws;
std::array<uint32_t, 3> lws;
};
struct cvk_command_kernel final : public cvk_command_batchable {
cvk_command_kernel(cvk_command_queue* q, cvk_kernel* kernel, uint32_t dims,
const cvk_ndrange& ndrange)
: cvk_command_batchable(CL_COMMAND_NDRANGE_KERNEL, q), m_kernel(kernel),
m_dimensions(dims), m_ndrange(ndrange), m_pipeline(VK_NULL_HANDLE),
m_argument_values(nullptr) {}
~cvk_command_kernel() {
if (m_argument_values) {
m_argument_values->release_resources();
}
}
CHECK_RETURN cl_int
build_batchable_inner(cvk_command_buffer& cmdbuf) override final;
bool can_be_batched() const override final {
return !m_kernel->uses_printf() &&
cvk_command_batchable::can_be_batched();
}
const std::vector<cvk_mem*> memory_objects() const override {
std::vector<cvk_mem*> ret;
std::shared_ptr<cvk_kernel_argument_values> argvals = m_argument_values;
if (argvals == nullptr) {
argvals = m_kernel->argument_values();
}
return argvals->memory_objects();
}
private:
CHECK_RETURN cl_int do_post_action() override final;
CHECK_RETURN cl_int
build_and_dispatch_regions(cvk_command_buffer& command_buffer);
CHECK_RETURN cl_int
update_global_push_constants(cvk_command_buffer& command_buffer);
CHECK_RETURN cl_int dispatch_uniform_region_within_vklimits(
const cvk_ndrange& region, cvk_command_buffer& command_buffer);
CHECK_RETURN cl_int dispatch_uniform_region_iterate(
unsigned int dim, const cvk_ndrange& region, const size_t* region_lws,
size_t* region_gws, size_t* region_offset,
cvk_command_buffer& command_buffer, uint32_t* num_workgroups);
CHECK_RETURN cl_int dispatch_uniform_region(
const cvk_ndrange& region, cvk_command_buffer& command_buffer);
cvk_kernel_holder m_kernel;
uint32_t m_dimensions;
cvk_ndrange m_ndrange;
VkPipeline m_pipeline;
std::shared_ptr<cvk_kernel_argument_values> m_argument_values;
};
struct cvk_command_batch : public cvk_command {
cvk_command_batch(cvk_command_queue* queue)
: cvk_command(CLVK_COMMAND_BATCH, queue) {}
cl_int add_command(cvk_command_batchable* cmd) {
if (!m_command_buffer) {
// Create command buffer and start recording on first call
m_command_buffer = std::make_unique<cvk_command_buffer>(m_queue);
if (!m_command_buffer->begin()) {
return CL_OUT_OF_RESOURCES;
}
}
cvk_command_pool_lock_holder lock(m_queue);
cl_int ret = cmd->build(*m_command_buffer);
if (ret != CL_SUCCESS) {
return ret;
}
cvk_debug_fn("add command %p (%s) to batch %p", cmd,
cl_command_type_to_string(cmd->type()), this);
m_commands.emplace_back(cmd);
return ret;
}
CHECK_RETURN bool end() {
cvk_command_pool_lock_holder lock(m_queue);
return m_command_buffer->end();
}
cl_uint batch_size() { return m_commands.size(); }
CHECK_RETURN cl_int
set_profiling_info(cl_profiling_info pinfo) override final {
cl_int status = cvk_command::set_profiling_info(pinfo);
if (m_queue->profiling_on_device()) {
if (pinfo == CL_PROFILING_COMMAND_START) {
return status;
} else {
for (auto& cmd : m_commands) {
cl_int err;
if (pinfo == CL_PROFILING_COMMAND_END) {
err = cmd->set_profiling_info_end();
} else {
err = cmd->set_profiling_info(pinfo);
}
// do not stop at first error, but record only the first one
if (err != CL_SUCCESS && status == CL_SUCCESS) {
status = err;
}
}
}
} else {
for (auto& cmd : m_commands) {
cmd->event()->copy_profiling_info(pinfo, m_event);
}
}
return status;
}
void set_event_status(cl_int status) override final {
m_event->set_status(status);
for (auto& cmd : m_commands) {
cmd->set_event_status(status);
}
}
private:
CHECK_RETURN cl_int do_action() override final;
std::vector<std::unique_ptr<cvk_command_batchable>> m_commands;
std::unique_ptr<cvk_command_buffer> m_command_buffer;
};
struct cvk_command_map_buffer final : public cvk_command_buffer_base_region {
cvk_command_map_buffer(cvk_command_queue* queue, cvk_buffer* buffer,
size_t offset, size_t size, cl_map_flags flags,
cl_command_type type, cvk_image* image = nullptr)
: cvk_command_buffer_base_region(queue, type, buffer, offset, size),
m_flags(flags), m_mapping_needs_releasing_on_destruction(false),
m_image(image) {}
~cvk_command_map_buffer() {
if (m_mapping_needs_releasing_on_destruction) {
m_buffer->cleanup_mapping(m_mapping);
}
}
CHECK_RETURN cl_int build(void** map_ptr);
const std::vector<cvk_mem*> memory_objects() const override {
return {m_buffer};
}
private:
CHECK_RETURN cl_int do_action() override final;
cl_map_flags m_flags;
cvk_buffer_mapping m_mapping;
bool m_mapping_needs_releasing_on_destruction;
cvk_image* m_image;
};
struct cvk_command_unmap_buffer final : public cvk_command_buffer_base {
cvk_command_unmap_buffer(cvk_command_queue* queue, cvk_buffer* buffer,
void* map_ptr)
: cvk_command_buffer_base(queue, CL_COMMAND_UNMAP_MEM_OBJECT, buffer),
m_mapped_ptr(map_ptr) {}
const std::vector<cvk_mem*> memory_objects() const override {
return {m_buffer};
}
private:
CHECK_RETURN cl_int do_action() override final;
void* m_mapped_ptr;
};
struct cvk_command_dep : public cvk_command {
cvk_command_dep(cvk_command_queue* q, cl_command_type type)
: cvk_command(type, q) {}
const std::vector<cvk_mem*> memory_objects() const override { return {}; }
private:
CHECK_RETURN cl_int do_action() override final { return CL_COMPLETE; }
};
struct cvk_command_buffer_image_copy final : public cvk_command_batchable {
cvk_command_buffer_image_copy(cl_command_type type,
cvk_command_queue* queue, cvk_buffer* buffer,
cvk_image* image, size_t offset,
const std::array<size_t, 3>& origin,
const std::array<size_t, 3>& region)
: cvk_command_batchable(type, queue), m_buffer(buffer), m_image(image),
m_offset(offset), m_origin(origin), m_region(region),
m_copy_type(type) {}
cvk_command_buffer_image_copy(cl_command_type type,
cl_command_type copy_type,
cvk_command_queue* queue, cvk_buffer* buffer,
cvk_image* image, size_t offset,
const std::array<size_t, 3>& origin,
const std::array<size_t, 3>& region)
: cvk_command_batchable(type, queue), m_buffer(buffer), m_image(image),
m_offset(offset), m_origin(origin), m_region(region),
m_copy_type(copy_type) {}
CHECK_RETURN cl_int
build_batchable_inner(cvk_command_buffer& cmdbuf) override final;
const std::vector<cvk_mem*> memory_objects() const override final {
return {m_buffer, m_image};
}
private:
void build_inner_image_to_buffer(cvk_command_buffer& cmdbuf,
const VkBufferImageCopy& region);
void build_inner_buffer_to_image(cvk_command_buffer& cmdbuf,
const VkBufferImageCopy& region);
cvk_buffer_holder m_buffer;
cvk_image_holder m_image;
size_t m_offset;
std::array<size_t, 3> m_origin;
std::array<size_t, 3> m_region;
cl_command_type m_copy_type;
};
struct cvk_command_map_image final : public cvk_command {
// This command intend to manage the dependency with m_image initialization
cvk_command_map_image(cvk_command_queue* q, cvk_image* img)
: cvk_command(CL_COMMAND_MAP_IMAGE, q), m_image(img) {}
const std::vector<cvk_mem*> memory_objects() const override {
return {m_image};
}
private:
CHECK_RETURN cl_int do_action() override final { return CL_COMPLETE; };
cvk_image_holder m_image;
};
struct cvk_command_unmap_image final : public cvk_command {
cvk_command_unmap_image(cvk_command_queue* queue, cvk_image* image,
void* mapped_ptr)
: cvk_command(CL_COMMAND_UNMAP_MEM_OBJECT, queue),
m_mapped_ptr(mapped_ptr),
m_image(image) { // FIXME crashes when the mapping doesn't exist
}
const std::vector<cvk_mem*> memory_objects() const override final {
return {m_image};
}
private:
CHECK_RETURN cl_int do_action() override final;
void* m_mapped_ptr;
cvk_image_holder m_image;
};
struct cvk_command_image_image_copy final : public cvk_command_batchable {
cvk_command_image_image_copy(cvk_command_queue* queue, cvk_image* src_image,
cvk_image* dst_image,
const std::array<size_t, 3>& src_origin,
const std::array<size_t, 3>& dst_origin,
const std::array<size_t, 3>& region)
: cvk_command_batchable(CL_COMMAND_COPY_IMAGE, queue),
m_src_image(src_image), m_dst_image(dst_image),
m_src_origin(src_origin), m_dst_origin(dst_origin), m_region(region) {
}
CHECK_RETURN cl_int
build_batchable_inner(cvk_command_buffer& cmdbuf) override final;
const std::vector<cvk_mem*> memory_objects() const override final {
return {m_src_image, m_dst_image};
}
private:
cvk_image_holder m_src_image;
cvk_image_holder m_dst_image;
std::array<size_t, 3> m_src_origin;
std::array<size_t, 3> m_dst_origin;
std::array<size_t, 3> m_region;
};
struct cvk_command_fill_image final : public cvk_command {
cvk_command_fill_image(cvk_command_queue* queue, void* ptr,
const cvk_image::fill_pattern_array& pattern,
size_t pattern_size,
const std::array<size_t, 3>& region)
: cvk_command(CL_COMMAND_FILL_IMAGE, queue), m_ptr(ptr),
m_pattern(pattern), m_pattern_size(pattern_size), m_region(region) {}
const std::vector<cvk_mem*> memory_objects() const override { return {}; }
private:
CHECK_RETURN cl_int do_action() override final;
void* m_ptr;
cvk_image::fill_pattern_array m_pattern;
size_t m_pattern_size;
std::array<size_t, 3> m_region;
};
struct cvk_command_image_init final : public cvk_command_batchable {
cvk_command_image_init(cvk_command_queue* queue, cvk_image* image)
: cvk_command_batchable(CLVK_COMMAND_IMAGE_INIT, queue),
m_image(image) {
CVK_ASSERT(!m_image->is_backed_by_buffer_view());
}
bool is_data_movement() const override { return true; }
CHECK_RETURN cl_int
build_batchable_inner(cvk_command_buffer& cmdbuf) override final;
~cvk_command_image_init() { m_image->discard_init_data(); }
private:
cvk_image_holder m_image;
};