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