Commit 247232d5 authored by Chia-I Wu's avatar Chia-I Wu Committed by Marge Bot
Browse files

venus: add experimental renderers



There are a virtio-gpu renderer and a vtest renderer.  The vtest
renderer must be enabled with VN_DEBUG=vtest.

Signed-off-by: Chia-I Wu's avatarChia-I Wu <olvaffe@gmail.com>
Reviewed-by: Ryan Neph's avatarRyan Neph <ryanneph@google.com>
Reviewed-by: Gert Wollny's avatarGert Wollny <gert.wollny@collabora.com>
Part-of: <!5800>
parent b5653e34
......@@ -34,9 +34,12 @@ libvn_files = files(
'vn_common.c',
'vn_device.c',
'vn_icd.c',
'vn_renderer_virtgpu.c',
'vn_renderer_vtest.c',
)
vn_deps = [
dep_libdrm,
dep_thread,
idep_mesautil,
idep_vulkan_util,
......
......@@ -25,6 +25,7 @@
static const struct debug_control vn_debug_options[] = {
{ "init", VN_DEBUG_INIT },
{ "result", VN_DEBUG_RESULT },
{ "vtest", VN_DEBUG_VTEST },
{ NULL, 0 },
};
......
......@@ -54,9 +54,14 @@ struct vn_device;
struct vn_queue;
struct vn_command_buffer;
struct vn_renderer;
struct vn_renderer_bo;
struct vn_renderer_sync;
enum vn_debug {
VN_DEBUG_INIT = 1ull << 0,
VN_DEBUG_RESULT = 1ull << 1,
VN_DEBUG_VTEST = 1ull << 2,
};
typedef uint64_t vn_object_id;
......
/*
* Copyright 2019 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef VN_RENDERER_H
#define VN_RENDERER_H
#include "vn_common.h"
struct vn_renderer_bo_ops {
void (*destroy)(struct vn_renderer_bo *bo);
/* allocate a CPU shared memory as the storage */
VkResult (*init_cpu)(struct vn_renderer_bo *bo, VkDeviceSize size);
/* import a VkDeviceMemory as the storage */
VkResult (*init_gpu)(struct vn_renderer_bo *bo,
VkDeviceSize size,
vn_object_id mem_id,
VkMemoryPropertyFlags flags,
VkExternalMemoryHandleTypeFlags external_handles);
/* import a dmabuf as the storage */
VkResult (*init_dmabuf)(struct vn_renderer_bo *bo,
VkDeviceSize size,
int fd,
VkMemoryPropertyFlags flags,
VkExternalMemoryHandleTypeFlags external_handles);
int (*export_dmabuf)(struct vn_renderer_bo *bo);
/* map is not thread-safe */
void *(*map)(struct vn_renderer_bo *bo);
void (*flush)(struct vn_renderer_bo *bo,
VkDeviceSize offset,
VkDeviceSize size);
void (*invalidate)(struct vn_renderer_bo *bo,
VkDeviceSize offset,
VkDeviceSize size);
};
struct vn_renderer_bo {
atomic_int refcount;
uint32_t res_id;
struct vn_renderer_bo_ops ops;
};
enum vn_renderer_sync_flags {
VN_RENDERER_SYNC_SHAREABLE = 1u << 0,
VN_RENDERER_SYNC_BINARY = 1u << 1,
};
struct vn_renderer_sync_ops {
void (*destroy)(struct vn_renderer_sync *sync);
/* a sync can be initialized/released multiple times */
VkResult (*init)(struct vn_renderer_sync *sync,
uint64_t initial_val,
uint32_t flags);
VkResult (*init_syncobj)(struct vn_renderer_sync *sync,
int fd,
bool sync_file);
void (*release)(struct vn_renderer_sync *sync);
int (*export_syncobj)(struct vn_renderer_sync *sync, bool sync_file);
/* reset the counter */
VkResult (*reset)(struct vn_renderer_sync *sync, uint64_t initial_val);
/* read the current value from the counter */
VkResult (*read)(struct vn_renderer_sync *sync, uint64_t *val);
/* write a new value (larger than the current one) to the counter */
VkResult (*write)(struct vn_renderer_sync *sync, uint64_t val);
};
/*
* A sync consists of a uint64_t counter. The counter can be updated by CPU
* or by GPU. It can also be waited on by CPU or by GPU until it reaches
* certain values.
*
* This models after timeline VkSemaphore rather than timeline drm_syncobj.
* The main difference is that drm_syncobj can have unsignaled value 0.
*/
struct vn_renderer_sync {
uint32_t sync_id;
struct vn_renderer_sync_ops ops;
};
struct vn_renderer_info {
struct {
uint16_t vendor_id;
uint16_t device_id;
bool has_bus_info;
uint16_t domain;
uint8_t bus;
uint8_t device;
uint8_t function;
} pci;
bool has_dmabuf_import;
bool has_cache_management;
bool has_timeline_sync;
bool has_external_sync;
uint32_t max_sync_queue_count;
/* hw capset */
uint32_t wire_format_version;
uint32_t vk_xml_version;
uint32_t vk_ext_command_serialization_spec_version;
uint32_t vk_mesa_venus_protocol_spec_version;
};
struct vn_renderer_submit_batch {
const void *cs_data;
size_t cs_size;
/*
* Submit cs to the virtual sync queue identified by sync_queue_index. The
* virtual queue is assumed to be associated with the physical VkQueue
* identified by vk_queue_id. After the execution completes on the
* VkQueue, the virtual sync queue is signaled.
*
* sync_queue_index must be less than max_sync_queue_count.
*
* vk_queue_id specifies the object id of a VkQueue.
*
* When sync_queue_cpu is true, it specifies the special CPU sync queue,
* and sync_queue_index/vk_queue_id are ignored. TODO revisit this later
*/
uint32_t sync_queue_index;
bool sync_queue_cpu;
vn_object_id vk_queue_id;
/* syncs to update when the virtual sync queue is signaled */
struct vn_renderer_sync *const *syncs;
/* TODO allow NULL when syncs are all binary? */
const uint64_t *sync_values;
uint32_t sync_count;
};
struct vn_renderer_submit {
/* BOs to pin and to fence implicitly
*
* TODO track all bos and automatically pin them. We don't do it yet
* because each vn_command_buffer owns a bo. We can probably make do by
* returning the bos to a bo cache and exclude bo cache from pinning.
*/
struct vn_renderer_bo *const *bos;
uint32_t bo_count;
const struct vn_renderer_submit_batch *batches;
uint32_t batch_count;
};
struct vn_renderer_wait {
bool wait_any;
uint64_t timeout;
struct vn_renderer_sync *const *syncs;
/* TODO allow NULL when syncs are all binary? */
const uint64_t *sync_values;
uint32_t sync_count;
};
struct vn_renderer_ops {
void (*destroy)(struct vn_renderer *renderer,
const VkAllocationCallbacks *alloc);
void (*get_info)(struct vn_renderer *renderer,
struct vn_renderer_info *info);
VkResult (*submit)(struct vn_renderer *renderer,
const struct vn_renderer_submit *submit);
/*
* On success, returns VK_SUCCESS or VK_TIMEOUT. On failure, returns
* VK_ERROR_DEVICE_LOST or out of device/host memory.
*/
VkResult (*wait)(struct vn_renderer *renderer,
const struct vn_renderer_wait *wait);
struct vn_renderer_bo *(*bo_create)(struct vn_renderer *renderer);
struct vn_renderer_sync *(*sync_create)(struct vn_renderer *renderer);
};
struct vn_renderer {
struct vn_renderer_ops ops;
};
VkResult
vn_renderer_create_virtgpu(struct vn_instance *instance,
const VkAllocationCallbacks *alloc,
struct vn_renderer **renderer);
VkResult
vn_renderer_create_vtest(struct vn_instance *instance,
const VkAllocationCallbacks *alloc,
struct vn_renderer **renderer);
static inline VkResult
vn_renderer_create(struct vn_instance *instance,
const VkAllocationCallbacks *alloc,
struct vn_renderer **renderer)
{
if (VN_DEBUG(VTEST)) {
VkResult result = vn_renderer_create_vtest(instance, alloc, renderer);
if (result == VK_SUCCESS)
return VK_SUCCESS;
}
return vn_renderer_create_virtgpu(instance, alloc, renderer);
}
static inline void
vn_renderer_destroy(struct vn_renderer *renderer,
const VkAllocationCallbacks *alloc)
{
renderer->ops.destroy(renderer, alloc);
}
static inline void
vn_renderer_get_info(struct vn_renderer *renderer,
struct vn_renderer_info *info)
{
renderer->ops.get_info(renderer, info);
}
static inline VkResult
vn_renderer_submit(struct vn_renderer *renderer,
const struct vn_renderer_submit *submit)
{
return renderer->ops.submit(renderer, submit);
}
static inline VkResult
vn_renderer_submit_simple(struct vn_renderer *renderer,
const void *cs_data,
size_t cs_size)
{
const struct vn_renderer_submit submit = {
.batches =
&(const struct vn_renderer_submit_batch){
.cs_data = cs_data,
.cs_size = cs_size,
},
.batch_count = 1,
};
return vn_renderer_submit(renderer, &submit);
}
static inline VkResult
vn_renderer_wait(struct vn_renderer *renderer,
const struct vn_renderer_wait *wait)
{
return renderer->ops.wait(renderer, wait);
}
static inline VkResult
vn_renderer_bo_create_cpu(struct vn_renderer *renderer,
VkDeviceSize size,
struct vn_renderer_bo **_bo)
{
struct vn_renderer_bo *bo = renderer->ops.bo_create(renderer);
if (!bo)
return VK_ERROR_OUT_OF_HOST_MEMORY;
VkResult result = bo->ops.init_cpu(bo, size);
if (result != VK_SUCCESS) {
bo->ops.destroy(bo);
return result;
}
atomic_init(&bo->refcount, 1);
*_bo = bo;
return VK_SUCCESS;
}
static inline VkResult
vn_renderer_bo_create_gpu(struct vn_renderer *renderer,
VkDeviceSize size,
vn_object_id mem_id,
VkMemoryPropertyFlags flags,
VkExternalMemoryHandleTypeFlags external_handles,
struct vn_renderer_bo **_bo)
{
struct vn_renderer_bo *bo = renderer->ops.bo_create(renderer);
if (!bo)
return VK_ERROR_OUT_OF_HOST_MEMORY;
VkResult result =
bo->ops.init_gpu(bo, size, mem_id, flags, external_handles);
if (result != VK_SUCCESS) {
bo->ops.destroy(bo);
return result;
}
atomic_init(&bo->refcount, 1);
*_bo = bo;
return VK_SUCCESS;
}
static inline VkResult
vn_renderer_bo_create_dmabuf(struct vn_renderer *renderer,
VkDeviceSize size,
int fd,
VkMemoryPropertyFlags flags,
VkExternalMemoryHandleTypeFlags external_handles,
struct vn_renderer_bo **_bo)
{
struct vn_renderer_bo *bo = renderer->ops.bo_create(renderer);
if (!bo)
return VK_ERROR_OUT_OF_HOST_MEMORY;
VkResult result =
bo->ops.init_dmabuf(bo, size, fd, flags, external_handles);
if (result != VK_SUCCESS) {
bo->ops.destroy(bo);
return result;
}
atomic_init(&bo->refcount, 1);
*_bo = bo;
return VK_SUCCESS;
}
static inline struct vn_renderer_bo *
vn_renderer_bo_ref(struct vn_renderer_bo *bo)
{
const int old =
atomic_fetch_add_explicit(&bo->refcount, 1, memory_order_relaxed);
assert(old >= 1);
return bo;
}
static inline bool
vn_renderer_bo_unref(struct vn_renderer_bo *bo)
{
const int old =
atomic_fetch_sub_explicit(&bo->refcount, 1, memory_order_release);
assert(old >= 1);
if (old == 1) {
atomic_thread_fence(memory_order_acquire);
bo->ops.destroy(bo);
return true;
}
return false;
}
static inline int
vn_renderer_bo_export_dmabuf(struct vn_renderer_bo *bo)
{
return bo->ops.export_dmabuf(bo);
}
static inline void *
vn_renderer_bo_map(struct vn_renderer_bo *bo)
{
return bo->ops.map(bo);
}
static inline void
vn_renderer_bo_flush(struct vn_renderer_bo *bo,
VkDeviceSize offset,
VkDeviceSize end)
{
bo->ops.flush(bo, offset, end);
}
static inline void
vn_renderer_bo_invalidate(struct vn_renderer_bo *bo,
VkDeviceSize offset,
VkDeviceSize size)
{
bo->ops.invalidate(bo, offset, size);
}
static inline VkResult
vn_renderer_sync_create_cpu(struct vn_renderer *renderer,
struct vn_renderer_sync **_sync)
{
struct vn_renderer_sync *sync = renderer->ops.sync_create(renderer);
if (!sync)
return VK_ERROR_OUT_OF_HOST_MEMORY;
const uint64_t initial_val = 0;
const uint32_t flags = 0;
VkResult result = sync->ops.init(sync, initial_val, flags);
if (result != VK_SUCCESS) {
sync->ops.destroy(sync);
return result;
}
*_sync = sync;
return VK_SUCCESS;
}
static inline VkResult
vn_renderer_sync_create_fence(struct vn_renderer *renderer,
bool signaled,
VkExternalFenceHandleTypeFlags external_handles,
struct vn_renderer_sync **_sync)
{
struct vn_renderer_sync *sync = renderer->ops.sync_create(renderer);
if (!sync)
return VK_ERROR_OUT_OF_HOST_MEMORY;
const uint64_t initial_val = signaled;
const uint32_t flags = VN_RENDERER_SYNC_BINARY |
(external_handles ? VN_RENDERER_SYNC_SHAREABLE : 0);
VkResult result = sync->ops.init(sync, initial_val, flags);
if (result != VK_SUCCESS) {
sync->ops.destroy(sync);
return result;
}
*_sync = sync;
return VK_SUCCESS;
}
static inline VkResult
vn_renderer_sync_create_semaphore(
struct vn_renderer *renderer,
VkSemaphoreType type,
uint64_t initial_val,
VkExternalSemaphoreHandleTypeFlags external_handles,
struct vn_renderer_sync **_sync)
{
struct vn_renderer_sync *sync = renderer->ops.sync_create(renderer);
if (!sync)
return VK_ERROR_OUT_OF_HOST_MEMORY;
const uint32_t flags =
(external_handles ? VN_RENDERER_SYNC_SHAREABLE : 0) |
(type == VK_SEMAPHORE_TYPE_BINARY ? VN_RENDERER_SYNC_BINARY : 0);
VkResult result = sync->ops.init(sync, initial_val, flags);
if (result != VK_SUCCESS) {
sync->ops.destroy(sync);
return result;
}
*_sync = sync;
return VK_SUCCESS;
}
static inline VkResult
vn_renderer_sync_create_empty(struct vn_renderer *renderer,
struct vn_renderer_sync **_sync)
{
struct vn_renderer_sync *sync = renderer->ops.sync_create(renderer);
if (!sync)
return VK_ERROR_OUT_OF_HOST_MEMORY;
/* no init */
*_sync = sync;
return VK_SUCCESS;
}
static inline void
vn_renderer_sync_destroy(struct vn_renderer_sync *sync)
{
sync->ops.destroy(sync);
}
static inline VkResult
vn_renderer_sync_init_signaled(struct vn_renderer_sync *sync)
{
const uint64_t initial_val = 1;
const uint32_t flags = VN_RENDERER_SYNC_BINARY;
return sync->ops.init(sync, initial_val, flags);
}
static inline VkResult
vn_renderer_sync_init_syncobj(struct vn_renderer_sync *sync,
int fd,
bool sync_file)
{
return sync->ops.init_syncobj(sync, fd, sync_file);
}
static inline void
vn_renderer_sync_release(struct vn_renderer_sync *sync)
{
sync->ops.release(sync);
}
static inline int
vn_renderer_sync_export_syncobj(struct vn_renderer_sync *sync, bool sync_file)
{
return sync->ops.export_syncobj(sync, sync_file);
}
static inline VkResult
vn_renderer_sync_reset(struct vn_renderer_sync *sync, uint64_t initial_val)
{
return sync->ops.reset(sync, initial_val);
}
static inline VkResult
vn_renderer_sync_read(struct vn_renderer_sync *sync, uint64_t *val)
{
return sync->ops.read(sync, val);
}
static inline VkResult
vn_renderer_sync_write(struct vn_renderer_sync *sync, uint64_t val)
{
return sync->ops.write(sync, val);
}
#endif /* VN_RENDERER_H */
This diff is collapsed.
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment