1 /* 2 * Copyright 2019 Raspberry Pi 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 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * 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 NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24 #include "v3dv_private.h" 25 #include "vulkan/util/vk_util.h" 26 #include "util/blob.h" 27 #include "nir/nir_serialize.h" 28 29 static const bool debug_cache = false; 30 static const bool dump_stats = false; 31 static const bool dump_stats_on_destroy = false; 32 33 /* Shared for nir/variants */ 34 #define V3DV_MAX_PIPELINE_CACHE_ENTRIES 4096 35 36 static uint32_t 37 sha1_hash_func(const void *sha1) 38 { 39 return _mesa_hash_data(sha1, 20); 40 } 41 42 static bool 43 sha1_compare_func(const void *sha1_a, const void *sha1_b) 44 { 45 return memcmp(sha1_a, sha1_b, 20) == 0; 46 } 47 48 struct serialized_nir { 49 unsigned char sha1_key[20]; 50 size_t size; 51 char data[0]; 52 }; 53 54 static void 55 cache_dump_stats(struct v3dv_pipeline_cache *cache) 56 { 57 fprintf(stderr, " NIR cache entries: %d\n", cache->nir_stats.count); 58 fprintf(stderr, " NIR cache miss count: %d\n", cache->nir_stats.miss); 59 fprintf(stderr, " NIR cache hit count: %d\n", cache->nir_stats.hit); 60 61 fprintf(stderr, " cache entries: %d\n", cache->stats.count); 62 fprintf(stderr, " cache miss count: %d\n", cache->stats.miss); 63 fprintf(stderr, " cache hit count: %d\n", cache->stats.hit); 64 } 65 66 static void 67 pipeline_cache_lock(struct v3dv_pipeline_cache *cache) 68 { 69 if (!cache->externally_synchronized) 70 pthread_mutex_lock(&cache->mutex); 71 } 72 73 static void 74 pipeline_cache_unlock(struct v3dv_pipeline_cache *cache) 75 { 76 if (!cache->externally_synchronized) 77 pthread_mutex_unlock(&cache->mutex); 78 } 79 80 void 81 v3dv_pipeline_cache_upload_nir(struct v3dv_pipeline *pipeline, 82 struct v3dv_pipeline_cache *cache, 83 nir_shader *nir, 84 unsigned char sha1_key[20]) 85 { 86 if (!cache || !cache->nir_cache) 87 return; 88 89 if (cache->nir_stats.count > V3DV_MAX_PIPELINE_CACHE_ENTRIES) 90 return; 91 92 pipeline_cache_lock(cache); 93 struct hash_entry *entry = 94 _mesa_hash_table_search(cache->nir_cache, sha1_key); 95 pipeline_cache_unlock(cache); 96 if (entry) 97 return; 98 99 struct blob blob; 100 blob_init(&blob); 101 102 nir_serialize(&blob, nir, false); 103 if (blob.out_of_memory) { 104 blob_finish(&blob); 105 return; 106 } 107 108 pipeline_cache_lock(cache); 109 /* Because ralloc isn't thread-safe, we have to do all this inside the 110 * lock. We could unlock for the big memcpy but it's probably not worth 111 * the hassle. 112 */ 113 entry = _mesa_hash_table_search(cache->nir_cache, sha1_key); 114 if (entry) { 115 blob_finish(&blob); 116 pipeline_cache_unlock(cache); 117 return; 118 } 119 120 struct serialized_nir *snir = 121 ralloc_size(cache->nir_cache, sizeof(*snir) + blob.size); 122 memcpy(snir->sha1_key, sha1_key, 20); 123 snir->size = blob.size; 124 memcpy(snir->data, blob.data, blob.size); 125 126 blob_finish(&blob); 127 128 cache->nir_stats.count++; 129 if (debug_cache) { 130 char sha1buf[41]; 131 _mesa_sha1_format(sha1buf, snir->sha1_key); 132 fprintf(stderr, "pipeline cache %p, new nir entry %s\n", cache, sha1buf); 133 if (dump_stats) 134 cache_dump_stats(cache); 135 } 136 137 _mesa_hash_table_insert(cache->nir_cache, snir->sha1_key, snir); 138 139 pipeline_cache_unlock(cache); 140 } 141 142 nir_shader* 143 v3dv_pipeline_cache_search_for_nir(struct v3dv_pipeline *pipeline, 144 struct v3dv_pipeline_cache *cache, 145 const nir_shader_compiler_options *nir_options, 146 unsigned char sha1_key[20]) 147 { 148 if (!cache || !cache->nir_cache) 149 return NULL; 150 151 if (debug_cache) { 152 char sha1buf[41]; 153 _mesa_sha1_format(sha1buf, sha1_key); 154 155 fprintf(stderr, "pipeline cache %p, search for nir %s\n", cache, sha1buf); 156 } 157 158 const struct serialized_nir *snir = NULL; 159 160 pipeline_cache_lock(cache); 161 struct hash_entry *entry = 162 _mesa_hash_table_search(cache->nir_cache, sha1_key); 163 if (entry) 164 snir = entry->data; 165 pipeline_cache_unlock(cache); 166 167 if (snir) { 168 struct blob_reader blob; 169 blob_reader_init(&blob, snir->data, snir->size); 170 171 /* We use context NULL as we want the p_stage to keep the reference to 172 * nir, as we keep open the possibility of provide a shader variant 173 * after cache creation 174 */ 175 nir_shader *nir = nir_deserialize(NULL, nir_options, &blob); 176 if (blob.overrun) { 177 ralloc_free(nir); 178 } else { 179 cache->nir_stats.hit++; 180 if (debug_cache) { 181 fprintf(stderr, "\tnir cache hit: %p\n", nir); 182 if (dump_stats) 183 cache_dump_stats(cache); 184 } 185 return nir; 186 } 187 } 188 189 cache->nir_stats.miss++; 190 if (debug_cache) { 191 fprintf(stderr, "\tnir cache miss\n"); 192 if (dump_stats) 193 cache_dump_stats(cache); 194 } 195 196 return NULL; 197 } 198 199 void 200 v3dv_pipeline_cache_init(struct v3dv_pipeline_cache *cache, 201 struct v3dv_device *device, 202 VkPipelineCacheCreateFlags flags, 203 bool cache_enabled) 204 { 205 cache->device = device; 206 pthread_mutex_init(&cache->mutex, NULL); 207 208 if (cache_enabled) { 209 cache->nir_cache = _mesa_hash_table_create(NULL, sha1_hash_func, 210 sha1_compare_func); 211 cache->nir_stats.miss = 0; 212 cache->nir_stats.hit = 0; 213 cache->nir_stats.count = 0; 214 215 cache->cache = _mesa_hash_table_create(NULL, sha1_hash_func, 216 sha1_compare_func); 217 cache->stats.miss = 0; 218 cache->stats.hit = 0; 219 cache->stats.count = 0; 220 221 cache->externally_synchronized = flags & 222 VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT; 223 } else { 224 cache->nir_cache = NULL; 225 cache->cache = NULL; 226 } 227 228 } 229 230 static struct v3dv_pipeline_shared_data * 231 v3dv_pipeline_shared_data_create_from_blob(struct v3dv_pipeline_cache *cache, 232 struct blob_reader *blob); 233 234 static void 235 pipeline_cache_upload_shared_data(struct v3dv_pipeline_cache *cache, 236 struct v3dv_pipeline_shared_data *shared_data, 237 bool from_disk_cache); 238 239 static bool 240 v3dv_pipeline_shared_data_write_to_blob(const struct v3dv_pipeline_shared_data *cache_entry, 241 struct blob *blob); 242 243 /** 244 * It searchs for pipeline cached data, and returns a v3dv_pipeline_shared_data with 245 * it, or NULL if doesn't have it cached. On the former, it will increases the 246 * ref_count, so caller is responsible to unref it. 247 */ 248 struct v3dv_pipeline_shared_data * 249 v3dv_pipeline_cache_search_for_pipeline(struct v3dv_pipeline_cache *cache, 250 unsigned char sha1_key[20], 251 bool *cache_hit) 252 { 253 if (!cache || !cache->cache) 254 return NULL; 255 256 if (debug_cache) { 257 char sha1buf[41]; 258 _mesa_sha1_format(sha1buf, sha1_key); 259 260 fprintf(stderr, "pipeline cache %p, search pipeline with key %s\n", cache, sha1buf); 261 } 262 263 pipeline_cache_lock(cache); 264 265 struct hash_entry *entry = 266 _mesa_hash_table_search(cache->cache, sha1_key); 267 268 if (entry) { 269 struct v3dv_pipeline_shared_data *cache_entry = 270 (struct v3dv_pipeline_shared_data *) entry->data; 271 assert(cache_entry); 272 273 cache->stats.hit++; 274 *cache_hit = true; 275 if (debug_cache) { 276 fprintf(stderr, "\tcache hit: %p\n", cache_entry); 277 if (dump_stats) 278 cache_dump_stats(cache); 279 } 280 281 282 v3dv_pipeline_shared_data_ref(cache_entry); 283 284 pipeline_cache_unlock(cache); 285 286 return cache_entry; 287 } 288 289 cache->stats.miss++; 290 if (debug_cache) { 291 fprintf(stderr, "\tcache miss\n"); 292 if (dump_stats) 293 cache_dump_stats(cache); 294 } 295 296 pipeline_cache_unlock(cache); 297 298 #ifdef ENABLE_SHADER_CACHE 299 struct v3dv_device *device = cache->device; 300 struct disk_cache *disk_cache = device->pdevice->disk_cache; 301 /* Note that the on-disk-cache can be independently disabled, while keeping 302 * the pipeline cache working, by using the environment variable 303 * MESA_GLSL_CACHE_DISABLE. In that case the calls to disk_cache_put/get 304 * will not do anything. 305 */ 306 if (disk_cache && device->instance->pipeline_cache_enabled) { 307 cache_key cache_key; 308 disk_cache_compute_key(disk_cache, sha1_key, 20, cache_key); 309 310 size_t buffer_size; 311 uint8_t *buffer = disk_cache_get(disk_cache, cache_key, &buffer_size); 312 if (buffer) { 313 struct blob_reader blob; 314 struct v3dv_pipeline_shared_data *shared_data; 315 316 if (debug_cache) 317 fprintf(stderr, "\ton-disk-cache hit\n"); 318 319 blob_reader_init(&blob, buffer, buffer_size); 320 shared_data = v3dv_pipeline_shared_data_create_from_blob(cache, &blob); 321 free(buffer); 322 323 if (shared_data) { 324 if (cache) 325 pipeline_cache_upload_shared_data(cache, shared_data, true); 326 return shared_data; 327 } 328 } else { 329 if (debug_cache) 330 fprintf(stderr, "\ton-disk-cache miss\n"); 331 } 332 } 333 #endif 334 335 return NULL; 336 } 337 338 void 339 v3dv_pipeline_shared_data_destroy(struct v3dv_device *device, 340 struct v3dv_pipeline_shared_data *shared_data) 341 { 342 assert(shared_data->ref_cnt == 0); 343 344 for (uint8_t stage = 0; stage < BROADCOM_SHADER_STAGES; stage++) { 345 if (shared_data->variants[stage] != NULL) 346 v3dv_shader_variant_destroy(device, shared_data->variants[stage]); 347 348 /* We don't free binning descriptor maps as we are sharing them 349 * with the render shaders. 350 */ 351 if (shared_data->maps[stage] != NULL && 352 !broadcom_shader_stage_is_binning(stage)) { 353 vk_free(&device->vk.alloc, shared_data->maps[stage]); 354 } 355 } 356 357 if (shared_data->assembly_bo) 358 v3dv_bo_free(device, shared_data->assembly_bo); 359 360 vk_free(&device->vk.alloc, shared_data); 361 } 362 363 static struct v3dv_pipeline_shared_data * 364 v3dv_pipeline_shared_data_new(struct v3dv_pipeline_cache *cache, 365 const unsigned char sha1_key[20], 366 struct v3dv_descriptor_maps **maps, 367 struct v3dv_shader_variant **variants, 368 const uint64_t *total_assembly, 369 const uint32_t total_assembly_size) 370 { 371 size_t size = sizeof(struct v3dv_pipeline_shared_data); 372 /* We create new_entry using the device alloc. Right now shared_data is ref 373 * and unref by both the pipeline and the pipeline cache, so we can't 374 * ensure that the cache or pipeline alloc will be available on the last 375 * unref. 376 */ 377 struct v3dv_pipeline_shared_data *new_entry = 378 vk_zalloc2(&cache->device->vk.alloc, NULL, size, 8, 379 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 380 381 if (new_entry == NULL) 382 return NULL; 383 384 new_entry->ref_cnt = 1; 385 memcpy(new_entry->sha1_key, sha1_key, 20); 386 387 for (uint8_t stage = 0; stage < BROADCOM_SHADER_STAGES; stage++) { 388 new_entry->maps[stage] = maps[stage]; 389 new_entry->variants[stage] = variants[stage]; 390 } 391 392 struct v3dv_bo *bo = v3dv_bo_alloc(cache->device, total_assembly_size, 393 "pipeline shader assembly", true); 394 if (!bo) { 395 fprintf(stderr, "failed to allocate memory for shaders assembly\n"); 396 v3dv_pipeline_shared_data_unref(cache->device, new_entry); 397 return NULL; 398 } 399 400 bool ok = v3dv_bo_map(cache->device, bo, total_assembly_size); 401 if (!ok) { 402 fprintf(stderr, "failed to map source shader buffer\n"); 403 v3dv_pipeline_shared_data_unref(cache->device, new_entry); 404 return NULL; 405 } 406 407 memcpy(bo->map, total_assembly, total_assembly_size); 408 409 new_entry->assembly_bo = bo; 410 411 return new_entry; 412 } 413 414 static void 415 pipeline_cache_upload_shared_data(struct v3dv_pipeline_cache *cache, 416 struct v3dv_pipeline_shared_data *shared_data, 417 bool from_disk_cache) 418 { 419 assert(shared_data); 420 421 if (!cache || !cache->cache) 422 return; 423 424 if (cache->stats.count > V3DV_MAX_PIPELINE_CACHE_ENTRIES) 425 return; 426 427 pipeline_cache_lock(cache); 428 struct hash_entry *entry = 429 _mesa_hash_table_search(cache->cache, shared_data->sha1_key); 430 431 if (entry) { 432 pipeline_cache_unlock(cache); 433 return; 434 } 435 436 v3dv_pipeline_shared_data_ref(shared_data); 437 _mesa_hash_table_insert(cache->cache, shared_data->sha1_key, shared_data); 438 cache->stats.count++; 439 if (debug_cache) { 440 char sha1buf[41]; 441 _mesa_sha1_format(sha1buf, shared_data->sha1_key); 442 443 fprintf(stderr, "pipeline cache %p, new cache entry with sha1 key %s:%p\n\n", 444 cache, sha1buf, shared_data); 445 if (dump_stats) 446 cache_dump_stats(cache); 447 } 448 449 pipeline_cache_unlock(cache); 450 451 #ifdef ENABLE_SHADER_CACHE 452 /* If we are being called from a on-disk-cache hit, we can skip writing to 453 * the disk cache 454 */ 455 if (from_disk_cache) 456 return; 457 458 struct v3dv_device *device = cache->device; 459 struct disk_cache *disk_cache = device->pdevice->disk_cache; 460 if (disk_cache) { 461 struct blob binary; 462 blob_init(&binary); 463 if (v3dv_pipeline_shared_data_write_to_blob(shared_data, &binary)) { 464 cache_key cache_key; 465 disk_cache_compute_key(disk_cache, shared_data->sha1_key, 20, cache_key); 466 467 disk_cache_put(disk_cache, cache_key, binary.data, binary.size, NULL); 468 if (debug_cache) { 469 char sha1buf[41]; 470 _mesa_sha1_format(sha1buf, shared_data->sha1_key); 471 472 fprintf(stderr, "on-disk-cache, new cache entry with sha1 key %s:%p\n\n", 473 sha1buf, shared_data); 474 } 475 } 476 477 blob_finish(&binary); 478 } 479 #endif 480 } 481 482 /* Uploads all the "cacheable" or shared data from the pipeline */ 483 void 484 v3dv_pipeline_cache_upload_pipeline(struct v3dv_pipeline *pipeline, 485 struct v3dv_pipeline_cache *cache) 486 { 487 pipeline_cache_upload_shared_data(cache, pipeline->shared_data, false); 488 } 489 490 static struct serialized_nir* 491 serialized_nir_create_from_blob(struct v3dv_pipeline_cache *cache, 492 struct blob_reader *blob) 493 { 494 const unsigned char *sha1_key = blob_read_bytes(blob, 20); 495 uint32_t snir_size = blob_read_uint32(blob); 496 const char* snir_data = blob_read_bytes(blob, snir_size); 497 if (blob->overrun) 498 return NULL; 499 500 struct serialized_nir *snir = 501 ralloc_size(cache->nir_cache, sizeof(*snir) + snir_size); 502 memcpy(snir->sha1_key, sha1_key, 20); 503 snir->size = snir_size; 504 memcpy(snir->data, snir_data, snir_size); 505 506 return snir; 507 } 508 509 static struct v3dv_shader_variant* 510 shader_variant_create_from_blob(struct v3dv_device *device, 511 struct blob_reader *blob) 512 { 513 VkResult result; 514 515 enum broadcom_shader_stage stage = blob_read_uint32(blob); 516 517 uint32_t prog_data_size = blob_read_uint32(blob); 518 /* FIXME: as we include the stage perhaps we can avoid prog_data_size? */ 519 assert(prog_data_size == v3d_prog_data_size(broadcom_shader_stage_to_gl(stage))); 520 521 const void *prog_data = blob_read_bytes(blob, prog_data_size); 522 if (blob->overrun) 523 return NULL; 524 525 uint32_t ulist_count = blob_read_uint32(blob); 526 uint32_t contents_size = sizeof(enum quniform_contents) * ulist_count; 527 const void *contents_data = blob_read_bytes(blob, contents_size); 528 if (blob->overrun) 529 return NULL; 530 531 uint ulist_data_size = sizeof(uint32_t) * ulist_count; 532 const void *ulist_data_data = blob_read_bytes(blob, ulist_data_size); 533 if (blob->overrun) 534 return NULL; 535 536 uint32_t assembly_offset = blob_read_uint32(blob); 537 uint32_t qpu_insts_size = blob_read_uint32(blob); 538 539 /* shader_variant_create expects a newly created prog_data for their own, 540 * as it is what the v3d compiler returns. So we are also allocating one 541 * (including the uniform list) and filled it up with the data that we read 542 * from the blob 543 */ 544 struct v3d_prog_data *new_prog_data = rzalloc_size(NULL, prog_data_size); 545 memcpy(new_prog_data, prog_data, prog_data_size); 546 struct v3d_uniform_list *ulist = &new_prog_data->uniforms; 547 ulist->count = ulist_count; 548 ulist->contents = ralloc_array(new_prog_data, enum quniform_contents, ulist->count); 549 memcpy(ulist->contents, contents_data, contents_size); 550 ulist->data = ralloc_array(new_prog_data, uint32_t, ulist->count); 551 memcpy(ulist->data, ulist_data_data, ulist_data_size); 552 553 return v3dv_shader_variant_create(device, stage, 554 new_prog_data, prog_data_size, 555 assembly_offset, 556 NULL, qpu_insts_size, 557 &result); 558 } 559 560 static struct v3dv_pipeline_shared_data * 561 v3dv_pipeline_shared_data_create_from_blob(struct v3dv_pipeline_cache *cache, 562 struct blob_reader *blob) 563 { 564 const unsigned char *sha1_key = blob_read_bytes(blob, 20); 565 566 struct v3dv_descriptor_maps *maps[BROADCOM_SHADER_STAGES] = { 0 }; 567 568 uint8_t descriptor_maps_count = blob_read_uint8(blob); 569 for (uint8_t count = 0; count < descriptor_maps_count; count++) { 570 uint8_t stage = blob_read_uint8(blob); 571 572 const struct v3dv_descriptor_maps *current_maps = 573 blob_read_bytes(blob, sizeof(struct v3dv_descriptor_maps)); 574 575 if (blob->overrun) 576 return NULL; 577 578 maps[stage] = vk_zalloc2(&cache->device->vk.alloc, NULL, 579 sizeof(struct v3dv_descriptor_maps), 8, 580 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 581 582 if (maps[stage] == NULL) 583 return NULL; 584 585 memcpy(maps[stage], current_maps, sizeof(struct v3dv_descriptor_maps)); 586 if (broadcom_shader_stage_is_render_with_binning(stage)) { 587 enum broadcom_shader_stage bin_stage = 588 broadcom_binning_shader_stage_for_render_stage(stage); 589 maps[bin_stage] = maps[stage]; 590 } 591 } 592 593 uint8_t variant_count = blob_read_uint8(blob); 594 595 struct v3dv_shader_variant *variants[BROADCOM_SHADER_STAGES] = { 0 }; 596 597 for (uint8_t count = 0; count < variant_count; count++) { 598 uint8_t stage = blob_read_uint8(blob); 599 struct v3dv_shader_variant *variant = 600 shader_variant_create_from_blob(cache->device, blob); 601 variants[stage] = variant; 602 } 603 604 uint32_t total_assembly_size = blob_read_uint32(blob); 605 const uint64_t *total_assembly = 606 blob_read_bytes(blob, total_assembly_size); 607 608 if (blob->overrun) 609 return NULL; 610 611 return v3dv_pipeline_shared_data_new(cache, sha1_key, maps, variants, 612 total_assembly, total_assembly_size); 613 } 614 615 static void 616 pipeline_cache_load(struct v3dv_pipeline_cache *cache, 617 size_t size, 618 const void *data) 619 { 620 struct v3dv_device *device = cache->device; 621 struct v3dv_physical_device *pdevice = &device->instance->physicalDevice; 622 struct vk_pipeline_cache_header header; 623 624 if (cache->cache == NULL || cache->nir_cache == NULL) 625 return; 626 627 struct blob_reader blob; 628 blob_reader_init(&blob, data, size); 629 630 blob_copy_bytes(&blob, &header, sizeof(header)); 631 if (size < sizeof(header)) 632 return; 633 memcpy(&header, data, sizeof(header)); 634 if (header.header_size < sizeof(header)) 635 return; 636 if (header.header_version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE) 637 return; 638 if (header.vendor_id != v3dv_physical_device_vendor_id(pdevice)) 639 return; 640 if (header.device_id != v3dv_physical_device_device_id(pdevice)) 641 return; 642 if (memcmp(header.uuid, pdevice->pipeline_cache_uuid, VK_UUID_SIZE) != 0) 643 return; 644 645 uint32_t nir_count = blob_read_uint32(&blob); 646 if (blob.overrun) 647 return; 648 649 for (uint32_t i = 0; i < nir_count; i++) { 650 struct serialized_nir *snir = 651 serialized_nir_create_from_blob(cache, &blob); 652 653 if (!snir) 654 break; 655 656 _mesa_hash_table_insert(cache->nir_cache, snir->sha1_key, snir); 657 cache->nir_stats.count++; 658 } 659 660 uint32_t count = blob_read_uint32(&blob); 661 if (blob.overrun) 662 return; 663 664 for (uint32_t i = 0; i < count; i++) { 665 struct v3dv_pipeline_shared_data *cache_entry = 666 v3dv_pipeline_shared_data_create_from_blob(cache, &blob); 667 if (!cache_entry) 668 break; 669 670 _mesa_hash_table_insert(cache->cache, cache_entry->sha1_key, cache_entry); 671 cache->stats.count++; 672 } 673 674 if (debug_cache) { 675 fprintf(stderr, "pipeline cache %p, loaded %i nir shaders and " 676 "%i entries\n", cache, nir_count, count); 677 if (dump_stats) 678 cache_dump_stats(cache); 679 } 680 } 681 682 VKAPI_ATTR VkResult VKAPI_CALL 683 v3dv_CreatePipelineCache(VkDevice _device, 684 const VkPipelineCacheCreateInfo *pCreateInfo, 685 const VkAllocationCallbacks *pAllocator, 686 VkPipelineCache *pPipelineCache) 687 { 688 V3DV_FROM_HANDLE(v3dv_device, device, _device); 689 struct v3dv_pipeline_cache *cache; 690 691 assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO); 692 693 cache = vk_object_zalloc(&device->vk, pAllocator, 694 sizeof(*cache), 695 VK_OBJECT_TYPE_PIPELINE_CACHE); 696 697 if (cache == NULL) 698 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); 699 700 v3dv_pipeline_cache_init(cache, device, pCreateInfo->flags, 701 device->instance->pipeline_cache_enabled); 702 703 if (pCreateInfo->initialDataSize > 0) { 704 pipeline_cache_load(cache, 705 pCreateInfo->initialDataSize, 706 pCreateInfo->pInitialData); 707 } 708 709 *pPipelineCache = v3dv_pipeline_cache_to_handle(cache); 710 711 return VK_SUCCESS; 712 } 713 714 void 715 v3dv_pipeline_cache_finish(struct v3dv_pipeline_cache *cache) 716 { 717 pthread_mutex_destroy(&cache->mutex); 718 719 if (dump_stats_on_destroy) 720 cache_dump_stats(cache); 721 722 if (cache->nir_cache) { 723 hash_table_foreach(cache->nir_cache, entry) 724 ralloc_free(entry->data); 725 726 _mesa_hash_table_destroy(cache->nir_cache, NULL); 727 } 728 729 if (cache->cache) { 730 hash_table_foreach(cache->cache, entry) { 731 struct v3dv_pipeline_shared_data *cache_entry = entry->data; 732 if (cache_entry) 733 v3dv_pipeline_shared_data_unref(cache->device, cache_entry); 734 } 735 736 _mesa_hash_table_destroy(cache->cache, NULL); 737 } 738 } 739 740 VKAPI_ATTR void VKAPI_CALL 741 v3dv_DestroyPipelineCache(VkDevice _device, 742 VkPipelineCache _cache, 743 const VkAllocationCallbacks *pAllocator) 744 { 745 V3DV_FROM_HANDLE(v3dv_device, device, _device); 746 V3DV_FROM_HANDLE(v3dv_pipeline_cache, cache, _cache); 747 748 if (!cache) 749 return; 750 751 v3dv_pipeline_cache_finish(cache); 752 753 vk_object_free(&device->vk, pAllocator, cache); 754 } 755 756 VKAPI_ATTR VkResult VKAPI_CALL 757 v3dv_MergePipelineCaches(VkDevice device, 758 VkPipelineCache dstCache, 759 uint32_t srcCacheCount, 760 const VkPipelineCache *pSrcCaches) 761 { 762 V3DV_FROM_HANDLE(v3dv_pipeline_cache, dst, dstCache); 763 764 if (!dst->cache || !dst->nir_cache) 765 return VK_SUCCESS; 766 767 for (uint32_t i = 0; i < srcCacheCount; i++) { 768 V3DV_FROM_HANDLE(v3dv_pipeline_cache, src, pSrcCaches[i]); 769 if (!src->cache || !src->nir_cache) 770 continue; 771 772 hash_table_foreach(src->nir_cache, entry) { 773 struct serialized_nir *src_snir = entry->data; 774 assert(src_snir); 775 776 if (_mesa_hash_table_search(dst->nir_cache, src_snir->sha1_key)) 777 continue; 778 779 /* FIXME: we are using serialized nir shaders because they are 780 * convenient to create and store on the cache, but requires to do a 781 * copy here (and some other places) of the serialized NIR. Perhaps 782 * it would make sense to move to handle the NIR shaders with shared 783 * structures with ref counts, as the variants. 784 */ 785 struct serialized_nir *snir_dst = 786 ralloc_size(dst->nir_cache, sizeof(*snir_dst) + src_snir->size); 787 memcpy(snir_dst->sha1_key, src_snir->sha1_key, 20); 788 snir_dst->size = src_snir->size; 789 memcpy(snir_dst->data, src_snir->data, src_snir->size); 790 791 _mesa_hash_table_insert(dst->nir_cache, snir_dst->sha1_key, snir_dst); 792 dst->nir_stats.count++; 793 if (debug_cache) { 794 char sha1buf[41]; 795 _mesa_sha1_format(sha1buf, snir_dst->sha1_key); 796 797 fprintf(stderr, "pipeline cache %p, added nir entry %s " 798 "from pipeline cache %p\n", 799 dst, sha1buf, src); 800 if (dump_stats) 801 cache_dump_stats(dst); 802 } 803 } 804 805 hash_table_foreach(src->cache, entry) { 806 struct v3dv_pipeline_shared_data *cache_entry = entry->data; 807 assert(cache_entry); 808 809 if (_mesa_hash_table_search(dst->cache, cache_entry->sha1_key)) 810 continue; 811 812 v3dv_pipeline_shared_data_ref(cache_entry); 813 _mesa_hash_table_insert(dst->cache, cache_entry->sha1_key, cache_entry); 814 815 dst->stats.count++; 816 if (debug_cache) { 817 char sha1buf[41]; 818 _mesa_sha1_format(sha1buf, cache_entry->sha1_key); 819 820 fprintf(stderr, "pipeline cache %p, added entry %s " 821 "from pipeline cache %p\n", 822 dst, sha1buf, src); 823 if (dump_stats) 824 cache_dump_stats(dst); 825 } 826 } 827 } 828 829 return VK_SUCCESS; 830 } 831 832 static bool 833 shader_variant_write_to_blob(const struct v3dv_shader_variant *variant, 834 struct blob *blob) 835 { 836 blob_write_uint32(blob, variant->stage); 837 838 blob_write_uint32(blob, variant->prog_data_size); 839 blob_write_bytes(blob, variant->prog_data.base, variant->prog_data_size); 840 841 struct v3d_uniform_list *ulist = &variant->prog_data.base->uniforms; 842 blob_write_uint32(blob, ulist->count); 843 blob_write_bytes(blob, ulist->contents, sizeof(enum quniform_contents) * ulist->count); 844 blob_write_bytes(blob, ulist->data, sizeof(uint32_t) * ulist->count); 845 846 blob_write_uint32(blob, variant->assembly_offset); 847 blob_write_uint32(blob, variant->qpu_insts_size); 848 849 return !blob->out_of_memory; 850 } 851 852 static bool 853 v3dv_pipeline_shared_data_write_to_blob(const struct v3dv_pipeline_shared_data *cache_entry, 854 struct blob *blob) 855 { 856 blob_write_bytes(blob, cache_entry->sha1_key, 20); 857 858 uint8_t descriptor_maps_count = 0; 859 for (uint8_t stage = 0; stage < BROADCOM_SHADER_STAGES; stage++) { 860 if (broadcom_shader_stage_is_binning(stage)) 861 continue; 862 if (cache_entry->maps[stage] == NULL) 863 continue; 864 descriptor_maps_count++; 865 } 866 867 /* Compute pipelines only have one descriptor map, 868 * graphics pipelines may have 2 (VS+FS) or 3 (VS+GS+FS), since the binning 869 * stages take the descriptor map from the render stage. 870 */ 871 assert((descriptor_maps_count >= 2 && descriptor_maps_count <= 3) || 872 (descriptor_maps_count == 1 && cache_entry->variants[BROADCOM_SHADER_COMPUTE])); 873 blob_write_uint8(blob, descriptor_maps_count); 874 875 for (uint8_t stage = 0; stage < BROADCOM_SHADER_STAGES; stage++) { 876 if (cache_entry->maps[stage] == NULL) 877 continue; 878 if (broadcom_shader_stage_is_binning(stage)) 879 continue; 880 881 blob_write_uint8(blob, stage); 882 blob_write_bytes(blob, cache_entry->maps[stage], 883 sizeof(struct v3dv_descriptor_maps)); 884 } 885 886 uint8_t variant_count = 0; 887 for (uint8_t stage = 0; stage < BROADCOM_SHADER_STAGES; stage++) { 888 if (cache_entry->variants[stage] == NULL) 889 continue; 890 variant_count++; 891 } 892 893 /* Graphics pipelines with VS+FS have 3 variants, VS+GS+FS will have 5 and 894 * compute pipelines only have 1. 895 */ 896 assert((variant_count == 5 || variant_count == 3) || 897 (variant_count == 1 && cache_entry->variants[BROADCOM_SHADER_COMPUTE])); 898 blob_write_uint8(blob, variant_count); 899 900 uint32_t total_assembly_size = 0; 901 for (uint8_t stage = 0; stage < BROADCOM_SHADER_STAGES; stage++) { 902 if (cache_entry->variants[stage] == NULL) 903 continue; 904 905 blob_write_uint8(blob, stage); 906 if (!shader_variant_write_to_blob(cache_entry->variants[stage], blob)) 907 return false; 908 909 total_assembly_size += cache_entry->variants[stage]->qpu_insts_size; 910 } 911 blob_write_uint32(blob, total_assembly_size); 912 913 assert(cache_entry->assembly_bo->map); 914 assert(cache_entry->assembly_bo->size >= total_assembly_size); 915 blob_write_bytes(blob, cache_entry->assembly_bo->map, total_assembly_size); 916 917 return !blob->out_of_memory; 918 } 919 920 921 VKAPI_ATTR VkResult VKAPI_CALL 922 v3dv_GetPipelineCacheData(VkDevice _device, 923 VkPipelineCache _cache, 924 size_t *pDataSize, 925 void *pData) 926 { 927 V3DV_FROM_HANDLE(v3dv_device, device, _device); 928 V3DV_FROM_HANDLE(v3dv_pipeline_cache, cache, _cache); 929 930 struct blob blob; 931 if (pData) { 932 blob_init_fixed(&blob, pData, *pDataSize); 933 } else { 934 blob_init_fixed(&blob, NULL, SIZE_MAX); 935 } 936 937 struct v3dv_physical_device *pdevice = &device->instance->physicalDevice; 938 VkResult result = VK_INCOMPLETE; 939 940 pipeline_cache_lock(cache); 941 942 struct vk_pipeline_cache_header header = { 943 .header_size = sizeof(struct vk_pipeline_cache_header), 944 .header_version = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, 945 .vendor_id = v3dv_physical_device_vendor_id(pdevice), 946 .device_id = v3dv_physical_device_device_id(pdevice), 947 }; 948 memcpy(header.uuid, pdevice->pipeline_cache_uuid, VK_UUID_SIZE); 949 blob_write_bytes(&blob, &header, sizeof(header)); 950 951 uint32_t nir_count = 0; 952 intptr_t nir_count_offset = blob_reserve_uint32(&blob); 953 if (nir_count_offset < 0) { 954 *pDataSize = 0; 955 goto done; 956 } 957 958 if (cache->nir_cache) { 959 hash_table_foreach(cache->nir_cache, entry) { 960 const struct serialized_nir *snir = entry->data; 961 962 size_t save_size = blob.size; 963 964 blob_write_bytes(&blob, snir->sha1_key, 20); 965 blob_write_uint32(&blob, snir->size); 966 blob_write_bytes(&blob, snir->data, snir->size); 967 968 if (blob.out_of_memory) { 969 blob.size = save_size; 970 goto done; 971 } 972 973 nir_count++; 974 } 975 } 976 blob_overwrite_uint32(&blob, nir_count_offset, nir_count); 977 978 uint32_t count = 0; 979 intptr_t count_offset = blob_reserve_uint32(&blob); 980 if (count_offset < 0) { 981 *pDataSize = 0; 982 goto done; 983 } 984 985 if (cache->cache) { 986 hash_table_foreach(cache->cache, entry) { 987 struct v3dv_pipeline_shared_data *cache_entry = entry->data; 988 989 size_t save_size = blob.size; 990 if (!v3dv_pipeline_shared_data_write_to_blob(cache_entry, &blob)) { 991 /* If it fails reset to the previous size and bail */ 992 blob.size = save_size; 993 goto done; 994 } 995 996 count++; 997 } 998 } 999 1000 blob_overwrite_uint32(&blob, count_offset, count); 1001 1002 *pDataSize = blob.size; 1003 1004 result = VK_SUCCESS; 1005 1006 if (debug_cache) { 1007 assert(count <= cache->stats.count); 1008 fprintf(stderr, "GetPipelineCacheData: serializing cache %p, " 1009 "%i nir shader entries " 1010 "%i entries, %u DataSize\n", 1011 cache, nir_count, count, (uint32_t) *pDataSize); 1012 } 1013 1014 done: 1015 blob_finish(&blob); 1016 1017 pipeline_cache_unlock(cache); 1018 1019 return result; 1020 } 1021