1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright 2018 Chromium. 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * on the rights to use, copy, modify, merge, publish, distribute, sub 8bf215546Sopenharmony_ci * license, and/or sell copies of the Software, and to permit persons to whom 9bf215546Sopenharmony_ci * the Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 19bf215546Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20bf215546Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21bf215546Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#include "util/u_box.h" 25bf215546Sopenharmony_ci#include "util/u_inlines.h" 26bf215546Sopenharmony_ci 27bf215546Sopenharmony_ci#include "virtio-gpu/virgl_protocol.h" 28bf215546Sopenharmony_ci#include "virgl_context.h" 29bf215546Sopenharmony_ci#include "virgl_screen.h" 30bf215546Sopenharmony_ci#include "virgl_encode.h" 31bf215546Sopenharmony_ci#include "virgl_resource.h" 32bf215546Sopenharmony_ci#include "virgl_transfer_queue.h" 33bf215546Sopenharmony_ci 34bf215546Sopenharmony_cistruct list_action_args 35bf215546Sopenharmony_ci{ 36bf215546Sopenharmony_ci void *data; 37bf215546Sopenharmony_ci struct virgl_transfer *queued; 38bf215546Sopenharmony_ci struct virgl_transfer *current; 39bf215546Sopenharmony_ci}; 40bf215546Sopenharmony_ci 41bf215546Sopenharmony_citypedef bool (*compare_transfers_t)(struct virgl_transfer *queued, 42bf215546Sopenharmony_ci struct virgl_transfer *current); 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_citypedef void (*list_action_t)(struct virgl_transfer_queue *queue, 45bf215546Sopenharmony_ci struct list_action_args *args); 46bf215546Sopenharmony_ci 47bf215546Sopenharmony_cistruct list_iteration_args 48bf215546Sopenharmony_ci{ 49bf215546Sopenharmony_ci void *data; 50bf215546Sopenharmony_ci list_action_t action; 51bf215546Sopenharmony_ci compare_transfers_t compare; 52bf215546Sopenharmony_ci struct virgl_transfer *current; 53bf215546Sopenharmony_ci}; 54bf215546Sopenharmony_ci 55bf215546Sopenharmony_cistatic int 56bf215546Sopenharmony_citransfer_dim(const struct virgl_transfer *xfer) 57bf215546Sopenharmony_ci{ 58bf215546Sopenharmony_ci switch (xfer->base.resource->target) { 59bf215546Sopenharmony_ci case PIPE_BUFFER: 60bf215546Sopenharmony_ci case PIPE_TEXTURE_1D: 61bf215546Sopenharmony_ci return 1; 62bf215546Sopenharmony_ci case PIPE_TEXTURE_2D: 63bf215546Sopenharmony_ci case PIPE_TEXTURE_RECT: 64bf215546Sopenharmony_ci return 2; 65bf215546Sopenharmony_ci default: 66bf215546Sopenharmony_ci return 3; 67bf215546Sopenharmony_ci } 68bf215546Sopenharmony_ci} 69bf215546Sopenharmony_ci 70bf215546Sopenharmony_cistatic void 71bf215546Sopenharmony_cibox_min_max(const struct pipe_box *box, int dim, int *min, int *max) 72bf215546Sopenharmony_ci{ 73bf215546Sopenharmony_ci switch (dim) { 74bf215546Sopenharmony_ci case 0: 75bf215546Sopenharmony_ci if (box->width > 0) { 76bf215546Sopenharmony_ci *min = box->x; 77bf215546Sopenharmony_ci *max = box->x + box->width; 78bf215546Sopenharmony_ci } else { 79bf215546Sopenharmony_ci *max = box->x; 80bf215546Sopenharmony_ci *min = box->x + box->width; 81bf215546Sopenharmony_ci } 82bf215546Sopenharmony_ci break; 83bf215546Sopenharmony_ci case 1: 84bf215546Sopenharmony_ci if (box->height > 0) { 85bf215546Sopenharmony_ci *min = box->y; 86bf215546Sopenharmony_ci *max = box->y + box->height; 87bf215546Sopenharmony_ci } else { 88bf215546Sopenharmony_ci *max = box->y; 89bf215546Sopenharmony_ci *min = box->y + box->height; 90bf215546Sopenharmony_ci } 91bf215546Sopenharmony_ci break; 92bf215546Sopenharmony_ci default: 93bf215546Sopenharmony_ci if (box->depth > 0) { 94bf215546Sopenharmony_ci *min = box->z; 95bf215546Sopenharmony_ci *max = box->z + box->depth; 96bf215546Sopenharmony_ci } else { 97bf215546Sopenharmony_ci *max = box->z; 98bf215546Sopenharmony_ci *min = box->z + box->depth; 99bf215546Sopenharmony_ci } 100bf215546Sopenharmony_ci break; 101bf215546Sopenharmony_ci } 102bf215546Sopenharmony_ci} 103bf215546Sopenharmony_ci 104bf215546Sopenharmony_cistatic bool 105bf215546Sopenharmony_citransfer_overlap(const struct virgl_transfer *xfer, 106bf215546Sopenharmony_ci const struct virgl_hw_res *hw_res, 107bf215546Sopenharmony_ci unsigned level, 108bf215546Sopenharmony_ci const struct pipe_box *box, 109bf215546Sopenharmony_ci bool include_touching) 110bf215546Sopenharmony_ci{ 111bf215546Sopenharmony_ci const int dim_count = transfer_dim(xfer); 112bf215546Sopenharmony_ci 113bf215546Sopenharmony_ci if (xfer->hw_res != hw_res || xfer->base.level != level) 114bf215546Sopenharmony_ci return false; 115bf215546Sopenharmony_ci 116bf215546Sopenharmony_ci for (int dim = 0; dim < dim_count; dim++) { 117bf215546Sopenharmony_ci int xfer_min; 118bf215546Sopenharmony_ci int xfer_max; 119bf215546Sopenharmony_ci int box_min; 120bf215546Sopenharmony_ci int box_max; 121bf215546Sopenharmony_ci 122bf215546Sopenharmony_ci box_min_max(&xfer->base.box, dim, &xfer_min, &xfer_max); 123bf215546Sopenharmony_ci box_min_max(box, dim, &box_min, &box_max); 124bf215546Sopenharmony_ci 125bf215546Sopenharmony_ci if (include_touching) { 126bf215546Sopenharmony_ci /* touching is considered overlapping */ 127bf215546Sopenharmony_ci if (xfer_min > box_max || xfer_max < box_min) 128bf215546Sopenharmony_ci return false; 129bf215546Sopenharmony_ci } else { 130bf215546Sopenharmony_ci /* touching is not considered overlapping */ 131bf215546Sopenharmony_ci if (xfer_min >= box_max || xfer_max <= box_min) 132bf215546Sopenharmony_ci return false; 133bf215546Sopenharmony_ci } 134bf215546Sopenharmony_ci } 135bf215546Sopenharmony_ci 136bf215546Sopenharmony_ci return true; 137bf215546Sopenharmony_ci} 138bf215546Sopenharmony_ci 139bf215546Sopenharmony_cistatic struct virgl_transfer * 140bf215546Sopenharmony_civirgl_transfer_queue_find_overlap(const struct virgl_transfer_queue *queue, 141bf215546Sopenharmony_ci const struct virgl_hw_res *hw_res, 142bf215546Sopenharmony_ci unsigned level, 143bf215546Sopenharmony_ci const struct pipe_box *box, 144bf215546Sopenharmony_ci bool include_touching) 145bf215546Sopenharmony_ci{ 146bf215546Sopenharmony_ci struct virgl_transfer *xfer; 147bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY(xfer, &queue->transfer_list, queue_link) { 148bf215546Sopenharmony_ci if (transfer_overlap(xfer, hw_res, level, box, include_touching)) 149bf215546Sopenharmony_ci return xfer; 150bf215546Sopenharmony_ci } 151bf215546Sopenharmony_ci 152bf215546Sopenharmony_ci return NULL; 153bf215546Sopenharmony_ci} 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_cistatic bool transfers_intersect(struct virgl_transfer *queued, 156bf215546Sopenharmony_ci struct virgl_transfer *current) 157bf215546Sopenharmony_ci{ 158bf215546Sopenharmony_ci return transfer_overlap(queued, current->hw_res, current->base.level, 159bf215546Sopenharmony_ci ¤t->base.box, true); 160bf215546Sopenharmony_ci} 161bf215546Sopenharmony_ci 162bf215546Sopenharmony_cistatic void remove_transfer(struct virgl_transfer_queue *queue, 163bf215546Sopenharmony_ci struct virgl_transfer *queued) 164bf215546Sopenharmony_ci{ 165bf215546Sopenharmony_ci list_del(&queued->queue_link); 166bf215546Sopenharmony_ci virgl_resource_destroy_transfer(queue->vctx, queued); 167bf215546Sopenharmony_ci} 168bf215546Sopenharmony_ci 169bf215546Sopenharmony_cistatic void replace_unmapped_transfer(struct virgl_transfer_queue *queue, 170bf215546Sopenharmony_ci struct list_action_args *args) 171bf215546Sopenharmony_ci{ 172bf215546Sopenharmony_ci struct virgl_transfer *current = args->current; 173bf215546Sopenharmony_ci struct virgl_transfer *queued = args->queued; 174bf215546Sopenharmony_ci 175bf215546Sopenharmony_ci u_box_union_2d(¤t->base.box, ¤t->base.box, &queued->base.box); 176bf215546Sopenharmony_ci current->offset = current->base.box.x; 177bf215546Sopenharmony_ci 178bf215546Sopenharmony_ci remove_transfer(queue, queued); 179bf215546Sopenharmony_ci queue->num_dwords -= (VIRGL_TRANSFER3D_SIZE + 1); 180bf215546Sopenharmony_ci} 181bf215546Sopenharmony_ci 182bf215546Sopenharmony_cistatic void transfer_put(struct virgl_transfer_queue *queue, 183bf215546Sopenharmony_ci struct list_action_args *args) 184bf215546Sopenharmony_ci{ 185bf215546Sopenharmony_ci struct virgl_transfer *queued = args->queued; 186bf215546Sopenharmony_ci 187bf215546Sopenharmony_ci queue->vs->vws->transfer_put(queue->vs->vws, queued->hw_res, 188bf215546Sopenharmony_ci &queued->base.box, 189bf215546Sopenharmony_ci queued->base.stride, queued->l_stride, 190bf215546Sopenharmony_ci queued->offset, queued->base.level); 191bf215546Sopenharmony_ci 192bf215546Sopenharmony_ci remove_transfer(queue, queued); 193bf215546Sopenharmony_ci} 194bf215546Sopenharmony_ci 195bf215546Sopenharmony_cistatic void transfer_write(struct virgl_transfer_queue *queue, 196bf215546Sopenharmony_ci struct list_action_args *args) 197bf215546Sopenharmony_ci{ 198bf215546Sopenharmony_ci struct virgl_transfer *queued = args->queued; 199bf215546Sopenharmony_ci struct virgl_cmd_buf *buf = args->data; 200bf215546Sopenharmony_ci 201bf215546Sopenharmony_ci // Takes a reference on the HW resource, which is released after 202bf215546Sopenharmony_ci // the exec buffer command. 203bf215546Sopenharmony_ci virgl_encode_transfer(queue->vs, buf, queued, VIRGL_TRANSFER_TO_HOST); 204bf215546Sopenharmony_ci 205bf215546Sopenharmony_ci remove_transfer(queue, queued); 206bf215546Sopenharmony_ci} 207bf215546Sopenharmony_ci 208bf215546Sopenharmony_cistatic void compare_and_perform_action(struct virgl_transfer_queue *queue, 209bf215546Sopenharmony_ci struct list_iteration_args *iter) 210bf215546Sopenharmony_ci{ 211bf215546Sopenharmony_ci struct list_action_args args; 212bf215546Sopenharmony_ci struct virgl_transfer *queued, *tmp; 213bf215546Sopenharmony_ci 214bf215546Sopenharmony_ci memset(&args, 0, sizeof(args)); 215bf215546Sopenharmony_ci args.current = iter->current; 216bf215546Sopenharmony_ci args.data = iter->data; 217bf215546Sopenharmony_ci 218bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE(queued, tmp, &queue->transfer_list, queue_link) { 219bf215546Sopenharmony_ci if (iter->compare(queued, iter->current)) { 220bf215546Sopenharmony_ci args.queued = queued; 221bf215546Sopenharmony_ci iter->action(queue, &args); 222bf215546Sopenharmony_ci } 223bf215546Sopenharmony_ci } 224bf215546Sopenharmony_ci} 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_cistatic void perform_action(struct virgl_transfer_queue *queue, 227bf215546Sopenharmony_ci struct list_iteration_args *iter) 228bf215546Sopenharmony_ci{ 229bf215546Sopenharmony_ci struct list_action_args args; 230bf215546Sopenharmony_ci struct virgl_transfer *queued, *tmp; 231bf215546Sopenharmony_ci 232bf215546Sopenharmony_ci memset(&args, 0, sizeof(args)); 233bf215546Sopenharmony_ci args.data = iter->data; 234bf215546Sopenharmony_ci 235bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE(queued, tmp, &queue->transfer_list, queue_link) { 236bf215546Sopenharmony_ci args.queued = queued; 237bf215546Sopenharmony_ci iter->action(queue, &args); 238bf215546Sopenharmony_ci } 239bf215546Sopenharmony_ci} 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_cistatic void add_internal(struct virgl_transfer_queue *queue, 242bf215546Sopenharmony_ci struct virgl_transfer *transfer) 243bf215546Sopenharmony_ci{ 244bf215546Sopenharmony_ci uint32_t dwords = VIRGL_TRANSFER3D_SIZE + 1; 245bf215546Sopenharmony_ci if (queue->tbuf) { 246bf215546Sopenharmony_ci if (queue->num_dwords + dwords >= VIRGL_MAX_TBUF_DWORDS) { 247bf215546Sopenharmony_ci struct list_iteration_args iter; 248bf215546Sopenharmony_ci struct virgl_winsys *vws = queue->vs->vws; 249bf215546Sopenharmony_ci 250bf215546Sopenharmony_ci memset(&iter, 0, sizeof(iter)); 251bf215546Sopenharmony_ci iter.action = transfer_write; 252bf215546Sopenharmony_ci iter.data = queue->tbuf; 253bf215546Sopenharmony_ci perform_action(queue, &iter); 254bf215546Sopenharmony_ci 255bf215546Sopenharmony_ci vws->submit_cmd(vws, queue->tbuf, NULL); 256bf215546Sopenharmony_ci queue->num_dwords = 0; 257bf215546Sopenharmony_ci } 258bf215546Sopenharmony_ci } 259bf215546Sopenharmony_ci 260bf215546Sopenharmony_ci list_addtail(&transfer->queue_link, &queue->transfer_list); 261bf215546Sopenharmony_ci queue->num_dwords += dwords; 262bf215546Sopenharmony_ci} 263bf215546Sopenharmony_ci 264bf215546Sopenharmony_civoid virgl_transfer_queue_init(struct virgl_transfer_queue *queue, 265bf215546Sopenharmony_ci struct virgl_context *vctx) 266bf215546Sopenharmony_ci{ 267bf215546Sopenharmony_ci struct virgl_screen *vs = virgl_screen(vctx->base.screen); 268bf215546Sopenharmony_ci 269bf215546Sopenharmony_ci queue->vs = vs; 270bf215546Sopenharmony_ci queue->vctx = vctx; 271bf215546Sopenharmony_ci queue->num_dwords = 0; 272bf215546Sopenharmony_ci 273bf215546Sopenharmony_ci list_inithead(&queue->transfer_list); 274bf215546Sopenharmony_ci 275bf215546Sopenharmony_ci if ((vs->caps.caps.v2.capability_bits & VIRGL_CAP_TRANSFER) && 276bf215546Sopenharmony_ci vs->vws->supports_encoded_transfers) 277bf215546Sopenharmony_ci queue->tbuf = vs->vws->cmd_buf_create(vs->vws, VIRGL_MAX_TBUF_DWORDS); 278bf215546Sopenharmony_ci else 279bf215546Sopenharmony_ci queue->tbuf = NULL; 280bf215546Sopenharmony_ci} 281bf215546Sopenharmony_ci 282bf215546Sopenharmony_civoid virgl_transfer_queue_fini(struct virgl_transfer_queue *queue) 283bf215546Sopenharmony_ci{ 284bf215546Sopenharmony_ci struct virgl_winsys *vws = queue->vs->vws; 285bf215546Sopenharmony_ci struct list_iteration_args iter; 286bf215546Sopenharmony_ci 287bf215546Sopenharmony_ci memset(&iter, 0, sizeof(iter)); 288bf215546Sopenharmony_ci 289bf215546Sopenharmony_ci iter.action = transfer_put; 290bf215546Sopenharmony_ci perform_action(queue, &iter); 291bf215546Sopenharmony_ci 292bf215546Sopenharmony_ci if (queue->tbuf) 293bf215546Sopenharmony_ci vws->cmd_buf_destroy(queue->tbuf); 294bf215546Sopenharmony_ci 295bf215546Sopenharmony_ci queue->vs = NULL; 296bf215546Sopenharmony_ci queue->vctx = NULL; 297bf215546Sopenharmony_ci queue->tbuf = NULL; 298bf215546Sopenharmony_ci queue->num_dwords = 0; 299bf215546Sopenharmony_ci} 300bf215546Sopenharmony_ci 301bf215546Sopenharmony_ciint virgl_transfer_queue_unmap(struct virgl_transfer_queue *queue, 302bf215546Sopenharmony_ci struct virgl_transfer *transfer) 303bf215546Sopenharmony_ci{ 304bf215546Sopenharmony_ci struct list_iteration_args iter; 305bf215546Sopenharmony_ci 306bf215546Sopenharmony_ci /* We don't support copy transfers in the transfer queue. */ 307bf215546Sopenharmony_ci assert(!transfer->copy_src_hw_res); 308bf215546Sopenharmony_ci 309bf215546Sopenharmony_ci /* Attempt to merge multiple intersecting transfers into a single one. */ 310bf215546Sopenharmony_ci if (transfer->base.resource->target == PIPE_BUFFER) { 311bf215546Sopenharmony_ci memset(&iter, 0, sizeof(iter)); 312bf215546Sopenharmony_ci iter.current = transfer; 313bf215546Sopenharmony_ci iter.compare = transfers_intersect; 314bf215546Sopenharmony_ci iter.action = replace_unmapped_transfer; 315bf215546Sopenharmony_ci compare_and_perform_action(queue, &iter); 316bf215546Sopenharmony_ci } 317bf215546Sopenharmony_ci 318bf215546Sopenharmony_ci add_internal(queue, transfer); 319bf215546Sopenharmony_ci return 0; 320bf215546Sopenharmony_ci} 321bf215546Sopenharmony_ci 322bf215546Sopenharmony_ciint virgl_transfer_queue_clear(struct virgl_transfer_queue *queue, 323bf215546Sopenharmony_ci struct virgl_cmd_buf *cbuf) 324bf215546Sopenharmony_ci{ 325bf215546Sopenharmony_ci struct list_iteration_args iter; 326bf215546Sopenharmony_ci 327bf215546Sopenharmony_ci memset(&iter, 0, sizeof(iter)); 328bf215546Sopenharmony_ci if (queue->tbuf) { 329bf215546Sopenharmony_ci uint32_t prior_num_dwords = cbuf->cdw; 330bf215546Sopenharmony_ci cbuf->cdw = 0; 331bf215546Sopenharmony_ci 332bf215546Sopenharmony_ci iter.action = transfer_write; 333bf215546Sopenharmony_ci iter.data = cbuf; 334bf215546Sopenharmony_ci perform_action(queue, &iter); 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci virgl_encode_end_transfers(cbuf); 337bf215546Sopenharmony_ci cbuf->cdw = prior_num_dwords; 338bf215546Sopenharmony_ci } else { 339bf215546Sopenharmony_ci iter.action = transfer_put; 340bf215546Sopenharmony_ci perform_action(queue, &iter); 341bf215546Sopenharmony_ci } 342bf215546Sopenharmony_ci 343bf215546Sopenharmony_ci queue->num_dwords = 0; 344bf215546Sopenharmony_ci 345bf215546Sopenharmony_ci return 0; 346bf215546Sopenharmony_ci} 347bf215546Sopenharmony_ci 348bf215546Sopenharmony_cibool virgl_transfer_queue_is_queued(struct virgl_transfer_queue *queue, 349bf215546Sopenharmony_ci struct virgl_transfer *transfer) 350bf215546Sopenharmony_ci{ 351bf215546Sopenharmony_ci return virgl_transfer_queue_find_overlap(queue, 352bf215546Sopenharmony_ci transfer->hw_res, 353bf215546Sopenharmony_ci transfer->base.level, 354bf215546Sopenharmony_ci &transfer->base.box, 355bf215546Sopenharmony_ci false); 356bf215546Sopenharmony_ci} 357bf215546Sopenharmony_ci 358bf215546Sopenharmony_cibool 359bf215546Sopenharmony_civirgl_transfer_queue_extend_buffer(struct virgl_transfer_queue *queue, 360bf215546Sopenharmony_ci const struct virgl_hw_res *hw_res, 361bf215546Sopenharmony_ci unsigned offset, unsigned size, 362bf215546Sopenharmony_ci const void *data) 363bf215546Sopenharmony_ci{ 364bf215546Sopenharmony_ci struct virgl_transfer *queued; 365bf215546Sopenharmony_ci struct pipe_box box; 366bf215546Sopenharmony_ci 367bf215546Sopenharmony_ci u_box_1d(offset, size, &box); 368bf215546Sopenharmony_ci queued = virgl_transfer_queue_find_overlap(queue, hw_res, 0, &box, true); 369bf215546Sopenharmony_ci if (!queued) 370bf215546Sopenharmony_ci return false; 371bf215546Sopenharmony_ci 372bf215546Sopenharmony_ci assert(queued->base.resource->target == PIPE_BUFFER); 373bf215546Sopenharmony_ci assert(queued->hw_res_map); 374bf215546Sopenharmony_ci 375bf215546Sopenharmony_ci memcpy(queued->hw_res_map + offset, data, size); 376bf215546Sopenharmony_ci u_box_union_2d(&queued->base.box, &queued->base.box, &box); 377bf215546Sopenharmony_ci queued->offset = queued->base.box.x; 378bf215546Sopenharmony_ci 379bf215546Sopenharmony_ci return true; 380bf215546Sopenharmony_ci} 381