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