1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     demo_DebugUtility.h
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 18943 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NW_DEMO_DEBUGUTILITY_H_
17 #define NW_DEMO_DEBUGUTILITY_H_
18 
19 //#include <nw/types.h>
20 
21 #include <nw/dev/dev_Screenshot.h>
22 #include <nw/demo/demo_HioPacketChannel.h>
23 #include <nw/demo/demo_GraphicsDrawing.h>
24 #include <nw/demo/demo_GraphicsSystem.h>
25 #include <nw/demo/demo_Memory.h>
26 #include <nw/demo/demo_Utility.h>
27 
28 namespace nw
29 {
30 
31 namespace demo
32 {
33 
34 //============================================================================
35 //! @name ユーティリティ
36 //@{
37 
38 //---------------------------------------------------------------------------
39 //! @brief        デモのデバッグに使用するユーティリティ関数をまとめたクラスです。
40 //---------------------------------------------------------------------------
41 class DebugUtility
42 {
43 private:
44     static const int NW_PROFILE_OUTPUT_INTERVAL = 600;
45 
46 public:
47     //=====================
48     //! @name 負荷の表示
49     //@{
50 
51     //---------------------------------------------------------------------------
52     //! @brief        負荷を表示を行います。
53     //!
54     //!               graphicsDrawingのBeginDrawingString()をあらかじめ実行し、
55     //!               DrawLoadMeter()後はFlushDrawing()を実行する必要があります。
56     //!
57     //! @param[in]    renderSystem 負荷計算をしているRenderSystemです。
58     //! @param[in]    graphicsDrawing 文字列表示に使うGraphicsDrawingです。
59     //! @param[in]    refreshMeter trueにすると負荷表示が更新されます。
60     //---------------------------------------------------------------------------
61     static void DrawLoadMeter(
62         nw::demo::RenderSystem* renderSystem,
63         nw::demo::GraphicsDrawing* graphicsDrawing,
64         bool refreshMeter
65     );
66 
67     //---------------------------------------------------------------------------
68     //! @brief      CPU パフォーマンス計測モードであるかを取得します。
69     //!
70     //!             DBG スイッチを ON にすることで、CPUパフォーマンス計測モードになります。
71     //!
72     //! @return     CPU パフォーマンス計測モードであれば true を返します。
73     //---------------------------------------------------------------------------
IsCpuProfilingMode()74     static bool IsCpuProfilingMode()
75     {
76         return s_IsForceCpuProfilingMode || nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_DEBUG);
77     }
78 
79     //@}
80 
81     //=====================
82     //! @name スクリーンショットテスト
83     //@{
84 
85     //---------------------------------------------------------------------------
86     //! @brief        自動テストを初期化します。
87     //!
88     //!               NW_AUTOTESTが定義されていない場合は何も行われません。
89     //!
90     //! @param[in]    allocator 初期化に使用するアロケーターです。
91     //! @param[in]    deviceAllocator Host IO 接続に使用するデバイスアロケーターです。
92     //---------------------------------------------------------------------------
93     static void InitializeAutoTest(
94         os::IAllocator* allocator,
95         os::IAllocator* deviceAllocator,
96         nw::demo::RenderSystem** renderSystem,
97         const u32* correctHashDisplay0 = NULL,
98         const u32* correctHashDisplay1 = NULL
99     )
100     {
101         #if defined(NW_AUTO_TEST)
102             s_AutoTester = AutoTester::Create(
103                 allocator,
104                 deviceAllocator
105             );
106 
107             s_AutoTester->Initialize(5, false, renderSystem, correctHashDisplay0, correctHashDisplay1);
108         #else
109             NW_UNUSED_VARIABLE(allocator);
110             NW_UNUSED_VARIABLE(deviceAllocator);
111             NW_UNUSED_VARIABLE(renderSystem);
112             NW_UNUSED_VARIABLE(correctHashDisplay0);
113             NW_UNUSED_VARIABLE(correctHashDisplay1);
114         #endif
115     }
116 
117     //---------------------------------------------------------------------------
118     //! @brief        自動テストの終了処理を行います。
119     //!
120     //!               NW_AUTOTESTが定義されていない場合は何も行われません。
121     //---------------------------------------------------------------------------
FinalizeAutoTest()122     static void FinalizeAutoTest()
123     {
124         #if defined(NW_AUTO_TEST)
125         nw::ut::SafeDestroy(s_AutoTester);
126         #endif
127     }
128 
129     //---------------------------------------------------------------------------
130     //! @brief        自動テストのフレームを進めます。
131     //---------------------------------------------------------------------------
AdvanceAutoTestFrame()132     static void AdvanceAutoTestFrame()
133     {
134         #if defined(NW_AUTO_TEST)
135         NW_ASSERT(s_AutoTester);
136         s_AutoTester->AdvanceFrame();
137         #endif
138         if (!s_SilentMode)
139         {
140             if (s_ProfileCallCount % NW_PROFILE_OUTPUT_INTERVAL == 0)
141             {
142                 NW_DUMP_PROFILE();
143             }
144             else if (s_ProfileCallCount % NW_PROFILE_OUTPUT_INTERVAL == 1)
145             {
146                 NW_CLEAR_PROFILE();
147             }
148             s_ProfileCallCount++;
149         }
150     }
151 
152     //@}
153 
154     //=====================
155     //! @name メモリリーク検出
156     //@{
157 
158     //---------------------------------------------------------------------------
159     //! @brief          メモリリークの検出機能をリセットします。
160     //!
161     //!                 この関数が実行されると、その時点でのメモリ使用量を記憶します。
162     //!                 メモリリークを検出するためには TestMemoryLeak() 関数を実行してください。
163     //!
164     //!                 この関数を複数回実行すると、最後の実行のみが有効になります。
165     //!
166     //! @param[in]      deviceAllocator 検出対象のデバイスメモリアロケータです。
167     //! @param[in]      particleAllocator 検出対象のパーティクルメモリアロケータです。
168     //---------------------------------------------------------------------------
169     static void
ResetMemoryLeakTester(nw::demo::DemoAllocator * deviceAllocator,nw::demo::DemoAllocator * particleAllocator)170     ResetMemoryLeakTester(
171         nw::demo::DemoAllocator* deviceAllocator,
172         nw::demo::DemoAllocator* particleAllocator
173     )
174     {
175         s_DeviceMemoryFreeSize = deviceAllocator->GetFreeSize();
176         if (particleAllocator)
177         {
178             s_ParticleMemoryFreeSize = particleAllocator->GetFreeSize();
179         }
180     }
181 
182     //---------------------------------------------------------------------------
183     //! @brief          メモリリークを検出します。
184     //!
185     //!                 最後に ResetMemoryLeakTester() が実行されたときとメモリ使用量を比べます。
186     //!                 メモリ使用量が ResetMemoryLeakTester() 実行時と一致しない場合はヒープのダンプを出力します。
187     //!
188     //! @param[in]      deviceAllocator 検出対象のデバイスメモリアロケータです。
189     //! @param[in]      particleAllocator 検出対象のパーティクルメモリアロケータです。
190     //!
191     //! @return         メモリ使用量が ResetMemoryLeakTester() 実行時と一致する場合は true を、そうでない場合は false を返します。
192     //---------------------------------------------------------------------------
193     static bool
TestMemoryLeak(nw::demo::DemoAllocator * deviceAllocator,nw::demo::DemoAllocator * particleAllocator)194     TestMemoryLeak(
195         nw::demo::DemoAllocator* deviceAllocator,
196         nw::demo::DemoAllocator* particleAllocator
197     )
198     {
199         bool result = true;
200 
201         s32 mainLeakSize = s_DeviceMemoryFreeSize - deviceAllocator->GetFreeSize();
202         s32 particleLeakSize = 0;
203         if (particleAllocator)
204         {
205             particleLeakSize = s_ParticleMemoryFreeSize - particleAllocator->GetFreeSize();
206         }
207 
208         if (!s_SilentMode)
209         {
210             if (mainLeakSize)
211             {
212                 NW_DEV_LOG(
213                     "######### Device Memory Leak Detected !! (%d bytes)\n", mainLeakSize);
214                 deviceAllocator->Dump();
215                 result = false;
216             }
217             if (particleLeakSize)
218             {
219                 NW_DEV_LOG(
220                     "######### Particle Memory Leak Detected !! (%d bytes)\n", particleLeakSize);
221                 particleAllocator->Dump();
222                 result = false;
223             }
224         }
225 
226     #if defined(NW_AUTO_TEST)
227         if (s_AutoTester)
228         {
229             s_AutoTester->NotifyMemoryLeak(mainLeakSize, particleLeakSize);
230         }
231     #endif
232 
233         return result;
234     }
235 
236     //@}
237 
238     //=====================
239     //! @name ログ出力制御
240     //@{
241 
242     //---------------------------------------------------------------------------
243     //! @brief          デバッグユーティリティクラスからのログへの出力を無効にします。
244     //!
245     //!                 isEnabled に true を指定すると、デバッグユーティリティはログへの出力を行わなくなります。
246     //!
247     //! @param          isEnabled true を指定すると、ログへの出力を行わなくなります。
248     //---------------------------------------------------------------------------
SetSilentMode(bool isEnabled)249     static void SetSilentMode(bool isEnabled) {s_SilentMode = isEnabled;}
250 
251     //@}
252 
253 private:
254     static size_t s_DeviceMemoryFreeSize;
255     static size_t s_ParticleMemoryFreeSize;
256     static bool s_IsForceCpuProfilingMode; //!< 強制的に CPU パフォーマンス計測モードにするフラグです。
257     static bool s_SilentMode; //!< ログへの出力を止めます。
258     static int s_ProfileCallCount;
259 
260 #if defined(NW_AUTO_TEST)
261     class AutoTester
262     {
263     public:
264         //! @brief 自動テスターを生成します。
265         static AutoTester* Create(
266             os::IAllocator* allocator,
267             os::IAllocator* deviceAllocator
268         );
269 
270         //! @brief      自動テスターを初期化します。
271         //!
272         //!             Host IO を使用し、PC 側の自動テスターとの接続を試みます。
273         //!             接続に成功し、カスタムテストのシナリオを受信できれば、カスタムテストの初期化を行います。
274         //!             接続できなければ再試行することなく接続処理を終了し、デフォルトテストの初期化を行います。
275         //!             そのため、カスタムテストを実行するためには、この関数が実行されるまでに PC 側のテスターを
276         //!             待機状態にしておく必要があります。
277         //!
278         //!             needCustom に true を指定すると、カスタムテストの初期化に成功するまで PC 側への接続を試行し続けます。
279         //!             それまで制御は戻りません。
280         //!
281         //! @param[in]  channelNumber PC 側の自動テスターと接続するための Host IO のチャンネル番号です。
282         //! @param[in]  needCustom カスタムテストを必ず実行する場合は true を指定します。
283         //!
284         //! @return カスタムテストが初期化されたら正の値を、デフォルトテストが初期化されたら 0 を、初期化に失敗したら負の値を返します。
285         int Initialize(
286             s32 channelNumber,
287             bool needCustom,
288             nw::demo::RenderSystem** renderSystem,
289             const u32* correctHashDisplay0,
290             const u32* correctHashDisplay1
291         );
292 
293         //! @brief 自動テスターの終了処理を行います。
294         void Finalize();
295 
296         //! @brief 自動テスターを破棄します。
297         void Destroy();
298 
299         //! @brief テストのフレームを進めます。
300         void AdvanceFrame();
301 
302         //! @brief メモリリークの検出結果を PC に通知します。
303         //!
304         //!        カスタムテスト中であればメモリリーク情報のパケットを送信します。
305         //!
306         //! @param deviceLeakSize デバイスメモリのリーク量です。
307         //! @param particleLeakSize パーティクルメモリのリーク量です。
308         void NotifyMemoryLeak(s32 deviceLeakSize, s32 particleLeakSize);
309 
310     private:
311         //! テストシナリオのイベント種です。
312         enum EventType
313         {
314             EVENT_SCREENSHOT = 1,               //!< スクリーンショットの撮影と送信です。
315             EVENT_SET_PAD_STATUS,               //!< デモパッドの状態指定です。
316             EVENT_BEACON,                       //!< ビーコン送信です。
317             EVENT_SEND_LOAD_METER,              //!< 負荷情報の送信です。
318             EVENT_RESET_LOAD_METER,             //!< 負荷情報のリセットです。
319             EVENT_TEST_DONE,                    //!< テスト完了パケットの送信です。
320             EVENT_SWITCH_CPU_PROFILING_MODE     //!< CPU パフォーマンス計測モードの切り替えです。
321         };
322 
323         struct ScreenshotParam
324         {
325             enum DisplayFlag
326             {
327                 DISPLAY_0 = 1,
328                 DISPLAY_0_EXT,
329                 DISPLAY_1,
330                 DISPLAY_0_BOTH,
331                 DISPLAY_ALL
332             };
333             s32 display;
334             u32 hash[5]; // すべて 0 ならテストを行いません。
335         };
336 
337         struct SetPadStatusParam
338         {
339             u32 padStatus;
340         };
341 
342         struct BeaconParam
343         {
344         };
345 
346         struct SendLoadMeterParam
347         {
348         };
349 
350         struct ResetLoadMeterParam
351         {
352         };
353 
354         struct SwitchCpuProfilingModeParam
355         {
356             s32 isModeEnabled;
357         };
358 
359         struct TestEvent
360         {
361             s32 frameCount;
362             EventType eventType;
363 
364             union
365             {
366                 ScreenshotParam screenshot;
367                 SetPadStatusParam setPadStatus;
368                 BeaconParam beacon;
369                 SendLoadMeterParam sendLoadMeter;
370                 ResetLoadMeterParam resetLoadMeter;
371                 SwitchCpuProfilingModeParam switchCpuProfilingMode;
372             } param;
373         };
374 
375         // 受信パケットの構造体です。
376 
377         //! @brief テスト情報パケットのヘッダです。
378         struct TestInfoHeader
379         {
380             s32 packetType;
381             s32 eventCount;
382             char programName[64];
383             enum { PACKET_TYPE_ID = 0x213FEB92 };
384 
TestInfoHeaderTestInfoHeader385             TestInfoHeader()
386             : packetType(PACKET_TYPE_ID),
387               eventCount(0)
388             {}
389         };
390 
391         //! @brief シナリオパケットのヘッダです。
392         struct ScenarioHeader
393         {
394             s32 packetType;
395             s32 eventCount;
396             enum { PACKET_TYPE_ID = 0x6794C4C8 };
397 
ScenarioHeaderScenarioHeader398             ScenarioHeader()
399             : packetType(PACKET_TYPE_ID),
400               eventCount(0)
401             {}
402 
403         };
404 
405         // 送信パケットの構造体です。
406 
407         //! @brief スクリーンショットパケットのヘッダです。
408         struct ScreenshotHeader
409         {
410             s32 packetType;
411             s32 frameCount;
412             s32 width;
413             s32 height;
414             s32 display;
415 
416             enum { PACKET_TYPE_ID = 0x23848aa6 };
417 
ScreenshotHeaderScreenshotHeader418             ScreenshotHeader()
419             : packetType(PACKET_TYPE_ID),
420               frameCount(0),
421               width(0),
422               height(0)
423             {}
424         };
425 
426         //! @brief ビーコンパケットのヘッダです。
427         struct BeaconHeader
428         {
429             s32 packetType;
430             s32 frameCount;
431 
432             enum { PACKET_TYPE_ID = 0x9538a79a };
433 
BeaconHeaderBeaconHeader434             BeaconHeader()
435             : packetType(PACKET_TYPE_ID),
436               frameCount(0)
437             {}
438         };
439 
440         //! @brief 負荷情報パケットのヘッダです。
441         struct LoadMeterHeader
442         {
443             s32 packetType;
444             s32 frameCount;
445 
446             enum { PACKET_TYPE_ID = 0xc57c557 };
447 
LoadMeterHeaderLoadMeterHeader448             LoadMeterHeader()
449             : packetType(PACKET_TYPE_ID),
450               frameCount(0)
451             {}
452         };
453 
454         //! @brief 負荷情報パケットのデータ構造です。
455         struct LoadMeterData
456         {
457             s32 callCount;
458             s32 cpuTime;
459             s32 gpuTime;
460             s32 otherGpuTime;
461             s32 cmdSize;
462         };
463 
464         //! @brief メモリリーク情報パケットのヘッダです。
465         struct MemoryLeakHeader
466         {
467             s32 packetType;
468             s32 frameCount;
469 
470             enum { PACKET_TYPE_ID = 0x4F1B0AD2 };
471 
MemoryLeakHeaderMemoryLeakHeader472             MemoryLeakHeader()
473             : packetType(PACKET_TYPE_ID),
474               frameCount(0)
475             {}
476         };
477 
478         //! @brief メモリリーク情報パケットのデータ構造です。
479         struct MemoryLeakData
480         {
481             s32 deviceLeakSize;
482             s32 particleLeakSize;
483         };
484 
485         //! @brief テスト終了通知パケットのヘッダです。
486         struct DoneHeader
487         {
488             s32 packetType;
489             s32 frameCount;
490 
491             enum { PACKET_TYPE_ID = 0x916bbe7 };
492 
DoneHeaderDoneHeader493             DoneHeader()
494             : packetType(PACKET_TYPE_ID),
495               frameCount(0)
496             {}
497         };
498 
499         AutoTester(
500             os::IAllocator* allocator,
501             os::IAllocator* deviceAllocator
502         );
503         ~AutoTester();
504 
505         void SendScreenshot(const ScreenshotParam& param);
506         void SetPadStatus(const SetPadStatusParam& param);
507         void SendBeacon(const BeaconParam& param);
508         void SendLoadMeter(const SendLoadMeterParam& param);
509         void NotifyDone();
510         void SwitchCpuProfilingMode(const SwitchCpuProfilingModeParam& param);
511 
512         os::IAllocator* m_Allocator;
513         os::IAllocator* m_DeviceAllocator;
514 
515         nw::dev::ScreenshotManager* m_ScreenshotManager;
516 
517         nw::demo::HioPacketChannel* m_HioPacketChannel;
518 
519         s32 m_FrameCount;
520 
521         nw::ut::MoveArray<TestEvent> m_Scenario;
522         nw::ut::MoveArray<TestEvent>::iterator m_CurrentEvent;
523 
524         nw::demo::RenderSystem** m_RenderSystem;
525     };
526 
527     static AutoTester* s_AutoTester;
528 #else
529     class AutoTester
530     {
531     public:
Create(os::IAllocator *,os::IAllocator *)532         static AutoTester* Create(os::IAllocator*,os::IAllocator*){return NULL;}
Initialize(s32,bool,nw::demo::RenderSystem **,const u32 *,const u32 *)533         int Initialize(s32,bool,nw::demo::RenderSystem**,const u32*,const u32*){return -1;}
Finalize()534         void Finalize(){}
Destroy()535         void Destroy(){}
AdvanceFrame()536         void AdvanceFrame(){}
NotifyMemoryLeak(s32,s32)537         void NotifyMemoryLeak(s32, s32){}
538     };
539 #endif
540 };
541 
542 //@}
543 
544 namespace internal
545 {
546 
547 
548 //! @details :private
549 class DemoTestLoop
550 {
551 private:
552     static const int NW_PROFILE_MAX_REPORT = 60;
553 public:
DemoTestLoop(nw::demo::DemoAllocator * deviceAllocator,nw::demo::DemoAllocator * particleAllocator,nw::demo::RenderSystem ** renderSystem,const u32 * hash1,const u32 * hash2)554     DemoTestLoop(
555         nw::demo::DemoAllocator* deviceAllocator,
556         nw::demo::DemoAllocator* particleAllocator,
557         nw::demo::RenderSystem** renderSystem,
558         const u32* hash1,
559         const u32* hash2
560     )
561     : m_DeviceAllocator(deviceAllocator),
562       m_ParticleAllocator(particleAllocator),
563       m_ContinueFlag(true)
564     {
565         nw::demo::DebugUtility::InitializeAutoTest(m_DeviceAllocator, m_DeviceAllocator, renderSystem, hash1, hash2);
566         NW_INITIALIZE_PROFILE(NW_PROFILE_MAX_REPORT, m_DeviceAllocator);
567 #if defined(NW_DEBUG_CHECK_MEMORY_LEAK)
568         nw::demo::DebugUtility::ResetMemoryLeakTester(m_DeviceAllocator, m_ParticleAllocator);
569 #endif
570     }
~DemoTestLoop()571     ~DemoTestLoop()
572     {
573         NW_FINALIZE_PROFILE(m_DeviceAllocator);
574         nw::demo::DebugUtility::FinalizeAutoTest();
575     }
Continue()576     void Continue() {
577 #if defined(NW_DEBUG_CHECK_MEMORY_LEAK)
578         nw::demo::DebugUtility::TestMemoryLeak(m_DeviceAllocator, m_ParticleAllocator);
579 #else
580         m_ContinueFlag = false;
581 #endif
582     }
IsContinuing()583     bool IsContinuing() {
584 #if defined(NW_DEBUG_CHECK_MEMORY_LEAK)
585         return ! ::nw::demo::internal::IsTerminatingImpl();
586 #else
587         return m_ContinueFlag;
588 #endif
589     }
590 private:
591     nw::demo::DemoAllocator* m_DeviceAllocator;
592     nw::demo::DemoAllocator* m_ParticleAllocator;
593     bool m_ContinueFlag;
594 };
595 
596 } // namespace internal
597 
598 //============================================================================
599 //! @name デモプログラム支援
600 //@{
601 
602 #define NW_DEMO_TEST_LOOP(deviceAllocator, particleAllocator, renderSystem)                                             \
603     for (                                                                                                               \
604         nw::demo::internal::DemoTestLoop demoTestLoop(deviceAllocator, particleAllocator, renderSystem, NULL, NULL) ;   \
605         demoTestLoop.IsContinuing() ;                                                                                   \
606         demoTestLoop.Continue() )
607 
608 //@}
609 
610 } // namespace demo
611 } // namespace nw
612 
613 #endif // NW_DEMO_DEBUGUTILITY_H_
614