/*---------------------------------------------------------------------------* 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 s32 NW_PROFILE_OUTPUT_INTERVAL = 600; static const s32 NW_LOAD_METER_INTERVAL = 60; static const s32 POS_COMMAND_SIZE_X = 15; static const s32 POS_COMMAND_SIZE_Y = 115; static const s32 POS_LOAD_METER_X = 16; static const s32 POS_LOAD_METER_Y = 140; static const s32 HEIGHT_LOAD_METER = 18; static const s32 POS_BAR_CHART_X = 15; static const s32 POS_BAR_CHART_Y = 188; static const s32 WIDTH_LOAD_METER_BAR_CHART = 256; static const s32 HEIGHT_LOAD_METER_BAR_CHART = 20; public: //===================== //! @name 負荷の表示 //@{ //--------------------------------------------------------------------------- //! @brief 負荷の計算を行います。 //! //! @param[in] renderSystem 負荷計算をしている RenderSystem です。 //--------------------------------------------------------------------------- static void CalcLoadMeter( nw::demo::RenderSystem* renderSystem ); //--------------------------------------------------------------------------- //! @brief 負荷計測結果の文字を描画します。 //! //! graphicsDrawing の BeginDrawingString() をあらかじめ実行し、 //! DrawLoadMeterText() 後は EndDrawingString() を実行する必要があります。 //! //! @param[in] renderSystem 負荷計算をしている RenderSystem です。 //! @param[in] graphicsDrawing 文字列表示に使う GraphicsDrawing です。 //--------------------------------------------------------------------------- static void DrawLoadMeterText( nw::demo::RenderSystem* renderSystem, nw::demo::GraphicsDrawing* graphicsDrawing ); //--------------------------------------------------------------------------- //! @brief 負荷計測結果の図形部分を描画します。 //! //! graphicsDrawing の BeginDrawingShape() をあらかじめ実行し、 //! DrawLoadMeter() 後は EndDrawingShape() を実行する必要があります。 //! //! @param[in] renderSystem 負荷計算をしている RenderSystem です。 //! @param[in] graphicsDrawing 文字列表示に使う GraphicsDrawing です。 //--------------------------------------------------------------------------- static void DrawLoadMeter( nw::demo::RenderSystem* renderSystem, nw::demo::GraphicsDrawing* graphicsDrawing ); //--------------------------------------------------------------------------- //! @brief 負荷計測結果をログに出力します。 //! //! @param[in] renderSystem 負荷計算をしている RenderSystem です。 //--------------------------------------------------------------------------- static void DumpLoadMeter( nw::demo::RenderSystem* renderSystem ); //--------------------------------------------------------------------------- //! @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, bool needCustomTest = false ) { s_ProfileCallCount = 0; s_DebugFrameCount = 0; #if defined(NW_AUTO_TEST) s_AutoTester = AutoTester::Create( allocator, deviceAllocator ); s_AutoTester->Initialize( 5, needCustomTest, renderSystem, correctHashDisplay0, correctHashDisplay1); #else NW_UNUSED_VARIABLE(allocator); NW_UNUSED_VARIABLE(deviceAllocator); NW_UNUSED_VARIABLE(renderSystem); NW_UNUSED_VARIABLE(correctHashDisplay0); NW_UNUSED_VARIABLE(correctHashDisplay1); NW_UNUSED_VARIABLE(needCustomTest); #endif } //--------------------------------------------------------------------------- //! @brief 自動テストの終了処理を行います。 //! //! NW_AUTOTESTが定義されていない場合は何も行われません。 //--------------------------------------------------------------------------- static void FinalizeAutoTest() { #if defined(NW_AUTO_TEST) s_AutoTester->Finalize(); 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++; } } //--------------------------------------------------------------------------- //! @brief InitializeScenes 後のテストを行ないます。 //--------------------------------------------------------------------------- static void PostInitializeScenes() { // SetAllocatorForTest でセットしたアロケータの状態を PC に送ります。 #if defined(NW_AUTO_TEST) NW_ASSERT(s_AutoTester); s_AutoTester->NotifyMemoryStatus( s_DeviceAllocator, s_ParticleAllocator, AutoTester::TIMING_POST_INITIALIZE_SCENES); #endif } //--------------------------------------------------------------------------- //! @brief TerminateScenes 後のテストを行ないます。 //--------------------------------------------------------------------------- static void PreTerminateScenes() { // SetAllocatorForTest でセットしたアロケータの状態を PC に送ります。 #if defined(NW_AUTO_TEST) NW_ASSERT(s_AutoTester); s_AutoTester->NotifyMemoryStatus( s_DeviceAllocator, s_ParticleAllocator, AutoTester::TIMING_PRE_TERMINATE_SCENES); #endif } //@} //===================== //! @name メモリリーク検出 //@{ //--------------------------------------------------------------------------- //! @brief メモリリークの検出などの対象となるアロケータを指定します。 //! //! @param[in] deviceAllocator 対象のデバイスメモリアロケータです。 //! @param[in] particleAllocator 対象のパーティクルメモリアロケータです。 //--------------------------------------------------------------------------- static void SetAllocatorForTest( nw::demo::DemoAllocator* deviceAllocator, nw::demo::DemoAllocator* particleAllocator = NULL ) { s_DeviceAllocator = deviceAllocator; s_ParticleAllocator = particleAllocator; } //--------------------------------------------------------------------------- //! @brief メモリリークの検出機能をリセットします。 //! //! この関数が実行されると、その時点でのメモリ使用量を記憶します。 //! メモリリークを検出するためには TestMemoryLeak() 関数を実行してください。 //! //! この関数を複数回実行すると、最後の実行のみが有効になります。 //--------------------------------------------------------------------------- static void ResetMemoryLeakTester() { s_DeviceMemoryFreeSize = s_DeviceAllocator->GetFreeSize(); if (s_ParticleAllocator) { s_ParticleMemoryFreeSize = s_ParticleAllocator->GetFreeSize(); } } //--------------------------------------------------------------------------- //! @brief メモリリークを検出します。 //! //! 最後に ResetMemoryLeakTester() が実行されたときとメモリ使用量を比べます。 //! メモリ使用量が ResetMemoryLeakTester() 実行時と一致しない場合はヒープのダンプを出力します。 //! //! @return メモリ使用量が ResetMemoryLeakTester() 実行時と一致する場合は true を、そうでない場合は false を返します。 //--------------------------------------------------------------------------- static bool TestMemoryLeak() { bool result = true; s32 mainLeakSize = s_DeviceMemoryFreeSize - s_DeviceAllocator->GetFreeSize(); s32 particleLeakSize = 0; if (s_ParticleAllocator) { particleLeakSize = s_ParticleMemoryFreeSize - s_ParticleAllocator->GetFreeSize(); } if (!s_SilentMode) { if (mainLeakSize) { NW_DEV_LOG( "######### Device Memory Leak Detected !! (%d bytes)\n", mainLeakSize); s_DeviceAllocator->Dump(); result = false; } if (particleLeakSize) { NW_DEV_LOG( "######### Particle Memory Leak Detected !! (%d bytes)\n", particleLeakSize); s_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 nw::demo::DemoAllocator* s_DeviceAllocator; static nw::demo::DemoAllocator* s_ParticleAllocator; static bool s_IsForceCpuProfilingMode; //!< 強制的に CPU パフォーマンス計測モードにするフラグです。 static bool s_SilentMode; //!< ログへの出力を止めます。 static s32 s_ProfileCallCount; static s32 s_DebugFrameCount; #if defined(NW_AUTO_TEST) class AutoTester { public: enum NOTIFY_TIMING { TIMING_POST_INITIALIZE_SCENES = 1, TIMING_PRE_TERMINATE_SCENES }; //! @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); //! @brief メモリ状態を PC に通知します。 //! //! カスタムテスト中であればメモリ状態のパケットを送信します。 //! //! @param timing タイミング情報です。 void NotifyMemoryStatus( nw::demo::DemoAllocator* deviceAllocator, nw::demo::DemoAllocator* particleAllocator, NOTIFY_TIMING timing ); 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) {} }; //! @brief メモリ状態通知パケットのヘッダです。 struct MemoryStatusHeader { s32 packetType; s32 frameCount; s32 timing; enum { PACKET_TYPE_ID = 0xE568134E }; MemoryStatusHeader() : packetType(PACKET_TYPE_ID), frameCount(0) {} }; //! @brief メモリ状態通知パケットのデータ構造です。 struct MemoryStatusData { s32 deviceMemoryUsed; s32 particleMemoryUsed; }; 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), m_RenderSystem(renderSystem) { nw::demo::DebugUtility::InitializeAutoTest(m_DeviceAllocator, m_DeviceAllocator, m_RenderSystem, hash1, hash2); NW_INITIALIZE_PROFILE(NW_PROFILE_MAX_REPORT, m_DeviceAllocator); #if defined(NW_DEBUG_CHECK_MEMORY_LEAK) nw::demo::DebugUtility::SetAllocatorForTest(m_DeviceAllocator, m_ParticleAllocator); nw::demo::DebugUtility::ResetMemoryLeakTester(); #endif } ~DemoTestLoop() { NW_FINALIZE_PROFILE(m_DeviceAllocator); nw::demo::DebugUtility::FinalizeAutoTest(); } void Continue() { #if defined(NW_DEBUG_CHECK_MEMORY_LEAK) nw::demo::DebugUtility::TestMemoryLeak(); #else m_ContinueFlag = false; #endif nw::demo::DebugUtility::FinalizeAutoTest(); // HACK: PC 側の AutoTester から任意の status が渡せないので、実機ではできない操作をトリガーにする。 const bool isAutotesting = nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_UP) && nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_RIGHT) && nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_DOWN) && nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_LEFT); nw::demo::DebugUtility::InitializeAutoTest(m_DeviceAllocator, m_DeviceAllocator, m_RenderSystem, NULL, NULL, isAutotesting); } 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; nw::demo::RenderSystem** m_RenderSystem; }; } // 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_