blob: 89a45e49b137483deed88425e3792119db6c2bf9 [file] [log] [blame]
/*
* Copyright 2020 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "vkr_transport.h"
#include "venus-protocol/vn_protocol_renderer_dispatches.h"
#include "venus-protocol/vn_protocol_renderer_transport.h"
#include "vkr_context.h"
#include "vkr_ring.h"
static void
vkr_dispatch_vkSetReplyCommandStreamMESA(
struct vn_dispatch_context *dispatch,
struct vn_command_vkSetReplyCommandStreamMESA *args)
{
struct vkr_context *ctx = dispatch->data;
struct vkr_resource *res = vkr_context_get_resource(ctx, args->pStream->resourceId);
if (!res) {
vkr_log("failed to set reply stream: invalid res_id %u", args->pStream->resourceId);
vkr_context_set_fatal(ctx);
return;
}
struct vkr_cs_encoder *enc = (struct vkr_cs_encoder *)dispatch->encoder;
mtx_lock(&enc->mutex);
vkr_cs_encoder_set_stream(enc, res, args->pStream->offset, args->pStream->size);
mtx_unlock(&enc->mutex);
}
static void
vkr_dispatch_vkSeekReplyCommandStreamMESA(
struct vn_dispatch_context *dispatch,
struct vn_command_vkSeekReplyCommandStreamMESA *args)
{
struct vkr_cs_encoder *enc = (struct vkr_cs_encoder *)dispatch->encoder;
vkr_cs_encoder_seek_stream(enc, args->position);
}
static void
vkr_dispatch_vkExecuteCommandStreamsMESA(
struct vn_dispatch_context *dispatch,
struct vn_command_vkExecuteCommandStreamsMESA *args)
{
struct vkr_context *ctx = dispatch->data;
struct vkr_cs_decoder *dec = (struct vkr_cs_decoder *)dispatch->decoder;
struct vkr_cs_encoder *enc = (struct vkr_cs_encoder *)dispatch->encoder;
if (unlikely(!args->streamCount)) {
vkr_log("failed to execute command streams: no stream specified");
vkr_context_set_fatal(ctx);
return;
}
/* note that nested vkExecuteCommandStreamsMESA is not allowed */
if (unlikely(!vkr_cs_decoder_push_state(dec))) {
vkr_log("failed to execute command streams: nested execution");
vkr_context_set_fatal(ctx);
return;
}
for (uint32_t i = 0; i < args->streamCount; i++) {
const VkCommandStreamDescriptionMESA *stream = &args->pStreams[i];
if (args->pReplyPositions)
vkr_cs_encoder_seek_stream(enc, args->pReplyPositions[i]);
if (!stream->size)
continue;
struct vkr_resource *res = vkr_context_get_resource(ctx, stream->resourceId);
if (!res) {
vkr_log("failed to execute command streams: invalid stream %u res_id %u", i,
stream->resourceId);
vkr_context_set_fatal(ctx);
break;
}
if (unlikely(stream->size > res->size ||
stream->offset > res->size - stream->size)) {
vkr_log("failed to execute command streams: invalid stream %u res_id %u", i,
stream->resourceId);
vkr_context_set_fatal(ctx);
break;
}
vkr_cs_decoder_set_stream(dec, res->u.data + stream->offset, stream->size);
while (vkr_cs_decoder_has_command(dec)) {
vn_dispatch_command(dispatch);
if (vkr_context_get_fatal(ctx))
break;
}
if (vkr_context_get_fatal(ctx))
break;
}
vkr_cs_decoder_pop_state(dec);
}
static struct vkr_ring *
lookup_ring(struct vkr_context *ctx, uint64_t ring_id)
{
struct vkr_ring *ring;
mtx_lock(&ctx->ring_mutex);
LIST_FOR_EACH_ENTRY (ring, &ctx->rings, head) {
if (ring->id == ring_id) {
mtx_unlock(&ctx->ring_mutex);
return ring;
}
}
mtx_unlock(&ctx->ring_mutex);
return NULL;
}
static bool
vkr_ring_layout_init(struct vkr_ring_layout *layout,
const struct vkr_resource *res,
const VkRingCreateInfoMESA *info)
{
*layout = (struct vkr_ring_layout){
.resource = res,
.head = VKR_REGION_INIT(info->offset + info->headOffset, sizeof(uint32_t)),
.tail = VKR_REGION_INIT(info->offset + info->tailOffset, sizeof(uint32_t)),
.status = VKR_REGION_INIT(info->offset + info->statusOffset, sizeof(uint32_t)),
.buffer = VKR_REGION_INIT(info->offset + info->bufferOffset, info->bufferSize),
.extra = VKR_REGION_INIT(info->offset + info->extraOffset, info->extraSize),
};
const struct vkr_region res_region = VKR_REGION_INIT(info->offset, info->size);
const struct vkr_region *regions[] = {
&layout->head, &layout->tail, &layout->status, &layout->buffer, &layout->extra,
};
const struct vkr_region res_size = VKR_REGION_INIT(0, res->size);
if (!vkr_region_is_valid(&res_region) || !vkr_region_is_within(&res_region, &res_size))
return false;
for (size_t i = 0; i < ARRAY_SIZE(regions); i++) {
const struct vkr_region *region = regions[i];
if (!vkr_region_is_valid(region) || !vkr_region_is_within(region, &res_region)) {
vkr_log("ring buffer control variable (begin=%lu, end=%lu) placed"
" out-of-bounds in shared memory layout",
region->begin, region->end);
return false;
}
if (!vkr_region_is_aligned(region, 4)) {
vkr_log("ring buffer control variable (begin=%lu, end=%lu) must be"
" 32-bit aligned in shared memory layout",
region->begin, region->end);
return false;
}
}
/* assumes region->size == 0 is valid */
for (size_t i = 0; i < ARRAY_SIZE(regions); i++) {
const struct vkr_region *region = regions[i];
for (size_t j = i + 1; j < ARRAY_SIZE(regions); j++) {
const struct vkr_region *other = regions[j];
if (!vkr_region_is_disjoint(region, other)) {
vkr_log("ring buffer control variable (begin=%lu, end=%lu)"
" overlaps with control variable (begin=%lu, end=%lu)",
other->begin, other->end, region->begin, region->end);
return false;
}
}
}
const size_t buf_size = vkr_region_size(&layout->buffer);
if (buf_size > VKR_RING_BUFFER_MAX_SIZE || !util_is_power_of_two_nonzero(buf_size)) {
vkr_log("ring buffer size (%z) must be a power of two and not exceed %lu", buf_size,
VKR_RING_BUFFER_MAX_SIZE);
return false;
}
return true;
}
static void
vkr_dispatch_vkCreateRingMESA(struct vn_dispatch_context *dispatch,
struct vn_command_vkCreateRingMESA *args)
{
struct vkr_context *ctx = dispatch->data;
const VkRingCreateInfoMESA *info = args->pCreateInfo;
const struct vkr_resource *res = vkr_context_get_resource(ctx, info->resourceId);
if (!res) {
vkr_context_set_fatal(ctx);
return;
}
struct vkr_ring_layout layout;
if (!vkr_ring_layout_init(&layout, res, info)) {
vkr_log("vkCreateRingMESA supplied with invalid buffer layout parameters");
vkr_context_set_fatal(ctx);
return;
}
struct vkr_ring *ring = vkr_ring_create(&layout, ctx, info->idleTimeout);
if (!ring) {
vkr_context_set_fatal(ctx);
return;
}
ring->id = args->ring;
mtx_lock(&ctx->ring_mutex);
list_addtail(&ring->head, &ctx->rings);
mtx_unlock(&ctx->ring_mutex);
const VkRingMonitorInfoMESA *monitor_info =
vkr_find_struct(info->pNext, VK_STRUCTURE_TYPE_RING_MONITOR_INFO_MESA);
if (monitor_info) {
if (!monitor_info->maxReportingPeriodMicroseconds) {
vkr_log("invalid ring reporting period");
vkr_context_set_fatal(ctx);
return;
}
/* Start the ring monitoring thread or update the reporting rate of the running
* thread to the smallest maxReportingPeriodMicroseconds recieved so far, and wake
* it to begin reporting at the faster rate before the first driver check occurs.
*/
if (!ctx->ring_monitor.started) {
if (!vkr_context_ring_monitor_init(
ctx, monitor_info->maxReportingPeriodMicroseconds)) {
vkr_context_set_fatal(ctx);
return;
}
} else if (monitor_info->maxReportingPeriodMicroseconds <
ctx->ring_monitor.report_period_us) {
mtx_lock(&ctx->ring_monitor.mutex);
ctx->ring_monitor.report_period_us =
monitor_info->maxReportingPeriodMicroseconds;
cnd_signal(&ctx->ring_monitor.cond);
mtx_unlock(&ctx->ring_monitor.mutex);
}
ring->monitor = true;
}
vkr_ring_start(ring);
}
static void
vkr_dispatch_vkDestroyRingMESA(struct vn_dispatch_context *dispatch,
struct vn_command_vkDestroyRingMESA *args)
{
struct vkr_context *ctx = dispatch->data;
struct vkr_ring *ring = lookup_ring(ctx, args->ring);
if (!ring || !vkr_ring_stop(ring)) {
vkr_context_set_fatal(ctx);
return;
}
mtx_lock(&ctx->ring_mutex);
vkr_ring_destroy(ring);
mtx_unlock(&ctx->ring_mutex);
}
static void
vkr_dispatch_vkNotifyRingMESA(struct vn_dispatch_context *dispatch,
struct vn_command_vkNotifyRingMESA *args)
{
struct vkr_context *ctx = dispatch->data;
struct vkr_ring *ring = lookup_ring(ctx, args->ring);
if (!ring) {
vkr_context_set_fatal(ctx);
return;
}
vkr_ring_notify(ring);
}
static void
vkr_dispatch_vkWriteRingExtraMESA(struct vn_dispatch_context *dispatch,
struct vn_command_vkWriteRingExtraMESA *args)
{
struct vkr_context *ctx = dispatch->data;
struct vkr_ring *ring = lookup_ring(ctx, args->ring);
if (!ring) {
vkr_context_set_fatal(ctx);
return;
}
if (!vkr_ring_write_extra(ring, args->offset, args->value))
vkr_context_set_fatal(ctx);
}
static void
vkr_dispatch_vkSubmitVirtqueueSeqnoMESA(struct vn_dispatch_context *dispatch,
struct vn_command_vkSubmitVirtqueueSeqnoMESA *args)
{
struct vkr_context *ctx = dispatch->data;
struct vkr_ring *ring = lookup_ring(ctx, args->ring);
if (!ring) {
vkr_context_set_fatal(ctx);
return;
}
vkr_ring_submit_virtqueue_seqno(ring, args->seqno);
}
static void
vkr_dispatch_vkWaitVirtqueueSeqnoMESA(struct vn_dispatch_context *dispatch,
struct vn_command_vkWaitVirtqueueSeqnoMESA *args)
{
struct vkr_context *ctx = dispatch->data;
/* this is for -Wgnu-statement-expression-from-macro-expansion */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
if (unlikely(ctx == container_of(dispatch, struct vkr_context, dispatch))) {
vkr_log("vkWaitVirtqueueSeqnoMESA must be called on ring dispatch");
vkr_context_set_fatal(ctx);
return;
}
struct vkr_ring *ring = container_of(dispatch, struct vkr_ring, dispatch);
#pragma GCC diagnostic pop
if (!vkr_ring_wait_virtqueue_seqno(ring, args->seqno))
vkr_context_set_fatal(ctx);
}
static void
vkr_dispatch_vkWaitRingSeqnoMESA(struct vn_dispatch_context *dispatch,
struct vn_command_vkWaitRingSeqnoMESA *args)
{
struct vkr_context *ctx = dispatch->data;
struct vkr_ring *ring = lookup_ring(ctx, args->ring);
if (!ring) {
vkr_context_set_fatal(ctx);
return;
}
if (!vkr_context_wait_ring_seqno(ctx, ring, args->seqno))
vkr_context_set_fatal(ctx);
}
void
vkr_context_init_transport_dispatch(struct vkr_context *ctx)
{
struct vn_dispatch_context *dispatch = &ctx->dispatch;
dispatch->dispatch_vkSetReplyCommandStreamMESA =
vkr_dispatch_vkSetReplyCommandStreamMESA;
dispatch->dispatch_vkSeekReplyCommandStreamMESA =
vkr_dispatch_vkSeekReplyCommandStreamMESA;
dispatch->dispatch_vkExecuteCommandStreamsMESA =
vkr_dispatch_vkExecuteCommandStreamsMESA;
dispatch->dispatch_vkCreateRingMESA = vkr_dispatch_vkCreateRingMESA;
dispatch->dispatch_vkDestroyRingMESA = vkr_dispatch_vkDestroyRingMESA;
dispatch->dispatch_vkNotifyRingMESA = vkr_dispatch_vkNotifyRingMESA;
dispatch->dispatch_vkWriteRingExtraMESA = vkr_dispatch_vkWriteRingExtraMESA;
dispatch->dispatch_vkSubmitVirtqueueSeqnoMESA =
vkr_dispatch_vkSubmitVirtqueueSeqnoMESA;
dispatch->dispatch_vkWaitVirtqueueSeqnoMESA = vkr_dispatch_vkWaitVirtqueueSeqnoMESA;
dispatch->dispatch_vkWaitRingSeqnoMESA = vkr_dispatch_vkWaitRingSeqnoMESA;
}