1/* 2 * Copyright 2014, 2015 Red Hat. 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#include <stdio.h> 24#include "util/u_memory.h" 25#include "util/u_format.h" 26#include "util/u_inlines.h" 27#include "os/os_time.h" 28#include "state_tracker/sw_winsys.h" 29 30#include "virgl_vtest_winsys.h" 31#include "virgl_vtest_public.h" 32 33static void *virgl_vtest_resource_map(struct virgl_winsys *vws, 34 struct virgl_hw_res *res); 35static void virgl_vtest_resource_unmap(struct virgl_winsys *vws, 36 struct virgl_hw_res *res); 37 38static inline boolean can_cache_resource(struct virgl_hw_res *res) 39{ 40 return res->cacheable == TRUE; 41} 42 43static uint32_t vtest_get_transfer_size(struct virgl_hw_res *res, 44 const struct pipe_box *box, 45 uint32_t stride, uint32_t layer_stride, 46 uint32_t level, uint32_t *valid_stride_p) 47{ 48 uint32_t valid_stride, valid_layer_stride; 49 50 valid_stride = util_format_get_stride(res->format, box->width); 51 if (stride) { 52 if (box->height > 1) 53 valid_stride = stride; 54 } 55 56 valid_layer_stride = util_format_get_2d_size(res->format, valid_stride, 57 box->height); 58 if (layer_stride) { 59 if (box->depth > 1) 60 valid_layer_stride = layer_stride; 61 } 62 63 *valid_stride_p = valid_stride; 64 return valid_layer_stride * box->depth; 65} 66 67static int 68virgl_vtest_transfer_put(struct virgl_winsys *vws, 69 struct virgl_hw_res *res, 70 const struct pipe_box *box, 71 uint32_t stride, uint32_t layer_stride, 72 uint32_t buf_offset, uint32_t level) 73{ 74 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 75 uint32_t size; 76 void *ptr; 77 uint32_t valid_stride; 78 79 size = vtest_get_transfer_size(res, box, stride, layer_stride, level, 80 &valid_stride); 81 82 virgl_vtest_send_transfer_cmd(vtws, VCMD_TRANSFER_PUT, res->res_handle, 83 level, stride, layer_stride, 84 box, size); 85 ptr = virgl_vtest_resource_map(vws, res); 86 virgl_vtest_send_transfer_put_data(vtws, ptr + buf_offset, size); 87 virgl_vtest_resource_unmap(vws, res); 88 return 0; 89} 90 91static int 92virgl_vtest_transfer_get(struct virgl_winsys *vws, 93 struct virgl_hw_res *res, 94 const struct pipe_box *box, 95 uint32_t stride, uint32_t layer_stride, 96 uint32_t buf_offset, uint32_t level) 97{ 98 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 99 uint32_t size; 100 void *ptr; 101 uint32_t valid_stride; 102 103 size = vtest_get_transfer_size(res, box, stride, layer_stride, level, 104 &valid_stride); 105 106 virgl_vtest_send_transfer_cmd(vtws, VCMD_TRANSFER_GET, res->res_handle, 107 level, stride, layer_stride, 108 box, size); 109 110 111 ptr = virgl_vtest_resource_map(vws, res); 112 virgl_vtest_recv_transfer_get_data(vtws, ptr + buf_offset, size, 113 valid_stride, box, res->format); 114 virgl_vtest_resource_unmap(vws, res); 115 return 0; 116} 117 118static void virgl_hw_res_destroy(struct virgl_vtest_winsys *vtws, 119 struct virgl_hw_res *res) 120{ 121 virgl_vtest_send_resource_unref(vtws, res->res_handle); 122 if (res->dt) 123 vtws->sws->displaytarget_destroy(vtws->sws, res->dt); 124 free(res->ptr); 125 FREE(res); 126} 127 128static boolean virgl_vtest_resource_is_busy(struct virgl_vtest_winsys *vtws, 129 struct virgl_hw_res *res) 130{ 131 /* implement busy check */ 132 int ret; 133 ret = virgl_vtest_busy_wait(vtws, res->res_handle, 0); 134 135 if (ret < 0) 136 return FALSE; 137 138 return ret == 1 ? TRUE : FALSE; 139} 140 141static void 142virgl_cache_flush(struct virgl_vtest_winsys *vtws) 143{ 144 struct list_head *curr, *next; 145 struct virgl_hw_res *res; 146 147 pipe_mutex_lock(vtws->mutex); 148 curr = vtws->delayed.next; 149 next = curr->next; 150 151 while (curr != &vtws->delayed) { 152 res = LIST_ENTRY(struct virgl_hw_res, curr, head); 153 LIST_DEL(&res->head); 154 virgl_hw_res_destroy(vtws, res); 155 curr = next; 156 next = curr->next; 157 } 158 pipe_mutex_unlock(vtws->mutex); 159} 160 161static void 162virgl_cache_list_check_free(struct virgl_vtest_winsys *vtws) 163{ 164 struct list_head *curr, *next; 165 struct virgl_hw_res *res; 166 int64_t now; 167 168 now = os_time_get(); 169 curr = vtws->delayed.next; 170 next = curr->next; 171 while (curr != &vtws->delayed) { 172 res = LIST_ENTRY(struct virgl_hw_res, curr, head); 173 if (!os_time_timeout(res->start, res->end, now)) 174 break; 175 176 LIST_DEL(&res->head); 177 virgl_hw_res_destroy(vtws, res); 178 curr = next; 179 next = curr->next; 180 } 181} 182 183static void virgl_vtest_resource_reference(struct virgl_vtest_winsys *vtws, 184 struct virgl_hw_res **dres, 185 struct virgl_hw_res *sres) 186{ 187 struct virgl_hw_res *old = *dres; 188 if (pipe_reference(&(*dres)->reference, &sres->reference)) { 189 if (!can_cache_resource(old)) { 190 virgl_hw_res_destroy(vtws, old); 191 } else { 192 pipe_mutex_lock(vtws->mutex); 193 virgl_cache_list_check_free(vtws); 194 195 old->start = os_time_get(); 196 old->end = old->start + vtws->usecs; 197 LIST_ADDTAIL(&old->head, &vtws->delayed); 198 vtws->num_delayed++; 199 pipe_mutex_unlock(vtws->mutex); 200 } 201 } 202 *dres = sres; 203} 204 205static struct virgl_hw_res * 206virgl_vtest_winsys_resource_create(struct virgl_winsys *vws, 207 enum pipe_texture_target target, 208 uint32_t format, 209 uint32_t bind, 210 uint32_t width, 211 uint32_t height, 212 uint32_t depth, 213 uint32_t array_size, 214 uint32_t last_level, 215 uint32_t nr_samples, 216 uint32_t size) 217{ 218 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 219 struct virgl_hw_res *res; 220 static int handle = 1; 221 222 res = CALLOC_STRUCT(virgl_hw_res); 223 if (!res) 224 return NULL; 225 226 if (bind & (VIRGL_BIND_DISPLAY_TARGET | VIRGL_BIND_SCANOUT)) { 227 res->dt = vtws->sws->displaytarget_create(vtws->sws, bind, format, 228 width, height, 64, NULL, 229 &res->stride); 230 231 } else { 232 res->ptr = align_malloc(size, 64); 233 if (!res->ptr) { 234 FREE(res); 235 return NULL; 236 } 237 } 238 239 res->bind = bind; 240 res->format = format; 241 res->height = height; 242 res->width = width; 243 virgl_vtest_send_resource_create(vtws, handle, target, format, bind, 244 width, height, depth, array_size, 245 last_level, nr_samples); 246 247 res->res_handle = handle++; 248 pipe_reference_init(&res->reference, 1); 249 return res; 250} 251 252static void virgl_vtest_winsys_resource_unref(struct virgl_winsys *vws, 253 struct virgl_hw_res *hres) 254{ 255 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 256 virgl_vtest_resource_reference(vtws, &hres, NULL); 257} 258 259static void *virgl_vtest_resource_map(struct virgl_winsys *vws, 260 struct virgl_hw_res *res) 261{ 262 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 263 264 if (res->dt) { 265 return vtws->sws->displaytarget_map(vtws->sws, res->dt, 0); 266 } else { 267 res->mapped = res->ptr; 268 return res->mapped; 269 } 270} 271 272static void virgl_vtest_resource_unmap(struct virgl_winsys *vws, 273 struct virgl_hw_res *res) 274{ 275 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 276 if (res->mapped) 277 res->mapped = NULL; 278 279 if (res->dt) 280 vtws->sws->displaytarget_unmap(vtws->sws, res->dt); 281} 282 283static void virgl_vtest_resource_wait(struct virgl_winsys *vws, 284 struct virgl_hw_res *res) 285{ 286 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 287 288 virgl_vtest_busy_wait(vtws, res->res_handle, VCMD_BUSY_WAIT_FLAG_WAIT); 289} 290 291static inline int virgl_is_res_compat(struct virgl_vtest_winsys *vtws, 292 struct virgl_hw_res *res, 293 uint32_t size, uint32_t bind, 294 uint32_t format) 295{ 296 if (res->bind != bind) 297 return 0; 298 if (res->format != format) 299 return 0; 300 if (res->size < size) 301 return 0; 302 if (res->size > size * 2) 303 return 0; 304 305 if (virgl_vtest_resource_is_busy(vtws, res)) { 306 return -1; 307 } 308 309 return 1; 310} 311 312static struct virgl_hw_res * 313virgl_vtest_winsys_resource_cache_create(struct virgl_winsys *vws, 314 enum pipe_texture_target target, 315 uint32_t format, 316 uint32_t bind, 317 uint32_t width, 318 uint32_t height, 319 uint32_t depth, 320 uint32_t array_size, 321 uint32_t last_level, 322 uint32_t nr_samples, 323 uint32_t size) 324{ 325 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 326 struct virgl_hw_res *res, *curr_res; 327 struct list_head *curr, *next; 328 int64_t now; 329 int ret; 330 331 /* only store binds for vertex/index/const buffers */ 332 if (bind != VIRGL_BIND_CONSTANT_BUFFER && bind != VIRGL_BIND_INDEX_BUFFER && 333 bind != VIRGL_BIND_VERTEX_BUFFER && bind != VIRGL_BIND_CUSTOM) 334 goto alloc; 335 336 pipe_mutex_lock(vtws->mutex); 337 338 res = NULL; 339 curr = vtws->delayed.next; 340 next = curr->next; 341 342 now = os_time_get(); 343 while (curr != &vtws->delayed) { 344 curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head); 345 346 if (!res && ((ret = virgl_is_res_compat(vtws, curr_res, size, bind, format)) > 0)) 347 res = curr_res; 348 else if (os_time_timeout(curr_res->start, curr_res->end, now)) { 349 LIST_DEL(&curr_res->head); 350 virgl_hw_res_destroy(vtws, curr_res); 351 } else 352 break; 353 354 if (ret == -1) 355 break; 356 357 curr = next; 358 next = curr->next; 359 } 360 361 if (!res && ret != -1) { 362 while (curr != &vtws->delayed) { 363 curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head); 364 ret = virgl_is_res_compat(vtws, curr_res, size, bind, format); 365 if (ret > 0) { 366 res = curr_res; 367 break; 368 } 369 if (ret == -1) 370 break; 371 curr = next; 372 next = curr->next; 373 } 374 } 375 376 if (res) { 377 LIST_DEL(&res->head); 378 --vtws->num_delayed; 379 pipe_mutex_unlock(vtws->mutex); 380 pipe_reference_init(&res->reference, 1); 381 return res; 382 } 383 384 pipe_mutex_unlock(vtws->mutex); 385 386alloc: 387 res = virgl_vtest_winsys_resource_create(vws, target, format, bind, 388 width, height, depth, array_size, 389 last_level, nr_samples, size); 390 if (bind == VIRGL_BIND_CONSTANT_BUFFER || bind == VIRGL_BIND_INDEX_BUFFER || 391 bind == VIRGL_BIND_VERTEX_BUFFER) 392 res->cacheable = TRUE; 393 return res; 394} 395 396static struct virgl_cmd_buf *virgl_vtest_cmd_buf_create(struct virgl_winsys *vws) 397{ 398 struct virgl_vtest_cmd_buf *cbuf; 399 400 cbuf = CALLOC_STRUCT(virgl_vtest_cmd_buf); 401 if (!cbuf) 402 return NULL; 403 404 cbuf->nres = 512; 405 cbuf->res_bo = CALLOC(cbuf->nres, sizeof(struct virgl_hw_buf*)); 406 if (!cbuf->res_bo) { 407 FREE(cbuf); 408 return NULL; 409 } 410 cbuf->ws = vws; 411 cbuf->base.buf = cbuf->buf; 412 return &cbuf->base; 413} 414 415static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf *_cbuf) 416{ 417 struct virgl_vtest_cmd_buf *cbuf = virgl_vtest_cmd_buf(_cbuf); 418 419 FREE(cbuf->res_bo); 420 FREE(cbuf); 421} 422 423static boolean virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf *cbuf, 424 struct virgl_hw_res *res) 425{ 426 unsigned hash = res->res_handle & (sizeof(cbuf->is_handle_added)-1); 427 int i; 428 429 if (cbuf->is_handle_added[hash]) { 430 i = cbuf->reloc_indices_hashlist[hash]; 431 if (cbuf->res_bo[i] == res) 432 return true; 433 434 for (i = 0; i < cbuf->cres; i++) { 435 if (cbuf->res_bo[i] == res) { 436 cbuf->reloc_indices_hashlist[hash] = i; 437 return true; 438 } 439 } 440 } 441 return false; 442} 443 444static void virgl_vtest_release_all_res(struct virgl_vtest_winsys *vtws, 445 struct virgl_vtest_cmd_buf *cbuf) 446{ 447 int i; 448 449 for (i = 0; i < cbuf->cres; i++) { 450 p_atomic_dec(&cbuf->res_bo[i]->num_cs_references); 451 virgl_vtest_resource_reference(vtws, &cbuf->res_bo[i], NULL); 452 } 453 cbuf->cres = 0; 454} 455 456static void virgl_vtest_add_res(struct virgl_vtest_winsys *vtws, 457 struct virgl_vtest_cmd_buf *cbuf, 458 struct virgl_hw_res *res) 459{ 460 unsigned hash = res->res_handle & (sizeof(cbuf->is_handle_added)-1); 461 462 if (cbuf->cres > cbuf->nres) { 463 fprintf(stderr,"failure to add relocation\n"); 464 return; 465 } 466 467 cbuf->res_bo[cbuf->cres] = NULL; 468 virgl_vtest_resource_reference(vtws, &cbuf->res_bo[cbuf->cres], res); 469 cbuf->is_handle_added[hash] = TRUE; 470 471 cbuf->reloc_indices_hashlist[hash] = cbuf->cres; 472 p_atomic_inc(&res->num_cs_references); 473 cbuf->cres++; 474} 475 476static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys *vws, 477 struct virgl_cmd_buf *_cbuf) 478{ 479 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 480 struct virgl_vtest_cmd_buf *cbuf = virgl_vtest_cmd_buf(_cbuf); 481 int ret; 482 483 if (cbuf->base.cdw == 0) 484 return 0; 485 486 ret = virgl_vtest_submit_cmd(vtws, cbuf); 487 488 virgl_vtest_release_all_res(vtws, cbuf); 489 memset(cbuf->is_handle_added, 0, sizeof(cbuf->is_handle_added)); 490 cbuf->base.cdw = 0; 491 return ret; 492} 493 494static void virgl_vtest_emit_res(struct virgl_winsys *vws, 495 struct virgl_cmd_buf *_cbuf, 496 struct virgl_hw_res *res, boolean write_buf) 497{ 498 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 499 struct virgl_vtest_cmd_buf *cbuf = virgl_vtest_cmd_buf(_cbuf); 500 boolean already_in_list = virgl_vtest_lookup_res(cbuf, res); 501 502 if (write_buf) 503 cbuf->base.buf[cbuf->base.cdw++] = res->res_handle; 504 if (!already_in_list) 505 virgl_vtest_add_res(vtws, cbuf, res); 506} 507 508static boolean virgl_vtest_res_is_ref(struct virgl_winsys *vws, 509 struct virgl_cmd_buf *_cbuf, 510 struct virgl_hw_res *res) 511{ 512 if (!res->num_cs_references) 513 return FALSE; 514 515 return TRUE; 516} 517 518static int virgl_vtest_get_caps(struct virgl_winsys *vws, 519 struct virgl_drm_caps *caps) 520{ 521 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 522 return virgl_vtest_send_get_caps(vtws, caps); 523} 524 525static struct pipe_fence_handle * 526virgl_cs_create_fence(struct virgl_winsys *vws) 527{ 528 struct virgl_hw_res *res; 529 530 res = virgl_vtest_winsys_resource_cache_create(vws, 531 PIPE_BUFFER, 532 PIPE_FORMAT_R8_UNORM, 533 PIPE_BIND_CUSTOM, 534 8, 1, 1, 0, 0, 0, 8); 535 536 return (struct pipe_fence_handle *)res; 537} 538 539static bool virgl_fence_wait(struct virgl_winsys *vws, 540 struct pipe_fence_handle *fence, 541 uint64_t timeout) 542{ 543 struct virgl_vtest_winsys *vdws = virgl_vtest_winsys(vws); 544 struct virgl_hw_res *res = virgl_hw_res(fence); 545 546 if (timeout == 0) 547 return !virgl_vtest_resource_is_busy(vdws, res); 548 549 if (timeout != PIPE_TIMEOUT_INFINITE) { 550 int64_t start_time = os_time_get(); 551 timeout /= 1000; 552 while (virgl_vtest_resource_is_busy(vdws, res)) { 553 if (os_time_get() - start_time >= timeout) 554 return FALSE; 555 os_time_sleep(10); 556 } 557 return TRUE; 558 } 559 virgl_vtest_resource_wait(vws, res); 560 return TRUE; 561} 562 563static void virgl_fence_reference(struct virgl_winsys *vws, 564 struct pipe_fence_handle **dst, 565 struct pipe_fence_handle *src) 566{ 567 struct virgl_vtest_winsys *vdws = virgl_vtest_winsys(vws); 568 virgl_vtest_resource_reference(vdws, (struct virgl_hw_res **)dst, 569 virgl_hw_res(src)); 570} 571 572static void virgl_vtest_flush_frontbuffer(struct virgl_winsys *vws, 573 struct virgl_hw_res *res, 574 unsigned level, unsigned layer, 575 void *winsys_drawable_handle, 576 struct pipe_box *sub_box) 577{ 578 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 579 struct pipe_box box; 580 void *map; 581 uint32_t size; 582 uint32_t offset = 0, valid_stride; 583 if (!res->dt) 584 return; 585 586 memset(&box, 0, sizeof(box)); 587 588 if (sub_box) { 589 box = *sub_box; 590 offset = box.y / util_format_get_blockheight(res->format) * res->stride + 591 box.x / util_format_get_blockwidth(res->format) * util_format_get_blocksize(res->format); 592 } else { 593 box.z = layer; 594 box.width = res->width; 595 box.height = res->height; 596 box.depth = 1; 597 } 598 599 size = vtest_get_transfer_size(res, &box, res->stride, 0, level, &valid_stride); 600 601 virgl_vtest_busy_wait(vtws, res->res_handle, VCMD_BUSY_WAIT_FLAG_WAIT); 602 map = vtws->sws->displaytarget_map(vtws->sws, res->dt, 0); 603 604 /* execute a transfer */ 605 virgl_vtest_send_transfer_cmd(vtws, VCMD_TRANSFER_GET, res->res_handle, 606 level, res->stride, 0, &box, size); 607 virgl_vtest_recv_transfer_get_data(vtws, map + offset, size, valid_stride, 608 &box, res->format); 609 vtws->sws->displaytarget_unmap(vtws->sws, res->dt); 610 611 vtws->sws->displaytarget_display(vtws->sws, res->dt, winsys_drawable_handle, 612 sub_box); 613} 614 615static void 616virgl_vtest_winsys_destroy(struct virgl_winsys *vws) 617{ 618 struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws); 619 620 virgl_cache_flush(vtws); 621 622 pipe_mutex_destroy(vtws->mutex); 623 FREE(vtws); 624} 625 626struct virgl_winsys * 627virgl_vtest_winsys_wrap(struct sw_winsys *sws) 628{ 629 struct virgl_vtest_winsys *vtws; 630 631 vtws = CALLOC_STRUCT(virgl_vtest_winsys); 632 if (!vtws) 633 return NULL; 634 635 virgl_vtest_connect(vtws); 636 vtws->sws = sws; 637 638 vtws->usecs = 1000000; 639 LIST_INITHEAD(&vtws->delayed); 640 pipe_mutex_init(vtws->mutex); 641 642 vtws->base.destroy = virgl_vtest_winsys_destroy; 643 644 vtws->base.transfer_put = virgl_vtest_transfer_put; 645 vtws->base.transfer_get = virgl_vtest_transfer_get; 646 647 vtws->base.resource_create = virgl_vtest_winsys_resource_cache_create; 648 vtws->base.resource_unref = virgl_vtest_winsys_resource_unref; 649 vtws->base.resource_map = virgl_vtest_resource_map; 650 vtws->base.resource_wait = virgl_vtest_resource_wait; 651 vtws->base.cmd_buf_create = virgl_vtest_cmd_buf_create; 652 vtws->base.cmd_buf_destroy = virgl_vtest_cmd_buf_destroy; 653 vtws->base.submit_cmd = virgl_vtest_winsys_submit_cmd; 654 655 vtws->base.emit_res = virgl_vtest_emit_res; 656 vtws->base.res_is_referenced = virgl_vtest_res_is_ref; 657 vtws->base.get_caps = virgl_vtest_get_caps; 658 659 vtws->base.cs_create_fence = virgl_cs_create_fence; 660 vtws->base.fence_wait = virgl_fence_wait; 661 vtws->base.fence_reference = virgl_fence_reference; 662 663 vtws->base.flush_frontbuffer = virgl_vtest_flush_frontbuffer; 664 665 return &vtws->base; 666} 667