Path: blob/21.2-virgl/src/gallium/drivers/virgl/virgl_transfer_queue.c
4570 views
/*1* Copyright 2018 Chromium.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* on the rights to use, copy, modify, merge, publish, distribute, sub7* license, and/or sell copies of the Software, and to permit persons to whom8* the Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL17* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,18* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR19* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE20* USE OR OTHER DEALINGS IN THE SOFTWARE.21*/2223#include "util/u_box.h"24#include "util/u_inlines.h"2526#include "virtio-gpu/virgl_protocol.h"27#include "virgl_context.h"28#include "virgl_screen.h"29#include "virgl_encode.h"30#include "virgl_resource.h"31#include "virgl_transfer_queue.h"3233struct list_action_args34{35void *data;36struct virgl_transfer *queued;37struct virgl_transfer *current;38};3940typedef bool (*compare_transfers_t)(struct virgl_transfer *queued,41struct virgl_transfer *current);4243typedef void (*list_action_t)(struct virgl_transfer_queue *queue,44struct list_action_args *args);4546struct list_iteration_args47{48void *data;49list_action_t action;50compare_transfers_t compare;51struct virgl_transfer *current;52};5354static int55transfer_dim(const struct virgl_transfer *xfer)56{57switch (xfer->base.resource->target) {58case PIPE_BUFFER:59case PIPE_TEXTURE_1D:60return 1;61case PIPE_TEXTURE_2D:62case PIPE_TEXTURE_RECT:63return 2;64default:65return 3;66}67}6869static void70box_min_max(const struct pipe_box *box, int dim, int *min, int *max)71{72switch (dim) {73case 0:74if (box->width > 0) {75*min = box->x;76*max = box->x + box->width;77} else {78*max = box->x;79*min = box->x + box->width;80}81break;82case 1:83if (box->height > 0) {84*min = box->y;85*max = box->y + box->height;86} else {87*max = box->y;88*min = box->y + box->height;89}90break;91default:92if (box->depth > 0) {93*min = box->z;94*max = box->z + box->depth;95} else {96*max = box->z;97*min = box->z + box->depth;98}99break;100}101}102103static bool104transfer_overlap(const struct virgl_transfer *xfer,105const struct virgl_hw_res *hw_res,106unsigned level,107const struct pipe_box *box,108bool include_touching)109{110const int dim_count = transfer_dim(xfer);111112if (xfer->hw_res != hw_res || xfer->base.level != level)113return false;114115for (int dim = 0; dim < dim_count; dim++) {116int xfer_min;117int xfer_max;118int box_min;119int box_max;120121box_min_max(&xfer->base.box, dim, &xfer_min, &xfer_max);122box_min_max(box, dim, &box_min, &box_max);123124if (include_touching) {125/* touching is considered overlapping */126if (xfer_min > box_max || xfer_max < box_min)127return false;128} else {129/* touching is not considered overlapping */130if (xfer_min >= box_max || xfer_max <= box_min)131return false;132}133}134135return true;136}137138static struct virgl_transfer *139virgl_transfer_queue_find_overlap(const struct virgl_transfer_queue *queue,140const struct virgl_hw_res *hw_res,141unsigned level,142const struct pipe_box *box,143bool include_touching)144{145struct virgl_transfer *xfer;146LIST_FOR_EACH_ENTRY(xfer, &queue->transfer_list, queue_link) {147if (transfer_overlap(xfer, hw_res, level, box, include_touching))148return xfer;149}150151return NULL;152}153154static bool transfers_intersect(struct virgl_transfer *queued,155struct virgl_transfer *current)156{157return transfer_overlap(queued, current->hw_res, current->base.level,158¤t->base.box, true);159}160161static void remove_transfer(struct virgl_transfer_queue *queue,162struct virgl_transfer *queued)163{164list_del(&queued->queue_link);165virgl_resource_destroy_transfer(queue->vctx, queued);166}167168static void replace_unmapped_transfer(struct virgl_transfer_queue *queue,169struct list_action_args *args)170{171struct virgl_transfer *current = args->current;172struct virgl_transfer *queued = args->queued;173174u_box_union_2d(¤t->base.box, ¤t->base.box, &queued->base.box);175current->offset = current->base.box.x;176177remove_transfer(queue, queued);178queue->num_dwords -= (VIRGL_TRANSFER3D_SIZE + 1);179}180181static void transfer_put(struct virgl_transfer_queue *queue,182struct list_action_args *args)183{184struct virgl_transfer *queued = args->queued;185186queue->vs->vws->transfer_put(queue->vs->vws, queued->hw_res,187&queued->base.box,188queued->base.stride, queued->l_stride,189queued->offset, queued->base.level);190191remove_transfer(queue, queued);192}193194static void transfer_write(struct virgl_transfer_queue *queue,195struct list_action_args *args)196{197struct virgl_transfer *queued = args->queued;198struct virgl_cmd_buf *buf = args->data;199200// Takes a reference on the HW resource, which is released after201// the exec buffer command.202virgl_encode_transfer(queue->vs, buf, queued, VIRGL_TRANSFER_TO_HOST);203204remove_transfer(queue, queued);205}206207static void compare_and_perform_action(struct virgl_transfer_queue *queue,208struct list_iteration_args *iter)209{210struct list_action_args args;211struct virgl_transfer *queued, *tmp;212213memset(&args, 0, sizeof(args));214args.current = iter->current;215args.data = iter->data;216217LIST_FOR_EACH_ENTRY_SAFE(queued, tmp, &queue->transfer_list, queue_link) {218if (iter->compare(queued, iter->current)) {219args.queued = queued;220iter->action(queue, &args);221}222}223}224225static void perform_action(struct virgl_transfer_queue *queue,226struct list_iteration_args *iter)227{228struct list_action_args args;229struct virgl_transfer *queued, *tmp;230231memset(&args, 0, sizeof(args));232args.data = iter->data;233234LIST_FOR_EACH_ENTRY_SAFE(queued, tmp, &queue->transfer_list, queue_link) {235args.queued = queued;236iter->action(queue, &args);237}238}239240static void add_internal(struct virgl_transfer_queue *queue,241struct virgl_transfer *transfer)242{243uint32_t dwords = VIRGL_TRANSFER3D_SIZE + 1;244if (queue->tbuf) {245if (queue->num_dwords + dwords >= VIRGL_MAX_TBUF_DWORDS) {246struct list_iteration_args iter;247struct virgl_winsys *vws = queue->vs->vws;248249memset(&iter, 0, sizeof(iter));250iter.action = transfer_write;251iter.data = queue->tbuf;252perform_action(queue, &iter);253254vws->submit_cmd(vws, queue->tbuf, NULL);255queue->num_dwords = 0;256}257}258259list_addtail(&transfer->queue_link, &queue->transfer_list);260queue->num_dwords += dwords;261}262263void virgl_transfer_queue_init(struct virgl_transfer_queue *queue,264struct virgl_context *vctx)265{266struct virgl_screen *vs = virgl_screen(vctx->base.screen);267268queue->vs = vs;269queue->vctx = vctx;270queue->num_dwords = 0;271272list_inithead(&queue->transfer_list);273274if ((vs->caps.caps.v2.capability_bits & VIRGL_CAP_TRANSFER) &&275vs->vws->supports_encoded_transfers)276queue->tbuf = vs->vws->cmd_buf_create(vs->vws, VIRGL_MAX_TBUF_DWORDS);277else278queue->tbuf = NULL;279}280281void virgl_transfer_queue_fini(struct virgl_transfer_queue *queue)282{283struct virgl_winsys *vws = queue->vs->vws;284struct list_iteration_args iter;285286memset(&iter, 0, sizeof(iter));287288iter.action = transfer_put;289perform_action(queue, &iter);290291if (queue->tbuf)292vws->cmd_buf_destroy(queue->tbuf);293294queue->vs = NULL;295queue->vctx = NULL;296queue->tbuf = NULL;297queue->num_dwords = 0;298}299300int virgl_transfer_queue_unmap(struct virgl_transfer_queue *queue,301struct virgl_transfer *transfer)302{303struct list_iteration_args iter;304305/* We don't support copy transfers in the transfer queue. */306assert(!transfer->copy_src_hw_res);307308/* Attempt to merge multiple intersecting transfers into a single one. */309if (transfer->base.resource->target == PIPE_BUFFER) {310memset(&iter, 0, sizeof(iter));311iter.current = transfer;312iter.compare = transfers_intersect;313iter.action = replace_unmapped_transfer;314compare_and_perform_action(queue, &iter);315}316317add_internal(queue, transfer);318return 0;319}320321int virgl_transfer_queue_clear(struct virgl_transfer_queue *queue,322struct virgl_cmd_buf *cbuf)323{324struct list_iteration_args iter;325326memset(&iter, 0, sizeof(iter));327if (queue->tbuf) {328uint32_t prior_num_dwords = cbuf->cdw;329cbuf->cdw = 0;330331iter.action = transfer_write;332iter.data = cbuf;333perform_action(queue, &iter);334335virgl_encode_end_transfers(cbuf);336cbuf->cdw = prior_num_dwords;337} else {338iter.action = transfer_put;339perform_action(queue, &iter);340}341342queue->num_dwords = 0;343344return 0;345}346347bool virgl_transfer_queue_is_queued(struct virgl_transfer_queue *queue,348struct virgl_transfer *transfer)349{350return virgl_transfer_queue_find_overlap(queue,351transfer->hw_res,352transfer->base.level,353&transfer->base.box,354false);355}356357bool358virgl_transfer_queue_extend_buffer(struct virgl_transfer_queue *queue,359const struct virgl_hw_res *hw_res,360unsigned offset, unsigned size,361const void *data)362{363struct virgl_transfer *queued;364struct pipe_box box;365366u_box_1d(offset, size, &box);367queued = virgl_transfer_queue_find_overlap(queue, hw_res, 0, &box, true);368if (!queued)369return false;370371assert(queued->base.resource->target == PIPE_BUFFER);372assert(queued->hw_res_map);373374memcpy(queued->hw_res_map + offset, data, size);375u_box_union_2d(&queued->base.box, &queued->base.box, &box);376queued->offset = queued->base.box.x;377378return true;379}380381382