/*---------------------------------------------------------------------------* Project: NintendoWare File: demo_DebugUtility.h Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 18943 $ *---------------------------------------------------------------------------*/ #ifndef NW_DEMO_DEBUGUTILITY_H_ #define NW_DEMO_DEBUGUTILITY_H_ //#include #include #include #include #include #include #include namespace nw { namespace demo { //============================================================================ //! @name ユーティリティ //@{ //--------------------------------------------------------------------------- //! @brief デモのデバッグに使用するユーティリティ関数をまとめたクラスです。 //--------------------------------------------------------------------------- class DebugUtility { private: static const int NW_PROFILE_OUTPUT_INTERVAL = 600; public: //===================== //! @name 負荷の表示 //@{ //--------------------------------------------------------------------------- //! @brief 負荷を表示を行います。 //! //! graphicsDrawingのBeginDrawingString()をあらかじめ実行し、 //! DrawLoadMeter()後はFlushDrawing()を実行する必要があります。 //! //! @param[in] renderSystem 負荷計算をしているRenderSystemです。 //! @param[in] graphicsDrawing 文字列表示に使うGraphicsDrawingです。 //! @param[in] refreshMeter trueにすると負荷表示が更新されます。 //--------------------------------------------------------------------------- static void DrawLoadMeter( nw::demo::RenderSystem* renderSystem, nw::demo::GraphicsDrawing* graphicsDrawing, bool refreshMeter ); //--------------------------------------------------------------------------- //! @brief CPU パフォーマンス計測モードであるかを取得します。 //! //! DBG スイッチを ON にすることで、CPUパフォーマンス計測モードになります。 //! //! @return CPU パフォーマンス計測モードであれば true を返します。 //--------------------------------------------------------------------------- static bool IsCpuProfilingMode() { return s_IsForceCpuProfilingMode || nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_DEBUG); } //@} //===================== //! @name スクリーンショットテスト //@{ //--------------------------------------------------------------------------- //! @brief 自動テストを初期化します。 //! //! NW_AUTOTESTが定義されていない場合は何も行われません。 //! //! @param[in] allocator 初期化に使用するアロケーターです。 //! @param[in] deviceAllocator Host IO 接続に使用するデバイスアロケーターです。 //--------------------------------------------------------------------------- static void InitializeAutoTest( os::IAllocator* allocator, os::IAllocator* deviceAllocator, nw::demo::RenderSystem** renderSystem, const u32* correctHashDisplay0 = NULL, const u32* correctHashDisplay1 = NULL ) { #if defined(NW_AUTO_TEST) s_AutoTester = AutoTester::Create( allocator, deviceAllocator ); s_AutoTester->Initialize(5, false, renderSystem, correctHashDisplay0, correctHashDisplay1); #else NW_UNUSED_VARIABLE(allocator); NW_UNUSED_VARIABLE(deviceAllocator); NW_UNUSED_VARIABLE(renderSystem); NW_UNUSED_VARIABLE(correctHashDisplay0); NW_UNUSED_VARIABLE(correctHashDisplay1); #endif } //--------------------------------------------------------------------------- //! @brief 自動テストの終了処理を行います。 //! //! NW_AUTOTESTが定義されていない場合は何も行われません。 //--------------------------------------------------------------------------- static void FinalizeAutoTest() { #if defined(NW_AUTO_TEST) nw::ut::SafeDestroy(s_AutoTester); #endif } //--------------------------------------------------------------------------- //! @brief 自動テストのフレームを進めます。 //--------------------------------------------------------------------------- static void AdvanceAutoTestFrame() { #if defined(NW_AUTO_TEST) NW_ASSERT(s_AutoTester); s_AutoTester->AdvanceFrame(); #endif if (!s_SilentMode) { if (s_ProfileCallCount % NW_PROFILE_OUTPUT_INTERVAL == 0) { NW_DUMP_PROFILE(); } else if (s_ProfileCallCount % NW_PROFILE_OUTPUT_INTERVAL == 1) { NW_CLEAR_PROFILE(); } s_ProfileCallCount++; } } //@} //===================== //! @name メモリリーク検出 //@{ //--------------------------------------------------------------------------- //! @brief メモリリークの検出機能をリセットします。 //! //! この関数が実行されると、その時点でのメモリ使用量を記憶します。 //! メモリリークを検出するためには TestMemoryLeak() 関数を実行してください。 //! //! この関数を複数回実行すると、最後の実行のみが有効になります。 //! //! @param[in] deviceAllocator 検出対象のデバイスメモリアロケータです。 //! @param[in] particleAllocator 検出対象のパーティクルメモリアロケータです。 //--------------------------------------------------------------------------- static void ResetMemoryLeakTester( nw::demo::DemoAllocator* deviceAllocator, nw::demo::DemoAllocator* particleAllocator ) { s_DeviceMemoryFreeSize = deviceAllocator->GetFreeSize(); if (particleAllocator) { s_ParticleMemoryFreeSize = particleAllocator->GetFreeSize(); } } //--------------------------------------------------------------------------- //! @brief メモリリークを検出します。 //! //! 最後に ResetMemoryLeakTester() が実行されたときとメモリ使用量を比べます。 //! メモリ使用量が ResetMemoryLeakTester() 実行時と一致しない場合はヒープのダンプを出力します。 //! //! @param[in] deviceAllocator 検出対象のデバイスメモリアロケータです。 //! @param[in] particleAllocator 検出対象のパーティクルメモリアロケータです。 //! //! @return メモリ使用量が ResetMemoryLeakTester() 実行時と一致する場合は true を、そうでない場合は false を返します。 //--------------------------------------------------------------------------- static bool TestMemoryLeak( nw::demo::DemoAllocator* deviceAllocator, nw::demo::DemoAllocator* particleAllocator ) { bool result = true; s32 mainLeakSize = s_DeviceMemoryFreeSize - deviceAllocator->GetFreeSize(); s32 particleLeakSize = 0; if (particleAllocator) { particleLeakSize = s_ParticleMemoryFreeSize - particleAllocator->GetFreeSize(); } if (!s_SilentMode) { if (mainLeakSize) { NW_DEV_LOG( "######### Device Memory Leak Detected !! (%d bytes)\n", mainLeakSize); deviceAllocator->Dump(); result = false; } if (particleLeakSize) { NW_DEV_LOG( "######### Particle Memory Leak Detected !! (%d bytes)\n", particleLeakSize); particleAllocator->Dump(); result = false; } } #if defined(NW_AUTO_TEST) if (s_AutoTester) { s_AutoTester->NotifyMemoryLeak(mainLeakSize, particleLeakSize); } #endif return result; } //@} //===================== //! @name ログ出力制御 //@{ //--------------------------------------------------------------------------- //! @brief デバッグユーティリティクラスからのログへの出力を無効にします。 //! //! isEnabled に true を指定すると、デバッグユーティリティはログへの出力を行わなくなります。 //! //! @param isEnabled true を指定すると、ログへの出力を行わなくなります。 //--------------------------------------------------------------------------- static void SetSilentMode(bool isEnabled) {s_SilentMode = isEnabled;} //@} private: static size_t s_DeviceMemoryFreeSize; static size_t s_ParticleMemoryFreeSize; static bool s_IsForceCpuProfilingMode; //!< 強制的に CPU パフォーマンス計測モードにするフラグです。 static bool s_SilentMode; //!< ログへの出力を止めます。 static int s_ProfileCallCount; #if defined(NW_AUTO_TEST) class AutoTester { public: //! @brief 自動テスターを生成します。 static AutoTester* Create( os::IAllocator* allocator, os::IAllocator* deviceAllocator ); //! @brief 自動テスターを初期化します。 //! //! Host IO を使用し、PC 側の自動テスターとの接続を試みます。 //! 接続に成功し、カスタムテストのシナリオを受信できれば、カスタムテストの初期化を行います。 //! 接続できなければ再試行することなく接続処理を終了し、デフォルトテストの初期化を行います。 //! そのため、カスタムテストを実行するためには、この関数が実行されるまでに PC 側のテスターを //! 待機状態にしておく必要があります。 //! //! needCustom に true を指定すると、カスタムテストの初期化に成功するまで PC 側への接続を試行し続けます。 //! それまで制御は戻りません。 //! //! @param[in] channelNumber PC 側の自動テスターと接続するための Host IO のチャンネル番号です。 //! @param[in] needCustom カスタムテストを必ず実行する場合は true を指定します。 //! //! @return カスタムテストが初期化されたら正の値を、デフォルトテストが初期化されたら 0 を、初期化に失敗したら負の値を返します。 int Initialize( s32 channelNumber, bool needCustom, nw::demo::RenderSystem** renderSystem, const u32* correctHashDisplay0, const u32* correctHashDisplay1 ); //! @brief 自動テスターの終了処理を行います。 void Finalize(); //! @brief 自動テスターを破棄します。 void Destroy(); //! @brief テストのフレームを進めます。 void AdvanceFrame(); //! @brief メモリリークの検出結果を PC に通知します。 //! //! カスタムテスト中であればメモリリーク情報のパケットを送信します。 //! //! @param deviceLeakSize デバイスメモリのリーク量です。 //! @param particleLeakSize パーティクルメモリのリーク量です。 void NotifyMemoryLeak(s32 deviceLeakSize, s32 particleLeakSize); private: //! テストシナリオのイベント種です。 enum EventType { EVENT_SCREENSHOT = 1, //!< スクリーンショットの撮影と送信です。 EVENT_SET_PAD_STATUS, //!< デモパッドの状態指定です。 EVENT_BEACON, //!< ビーコン送信です。 EVENT_SEND_LOAD_METER, //!< 負荷情報の送信です。 EVENT_RESET_LOAD_METER, //!< 負荷情報のリセットです。 EVENT_TEST_DONE, //!< テスト完了パケットの送信です。 EVENT_SWITCH_CPU_PROFILING_MODE //!< CPU パフォーマンス計測モードの切り替えです。 }; struct ScreenshotParam { enum DisplayFlag { DISPLAY_0 = 1, DISPLAY_0_EXT, DISPLAY_1, DISPLAY_0_BOTH, DISPLAY_ALL }; s32 display; u32 hash[5]; // すべて 0 ならテストを行いません。 }; struct SetPadStatusParam { u32 padStatus; }; struct BeaconParam { }; struct SendLoadMeterParam { }; struct ResetLoadMeterParam { }; struct SwitchCpuProfilingModeParam { s32 isModeEnabled; }; struct TestEvent { s32 frameCount; EventType eventType; union { ScreenshotParam screenshot; SetPadStatusParam setPadStatus; BeaconParam beacon; SendLoadMeterParam sendLoadMeter; ResetLoadMeterParam resetLoadMeter; SwitchCpuProfilingModeParam switchCpuProfilingMode; } param; }; // 受信パケットの構造体です。 //! @brief テスト情報パケットのヘッダです。 struct TestInfoHeader { s32 packetType; s32 eventCount; char programName[64]; enum { PACKET_TYPE_ID = 0x213FEB92 }; TestInfoHeader() : packetType(PACKET_TYPE_ID), eventCount(0) {} }; //! @brief シナリオパケットのヘッダです。 struct ScenarioHeader { s32 packetType; s32 eventCount; enum { PACKET_TYPE_ID = 0x6794C4C8 }; ScenarioHeader() : packetType(PACKET_TYPE_ID), eventCount(0) {} }; // 送信パケットの構造体です。 //! @brief スクリーンショットパケットのヘッダです。 struct ScreenshotHeader { s32 packetType; s32 frameCount; s32 width; s32 height; s32 display; enum { PACKET_TYPE_ID = 0x23848aa6 }; ScreenshotHeader() : packetType(PACKET_TYPE_ID), frameCount(0), width(0), height(0) {} }; //! @brief ビーコンパケットのヘッダです。 struct BeaconHeader { s32 packetType; s32 frameCount; enum { PACKET_TYPE_ID = 0x9538a79a }; BeaconHeader() : packetType(PACKET_TYPE_ID), frameCount(0) {} }; //! @brief 負荷情報パケットのヘッダです。 struct LoadMeterHeader { s32 packetType; s32 frameCount; enum { PACKET_TYPE_ID = 0xc57c557 }; LoadMeterHeader() : packetType(PACKET_TYPE_ID), frameCount(0) {} }; //! @brief 負荷情報パケットのデータ構造です。 struct LoadMeterData { s32 callCount; s32 cpuTime; s32 gpuTime; s32 otherGpuTime; s32 cmdSize; }; //! @brief メモリリーク情報パケットのヘッダです。 struct MemoryLeakHeader { s32 packetType; s32 frameCount; enum { PACKET_TYPE_ID = 0x4F1B0AD2 }; MemoryLeakHeader() : packetType(PACKET_TYPE_ID), frameCount(0) {} }; //! @brief メモリリーク情報パケットのデータ構造です。 struct MemoryLeakData { s32 deviceLeakSize; s32 particleLeakSize; }; //! @brief テスト終了通知パケットのヘッダです。 struct DoneHeader { s32 packetType; s32 frameCount; enum { PACKET_TYPE_ID = 0x916bbe7 }; DoneHeader() : packetType(PACKET_TYPE_ID), frameCount(0) {} }; AutoTester( os::IAllocator* allocator, os::IAllocator* deviceAllocator ); ~AutoTester(); void SendScreenshot(const ScreenshotParam& param); void SetPadStatus(const SetPadStatusParam& param); void SendBeacon(const BeaconParam& param); void SendLoadMeter(const SendLoadMeterParam& param); void NotifyDone(); void SwitchCpuProfilingMode(const SwitchCpuProfilingModeParam& param); os::IAllocator* m_Allocator; os::IAllocator* m_DeviceAllocator; nw::dev::ScreenshotManager* m_ScreenshotManager; nw::demo::HioPacketChannel* m_HioPacketChannel; s32 m_FrameCount; nw::ut::MoveArray m_Scenario; nw::ut::MoveArray::iterator m_CurrentEvent; nw::demo::RenderSystem** m_RenderSystem; }; static AutoTester* s_AutoTester; #else class AutoTester { public: static AutoTester* Create(os::IAllocator*,os::IAllocator*){return NULL;} int Initialize(s32,bool,nw::demo::RenderSystem**,const u32*,const u32*){return -1;} void Finalize(){} void Destroy(){} void AdvanceFrame(){} void NotifyMemoryLeak(s32, s32){} }; #endif }; //@} namespace internal { //! @details :private class DemoTestLoop { private: static const int NW_PROFILE_MAX_REPORT = 60; public: DemoTestLoop( nw::demo::DemoAllocator* deviceAllocator, nw::demo::DemoAllocator* particleAllocator, nw::demo::RenderSystem** renderSystem, const u32* hash1, const u32* hash2 ) : m_DeviceAllocator(deviceAllocator), m_ParticleAllocator(particleAllocator), m_ContinueFlag(true) { nw::demo::DebugUtility::InitializeAutoTest(m_DeviceAllocator, m_DeviceAllocator, renderSystem, hash1, hash2); NW_INITIALIZE_PROFILE(NW_PROFILE_MAX_REPORT, m_DeviceAllocator); #if defined(NW_DEBUG_CHECK_MEMORY_LEAK) nw::demo::DebugUtility::ResetMemoryLeakTester(m_DeviceAllocator, m_ParticleAllocator); #endif } ~DemoTestLoop() { NW_FINALIZE_PROFILE(m_DeviceAllocator); nw::demo::DebugUtility::FinalizeAutoTest(); } void Continue() { #if defined(NW_DEBUG_CHECK_MEMORY_LEAK) nw::demo::DebugUtility::TestMemoryLeak(m_DeviceAllocator, m_ParticleAllocator); #else m_ContinueFlag = false; #endif } bool IsContinuing() { #if defined(NW_DEBUG_CHECK_MEMORY_LEAK) return ! ::nw::demo::internal::IsTerminatingImpl(); #else return m_ContinueFlag; #endif } private: nw::demo::DemoAllocator* m_DeviceAllocator; nw::demo::DemoAllocator* m_ParticleAllocator; bool m_ContinueFlag; }; } // namespace internal //============================================================================ //! @name デモプログラム支援 //@{ #define NW_DEMO_TEST_LOOP(deviceAllocator, particleAllocator, renderSystem) \ for ( \ nw::demo::internal::DemoTestLoop demoTestLoop(deviceAllocator, particleAllocator, renderSystem, NULL, NULL) ; \ demoTestLoop.IsContinuing() ; \ demoTestLoop.Continue() ) //@} } // namespace demo } // namespace nw #endif // NW_DEMO_DEBUGUTILITY_H_