1 /* 2 * Copyright (c) 2022-2024, Intel Corporation 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 shall be included 12 * in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 */ 22 //! 23 //! \file media_debug_fast_dump_imp.hpp 24 //! 25 26 #pragma once 27 28 #include "media_debug_fast_dump.h" 29 30 #if USE_MEDIA_DEBUG_TOOL 31 32 #include <algorithm> 33 #include <array> 34 #include <atomic> 35 #include <cassert> 36 #include <chrono> 37 #include <condition_variable> 38 #include <fstream> 39 #include <future> 40 #include <map> 41 #include <memory> 42 #include <mutex> 43 #include <queue> 44 #include <random> 45 #include <regex> 46 #include <thread> 47 #include <vector> 48 49 class MediaDebugFastDumpImp : public MediaDebugFastDump 50 { 51 protected: 52 using ResInfo = MOS_ALLOC_GFXRES_PARAMS; 53 54 struct ResInfoCmp 55 { operator ()MediaDebugFastDumpImp::ResInfoCmp56 bool operator()(const ResInfo &a, const ResInfo &b) const 57 { 58 return a.Type < b.Type ? true 59 : a.dwWidth < b.dwWidth ? true 60 : a.dwHeight < b.dwHeight ? true 61 : a.Format < b.Format ? true 62 : false; 63 } 64 }; 65 66 struct ResBase 67 { 68 public: 69 virtual ~ResBase() = default; 70 71 public: 72 bool occupied = false; 73 size_t size = 0; 74 size_t offset = 0; 75 std::string name; 76 std::function< 77 void(std::ostream &, const void *, size_t)> 78 serializer; 79 }; 80 81 struct ResGfx final : public ResBase 82 { 83 public: SetOsInterfaceMediaDebugFastDumpImp::ResGfx84 static void SetOsInterface(PMOS_INTERFACE itf) 85 { 86 osItf = itf; 87 } 88 89 private: 90 static PMOS_INTERFACE osItf; 91 92 public: ~ResGfxMediaDebugFastDumpImp::ResGfx93 ~ResGfx() 94 { 95 if (Mos_ResourceIsNull(&res) == false) 96 { 97 osItf->pfnFreeResource(osItf, &res); 98 } 99 } 100 101 public: 102 bool localMem = false; 103 MOS_RESOURCE res = {}; 104 }; 105 106 struct ResSys final : public ResBase 107 { 108 public: ~ResSysMediaDebugFastDumpImp::ResSys109 ~ResSys() 110 { 111 MOS_DeleteArray(res); 112 } 113 114 public: 115 char *res = nullptr; 116 }; 117 118 struct MemMng 119 { 120 Mos_MemPool policy = MOS_MEMPOOL_VIDEOMEMORY; 121 size_t cap = 0; 122 size_t usage = 0; 123 }; 124 125 class BufferedWriter final 126 { 127 private: 128 struct File 129 { FileMediaDebugFastDumpImp::BufferedWriter::File130 File(std::string &&n, size_t p, size_t s) 131 { 132 name = std::move(n); 133 pos = p; 134 size = s; 135 } 136 137 std::string name; 138 size_t pos = 0; 139 size_t size = 0; 140 }; 141 142 public: BufferedWriter(size_t bufferSizeInMB)143 BufferedWriter(size_t bufferSizeInMB) 144 { 145 m_bufSize = bufferSizeInMB << 20; // byte 146 } 147 ~BufferedWriter()148 ~BufferedWriter() 149 { 150 std::ofstream ofs; 151 152 Flush(ofs); 153 } 154 operator ()(std::string && name,const void * data,size_t size)155 void operator()(std::string &&name, const void *data, size_t size) 156 { 157 if (m_buf.size() == 0) 158 { 159 m_buf.resize(m_bufSize); 160 m_buf.shrink_to_fit(); 161 } 162 163 size_t pos = 0; 164 size_t space = m_buf.size(); 165 if (!m_files.empty()) 166 { 167 pos = m_files.back().pos + m_files.back().size; 168 space -= pos; 169 } 170 171 if (size <= space) 172 { 173 memcpy(m_buf.data() + pos, data, size); 174 m_files.emplace_back(std::move(name), pos, size); 175 } 176 else 177 { 178 std::ofstream ofs; 179 180 Flush(ofs); 181 182 ofs.open(name, std::ios_base::out | std::ios_base::binary); 183 ofs.write(static_cast<const char *>(data), size); 184 } 185 } 186 operator ()(std::string && name,const void * data,size_t size,std::function<void (std::ostream &,const void *,size_t)> &&)187 void operator()( 188 std::string &&name, 189 const void *data, 190 size_t size, 191 std::function<void(std::ostream &, const void *, size_t)> &&) 192 { 193 (*this)(std::move(name), data, size); 194 } 195 196 private: Flush(std::ofstream & ofs)197 void Flush(std::ofstream &ofs) 198 { 199 std::for_each( 200 m_files.begin(), 201 m_files.end(), 202 [this, &ofs](decltype(m_files)::const_reference file) { 203 ofs.open(file.name, std::ios_base::out | std::ios_base::binary); 204 ofs.write(m_buf.data() + file.pos, file.size); 205 ofs.close(); 206 }); 207 208 m_files.clear(); 209 } 210 211 private: 212 size_t m_bufSize = 0; 213 std::vector<char> m_buf; 214 std::vector<File> m_files; 215 }; 216 217 protected: GetResSizeAndFixName(PGMM_RESOURCE_INFO pGmmResInfo,std::string & name)218 static size_t GetResSizeAndFixName(PGMM_RESOURCE_INFO pGmmResInfo, std::string &name) 219 { 220 auto resSize = static_cast<size_t>(pGmmResInfo->GetSizeMainSurface()); 221 auto w = static_cast<size_t>(pGmmResInfo->GetBaseWidth()); 222 auto h = static_cast<size_t>(pGmmResInfo->GetBaseHeight()); 223 auto bytesPerPixel = static_cast<size_t>((pGmmResInfo->GetBitsPerPixel() + 7) >> 3); 224 auto p = static_cast<size_t>(pGmmResInfo->GetRenderPitch()) / bytesPerPixel; 225 auto sizeY = p * h * bytesPerPixel; 226 227 // a lazy method to get real resource size without checking resource format 228 if (sizeY * 3 <= resSize) 229 { 230 resSize = sizeY * 3; // 444 231 } 232 else if (sizeY * 2 <= resSize) 233 { 234 resSize = sizeY * 2; // 422 235 } 236 else if (sizeY * 3 / 2 <= resSize) 237 { 238 resSize = sizeY * 3 / 2; // 420 239 } 240 else 241 { 242 resSize = sizeY; // 400, buffer or packed YUV formats like Y210 243 } 244 245 name = std::regex_replace( 246 name, 247 std::regex("w\\[[0-9]+\\]_h\\[[0-9]+\\]_p\\[[0-9]+\\]"), 248 "w[" + std::to_string(w) + 249 "]_h[" + std::to_string(h) + 250 "]_p[" + std::to_string(p) + 251 "]"); 252 253 return resSize; 254 } 255 256 public: MediaDebugFastDumpImp(MOS_INTERFACE & osItf,MediaCopyWrapper & mediaCopyWrapper,const Config * cfg)257 MediaDebugFastDumpImp( 258 MOS_INTERFACE &osItf, 259 MediaCopyWrapper &mediaCopyWrapper, 260 const Config *cfg) : m_osItf(osItf), 261 m_mediaCopyWrapper(mediaCopyWrapper) 262 { 263 std::unique_ptr<const Config> cfg1 = nullptr; 264 265 const auto &c = cfg ? *cfg : *(cfg1 = decltype(cfg1)(new Config{})); 266 267 m_allowDataLoss = c.allowDataLoss; 268 269 ConfigureSamplingMode(c); 270 ConfigureAllocator(c); 271 ConfigureCopyMethod(c); 272 ConfigureWriter(c); 273 274 LaunchScheduler(); 275 276 ResGfx::SetOsInterface(&osItf); 277 } 278 ~MediaDebugFastDumpImp()279 ~MediaDebugFastDumpImp() 280 { 281 { 282 std::lock_guard<std::mutex> lk(m_gfxMemAsyncData.mutex); 283 m_gfxMemAsyncData.stopScheduler = true; 284 } 285 if (m_gfxMemAsyncData.scheduler.joinable()) 286 { 287 m_gfxMemAsyncData.cond.notify_one(); 288 m_gfxMemAsyncData.scheduler.join(); 289 } 290 291 { 292 std::lock_guard<std::mutex> lk(m_sysMemAsyncData.mutex); 293 m_sysMemAsyncData.stopScheduler = true; 294 } 295 if (m_sysMemAsyncData.scheduler.joinable()) 296 { 297 m_sysMemAsyncData.cond.notify_one(); 298 m_sysMemAsyncData.scheduler.join(); 299 } 300 } 301 operator ()(MOS_RESOURCE & res,std::string && name,size_t dumpSize,size_t offset,std::function<void (std::ostream &,const void *,size_t)> && serializer)302 void operator()( 303 MOS_RESOURCE &res, 304 std::string &&name, 305 size_t dumpSize, 306 size_t offset, 307 std::function< 308 void(std::ostream &, const void *, size_t)> 309 &&serializer) 310 { 311 if (m_2CacheTask() == false) 312 { 313 return; 314 } 315 316 ResInfo resInfo{}; 317 if (GetResInfo(res, resInfo) != MOS_STATUS_SUCCESS) 318 { 319 return m_writeError( 320 name, 321 "get_input_resource_info_failed"); 322 } 323 324 // prepare resource pool and resource queue 325 { 326 std::unique_lock<std::mutex> lk(m_gfxMemAsyncData.mutex); 327 328 auto &resArray = m_gfxMemAsyncData.resPool[resInfo]; 329 330 using CR = std::remove_reference<decltype(resArray)>::type::const_reference; 331 332 auto resIt = std::find_if( 333 resArray.begin(), 334 resArray.end(), 335 [](CR r) { 336 return r->occupied == false; 337 }); 338 339 if (resIt == resArray.end()) 340 { 341 auto tmpRes = std::make_shared<ResGfx>(); 342 if (m_allocate(resInfo, tmpRes->res) > 0) 343 { 344 resArray.emplace_back(std::move(tmpRes)); 345 --(resIt = resArray.end()); 346 } 347 else if (!m_allowDataLoss && !resArray.empty()) 348 { 349 m_gfxMemAsyncData.cond.wait( 350 lk, 351 [&] { 352 resIt = std::find_if( 353 resArray.begin(), 354 resArray.end(), 355 [](CR r) { 356 return r->occupied == false; 357 }); 358 return resIt != resArray.end(); 359 }); 360 } 361 else 362 { 363 return m_writeError( 364 name, 365 "discarded"); 366 } 367 } 368 369 if (m_mediaCopyWrapper.MediaCopy(&res, &(*resIt)->res, m_copyMethod()) != 370 MOS_STATUS_SUCCESS) 371 { 372 m_mediaCopyIsGood = false; 373 return m_writeError( 374 name, 375 "input_surface_copy_failed"); 376 } 377 else 378 { 379 m_mediaCopyIsGood = true; 380 } 381 382 (*resIt)->occupied = true; 383 (*resIt)->localMem = resInfo.dwMemType != MOS_MEMPOOL_SYSTEMMEMORY; 384 (*resIt)->size = dumpSize; 385 (*resIt)->offset = offset; 386 (*resIt)->name = std::move(name); 387 (*resIt)->serializer = std::move(serializer); 388 m_gfxMemAsyncData.resQueue.emplace(*resIt); 389 } 390 391 m_gfxMemAsyncData.cond.notify_one(); 392 } 393 operator ()(const void * res,std::string && name,size_t dumpSize,size_t offset,std::function<void (std::ostream &,const void *,size_t)> && serializer)394 void operator()( 395 const void *res, 396 std::string &&name, 397 size_t dumpSize, 398 size_t offset, 399 std::function< 400 void(std::ostream &, const void *, size_t)> 401 &&serializer) 402 { 403 if (m_2CacheTask() == false) 404 { 405 return; 406 } 407 408 if (res == nullptr) 409 { 410 return m_writeError( 411 name, 412 "resource_is_null"); 413 } 414 415 if (dumpSize == 0) 416 { 417 return m_writeError( 418 name, 419 "dump_size_is_0"); 420 } 421 422 // prepare resource pool and resource queue 423 { 424 std::unique_lock<std::mutex> lk(m_sysMemAsyncData.mutex); 425 426 auto &resArray = m_sysMemAsyncData.resPool[dumpSize]; 427 428 using CR = std::remove_reference<decltype(resArray)>::type::const_reference; 429 430 auto resIt = std::find_if( 431 resArray.begin(), 432 resArray.end(), 433 [](CR r) { 434 return r->occupied == false; 435 }); 436 437 if (resIt == resArray.end()) 438 { 439 auto tmpRes = std::make_shared<ResSys>(); 440 if ((tmpRes->res = MOS_NewArray(char, dumpSize)) != nullptr) 441 { 442 resArray.emplace_back(std::move(tmpRes)); 443 --(resIt = resArray.end()); 444 } 445 else if (!m_allowDataLoss && !resArray.empty()) 446 { 447 m_sysMemAsyncData.cond.wait( 448 lk, 449 [&] { 450 resIt = std::find_if( 451 resArray.begin(), 452 resArray.end(), 453 [](CR r) { 454 return r->occupied == false; 455 }); 456 return resIt != resArray.end(); 457 }); 458 } 459 else 460 { 461 return m_writeError( 462 name, 463 "discarded"); 464 } 465 } 466 467 MOS_SecureMemcpy( 468 (*resIt)->res, 469 dumpSize, 470 static_cast<const char *>(res) + offset, 471 dumpSize); 472 473 (*resIt)->occupied = true; 474 (*resIt)->size = dumpSize; 475 (*resIt)->offset = 0; 476 (*resIt)->name = std::move(name); 477 (*resIt)->serializer = std::move(serializer); 478 m_sysMemAsyncData.resQueue.emplace(*resIt); 479 } 480 481 m_sysMemAsyncData.cond.notify_one(); 482 } 483 IsGood() const484 bool IsGood() const 485 { 486 return m_mediaCopyIsGood; 487 } 488 489 protected: ConfigureSamplingMode(const Config & cfg)490 void ConfigureSamplingMode(const Config &cfg) 491 { 492 if (cfg.samplingTime + cfg.samplingInterval == 0) // sampling disabled 493 { 494 m_2CacheTask = [] { return true; }; 495 } 496 else if (cfg.frameIdx == nullptr) // time based sampling 497 { 498 using Clock = std::chrono::system_clock; 499 using MS = std::chrono::duration<size_t, std::milli>; 500 501 const auto samplingTime = MS(cfg.samplingTime); 502 const auto samplingInterval = MS(cfg.samplingInterval); 503 const auto startTime = Clock::now(); 504 505 m_2CacheTask = [=] { 506 auto elapsed = std::chrono::duration_cast<MS>(Clock::now() - startTime); 507 return (elapsed % (samplingTime + samplingInterval)) < samplingTime; 508 }; 509 } 510 else // frame index based sampling 511 { 512 const auto samplingTime = cfg.samplingTime; 513 const auto samplingInterval = cfg.samplingInterval; 514 const auto *frameIdx = cfg.frameIdx; 515 516 m_2CacheTask = [=] { 517 return (*frameIdx % (samplingTime + samplingInterval)) < samplingTime; 518 }; 519 } 520 } 521 ConfigureAllocator(const Config & cfg)522 void ConfigureAllocator(const Config &cfg) 523 { 524 #define TMP_ASSIGN(shared, local, dst) \ 525 m_memMng[0].dst = cfg.memUsagePolicy != 2 ? (shared) : (local); \ 526 m_memMng[1].dst = cfg.memUsagePolicy == 2 ? (shared) : (local) 527 528 TMP_ASSIGN( 529 MOS_MEMPOOL_SYSTEMMEMORY, 530 MOS_MEMPOOL_VIDEOMEMORY, 531 policy); 532 533 auto adapter = m_osItf.pfnGetAdapterInfo(m_osItf.osStreamState); 534 if (adapter) 535 { 536 TMP_ASSIGN( 537 static_cast<size_t>(adapter->SystemSharedMemory), 538 static_cast<size_t>(adapter->DedicatedVideoMemory), 539 cap); 540 m_memMng[0].cap = m_memMng[0].cap / 100 * cfg.maxPrioritizedMem; 541 m_memMng[1].cap = m_memMng[1].cap / 100 * cfg.maxDeprioritizedMem; 542 } 543 m_memMng[0].cap = m_memMng[0].cap ? m_memMng[0].cap : -1; 544 m_memMng[1].cap = m_memMng[1].cap || cfg.maxDeprioritizedMem == 0 ? m_memMng[1].cap : -1; 545 546 #undef TMP_ASSIGN 547 548 if (m_memMng[1].cap == 0) 549 { 550 m_allocate = [this](ResInfo &resInfo, MOS_RESOURCE &res) -> size_t { 551 if (m_memMng[0].usage >= m_memMng[0].cap) 552 { 553 return 0; 554 } 555 resInfo.dwMemType = m_memMng[0].policy; 556 if (m_osItf.pfnAllocateResource(&m_osItf, &resInfo, &res) == MOS_STATUS_SUCCESS) 557 { 558 assert(res.pGmmResInfo != nullptr); 559 auto resSize = static_cast<size_t>(res.pGmmResInfo->GetSizeAllocation()); 560 m_memMng[0].usage += resSize; 561 return resSize; 562 } 563 return 0; 564 }; 565 } 566 else 567 { 568 auto allocator = [this](ResInfo &resInfo, MOS_RESOURCE &res) -> size_t { 569 if (m_memMng[0].usage >= m_memMng[0].cap) 570 { 571 if (m_memMng[1].usage >= m_memMng[1].cap) 572 { 573 return 0; 574 } 575 resInfo.dwMemType = m_memMng[1].policy; 576 } 577 else 578 { 579 resInfo.dwMemType = m_memMng[0].policy; 580 } 581 if (m_osItf.pfnAllocateResource(&m_osItf, &resInfo, &res) == MOS_STATUS_SUCCESS) 582 { 583 assert(res.pGmmResInfo != nullptr); 584 auto resSize = static_cast<size_t>(res.pGmmResInfo->GetSizeAllocation()); 585 m_memMng[0].usage += (resInfo.dwMemType == m_memMng[0].policy ? resSize : 0); 586 m_memMng[1].usage += (resInfo.dwMemType == m_memMng[1].policy ? resSize : 0); 587 return resSize; 588 } 589 return 0; 590 }; 591 592 if (cfg.memUsagePolicy == 0) 593 { 594 m_allocate = [this, allocator](ResInfo &resInfo, MOS_RESOURCE &res) -> size_t { 595 std::random_device rd; 596 std::mt19937 gen(rd()); 597 std::discrete_distribution<> distribution({ 598 static_cast<double>(m_memMng[0].cap - m_memMng[0].usage), 599 static_cast<double>(m_memMng[1].cap - m_memMng[1].usage), 600 }); 601 if (distribution(gen)) 602 { 603 std::swap(m_memMng[0], m_memMng[1]); 604 } 605 return allocator(resInfo, res); 606 }; 607 } 608 else 609 { 610 m_allocate = std::move(allocator); 611 } 612 } 613 } 614 ConfigureCopyMethod(const Config & cfg)615 void ConfigureCopyMethod(const Config &cfg) 616 { 617 if (cfg.weightRenderCopy + cfg.weightVECopy + cfg.weightBLTCopy == 0) 618 { 619 m_copyMethod = [] { 620 return MCPY_METHOD_DEFAULT; 621 }; 622 } 623 else 624 { 625 const auto weightRenderCopy = static_cast<double>(cfg.weightRenderCopy); 626 const auto weightVECopy = static_cast<double>(cfg.weightVECopy); 627 const auto weightBLTCopy = static_cast<double>(cfg.weightBLTCopy); 628 629 m_copyMethod = [=] { 630 const MCPY_METHOD methods[] = { 631 MCPY_METHOD_PERFORMANCE, 632 MCPY_METHOD_BALANCE, 633 MCPY_METHOD_POWERSAVING, 634 }; 635 std::random_device rd; 636 std::mt19937 gen(rd()); 637 std::discrete_distribution<> distribution({ 638 weightRenderCopy, 639 weightVECopy, 640 weightBLTCopy, 641 }); 642 return methods[distribution(gen)]; 643 }; 644 } 645 } 646 ConfigureWriter(const Config & cfg)647 void ConfigureWriter(const Config &cfg) 648 { 649 switch (cfg.writeDst) 650 { 651 case 0: { 652 if (cfg.writeMode == 0 && cfg.bufferSize > 0) 653 { 654 m_write = BufferedWriter(cfg.bufferSize); 655 } 656 else if (cfg.writeMode == 0) 657 { 658 m_write = [=]( 659 std::string &&name, 660 const void *data, 661 size_t size, 662 std::function<void(std::ostream &, const void *, size_t)> &&) { 663 std::ofstream ofs(name, std::ios_base::out | std::ios_base::binary); 664 ofs.write(static_cast<const char *>(data), size); 665 }; 666 } 667 else if (cfg.writeMode == 1) 668 { 669 m_write = [](std::string &&name, 670 const void *data, 671 size_t size, 672 std::function<void(std::ostream &, const void *, size_t)> 673 &&serializer) { 674 std::ofstream ofs(name, std::ios_base::out); 675 serializer(ofs, data, size); 676 }; 677 } 678 else 679 { 680 m_write = [](std::string &&name, 681 const void *data, 682 size_t size, 683 std::function<void(std::ostream &, const void *, size_t)> 684 &&serializer) { 685 if (serializer.target_type() == 686 std::function<void(std::ostream &, const void *, size_t)>( 687 DefaultSerializer()) 688 .target_type()) 689 { 690 std::ofstream ofs(name, std::ios_base::out | std::ios_base::binary); 691 ofs.write(static_cast<const char *>(data), size); 692 } 693 else 694 { 695 std::ofstream ofs(name, std::ios_base::out); 696 serializer(ofs, data, size); 697 } 698 }; 699 } 700 break; 701 } 702 case 1: { 703 m_write = []( 704 std::string &&name, 705 const void *data, 706 size_t size, 707 std::function<void(std::ostream &, const void *, size_t)> &&) { 708 MOS_TraceDataDump(name.c_str(), 0, data, size); 709 }; 710 break; 711 } 712 case 2: 713 default: { 714 m_write = []( 715 std::string &&, 716 const void *, 717 size_t, 718 std::function<void(std::ostream &, const void *, size_t)> &&) {}; 719 break; 720 } 721 } 722 723 if (cfg.informOnError && cfg.writeDst == 0) 724 { 725 m_writeError = [this](const std::string &name, const std::string &error) { 726 static const char dummy = 0; 727 std::thread w( 728 [=] { 729 std::ofstream ofs( 730 name + "." + error, 731 std::ios_base::out | std::ios_base::binary); 732 ofs.write(&dummy, sizeof(dummy)); 733 }); 734 w.detach(); 735 }; 736 } 737 else 738 { 739 m_writeError = [](const std::string &, const std::string &) {}; 740 } 741 } 742 LaunchScheduler()743 void LaunchScheduler() 744 { 745 m_gfxMemAsyncData.scheduler = std::thread( 746 [this] { 747 std::future<void> future; 748 while (true) 749 { 750 std::unique_lock<std::mutex> lk(m_gfxMemAsyncData.mutex); 751 m_gfxMemAsyncData.cond.wait( 752 lk, 753 [this] { 754 return (m_gfxMemAsyncData.ready4Dump && !m_gfxMemAsyncData.resQueue.empty()) || m_gfxMemAsyncData.stopScheduler; 755 }); 756 if (m_gfxMemAsyncData.stopScheduler) 757 { 758 break; 759 } 760 auto qf = m_gfxMemAsyncData.resQueue.front(); 761 m_gfxMemAsyncData.ready4Dump = false; 762 lk.unlock(); 763 future = std::async( 764 std::launch::async, 765 [this, qf] { 766 DoDump(qf); 767 { 768 std::lock_guard<std::mutex> lk(m_gfxMemAsyncData.mutex); 769 m_gfxMemAsyncData.resQueue.front()->occupied = false; 770 m_gfxMemAsyncData.resQueue.pop(); 771 m_gfxMemAsyncData.ready4Dump = true; 772 } 773 m_gfxMemAsyncData.cond.notify_all(); 774 }); 775 } 776 if (future.valid()) 777 { 778 future.wait(); 779 } 780 std::lock_guard<std::mutex> lk(m_gfxMemAsyncData.mutex); 781 while (!m_gfxMemAsyncData.resQueue.empty()) 782 { 783 DoDump(m_gfxMemAsyncData.resQueue.front()); 784 m_gfxMemAsyncData.resQueue.front()->occupied = false; 785 m_gfxMemAsyncData.resQueue.pop(); 786 } 787 }); 788 789 m_sysMemAsyncData.scheduler = std::thread( 790 [this] { 791 std::future<void> future; 792 while (true) 793 { 794 std::unique_lock<std::mutex> lk(m_sysMemAsyncData.mutex); 795 m_sysMemAsyncData.cond.wait( 796 lk, 797 [this] { 798 return (m_sysMemAsyncData.ready4Dump && !m_sysMemAsyncData.resQueue.empty()) || m_sysMemAsyncData.stopScheduler; 799 }); 800 if (m_sysMemAsyncData.stopScheduler) 801 { 802 break; 803 } 804 auto qf = m_sysMemAsyncData.resQueue.front(); 805 m_sysMemAsyncData.ready4Dump = false; 806 lk.unlock(); 807 future = std::async( 808 std::launch::async, 809 [this, qf] { 810 m_write( 811 std::move(qf->name), 812 qf->res + qf->offset, 813 qf->size, 814 std::move(qf->serializer)); 815 { 816 std::lock_guard<std::mutex> lk(m_sysMemAsyncData.mutex); 817 m_sysMemAsyncData.resQueue.front()->occupied = false; 818 m_sysMemAsyncData.resQueue.pop(); 819 m_sysMemAsyncData.ready4Dump = true; 820 } 821 m_sysMemAsyncData.cond.notify_all(); 822 }); 823 } 824 if (future.valid()) 825 { 826 future.wait(); 827 } 828 std::lock_guard<std::mutex> lk(m_sysMemAsyncData.mutex); 829 while (!m_sysMemAsyncData.resQueue.empty()) 830 { 831 auto qf = m_sysMemAsyncData.resQueue.front(); 832 m_write( 833 std::move(qf->name), 834 qf->res + qf->offset, 835 qf->size, 836 std::move(qf->serializer)); 837 qf->occupied = false; 838 m_sysMemAsyncData.resQueue.pop(); 839 } 840 }); 841 } 842 GetResInfo(MOS_RESOURCE & res,ResInfo & resInfo) const843 MOS_STATUS GetResInfo(MOS_RESOURCE &res, ResInfo &resInfo) const 844 { 845 auto ret = MOS_STATUS_SUCCESS; 846 auto resType = m_osItf.pfnGetResType(&res); 847 MOS_SURFACE details = {}; 848 849 if (resType != MOS_GFXRES_BUFFER) 850 { 851 details.Format = Format_Invalid; 852 } 853 854 ret = m_osItf.pfnGetResourceInfo(&m_osItf, &res, &details); 855 856 resInfo.Type = resType; 857 resInfo.dwWidth = details.dwWidth; 858 resInfo.dwHeight = details.dwHeight; 859 resInfo.TileType = MOS_TILE_LINEAR; 860 resInfo.Format = details.Format; 861 resInfo.Flags.bCacheable = 1; 862 863 return ret; 864 } 865 DoDump(std::shared_ptr<ResGfx> res) const866 void DoDump(std::shared_ptr<ResGfx> res) const 867 { 868 MOS_LOCK_PARAMS lockFlags{}; 869 lockFlags.ReadOnly = 1; 870 lockFlags.TiledAsTiled = 1; 871 872 auto pRes = &res->res; 873 MOS_RESOURCE tmpRes = {}; 874 875 if (res->localMem) 876 { 877 // locking/reading resource from local graphic memory is extremely inefficient, so 878 // copy resource to a temporary buffer allocated in shared memory before lock 879 880 ResInfo resInfo{}; 881 if (GetResInfo(res->res, resInfo) != MOS_STATUS_SUCCESS) 882 { 883 return m_writeError( 884 res->name, 885 "get_internal_resource_info_failed"); 886 } 887 resInfo.dwMemType = MOS_MEMPOOL_SYSTEMMEMORY; 888 889 if (m_osItf.pfnAllocateResource(&m_osItf, &resInfo, &tmpRes) != MOS_STATUS_SUCCESS) 890 { 891 return m_writeError( 892 res->name, 893 "allocate_tmp_resource_failed"); 894 } 895 896 if (m_mediaCopyWrapper.MediaCopy(&res->res, &tmpRes, m_copyMethod()) != 897 MOS_STATUS_SUCCESS) 898 { 899 m_mediaCopyIsGood = false; 900 return m_writeError( 901 res->name, 902 "internal_surface_copy_failed"); 903 } 904 else 905 { 906 m_mediaCopyIsGood = true; 907 } 908 909 pRes = &tmpRes; 910 } 911 912 auto resSize = GetResSizeAndFixName(pRes->pGmmResInfo, res->name); 913 if (resSize < res->offset + res->size) 914 { 915 return m_writeError( 916 res->name, 917 "incorrect_size_offset"); 918 } 919 920 auto data = static_cast<const char *>( 921 m_osItf.pfnLockResource(&m_osItf, pRes, &lockFlags)); 922 923 if (data) 924 { 925 m_write( 926 std::move(res->name), 927 data + res->offset, 928 res->size == 0 ? resSize - res->offset : res->size, 929 std::move(res->serializer)); 930 m_osItf.pfnUnlockResource(&m_osItf, pRes); 931 } 932 else 933 { 934 m_writeError( 935 res->name, 936 "lock_failed"); 937 } 938 939 if (Mos_ResourceIsNull(&tmpRes) == false) 940 { 941 m_osItf.pfnFreeResource(&m_osItf, &tmpRes); 942 } 943 } 944 945 protected: 946 template <typename RES> 947 struct CommonAsyncData 948 { 949 std::thread scheduler; 950 std::mutex mutex; 951 std::condition_variable cond; 952 std::queue< 953 std::shared_ptr<RES>> 954 resQueue; 955 bool ready4Dump = true; 956 bool stopScheduler = false; 957 }; 958 959 struct GfxMemAsyncData : CommonAsyncData<ResGfx> 960 { 961 std::map< 962 ResInfo, 963 std::vector<std::shared_ptr<ResGfx>>, 964 ResInfoCmp> 965 resPool; 966 }; 967 968 struct SysMemAsyncData : CommonAsyncData<ResSys> 969 { 970 std::map< 971 size_t, 972 std::vector<std::shared_ptr<ResSys>>> 973 resPool; 974 }; 975 976 protected: 977 bool m_allowDataLoss = true; 978 979 std::array< 980 MemMng, 981 2> 982 m_memMng; 983 984 std::function< 985 bool()> 986 m_2CacheTask; 987 988 std::function< 989 size_t(ResInfo &, MOS_RESOURCE &)> 990 m_allocate; 991 992 std::function< 993 MCPY_METHOD()> 994 m_copyMethod; 995 996 std::function< 997 void( 998 std::string &&, 999 const void *, 1000 size_t, 1001 std::function<void(std::ostream &, const void *, size_t)> &&)> 1002 m_write; 1003 1004 std::function< 1005 void(const std::string &, const std::string &)> 1006 m_writeError; 1007 1008 mutable std::atomic_bool m_mediaCopyIsGood{true}; 1009 1010 MOS_INTERFACE &m_osItf; 1011 MediaCopyWrapper &m_mediaCopyWrapper; 1012 1013 GfxMemAsyncData m_gfxMemAsyncData; 1014 SysMemAsyncData m_sysMemAsyncData; 1015 1016 MEDIA_CLASS_DEFINE_END(MediaDebugFastDumpImp) 1017 }; 1018 1019 PMOS_INTERFACE MediaDebugFastDumpImp::ResGfx::osItf = nullptr; 1020 1021 #endif // USE_MEDIA_DEBUG_TOOL 1022