void CvSIFT::onNewImage() { LOG(LTRACE)<< "CvSIFT::onNewImage\n"; try { // Input: a grayscale image. cv::Mat input = in_img.read(); std::ofstream feature_calc_time; if(!string(prop_calc_path).empty()) { feature_calc_time.open((string(prop_calc_path)+string("czas_wyznaczenia_cech_sift.txt")).c_str(), ios::out|ios::app); } Common::Timer timer; timer.restart(); //-- Step 1: Detect the keypoints. cv::SiftFeatureDetector detector(0,4); std::vector<cv::KeyPoint> keypoints; detector.detect(input, keypoints); //-- Step 2: Calculate descriptors (feature vectors). cv::SiftDescriptorExtractor extractor; Mat descriptors; extractor.compute( input, keypoints, descriptors); if(!string(prop_calc_path).empty()) { feature_calc_time << timer.elapsed() << endl; } // Write results to outputs. Types::Features features(keypoints); features.type = "SIFT"; out_features.write(features); out_descriptors.write(descriptors); } catch (...) { LOG(LERROR) << "CvSIFT::onNewImage failed\n"; } }
// Apply Frame Limit and Display FPS info // This should only be called from VI void VideoThrottle() { u32 TargetVPS = (SConfig::GetInstance().m_Framelimit > 2) ? (SConfig::GetInstance().m_Framelimit - 1) * 5 : VideoInterface::TargetRefreshRate; // Disable the frame-limiter when the throttle (Tab) key is held down. Audio throttle: m_Framelimit = 2 if (SConfig::GetInstance().m_Framelimit && SConfig::GetInstance().m_Framelimit != 2 && !Host_GetKeyState('\t')) { u32 frametime = ((SConfig::GetInstance().b_UseFPS)? Common::AtomicLoad(DrawnFrame) : DrawnVideo) * 1000 / TargetVPS; u32 timeDifference = (u32)Timer.GetTimeDifference(); if (timeDifference < frametime) { Common::SleepCurrentThread(frametime - timeDifference - 1); } while ((u32)Timer.GetTimeDifference() < frametime) Common::YieldCPU(); //Common::SleepCurrentThread(1); } // Update info per second u32 ElapseTime = (u32)Timer.GetTimeDifference(); if ((ElapseTime >= 1000 && DrawnVideo > 0) || g_requestRefreshInfo) { UpdateTitle(); // Reset counter Timer.Update(); Common::AtomicStore(DrawnFrame, 0); DrawnVideo = 0; } DrawnVideo++; }
void BVHTest() { Common::Timer timer; Random rand = Random(1); #define NUM_INSERTIONS 100000 Box* pBoxes = (Box*)_aligned_malloc(NUM_INSERTIONS * sizeof(Box), 16); for (int i = 0; i < NUM_INSERTIONS; i++) { Vector pos = rand.GetFloat3(); pos *= 100.0f; pBoxes[i] = Box(pos - Vector(0.5f, 0.5f, 0.5), pos + Vector(0.5f, 0.5f, 0.5)); } Util::BVH bvh; timer.Start(); for (int i = 0; i < NUM_INSERTIONS; i++) { bvh.Insert(pBoxes[i], (void*)i); } double buildTime = timer.Stop(); Box testBox = Box(Vector(50, 50, 50), Vector(60, 60, 60)); //__asm int 3; }
// Display FPS info // This should only be called from VI void VideoThrottle() { // Update info per second u32 ElapseTime = (u32)s_timer.GetTimeDifference(); if ((ElapseTime >= 1000 && s_drawn_video.load() > 0) || s_request_refresh_info) { UpdateTitle(); // Reset counter s_timer.Update(); s_drawn_frame.store(0); s_drawn_video.store(0); } s_drawn_video++; bool update_ss_speed = true; if (SConfig::GetInstance().bDoubleVideoRate) { update_ss_speed = s_drawn_video & 1; } // Update the audio timestretcher with the current speed if (g_sound_stream && update_ss_speed) { float Speed = (float)(s_drawn_video.load() * 1000.0 / (VideoInterface::GetTargetRefreshRate() * ElapseTime)); g_sound_stream->GetMixer()->UpdateSpeed((float)Speed); } }
// Write to the status bar void WriteStatus() { std::string TmpStr = "Time: " + ReRecTimer.GetTimeElapsedFormatted(); TmpStr += StringFromFormat(" Frame: %s", ThousandSeparate(g_FrameCounter).c_str()); // The FPS is the total average since the game was booted TmpStr += StringFromFormat(" FPS: %i", (g_FrameCounter * 1000) / ReRecTimer.GetTimeElapsed()); TmpStr += StringFromFormat(" FrameStep: %s", g_FrameStep ? "On" : "Off"); Host_UpdateStatusBar(TmpStr.c_str(), 1); }
void BlobExtractor_Processor::onNewImage() { LOG(LTRACE) << "BlobExtractor_Processor::onNewImage() called!\n"; Common::Timer timer; timer.restart(); cv::Mat in = in_img.read(); in.convertTo(img_uchar, CV_8UC1); IplImage ipl_img = IplImage(img_uchar); // cv::Mat mat_img = img_uchar; // cv::Mat out = cv::Mat::zeros(in.size(), CV_8UC3); Types::Blobs::Blob_vector res; bool success; try { success = ComponentLabeling( &ipl_img, NULL, props.bkg_color, res ); } catch(...) { success = false; LOG(LWARNING) << "blob find error\n"; } try { if( !success ) { LOG(LERROR) << "Blob find error\n"; } else { LOG(LTRACE) << "blobs found"; Types::Blobs::BlobResult result(res); result.Filter( result, B_EXCLUDE, Types::Blobs::BlobGetArea(), B_LESS, min_size ); out_blobs.write(result); LOG(LTRACE) << "blobs written"; newBlobs->raise(); LOG(LTRACE) << "blobs sent"; // result.draw(out, CV_RGB(255, 0, 0), 0, 0); // out_img.write(in); // newImage->raise(); } LOG(LINFO) << "Blobing took " << timer.elapsed() << " seconds\n"; } catch(...) { LOG(LERROR) << "BlobExtractor onNewImage failure"; } }
// Apply Frame Limit and Display FPS info // This should only be called from VI void VideoThrottle() { // Update info per second u32 ElapseTime = (u32)s_timer.GetTimeDifference(); if ((ElapseTime >= 1000 && s_drawn_video > 0) || s_request_refresh_info) { UpdateTitle(); // Reset counter s_timer.Update(); Common::AtomicStore(s_drawn_frame, 0); s_drawn_video = 0; } s_drawn_video++; }
// Display FPS info // This should only be called from VI void VideoThrottle() { // Update info per second u32 ElapseTime = (u32)s_timer.GetTimeDifference(); if ((ElapseTime >= 1000 && s_drawn_video.load() > 0) || s_request_refresh_info) { UpdateTitle(); // Reset counter s_timer.Update(); s_drawn_frame.store(0); s_drawn_video.store(0); } s_drawn_video++; }
// Apply Frame Limit and Display FPS info // This should only be called from VI void VideoThrottle() { // Update info per second u32 ElapseTime = (u32)Timer.GetTimeDifference(); if ((ElapseTime >= 1000 && DrawnVideo > 0) || g_requestRefreshInfo) { UpdateTitle(); // Reset counter Timer.Update(); Common::AtomicStore(DrawnFrame, 0); DrawnVideo = 0; } DrawnVideo++; }
// Start the timer when a game is booted void RerecordingStart() { g_FrameCounter = 0; ReRecTimer.Start(); // Logging //DEBUG_LOG(CONSOLE, "RerecordingStart: %i\n", g_FrameCounter); }
// Reset the frame counter void RerecordingStop() { // Write the final time and Stop the timer ReRecTimer.Stop(); // Update status bar WriteStatus(); }
void UpdateTitle() { u32 ElapseTime = (u32)s_timer.GetTimeDifference(); s_request_refresh_info = false; SConfig& _CoreParameter = SConfig::GetInstance(); if (ElapseTime == 0) ElapseTime = 1; float FPS = (float)(s_drawn_frame.load() * 1000.0 / ElapseTime); float VPS = (float)(s_drawn_video.load() * 1000.0 / ElapseTime); float Speed = (float)(s_drawn_video.load() * (100 * 1000.0) / (VideoInterface::GetTargetRefreshRate() * ElapseTime)); // Settings are shown the same for both extended and summary info std::string SSettings = StringFromFormat("%s %s | %s | %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC", g_video_backend->GetDisplayName().c_str(), _CoreParameter.bDSPHLE ? "HLE" : "LLE"); std::string SFPS; if (Movie::IsPlayingInput()) SFPS = StringFromFormat("VI: %u/%u - Input: %u/%u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed); else if (Movie::IsRecordingInput()) SFPS = StringFromFormat("VI: %u - Input: %u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed); else { SFPS = StringFromFormat("FPS: %.0f - VPS: %.0f - %.0f%%", FPS, VPS, Speed); if (SConfig::GetInstance().m_InterfaceExtendedFPSInfo) { // Use extended or summary information. The summary information does not print the ticks data, // that's more of a debugging interest, it can always be optional of course if someone is interested. static u64 ticks = 0; static u64 idleTicks = 0; u64 newTicks = CoreTiming::GetTicks(); u64 newIdleTicks = CoreTiming::GetIdleTicks(); u64 diff = (newTicks - ticks) / 1000000; u64 idleDiff = (newIdleTicks - idleTicks) / 1000000; ticks = newTicks; idleTicks = newIdleTicks; float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100; SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)", _CoreParameter.bSkipIdle ? "~" : "", (int)(diff), (int)(diff - idleDiff), (int)(idleDiff), SystemTimers::GetTicksPerSecond() / 1000000, _CoreParameter.bSkipIdle ? "~" : "", TicksPercentage); } } // This is our final "frame counter" string std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str()); Host_UpdateTitle(SMessage); }
// Executed from GPU thread // reports if a frame should be skipped or not // depending on the framelimit set bool ShouldSkipFrame(int skipped) { const u32 TargetFPS = (SConfig::GetInstance().m_Framelimit > 1) ? (SConfig::GetInstance().m_Framelimit - 1) * 5 : VideoInterface::TargetRefreshRate; const u32 frames = Common::AtomicLoad(s_drawn_frame); const bool fps_slow = !(s_timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS); return fps_slow; }
// Executed from GPU thread // reports if a frame should be skipped or not // depending on the emulation speed set bool ShouldSkipFrame(int skipped) { u32 TargetFPS = VideoInterface::GetTargetRefreshRate(); if (SConfig::GetInstance().m_EmulationSpeed > 0.0f) TargetFPS = u32(TargetFPS * SConfig::GetInstance().m_EmulationSpeed); const u32 frames = s_drawn_frame.load(); const bool fps_slow = !(s_timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS); return fps_slow; }
/* Wind back the frame counter when a save state is loaded. Currently we don't know what that means in time so we just guess that the time is proportional the the number of frames Todo: There are many assumptions here: We probably want to replace the time here by the actual time that we save together with the save state or the input recording for example. And have it adjusted for full speed playback (whether it's 30 fps or 60 fps or some other speed that the game is natively capped at). Also the input interrupts do not occur as often as the frame renderings, they occur more often. So we may want to move the input recording to fram updates, or perhaps sync the input interrupts to frame updates. */ void WindBack(int Counter) { /* Counter should be smaller than g_FrameCounter, however it currently updates faster than the frames so currently it may not be the same. Therefore I use the abs() function. */ int AbsoluteFrameDifference = abs(g_FrameCounter - Counter); float FractionalFrameDifference = (float) AbsoluteFrameDifference / (float) g_FrameCounter; // Update the frame counter g_FrameCounter = Counter; // Approximate a time to wind back the clock to // Get the current time u64 CurrentTimeMs = ReRecTimer.GetTimeElapsed(); // Save the current time in seconds in a new double double CurrentTimeSeconds = (double) (CurrentTimeMs / 1000); // Reduce it by the same proportion as the counter was wound back CurrentTimeSeconds = CurrentTimeSeconds * FractionalFrameDifference; // Update the clock ReRecTimer.WindBackStartingTime((u64)CurrentTimeSeconds * 1000); // Logging DEBUG_LOG(CONSOLE, "WindBack: %i %u\n", Counter, (u64)CurrentTimeSeconds); }
void UDPWiimote::mainThread() { std::unique_lock<std::mutex> lk(d->termLock); Common::Timer time; fd_set fds; struct timeval timeout; timeout.tv_sec=0; timeout.tv_usec=0; time.Update(); do { int maxfd=0; FD_ZERO(&fds); for (auto& fd : d->sockfds) { FD_SET(fd,&fds); #ifndef _WIN32 if (fd>=maxfd) maxfd=(fd)+1; #endif } u64 tleft=timeout.tv_sec*1000+timeout.tv_usec/1000; u64 telapsed=time.GetTimeDifference(); time.Update(); if (tleft<=telapsed) { timeout.tv_sec=1; timeout.tv_usec=500000; broadcastPresence(); } else { tleft-=telapsed; timeout.tv_sec=(long)(tleft/1000); timeout.tv_usec=(tleft%1000)*1000; } lk.unlock(); //VERY hacky. don't like it if (d->exit) return; int rt=select(maxfd,&fds,NULL,NULL,&timeout); if (d->exit) return; lk.lock(); if (d->exit) return; if (rt) { for (sock_t fd : d->sockfds) { if (FD_ISSET(fd,&fds)) { u8 bf[64]; int size=60; size_t addr_len; struct sockaddr_storage their_addr; addr_len = sizeof their_addr; if ((size = recvfrom(fd, (dataz)bf, size , 0,(struct sockaddr *)&their_addr, (socklen_t*)&addr_len)) == -1) { ERROR_LOG(WIIMOTE,"UDPWii Packet error"); } else { std::lock_guard<std::mutex> lkm(d->mutex); if (pharsePacket(bf,size)==0) { //NOTICE_LOG(WIIMOTE,"UDPWII New pack"); } else { //NOTICE_LOG(WIIMOTE,"UDPWII Wrong pack format... ignoring"); } } } } } } while (!(d->exit)); }
namespace Core { bool g_want_determinism; // Declarations and definitions static Common::Timer s_timer; static volatile u32 s_drawn_frame = 0; static u32 s_drawn_video = 0; // Function forwarding void Callback_WiimoteInterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size); // Function declarations void EmuThread(); static bool s_is_stopping = false; static bool s_hardware_initialized = false; static bool s_is_started = false; static void* s_window_handle = nullptr; static std::string s_state_filename; static std::thread s_emu_thread; static StoppedCallbackFunc s_on_stopped_callback = nullptr; static std::thread s_cpu_thread; static bool s_request_refresh_info = false; static int s_pause_and_lock_depth = 0; static bool s_is_framelimiter_temp_disabled = false; bool GetIsFramelimiterTempDisabled() { return s_is_framelimiter_temp_disabled; } void SetIsFramelimiterTempDisabled(bool disable) { s_is_framelimiter_temp_disabled = disable; } std::string GetStateFileName() { return s_state_filename; } void SetStateFileName(std::string val) { s_state_filename = val; } // Display messages and return values // Formatted stop message std::string StopMessage(bool bMainThread, std::string Message) { return StringFromFormat("Stop [%s %i]\t%s\t%s", bMainThread ? "Main Thread" : "Video Thread", Common::CurrentThreadId(), MemUsage().c_str(), Message.c_str()); } void DisplayMessage(const std::string& message, int time_in_ms) { // Actually displaying non-ASCII could cause things to go pear-shaped for (const char& c : message) { if (!std::isprint(c)) return; } g_video_backend->Video_AddMessage(message, time_in_ms); Host_UpdateTitle(message); } bool IsRunning() { return (GetState() != CORE_UNINITIALIZED) || s_hardware_initialized; } bool IsRunningAndStarted() { return s_is_started && !s_is_stopping; } bool IsRunningInCurrentThread() { return IsRunning() && IsCPUThread(); } bool IsCPUThread() { return (s_cpu_thread.joinable() ? (s_cpu_thread.get_id() == std::this_thread::get_id()) : !s_is_started); } bool IsGPUThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (_CoreParameter.bCPUThread) { return (s_emu_thread.joinable() && (s_emu_thread.get_id() == std::this_thread::get_id())); } else { return IsCPUThread(); } } // This is called from the GUI thread. See the booting call schedule in // BootManager.cpp bool Init() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (s_emu_thread.joinable()) { if (IsRunning()) { PanicAlertT("Emu Thread already running"); return false; } // The Emu Thread was stopped, synchronize with it. s_emu_thread.join(); } Core::UpdateWantDeterminism(/*initial*/ true); INFO_LOG(OSREPORT, "Starting core = %s mode", _CoreParameter.bWii ? "Wii" : "GameCube"); INFO_LOG(OSREPORT, "CPU Thread separate = %s", _CoreParameter.bCPUThread ? "Yes" : "No"); Host_UpdateMainFrame(); // Disable any menus or buttons at boot g_aspect_wide = _CoreParameter.bWii; if (g_aspect_wide) { IniFile gameIni = _CoreParameter.LoadGameIni(); gameIni.GetOrCreateSection("Wii")->Get("Widescreen", &g_aspect_wide, !!SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.AR")); } s_window_handle = Host_GetRenderHandle(); // Start the emu thread s_emu_thread = std::thread(EmuThread); return true; } // Called from GUI thread void Stop() // - Hammertime! { if (GetState() == CORE_STOPPING) return; const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; s_is_stopping = true; g_video_backend->EmuStateChange(EMUSTATE_CHANGE_STOP); INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutting down ----"); // Stop the CPU INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stop CPU").c_str()); PowerPC::Stop(); // Kick it if it's waiting (code stepping wait loop) CCPU::StepOpcode(); if (_CoreParameter.bCPUThread) { // Video_EnterLoop() should now exit so that EmuThread() // will continue concurrently with the rest of the commands // in this function. We no longer rely on Postmessage. INFO_LOG(CONSOLE, "%s", StopMessage(true, "Wait for Video Loop to exit ...").c_str()); g_video_backend->Video_ExitLoop(); } } // Create the CPU thread, which is a CPU + Video thread in Single Core mode. static void CpuThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (_CoreParameter.bCPUThread) { Common::SetCurrentThreadName("CPU thread"); } else { Common::SetCurrentThreadName("CPU-GPU thread"); g_video_backend->Video_Prepare(); } if (_CoreParameter.bFastmem) EMM::InstallExceptionHandler(); // Let's run under memory watch if (!s_state_filename.empty()) State::LoadAs(s_state_filename); s_is_started = true; #ifdef USE_GDBSTUB if (_CoreParameter.iGDBPort > 0) { gdb_init(_CoreParameter.iGDBPort); // break at next instruction (the first instruction) gdb_break(); } #endif // Enter CPU run loop. When we leave it - we are done. CCPU::Run(); s_is_started = false; if (!_CoreParameter.bCPUThread) g_video_backend->Video_Cleanup(); EMM::UninstallExceptionHandler(); return; } static void FifoPlayerThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (_CoreParameter.bCPUThread) { Common::SetCurrentThreadName("FIFO player thread"); } else { g_video_backend->Video_Prepare(); Common::SetCurrentThreadName("FIFO-GPU thread"); } s_is_started = true; // Enter CPU run loop. When we leave it - we are done. if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename)) { FifoPlayer::GetInstance().Play(); FifoPlayer::GetInstance().Close(); } s_is_started = false; if (!_CoreParameter.bCPUThread) g_video_backend->Video_Cleanup(); return; } // Initialize and create emulation thread // Call browser: Init():s_emu_thread(). // See the BootManager.cpp file description for a complete call schedule. void EmuThread() { const SCoreStartupParameter& core_parameter = SConfig::GetInstance().m_LocalCoreStartupParameter; Common::SetCurrentThreadName("Emuthread - Starting"); if (SConfig::GetInstance().m_OCEnable) DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); DisplayMessage(core_parameter.m_strFilename, 3000); Movie::Init(); HW::Init(); if (!g_video_backend->Initialize(s_window_handle)) { PanicAlert("Failed to initialize video backend!"); Host_Message(WM_USER_STOP); return; } OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000); if (cpu_info.HTT) SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread = cpu_info.num_cores > 4; else SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread = cpu_info.num_cores > 2; if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread)) { HW::Shutdown(); g_video_backend->Shutdown(); PanicAlert("Failed to initialize DSP emulator!"); Host_Message(WM_USER_STOP); return; } Keyboard::Initialize(s_window_handle); Pad::Initialize(s_window_handle); // Load and Init Wiimotes - only if we are booting in Wii mode if (core_parameter.bWii) { Wiimote::Initialize(s_window_handle, !s_state_filename.empty()); // Activate Wiimotes which don't have source set to "None" for (unsigned int i = 0; i != MAX_BBMOTES; ++i) if (g_wiimote_sources[i]) GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true); } AudioCommon::InitSoundStream(); // The hardware is initialized. s_hardware_initialized = true; // Boot to pause or not Core::SetState(core_parameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN); // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::MODE_INTERPRETER); CBoot::BootUp(); // Setup our core, but can't use dynarec if we are compare server if (core_parameter.iCPUCore != SCoreStartupParameter::CORE_INTERPRETER && (!core_parameter.bRunCompareServer || core_parameter.bRunCompareClient)) { PowerPC::SetMode(PowerPC::MODE_JIT); } else { PowerPC::SetMode(PowerPC::MODE_INTERPRETER); } // Update the window again because all stuff is initialized Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); // Determine the CPU thread function void (*cpuThreadFunc)(void); if (core_parameter.m_BootType == SCoreStartupParameter::BOOT_DFF) cpuThreadFunc = FifoPlayerThread; else cpuThreadFunc = CpuThread; // ENTER THE VIDEO THREAD LOOP if (core_parameter.bCPUThread) { // This thread, after creating the EmuWindow, spawns a CPU // thread, and then takes over and becomes the video thread Common::SetCurrentThreadName("Video thread"); g_video_backend->Video_Prepare(); // Spawn the CPU thread s_cpu_thread = std::thread(cpuThreadFunc); // become the GPU thread g_video_backend->Video_EnterLoop(); // We have now exited the Video Loop INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str()); } else // SingleCore mode { // The spawned CPU Thread also does the graphics. // The EmuThread is thus an idle thread, which sleeps while // waiting for the program to terminate. Without this extra // thread, the video backend window hangs in single core mode // because no one is pumping messages. Common::SetCurrentThreadName("Emuthread - Idle"); // Spawn the CPU+GPU thread s_cpu_thread = std::thread(cpuThreadFunc); while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) { g_video_backend->PeekMessages(); Common::SleepCurrentThread(20); } } INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str()); // Wait for s_cpu_thread to exit INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str()); #ifdef USE_GDBSTUB INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str()); gdb_deinit(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str()); #endif s_cpu_thread.join(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); if (core_parameter.bCPUThread) g_video_backend->Video_Cleanup(); VolumeHandler::EjectVolume(); FileMon::Close(); // Stop audio thread - Actually this does nothing when using HLE // emulation, but stops the DSP Interpreter when using LLE emulation. DSP::GetDSPEmulator()->DSP_StopSoundStream(); // We must set up this flag before executing HW::Shutdown() s_hardware_initialized = false; INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str()); HW::Shutdown(); INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str()); Wiimote::Shutdown(); Keyboard::Shutdown(); Pad::Shutdown(); g_video_backend->Shutdown(); AudioCommon::ShutdownSoundStream(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str()); // Clear on screen messages that haven't expired g_video_backend->Video_ClearMessages(); // Reload sysconf file in order to see changes committed during emulation if (core_parameter.bWii) SConfig::GetInstance().m_SYSCONF->Reload(); INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----"); Movie::Shutdown(); PatchEngine::Shutdown(); s_is_stopping = false; if (s_on_stopped_callback) s_on_stopped_callback(); } // Set or get the running state void SetState(EState _State) { switch (_State) { case CORE_PAUSE: CCPU::EnableStepping(true); // Break Wiimote::Pause(); break; case CORE_RUN: CCPU::EnableStepping(false); Wiimote::Resume(); break; default: PanicAlertT("Invalid state"); break; } } EState GetState() { if (s_is_stopping) return CORE_STOPPING; if (s_hardware_initialized) { if (CCPU::IsStepping()) return CORE_PAUSE; else return CORE_RUN; } return CORE_UNINITIALIZED; } static std::string GenerateScreenshotName() { const std::string& gameId = SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID(); std::string path = File::GetUserPath(D_SCREENSHOTS_IDX) + gameId + DIR_SEP_CHR; if (!File::CreateFullPath(path)) { // fallback to old-style screenshots, without folder. path = File::GetUserPath(D_SCREENSHOTS_IDX); } //append gameId, path only contains the folder here. path += gameId; std::string name; for (int i = 1; File::Exists(name = StringFromFormat("%s-%d.png", path.c_str(), i)); ++i) { // TODO? } return name; } void SaveScreenShot() { const bool bPaused = (GetState() == CORE_PAUSE); SetState(CORE_PAUSE); g_video_backend->Video_Screenshot(GenerateScreenshotName()); if (!bPaused) SetState(CORE_RUN); } void RequestRefreshInfo() { s_request_refresh_info = true; } bool PauseAndLock(bool doLock, bool unpauseOnUnlock) { if (!IsRunning()) return true; // let's support recursive locking to simplify things on the caller's side, // and let's do it at this outer level in case the individual systems don't support it. if (doLock ? s_pause_and_lock_depth++ : --s_pause_and_lock_depth) return true; // first pause or unpause the CPU bool wasUnpaused = CCPU::PauseAndLock(doLock, unpauseOnUnlock); ExpansionInterface::PauseAndLock(doLock, unpauseOnUnlock); // audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle). AudioCommon::PauseAndLock(doLock, unpauseOnUnlock); DSP::GetDSPEmulator()->PauseAndLock(doLock, unpauseOnUnlock); // video has to come after CPU, because CPU thread can wait for video thread (s_efbAccessRequested). g_video_backend->PauseAndLock(doLock, unpauseOnUnlock); return wasUnpaused; } // Apply Frame Limit and Display FPS info // This should only be called from VI void VideoThrottle() { // Update info per second u32 ElapseTime = (u32)s_timer.GetTimeDifference(); if ((ElapseTime >= 1000 && s_drawn_video > 0) || s_request_refresh_info) { UpdateTitle(); // Reset counter s_timer.Update(); Common::AtomicStore(s_drawn_frame, 0); s_drawn_video = 0; } s_drawn_video++; } // Executed from GPU thread // reports if a frame should be skipped or not // depending on the framelimit set bool ShouldSkipFrame(int skipped) { const u32 TargetFPS = (SConfig::GetInstance().m_Framelimit > 1) ? (SConfig::GetInstance().m_Framelimit - 1) * 5 : VideoInterface::TargetRefreshRate; const u32 frames = Common::AtomicLoad(s_drawn_frame); const bool fps_slow = !(s_timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS); return fps_slow; } // --- Callbacks for backends / engine --- // Should be called from GPU thread when a frame is drawn void Callback_VideoCopiedToXFB(bool video_update) { if (video_update) Common::AtomicIncrement(s_drawn_frame); Movie::FrameUpdate(); //Dragonbane if (Movie::updateMainFrame) { Movie::updateMainFrame = false; Host_UpdateMainFrame(); } } void UpdateTitle() { u32 ElapseTime = (u32)s_timer.GetTimeDifference(); s_request_refresh_info = false; SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (ElapseTime == 0) ElapseTime = 1; float FPS = (float) (Common::AtomicLoad(s_drawn_frame) * 1000.0 / ElapseTime); float VPS = (float) (s_drawn_video * 1000.0 / ElapseTime); float Speed = (float) (s_drawn_video * (100 * 1000.0) / (VideoInterface::TargetRefreshRate * ElapseTime)); // Settings are shown the same for both extended and summary info std::string SSettings = StringFromFormat("%s %s | %s | %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC", g_video_backend->GetDisplayName().c_str(), _CoreParameter.bDSPHLE ? "HLE" : "LLE"); std::string SFPS; if (Movie::IsPlayingInput()) SFPS = StringFromFormat("VI: %u/%u - Input: %u/%u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed); else if (Movie::IsRecordingInput()) SFPS = StringFromFormat("VI: %u - Input: %u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed); else { SFPS = StringFromFormat("FPS: %.0f - VPS: %.0f - %.0f%%", FPS, VPS, Speed); if (SConfig::GetInstance().m_InterfaceExtendedFPSInfo) { // Use extended or summary information. The summary information does not print the ticks data, // that's more of a debugging interest, it can always be optional of course if someone is interested. static u64 ticks = 0; static u64 idleTicks = 0; u64 newTicks = CoreTiming::GetTicks(); u64 newIdleTicks = CoreTiming::GetIdleTicks(); u64 diff = (newTicks - ticks) / 1000000; u64 idleDiff = (newIdleTicks - idleTicks) / 1000000; ticks = newTicks; idleTicks = newIdleTicks; float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100; SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)", _CoreParameter.bSkipIdle ? "~" : "", (int)(diff), (int)(diff - idleDiff), (int)(idleDiff), SystemTimers::GetTicksPerSecond() / 1000000, _CoreParameter.bSkipIdle ? "~" : "", TicksPercentage); } } // This is our final "frame counter" string std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str()); // Update the audio timestretcher with the current speed if (g_sound_stream) { CMixer* pMixer = g_sound_stream->GetMixer(); pMixer->UpdateSpeed((float)Speed / 100); } Host_UpdateTitle(SMessage); } void Shutdown() { if (s_emu_thread.joinable()) s_emu_thread.join(); } void SetOnStoppedCallback(StoppedCallbackFunc callback) { s_on_stopped_callback = callback; } void UpdateWantDeterminism(bool initial) { // For now, this value is not itself configurable. Instead, individual // settings that depend on it, such as GPU determinism mode. should have // override options for testing, bool new_want_determinism = Movie::IsPlayingInput() || Movie::IsRecordingInput() || NetPlay::IsNetPlayRunning(); if (new_want_determinism != g_want_determinism || initial) { WARN_LOG(COMMON, "Want determinism <- %s", new_want_determinism ? "true" : "false"); bool was_unpaused = Core::PauseAndLock(true); g_want_determinism = new_want_determinism; WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism); g_video_backend->UpdateWantDeterminism(new_want_determinism); // We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use of FMA. JitInterface::ClearCache(); Core::PauseAndLock(false, was_unpaused); } } } // Core
namespace Core { // Declarations and definitions // --------------- int g_FrameCounter = 0; bool g_FrameStep = false; Common::Timer ReRecTimer; // Control Run, Pause, Stop and the Timer. // --------------- // Subtract the paused time when we run again void Run() { ReRecTimer.AddTimeDifference(); } // Update the time void Pause() { ReRecTimer.Update(); } // Start the timer when a game is booted void RerecordingStart() { g_FrameCounter = 0; ReRecTimer.Start(); // Logging //DEBUG_LOG(CONSOLE, "RerecordingStart: %i\n", g_FrameCounter); } // Reset the frame counter void RerecordingStop() { // Write the final time and Stop the timer ReRecTimer.Stop(); // Update status bar WriteStatus(); } /* Wind back the frame counter when a save state is loaded. Currently we don't know what that means in time so we just guess that the time is proportional the the number of frames Todo: There are many assumptions here: We probably want to replace the time here by the actual time that we save together with the save state or the input recording for example. And have it adjusted for full speed playback (whether it's 30 fps or 60 fps or some other speed that the game is natively capped at). Also the input interrupts do not occur as often as the frame renderings, they occur more often. So we may want to move the input recording to fram updates, or perhaps sync the input interrupts to frame updates. */ void WindBack(int Counter) { /* Counter should be smaller than g_FrameCounter, however it currently updates faster than the frames so currently it may not be the same. Therefore I use the abs() function. */ int AbsoluteFrameDifference = abs(g_FrameCounter - Counter); float FractionalFrameDifference = (float) AbsoluteFrameDifference / (float) g_FrameCounter; // Update the frame counter g_FrameCounter = Counter; // Approximate a time to wind back the clock to // Get the current time u64 CurrentTimeMs = ReRecTimer.GetTimeElapsed(); // Save the current time in seconds in a new double double CurrentTimeSeconds = (double) (CurrentTimeMs / 1000); // Reduce it by the same proportion as the counter was wound back CurrentTimeSeconds = CurrentTimeSeconds * FractionalFrameDifference; // Update the clock ReRecTimer.WindBackStartingTime((u64)CurrentTimeSeconds * 1000); // Logging DEBUG_LOG(CONSOLE, "WindBack: %i %u\n", Counter, (u64)CurrentTimeSeconds); } // Frame advance // --------------- void FrameAdvance() { // Update status bar WriteStatus(); // If a game is not started, return if (Core::GetState() == Core::CORE_UNINITIALIZED) return; // Play to the next frame if (g_FrameStep) { Run(); Core::SetState(Core::CORE_RUN); } } // Turn on frame stepping void FrameStepOnOff() { /* Turn frame step on or off. If a game is running and we turn this on it means that the game will pause after the next frame update */ g_FrameStep = !g_FrameStep; // Update status bar WriteStatus(); // If a game is not started, return if(Core::GetState() == Core::CORE_UNINITIALIZED) return; // Run the emulation if we turned off framestepping if (!g_FrameStep) { Run(); Core::SetState(Core::CORE_RUN); } } // General functions // --------------- // Write to the status bar void WriteStatus() { std::string TmpStr = "Time: " + ReRecTimer.GetTimeElapsedFormatted(); TmpStr += StringFromFormat(" Frame: %s", ThousandSeparate(g_FrameCounter).c_str()); // The FPS is the total average since the game was booted TmpStr += StringFromFormat(" FPS: %i", (g_FrameCounter * 1000) / ReRecTimer.GetTimeElapsed()); TmpStr += StringFromFormat(" FrameStep: %s", g_FrameStep ? "On" : "Off"); Host_UpdateStatusBar(TmpStr.c_str(), 1); } // When a new frame is drawn void FrameUpdate() { // Write to the status bar WriteStatus(); /* I don't think the frequent update has any material speed inpact at all, but should it have you can controls the update speed by changing the "% 10" in this line */ //if (g_FrameCounter % 10 == 0) WriteStatus(); // Pause if frame stepping is on if(g_FrameStep) { Pause(); Core::SetState(Core::CORE_PAUSE); } // Count one frame g_FrameCounter++; } } // Core
namespace Core { // TODO: ugly, remove bool g_aspect_wide; bool g_want_determinism; // Declarations and definitions static Common::Timer s_timer; static std::atomic<u32> s_drawn_frame; static std::atomic<u32> s_drawn_video; // Function forwarding void Callback_WiimoteInterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size); // Function declarations void EmuThread(); static bool s_is_stopping = false; static bool s_hardware_initialized = false; static bool s_is_started = false; static std::atomic<bool> s_is_booting{false}; static void* s_window_handle = nullptr; static std::string s_state_filename; static std::thread s_emu_thread; static StoppedCallbackFunc s_on_stopped_callback = nullptr; static std::thread s_cpu_thread; static bool s_request_refresh_info = false; static int s_pause_and_lock_depth = 0; static bool s_is_throttler_temp_disabled = false; struct HostJob { std::function<void()> job; bool run_after_stop; }; static std::mutex s_host_jobs_lock; static std::queue<HostJob> s_host_jobs_queue; #ifdef ThreadLocalStorage static ThreadLocalStorage bool tls_is_cpu_thread = false; #else static pthread_key_t s_tls_is_cpu_key; static pthread_once_t s_cpu_key_is_init = PTHREAD_ONCE_INIT; static void InitIsCPUKey() { pthread_key_create(&s_tls_is_cpu_key, nullptr); } #endif bool GetIsThrottlerTempDisabled() { return s_is_throttler_temp_disabled; } void SetIsThrottlerTempDisabled(bool disable) { s_is_throttler_temp_disabled = disable; } std::string GetStateFileName() { return s_state_filename; } void SetStateFileName(const std::string& val) { s_state_filename = val; } void FrameUpdateOnCPUThread() { if (NetPlay::IsNetPlayRunning()) NetPlayClient::SendTimeBase(); } // Display messages and return values // Formatted stop message std::string StopMessage(bool main_thread, const std::string& message) { return StringFromFormat("Stop [%s %i]\t%s\t%s", main_thread ? "Main Thread" : "Video Thread", Common::CurrentThreadId(), MemUsage().c_str(), message.c_str()); } void DisplayMessage(const std::string& message, int time_in_ms) { if (!IsRunning()) return; // Actually displaying non-ASCII could cause things to go pear-shaped for (const char& c : message) { if (!std::isprint(c)) return; } OSD::AddMessage(message, time_in_ms); Host_UpdateTitle(message); } bool IsRunning() { return (GetState() != CORE_UNINITIALIZED || s_hardware_initialized) && !s_is_stopping; } bool IsRunningAndStarted() { return s_is_started && !s_is_stopping; } bool IsRunningInCurrentThread() { return IsRunning() && IsCPUThread(); } bool IsCPUThread() { #ifdef ThreadLocalStorage return tls_is_cpu_thread; #else // Use pthread implementation for Android and Mac // Make sure that s_tls_is_cpu_key is initialized pthread_once(&s_cpu_key_is_init, InitIsCPUKey); return pthread_getspecific(s_tls_is_cpu_key); #endif } bool IsGPUThread() { const SConfig& _CoreParameter = SConfig::GetInstance(); if (_CoreParameter.bCPUThread) { return (s_emu_thread.joinable() && (s_emu_thread.get_id() == std::this_thread::get_id())); } else { return IsCPUThread(); } } // This is called from the GUI thread. See the booting call schedule in // BootManager.cpp bool Init() { const SConfig& _CoreParameter = SConfig::GetInstance(); if (s_emu_thread.joinable()) { if (IsRunning()) { PanicAlertT("Emu Thread already running"); return false; } // The Emu Thread was stopped, synchronize with it. s_emu_thread.join(); } // Drain any left over jobs HostDispatchJobs(); Core::UpdateWantDeterminism(/*initial*/ true); INFO_LOG(OSREPORT, "Starting core = %s mode", _CoreParameter.bWii ? "Wii" : "GameCube"); INFO_LOG(OSREPORT, "CPU Thread separate = %s", _CoreParameter.bCPUThread ? "Yes" : "No"); Host_UpdateMainFrame(); // Disable any menus or buttons at boot g_aspect_wide = _CoreParameter.bWii; if (g_aspect_wide) { IniFile gameIni = _CoreParameter.LoadGameIni(); gameIni.GetOrCreateSection("Wii")->Get( "Widescreen", &g_aspect_wide, !!SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.AR")); } s_window_handle = Host_GetRenderHandle(); // Start the emu thread s_emu_thread = std::thread(EmuThread); return true; } // Called from GUI thread void Stop() // - Hammertime! { if (GetState() == CORE_STOPPING) return; const SConfig& _CoreParameter = SConfig::GetInstance(); s_is_stopping = true; // Dump left over jobs HostDispatchJobs(); Fifo::EmulatorState(false); INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutting down ----"); // Stop the CPU INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stop CPU").c_str()); CPU::Stop(); if (_CoreParameter.bCPUThread) { // Video_EnterLoop() should now exit so that EmuThread() // will continue concurrently with the rest of the commands // in this function. We no longer rely on Postmessage. INFO_LOG(CONSOLE, "%s", StopMessage(true, "Wait for Video Loop to exit ...").c_str()); g_video_backend->Video_ExitLoop(); } #if defined(__LIBUSB__) || defined(_WIN32) GCAdapter::ResetRumble(); #endif #ifdef USE_MEMORYWATCHER MemoryWatcher::Shutdown(); #endif } void DeclareAsCPUThread() { #ifdef ThreadLocalStorage tls_is_cpu_thread = true; #else // Use pthread implementation for Android and Mac // Make sure that s_tls_is_cpu_key is initialized pthread_once(&s_cpu_key_is_init, InitIsCPUKey); pthread_setspecific(s_tls_is_cpu_key, (void*)true); #endif } void UndeclareAsCPUThread() { #ifdef ThreadLocalStorage tls_is_cpu_thread = false; #else // Use pthread implementation for Android and Mac // Make sure that s_tls_is_cpu_key is initialized pthread_once(&s_cpu_key_is_init, InitIsCPUKey); pthread_setspecific(s_tls_is_cpu_key, (void*)false); #endif } // For the CPU Thread only. static void CPUSetInitialExecutionState() { QueueHostJob([] { SetState(SConfig::GetInstance().bBootToPause ? CORE_PAUSE : CORE_RUN); Host_UpdateMainFrame(); }); } // Create the CPU thread, which is a CPU + Video thread in Single Core mode. static void CpuThread() { DeclareAsCPUThread(); const SConfig& _CoreParameter = SConfig::GetInstance(); if (_CoreParameter.bCPUThread) { Common::SetCurrentThreadName("CPU thread"); } else { Common::SetCurrentThreadName("CPU-GPU thread"); g_video_backend->Video_Prepare(); } // This needs to be delayed until after the video backend is ready. DolphinAnalytics::Instance()->ReportGameStart(); if (_CoreParameter.bFastmem) EMM::InstallExceptionHandler(); // Let's run under memory watch if (!s_state_filename.empty()) { // Needs to PauseAndLock the Core // NOTE: EmuThread should have left us in CPU_STEPPING so nothing will happen // until after the job is serviced. QueueHostJob([] { // Recheck in case Movie cleared it since. if (!s_state_filename.empty()) State::LoadAs(s_state_filename); }); } s_is_started = true; CPUSetInitialExecutionState(); #ifdef USE_GDBSTUB #ifndef _WIN32 if (!_CoreParameter.gdb_socket.empty()) { gdb_init_local(_CoreParameter.gdb_socket.data()); gdb_break(); } else #endif if (_CoreParameter.iGDBPort > 0) { gdb_init(_CoreParameter.iGDBPort); // break at next instruction (the first instruction) gdb_break(); } #endif #ifdef USE_MEMORYWATCHER MemoryWatcher::Init(); #endif // Enter CPU run loop. When we leave it - we are done. CPU::Run(); s_is_started = false; if (!_CoreParameter.bCPUThread) g_video_backend->Video_Cleanup(); if (_CoreParameter.bFastmem) EMM::UninstallExceptionHandler(); return; } static void FifoPlayerThread() { DeclareAsCPUThread(); const SConfig& _CoreParameter = SConfig::GetInstance(); if (_CoreParameter.bCPUThread) { Common::SetCurrentThreadName("FIFO player thread"); } else { g_video_backend->Video_Prepare(); Common::SetCurrentThreadName("FIFO-GPU thread"); } // Enter CPU run loop. When we leave it - we are done. if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename)) { if (auto cpu_core = FifoPlayer::GetInstance().GetCPUCore()) { PowerPC::InjectExternalCPUCore(cpu_core.get()); s_is_started = true; CPUSetInitialExecutionState(); CPU::Run(); s_is_started = false; PowerPC::InjectExternalCPUCore(nullptr); } FifoPlayer::GetInstance().Close(); } // If we did not enter the CPU Run Loop above then run a fake one instead. // We need to be IsRunningAndStarted() for DolphinWX to stop us. if (CPU::GetState() != CPU::CPU_POWERDOWN) { s_is_started = true; Host_Message(WM_USER_STOP); while (CPU::GetState() != CPU::CPU_POWERDOWN) { if (!_CoreParameter.bCPUThread) g_video_backend->PeekMessages(); std::this_thread::sleep_for(std::chrono::milliseconds(20)); } s_is_started = false; } if (!_CoreParameter.bCPUThread) g_video_backend->Video_Cleanup(); return; } // Initialize and create emulation thread // Call browser: Init():s_emu_thread(). // See the BootManager.cpp file description for a complete call schedule. void EmuThread() { const SConfig& core_parameter = SConfig::GetInstance(); s_is_booting.store(true); Common::SetCurrentThreadName("Emuthread - Starting"); if (SConfig::GetInstance().m_OCEnable) DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); DisplayMessage(core_parameter.m_strFilename, 3000); // For a time this acts as the CPU thread... DeclareAsCPUThread(); Movie::Init(); HW::Init(); if (!g_video_backend->Initialize(s_window_handle)) { s_is_booting.store(false); PanicAlert("Failed to initialize video backend!"); Host_Message(WM_USER_STOP); return; } OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000); if (cpu_info.HTT) SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 4; else SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 2; if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread)) { s_is_booting.store(false); HW::Shutdown(); g_video_backend->Shutdown(); PanicAlert("Failed to initialize DSP emulation!"); Host_Message(WM_USER_STOP); return; } bool init_controllers = false; if (!g_controller_interface.IsInit()) { Pad::Initialize(s_window_handle); Keyboard::Initialize(s_window_handle); init_controllers = true; } else { // Update references in case controllers were refreshed Pad::LoadConfig(); Keyboard::LoadConfig(); } // Load and Init Wiimotes - only if we are booting in Wii mode if (core_parameter.bWii) { if (init_controllers) Wiimote::Initialize(s_window_handle, !s_state_filename.empty()); else Wiimote::LoadConfig(); // Activate Wiimotes which don't have source set to "None" for (unsigned int i = 0; i != MAX_BBMOTES; ++i) if (g_wiimote_sources[i]) GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true); } AudioCommon::InitSoundStream(); // The hardware is initialized. s_hardware_initialized = true; s_is_booting.store(false); // Set execution state to known values (CPU/FIFO/Audio Paused) CPU::Break(); // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::MODE_INTERPRETER); CBoot::BootUp(); // This adds the SyncGPU handler to CoreTiming, so now CoreTiming::Advance might block. Fifo::Prepare(); // Thread is no longer acting as CPU Thread UndeclareAsCPUThread(); // Setup our core, but can't use dynarec if we are compare server if (core_parameter.iCPUCore != PowerPC::CORE_INTERPRETER && (!core_parameter.bRunCompareServer || core_parameter.bRunCompareClient)) { PowerPC::SetMode(PowerPC::MODE_JIT); } else { PowerPC::SetMode(PowerPC::MODE_INTERPRETER); } // Update the window again because all stuff is initialized Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); // Determine the CPU thread function void (*cpuThreadFunc)(void); if (core_parameter.m_BootType == SConfig::BOOT_DFF) cpuThreadFunc = FifoPlayerThread; else cpuThreadFunc = CpuThread; // ENTER THE VIDEO THREAD LOOP if (core_parameter.bCPUThread) { // This thread, after creating the EmuWindow, spawns a CPU // thread, and then takes over and becomes the video thread Common::SetCurrentThreadName("Video thread"); g_video_backend->Video_Prepare(); // Spawn the CPU thread s_cpu_thread = std::thread(cpuThreadFunc); // become the GPU thread Fifo::RunGpuLoop(); // We have now exited the Video Loop INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str()); } else // SingleCore mode { // The spawned CPU Thread also does the graphics. // The EmuThread is thus an idle thread, which sleeps while // waiting for the program to terminate. Without this extra // thread, the video backend window hangs in single core mode // because no one is pumping messages. Common::SetCurrentThreadName("Emuthread - Idle"); // Spawn the CPU+GPU thread s_cpu_thread = std::thread(cpuThreadFunc); while (CPU::GetState() != CPU::CPU_POWERDOWN) { g_video_backend->PeekMessages(); Common::SleepCurrentThread(20); } } INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str()); // Wait for s_cpu_thread to exit INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str()); #ifdef USE_GDBSTUB INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str()); gdb_deinit(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str()); #endif s_cpu_thread.join(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); if (core_parameter.bCPUThread) g_video_backend->Video_Cleanup(); FileMon::Close(); // Stop audio thread - Actually this does nothing when using HLE // emulation, but stops the DSP Interpreter when using LLE emulation. DSP::GetDSPEmulator()->DSP_StopSoundStream(); // We must set up this flag before executing HW::Shutdown() s_hardware_initialized = false; INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str()); HW::Shutdown(); INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str()); if (init_controllers) { Wiimote::Shutdown(); Keyboard::Shutdown(); Pad::Shutdown(); init_controllers = false; } g_video_backend->Shutdown(); AudioCommon::ShutdownSoundStream(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str()); // Clear on screen messages that haven't expired OSD::ClearMessages(); // Reload sysconf file in order to see changes committed during emulation if (core_parameter.bWii) SConfig::GetInstance().m_SYSCONF->Reload(); INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----"); Movie::Shutdown(); PatchEngine::Shutdown(); s_is_stopping = false; if (s_on_stopped_callback) s_on_stopped_callback(); } // Set or get the running state void SetState(EState state) { // State cannot be controlled until the CPU Thread is operational if (!IsRunningAndStarted()) return; switch (state) { case CORE_PAUSE: // NOTE: GetState() will return CORE_PAUSE immediately, even before anything has // stopped (including the CPU). CPU::EnableStepping(true); // Break Wiimote::Pause(); #if defined(__LIBUSB__) || defined(_WIN32) GCAdapter::ResetRumble(); #endif break; case CORE_RUN: CPU::EnableStepping(false); Wiimote::Resume(); break; default: PanicAlert("Invalid state"); break; } } EState GetState() { if (s_is_stopping) return CORE_STOPPING; if (s_hardware_initialized) { if (CPU::IsStepping()) return CORE_PAUSE; return CORE_RUN; } return CORE_UNINITIALIZED; } static std::string GenerateScreenshotFolderPath() { const std::string& gameId = SConfig::GetInstance().GetUniqueID(); std::string path = File::GetUserPath(D_SCREENSHOTS_IDX) + gameId + DIR_SEP_CHR; if (!File::CreateFullPath(path)) { // fallback to old-style screenshots, without folder. path = File::GetUserPath(D_SCREENSHOTS_IDX); } return path; } static std::string GenerateScreenshotName() { std::string path = GenerateScreenshotFolderPath(); // append gameId, path only contains the folder here. path += SConfig::GetInstance().GetUniqueID(); std::string name; for (int i = 1; File::Exists(name = StringFromFormat("%s-%d.png", path.c_str(), i)); ++i) { // TODO? } return name; } void SaveScreenShot() { const bool bPaused = (GetState() == CORE_PAUSE); SetState(CORE_PAUSE); Renderer::SetScreenshot(GenerateScreenshotName()); if (!bPaused) SetState(CORE_RUN); } void SaveScreenShot(const std::string& name) { const bool bPaused = (GetState() == CORE_PAUSE); SetState(CORE_PAUSE); std::string filePath = GenerateScreenshotFolderPath() + name + ".png"; Renderer::SetScreenshot(filePath); if (!bPaused) SetState(CORE_RUN); } void RequestRefreshInfo() { s_request_refresh_info = true; } bool PauseAndLock(bool do_lock, bool unpause_on_unlock) { // WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread if (!IsRunning()) return true; // let's support recursive locking to simplify things on the caller's side, // and let's do it at this outer level in case the individual systems don't support it. if (do_lock ? s_pause_and_lock_depth++ : --s_pause_and_lock_depth) return true; bool was_unpaused = true; if (do_lock) { // first pause the CPU // This acquires a wrapper mutex and converts the current thread into // a temporary replacement CPU Thread. was_unpaused = CPU::PauseAndLock(true); } ExpansionInterface::PauseAndLock(do_lock, false); // audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle). DSP::GetDSPEmulator()->PauseAndLock(do_lock, false); // video has to come after CPU, because CPU thread can wait for video thread // (s_efbAccessRequested). Fifo::PauseAndLock(do_lock, false); #if defined(__LIBUSB__) || defined(_WIN32) GCAdapter::ResetRumble(); #endif // CPU is unlocked last because CPU::PauseAndLock contains the synchronization // mechanism that prevents CPU::Break from racing. if (!do_lock) { // The CPU is responsible for managing the Audio and FIFO state so we use its // mechanism to unpause them. If we unpaused the systems above when releasing // the locks then they could call CPU::Break which would require detecting it // and re-pausing with CPU::EnableStepping. was_unpaused = CPU::PauseAndLock(false, unpause_on_unlock, true); } return was_unpaused; } // Display FPS info // This should only be called from VI void VideoThrottle() { // Update info per second u32 ElapseTime = (u32)s_timer.GetTimeDifference(); if ((ElapseTime >= 1000 && s_drawn_video.load() > 0) || s_request_refresh_info) { UpdateTitle(); // Reset counter s_timer.Update(); s_drawn_frame.store(0); s_drawn_video.store(0); } s_drawn_video++; } // Executed from GPU thread // reports if a frame should be skipped or not // depending on the emulation speed set bool ShouldSkipFrame(int skipped) { u32 TargetFPS = VideoInterface::GetTargetRefreshRate(); if (SConfig::GetInstance().m_EmulationSpeed > 0.0f) TargetFPS = u32(TargetFPS * SConfig::GetInstance().m_EmulationSpeed); const u32 frames = s_drawn_frame.load(); const bool fps_slow = !(s_timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS); return fps_slow; } // --- Callbacks for backends / engine --- // Should be called from GPU thread when a frame is drawn void Callback_VideoCopiedToXFB(bool video_update) { if (video_update) s_drawn_frame++; Movie::FrameUpdate(); } void UpdateTitle() { u32 ElapseTime = (u32)s_timer.GetTimeDifference(); s_request_refresh_info = false; SConfig& _CoreParameter = SConfig::GetInstance(); if (ElapseTime == 0) ElapseTime = 1; float FPS = (float)(s_drawn_frame.load() * 1000.0 / ElapseTime); float VPS = (float)(s_drawn_video.load() * 1000.0 / ElapseTime); float Speed = (float)(s_drawn_video.load() * (100 * 1000.0) / (VideoInterface::GetTargetRefreshRate() * ElapseTime)); // Settings are shown the same for both extended and summary info std::string SSettings = StringFromFormat( "%s %s | %s | %s", PowerPC::GetCPUName(), _CoreParameter.bCPUThread ? "DC" : "SC", g_video_backend->GetDisplayName().c_str(), _CoreParameter.bDSPHLE ? "HLE" : "LLE"); std::string SFPS; if (Movie::IsPlayingInput()) SFPS = StringFromFormat("VI: %u/%u - Input: %u/%u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed); else if (Movie::IsRecordingInput()) SFPS = StringFromFormat("VI: %u - Input: %u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed); else { SFPS = StringFromFormat("FPS: %.0f - VPS: %.0f - %.0f%%", FPS, VPS, Speed); if (SConfig::GetInstance().m_InterfaceExtendedFPSInfo) { // Use extended or summary information. The summary information does not print the ticks data, // that's more of a debugging interest, it can always be optional of course if someone is // interested. static u64 ticks = 0; static u64 idleTicks = 0; u64 newTicks = CoreTiming::GetTicks(); u64 newIdleTicks = CoreTiming::GetIdleTicks(); u64 diff = (newTicks - ticks) / 1000000; u64 idleDiff = (newIdleTicks - idleTicks) / 1000000; ticks = newTicks; idleTicks = newIdleTicks; float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100; SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)", _CoreParameter.bSkipIdle ? "~" : "", (int)(diff), (int)(diff - idleDiff), (int)(idleDiff), SystemTimers::GetTicksPerSecond() / 1000000, _CoreParameter.bSkipIdle ? "~" : "", TicksPercentage); } } // This is our final "frame counter" string std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str()); // Update the audio timestretcher with the current speed if (g_sound_stream) { CMixer* pMixer = g_sound_stream->GetMixer(); pMixer->UpdateSpeed((float)Speed / 100); } Host_UpdateTitle(SMessage); } void Shutdown() { // During shutdown DXGI expects us to handle some messages on the UI thread. // Therefore we can't immediately block and wait for the emu thread to shut // down, so we join the emu thread as late as possible when the UI has already // shut down. // For more info read "DirectX Graphics Infrastructure (DXGI): Best Practices" // on MSDN. if (s_emu_thread.joinable()) s_emu_thread.join(); // Make sure there's nothing left over in case we're about to exit. HostDispatchJobs(); } void SetOnStoppedCallback(StoppedCallbackFunc callback) { s_on_stopped_callback = callback; } void UpdateWantDeterminism(bool initial) { // For now, this value is not itself configurable. Instead, individual // settings that depend on it, such as GPU determinism mode. should have // override options for testing, bool new_want_determinism = Movie::IsPlayingInput() || Movie::IsRecordingInput() || NetPlay::IsNetPlayRunning(); if (new_want_determinism != g_want_determinism || initial) { WARN_LOG(COMMON, "Want determinism <- %s", new_want_determinism ? "true" : "false"); bool was_unpaused = Core::PauseAndLock(true); g_want_determinism = new_want_determinism; WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism); Fifo::UpdateWantDeterminism(new_want_determinism); // We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use // of FMA. JitInterface::ClearCache(); Common::InitializeWiiRoot(g_want_determinism); Core::PauseAndLock(false, was_unpaused); } } void QueueHostJob(std::function<void()> job, bool run_during_stop) { if (!job) return; bool send_message = false; { std::lock_guard<std::mutex> guard(s_host_jobs_lock); send_message = s_host_jobs_queue.empty(); s_host_jobs_queue.emplace(HostJob{std::move(job), run_during_stop}); } // If the the queue was empty then kick the Host to come and get this job. if (send_message) Host_Message(WM_USER_JOB_DISPATCH); } void HostDispatchJobs() { // WARNING: This should only run on the Host Thread. // NOTE: This function is potentially re-entrant. If a job calls // Core::Stop for instance then we'll enter this a second time. std::unique_lock<std::mutex> guard(s_host_jobs_lock); while (!s_host_jobs_queue.empty()) { HostJob job = std::move(s_host_jobs_queue.front()); s_host_jobs_queue.pop(); // NOTE: Memory ordering is important. The booting flag needs to be // checked first because the state transition is: // CORE_UNINITIALIZED: s_is_booting -> s_hardware_initialized // We need to check variables in the same order as the state // transition, otherwise we race and get transient failures. if (!job.run_after_stop && !s_is_booting.load() && !IsRunning()) continue; guard.unlock(); job.job(); guard.lock(); } } } // Core
// Subtract the paused time when we run again void Run() { ReRecTimer.AddTimeDifference(); }
/* TODO: Font rendering MUST be redesigned. */ Font* GuiRendererD3D11::MakeFont(const char* pPath, int height) { Common::Timer timer; timer.Start(); FT_Face face; int texWidth = 4096; int texHeight = 4096; //create pixels array unsigned char* pTexData = (unsigned char*)malloc(texWidth * texHeight); ZeroMemory(pTexData, texWidth * texHeight); if (FT_New_Face(mFreeTypeLibrary, pPath, 0, &face) != 0) { free(pTexData); // TODO // LOG_ERROR("Failed to load font '%s'.", pPath); return 0; } //FT_Set_Pixel_Sizes(face, 2*height, 2*height); FT_Set_Char_Size(face, 0, height * 64, 96, 96); Font* pFont = new Font; /* FT_Matrix Transform; Transform.xx = (FT_Fixed)(1.0f * 0x10000L); Transform.xy = (FT_Fixed)(0.0f * 0x10000L); Transform.yx = (FT_Fixed)(0.0f * 0x10000L); Transform.yy = (FT_Fixed)(1.0f * 0x10000L); FT_Set_Transform(face, &Transform, 0); */ int Width; int Height; int OffsetX = 0; int OffsetY = 0; pFont->height = height; pFont->charCount = 65536; pFont->characters = (CharacterInfo*)malloc(sizeof(CharacterInfo) * pFont->charCount); int Index = 0; for (int ChrId = 0; ChrId < (65536); ChrId++) { // Load The Glyph For Our Character. unsigned int GlyphIndex = FT_Get_Char_Index(face, ChrId); if (GlyphIndex == 0) { pFont->characters[ChrId].exists = false; continue; } FT_Load_Glyph(face, GlyphIndex, FT_LOAD_DEFAULT); // Move The Face's Glyph Into A Glyph Object. FT_Glyph glyph; FT_Get_Glyph(face->glyph, &glyph); // Convert The Glyph To A Bitmap. FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; // This Reference Will Make Accessing The Bitmap Easier. FT_Bitmap& bitmap = bitmap_glyph->bitmap; Width = bitmap.width; Height = bitmap.rows; //char won't fit to texture if (OffsetX + Width + 2 > texWidth) { OffsetX = 0; OffsetY += 2 * height; } for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { unsigned char Value = bitmap.buffer[x + Width * y]; int PxOffset = (y + OffsetY) * texWidth + x + OffsetX; pTexData[PxOffset] = Value; } } pFont->characters[ChrId].exists = true; pFont->characters[ChrId].top = (short)bitmap_glyph->top; pFont->characters[ChrId].left = (short)bitmap_glyph->left; pFont->characters[ChrId].height = (short)Height; pFont->characters[ChrId].width = (short)Width; pFont->characters[ChrId].u = (short)OffsetX; pFont->characters[ChrId].v = (short)OffsetY; pFont->characters[ChrId].spacing = (short)(face->glyph->advance.x >> 6) + 1; FT_Done_Glyph(glyph); OffsetX += pFont->characters[ChrId].width + 1; Index++; } //close font FT_Done_Face(face); OffsetY += 2 * height; // Used texture height texHeight = CeilToPowerOf2(OffsetY); Common::Image fontImage; fontImage.SetData(pTexData, texWidth, texHeight, Common::ImageFormat::A_UBYTE); // pFont->texture = pRenderer->CreateTexture(&fontImage, false); pFont->texture = new RendererTextureD3D11; pFont->texture->FromImage(fontImage); pFont->texHeight = texHeight; pFont->texWidth = texWidth; free(pTexData); // TODO // LOG_SUCCESS("Font '%s', size %i loaded in %.3lf seconds.", pPath, height, timer.Stop()); return pFont; }
void UpdateTitle() { u32 ElapseTime = (u32)Timer.GetTimeDifference(); g_requestRefreshInfo = false; SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (ElapseTime == 0) ElapseTime = 1; float FPS = (float) (Common::AtomicLoad(DrawnFrame) * 1000.0 / ElapseTime); float VPS = (float) (DrawnVideo * 1000.0 / ElapseTime); float Speed = (float) (DrawnVideo * (100 * 1000.0) / (VideoInterface::TargetRefreshRate * ElapseTime)); // Settings are shown the same for both extended and summary info std::string SSettings = StringFromFormat("%s %s | %s | %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC", g_video_backend->GetDisplayName().c_str(), _CoreParameter.bDSPHLE ? "HLE" : "LLE"); std::string SFPS; if (Movie::IsPlayingInput()) SFPS = StringFromFormat("VI: %u/%u - Frame: %u/%u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed); else if (Movie::IsRecordingInput()) SFPS = StringFromFormat("VI: %u - Frame: %u - FPS: %.0f - VPS: %.0f - %.0f%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed); else { SFPS = StringFromFormat("FPS: %.0f - VPS: %.0f - %.0f%%", FPS, VPS, Speed); if (SConfig::GetInstance().m_InterfaceExtendedFPSInfo) { // Use extended or summary information. The summary information does not print the ticks data, // that's more of a debugging interest, it can always be optional of course if someone is interested. static u64 ticks = 0; static u64 idleTicks = 0; u64 newTicks = CoreTiming::GetTicks(); u64 newIdleTicks = CoreTiming::GetIdleTicks(); u64 diff = (newTicks - ticks) / 1000000; u64 idleDiff = (newIdleTicks - idleTicks) / 1000000; ticks = newTicks; idleTicks = newIdleTicks; float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100; SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)", _CoreParameter.bSkipIdle ? "~" : "", (int)(diff), (int)(diff - idleDiff), (int)(idleDiff), SystemTimers::GetTicksPerSecond() / 1000000, _CoreParameter.bSkipIdle ? "~" : "", TicksPercentage); } } // This is our final "frame counter" string std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str()); std::string TMessage = StringFromFormat("%s | %s", scm_rev_str, SMessage.c_str()); // Show message g_video_backend->UpdateFPSDisplay(SMessage); // Update the audio timestretcher with the current speed if (soundStream) { CMixer* pMixer = soundStream->GetMixer(); pMixer->UpdateSpeed((float)Speed / 100); } if (_CoreParameter.bRenderToMain && SConfig::GetInstance().m_InterfaceStatusbar) { Host_UpdateStatusBar(SMessage); Host_UpdateTitle(scm_rev_str); } else { Host_UpdateTitle(TMessage); } }
namespace Core { // Declarations and definitions Common::Timer Timer; volatile u32 DrawnFrame = 0; u32 DrawnVideo = 0; // Function forwarding const char *Callback_ISOName(void); void Callback_WiimoteInterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size); // Function declarations void EmuThread(); void Stop(); bool g_bStopping = false; bool g_bHwInit = false; bool g_bStarted = false; bool g_bRealWiimote = false; void *g_pWindowHandle = NULL; std::string g_stateFileName; std::thread g_EmuThread; static std::thread g_cpu_thread; static bool g_requestRefreshInfo = false; static int g_pauseAndLockDepth = 0; SCoreStartupParameter g_CoreStartupParameter; std::string GetStateFileName() { return g_stateFileName; } void SetStateFileName(std::string val) { g_stateFileName = val; } // Display messages and return values // Formatted stop message std::string StopMessage(bool bMainThread, std::string Message) { return StringFromFormat("Stop [%s %i]\t%s\t%s", bMainThread ? "Main Thread" : "Video Thread", Common::CurrentThreadId(), MemUsage().c_str(), Message.c_str()); } // bool PanicAlertToVideo(const char* text, bool yes_no) { DisplayMessage(text, 3000); return true; } void DisplayMessage(const char *message, int time_in_ms) { SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; // Actually displaying non-ASCII could cause things to go pear-shaped for (const char *c = message; *c != '\0'; ++c) if (*c < ' ') return; g_video_backend->Video_AddMessage(message, time_in_ms); if (_CoreParameter.bRenderToMain && SConfig::GetInstance().m_InterfaceStatusbar) { Host_UpdateStatusBar(message); } else Host_UpdateTitle(message); } void Callback_DebuggerBreak() { CCPU::Break(); } void *GetWindowHandle() { return g_pWindowHandle; } bool IsRunning() { return (GetState() != CORE_UNINITIALIZED) || g_bHwInit; } bool IsRunningAndStarted() { return g_bStarted; } bool IsRunningInCurrentThread() { return IsRunning() && IsCPUThread(); } bool IsCPUThread() { return (g_cpu_thread.joinable() ? (g_cpu_thread.get_id() == std::this_thread::get_id()) : !g_bStarted); } bool IsGPUThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (_CoreParameter.bCPUThread) { return (g_EmuThread.joinable() && (g_EmuThread.get_id() == std::this_thread::get_id())); } else { return IsCPUThread(); } } // This is called from the GUI thread. See the booting call schedule in // BootManager.cpp bool Init() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (g_EmuThread.joinable()) { PanicAlertT("Emu Thread already running"); return false; } g_CoreStartupParameter = _CoreParameter; INFO_LOG(OSREPORT, "Starting core = %s mode", g_CoreStartupParameter.bWii ? "Wii" : "Gamecube"); INFO_LOG(OSREPORT, "CPU Thread separate = %s", g_CoreStartupParameter.bCPUThread ? "Yes" : "No"); Host_UpdateMainFrame(); // Disable any menus or buttons at boot g_aspect_wide = _CoreParameter.bWii; if (g_aspect_wide) { IniFile gameIni; gameIni.Load(_CoreParameter.m_strGameIni.c_str()); gameIni.Get("Wii", "Widescreen", &g_aspect_wide, !!SConfig::GetInstance().m_SYSCONF-> GetData<u8>("IPL.AR")); } // g_pWindowHandle is first the m_Panel handle, // then it is updated to the render window handle, // within g_video_backend->Initialize() g_pWindowHandle = Host_GetRenderHandle(); // Start the emu thread g_EmuThread = std::thread(EmuThread); return true; } // Called from GUI thread void Stop() // - Hammertime! { if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN) { if (g_EmuThread.joinable()) g_EmuThread.join(); return; } const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; g_bStopping = true; g_video_backend->EmuStateChange(EMUSTATE_CHANGE_STOP); INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutting down ----"); // Stop the CPU INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stop CPU").c_str()); PowerPC::Stop(); // Kick it if it's waiting (code stepping wait loop) CCPU::StepOpcode(); if (_CoreParameter.bCPUThread) { // Video_EnterLoop() should now exit so that EmuThread() // will continue concurrently with the rest of the commands // in this function. We no longer rely on Postmessage. INFO_LOG(CONSOLE, "%s", StopMessage(true, "Wait for Video Loop to exit ...").c_str()); g_video_backend->Video_ExitLoop(); } INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str()); g_EmuThread.join(); // Wait for emuthread to close. INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str()); #ifdef _WIN32 EmuWindow::Close(); #endif // Clear on screen messages that haven't expired g_video_backend->Video_ClearMessages(); // Close the trace file Core::StopTrace(); // Reload sysconf file in order to see changes committed during emulation if (_CoreParameter.bWii) SConfig::GetInstance().m_SYSCONF->Reload(); INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutdown complete ----"); Movie::Shutdown(); g_bStopping = false; } // Create the CPU thread, which is a CPU + Video thread in Single Core mode. void CpuThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (_CoreParameter.bCPUThread) { Common::SetCurrentThreadName("CPU thread"); } else { Common::SetCurrentThreadName("CPU-GPU thread"); g_video_backend->Video_Prepare(); } #if defined(_M_X64) EMM::InstallExceptionHandler(); // Let's run under memory watch #endif if (!g_stateFileName.empty()) State::LoadAs(g_stateFileName); g_bStarted = true; // Enter CPU run loop. When we leave it - we are done. CCPU::Run(); g_bStarted = false; return; } void FifoPlayerThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (_CoreParameter.bCPUThread) { Common::SetCurrentThreadName("FIFO player thread"); } else { g_video_backend->Video_Prepare(); Common::SetCurrentThreadName("FIFO-GPU thread"); } g_bStarted = true; // Enter CPU run loop. When we leave it - we are done. if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename)) { FifoPlayer::GetInstance().Play(); FifoPlayer::GetInstance().Close(); } g_bStarted = false; return; } // Initalize and create emulation thread // Call browser: Init():g_EmuThread(). // See the BootManager.cpp file description for a complete call schedule. void EmuThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; Common::SetCurrentThreadName("Emuthread - Starting"); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); DisplayMessage(_CoreParameter.m_strFilename, 3000); Movie::Init(); HW::Init(); if (!g_video_backend->Initialize(g_pWindowHandle)) { PanicAlert("Failed to initialize video backend!"); Host_Message(WM_USER_STOP); return; } OSD::AddMessage(("Dolphin " + g_video_backend->GetName() + " Video Backend.").c_str(), 5000); if (!DSP::GetDSPEmulator()->Initialize(g_pWindowHandle, _CoreParameter.bWii, _CoreParameter.bDSPThread)) { HW::Shutdown(); g_video_backend->Shutdown(); PanicAlert("Failed to initialize DSP emulator!"); Host_Message(WM_USER_STOP); return; } Pad::Initialize(g_pWindowHandle); // Load and Init Wiimotes - only if we are booting in wii mode if (g_CoreStartupParameter.bWii) { Wiimote::Initialize(g_pWindowHandle); // Activate wiimotes which don't have source set to "None" for (unsigned int i = 0; i != MAX_WIIMOTES; ++i) if (g_wiimote_sources[i]) GetUsbPointer()->AccessWiiMote(i | 0x100)-> Activate(true); } // The hardware is initialized. g_bHwInit = true; // Boot to pause or not Core::SetState(_CoreParameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN); // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::MODE_INTERPRETER); CBoot::BootUp(); // Setup our core, but can't use dynarec if we are compare server if (_CoreParameter.iCPUCore && (!_CoreParameter.bRunCompareServer || _CoreParameter.bRunCompareClient)) PowerPC::SetMode(PowerPC::MODE_JIT); else PowerPC::SetMode(PowerPC::MODE_INTERPRETER); // Update the window again because all stuff is initialized Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); // Determine the cpu thread function void (*cpuThreadFunc)(void); if (_CoreParameter.m_BootType == SCoreStartupParameter::BOOT_DFF) cpuThreadFunc = FifoPlayerThread; else cpuThreadFunc = CpuThread; // ENTER THE VIDEO THREAD LOOP if (_CoreParameter.bCPUThread) { // This thread, after creating the EmuWindow, spawns a CPU // thread, and then takes over and becomes the video thread Common::SetCurrentThreadName("Video thread"); g_video_backend->Video_Prepare(); // Spawn the CPU thread g_cpu_thread = std::thread(cpuThreadFunc); // become the GPU thread g_video_backend->Video_EnterLoop(); // We have now exited the Video Loop INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str()); } else // SingleCore mode { // The spawned CPU Thread also does the graphics. // The EmuThread is thus an idle thread, which sleeps while // waiting for the program to terminate. Without this extra // thread, the video backend window hangs in single core mode // because noone is pumping messages. Common::SetCurrentThreadName("Emuthread - Idle"); // Spawn the CPU+GPU thread g_cpu_thread = std::thread(cpuThreadFunc); while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) { g_video_backend->PeekMessages(); Common::SleepCurrentThread(20); } } // Wait for g_cpu_thread to exit INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str()); g_cpu_thread.join(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); VolumeHandler::EjectVolume(); FileMon::Close(); // Stop audio thread - Actually this does nothing when using HLE // emulation, but stops the DSP Interpreter when using LLE emulation. DSP::GetDSPEmulator()->DSP_StopSoundStream(); // We must set up this flag before executing HW::Shutdown() g_bHwInit = false; INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str()); HW::Shutdown(); INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str()); Pad::Shutdown(); Wiimote::Shutdown(); g_video_backend->Shutdown(); } // Set or get the running state void SetState(EState _State) { switch (_State) { case CORE_UNINITIALIZED: Stop(); break; case CORE_PAUSE: CCPU::EnableStepping(true); // Break break; case CORE_RUN: CCPU::EnableStepping(false); break; default: PanicAlertT("Invalid state"); break; } } EState GetState() { if (g_bHwInit) { if (CCPU::IsStepping()) return CORE_PAUSE; else if (g_bStopping) return CORE_STOPPING; else return CORE_RUN; } return CORE_UNINITIALIZED; } static std::string GenerateScreenshotName() { const std::string& gameId = SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID(); std::string path = File::GetUserPath(D_SCREENSHOTS_IDX) + gameId + DIR_SEP_CHR; if (!File::CreateFullPath(path)) { // fallback to old-style screenshots, without folder. path = File::GetUserPath(D_SCREENSHOTS_IDX); } //append gameId, path only contains the folder here. path += gameId; std::string name; for (int i = 1; File::Exists(name = StringFromFormat("%s-%d.png", path.c_str(), i)); ++i) {} return name; } void SaveScreenShot() { const bool bPaused = (GetState() == CORE_PAUSE); SetState(CORE_PAUSE); g_video_backend->Video_Screenshot(GenerateScreenshotName().c_str()); if (!bPaused) SetState(CORE_RUN); } void RequestRefreshInfo() { g_requestRefreshInfo = true; } bool PauseAndLock(bool doLock, bool unpauseOnUnlock) { // let's support recursive locking to simplify things on the caller's side, // and let's do it at this outer level in case the individual systems don't support it. if (doLock ? g_pauseAndLockDepth++ : --g_pauseAndLockDepth) return true; // first pause or unpause the cpu bool wasUnpaused = CCPU::PauseAndLock(doLock, unpauseOnUnlock); ExpansionInterface::PauseAndLock(doLock, unpauseOnUnlock); // audio has to come after cpu, because cpu thread can wait for audio thread (m_throttle). AudioCommon::PauseAndLock(doLock, unpauseOnUnlock); DSP::GetDSPEmulator()->PauseAndLock(doLock, unpauseOnUnlock); // video has to come after cpu, because cpu thread can wait for video thread (s_efbAccessRequested). g_video_backend->PauseAndLock(doLock, unpauseOnUnlock); return wasUnpaused; } // Apply Frame Limit and Display FPS info // This should only be called from VI void VideoThrottle() { u32 TargetVPS = (SConfig::GetInstance().m_Framelimit > 2) ? (SConfig::GetInstance().m_Framelimit - 1) * 5 : VideoInterface::TargetRefreshRate; // Disable the frame-limiter when the throttle (Tab) key is held down. Audio throttle: m_Framelimit = 2 if (SConfig::GetInstance().m_Framelimit && SConfig::GetInstance().m_Framelimit != 2 && !Host_GetKeyState('\t')) { u32 frametime = ((SConfig::GetInstance().b_UseFPS)? Common::AtomicLoad(DrawnFrame) : DrawnVideo) * 1000 / TargetVPS; u32 timeDifference = (u32)Timer.GetTimeDifference(); if (timeDifference < frametime) { Common::SleepCurrentThread(frametime - timeDifference - 1); } while ((u32)Timer.GetTimeDifference() < frametime) Common::YieldCPU(); //Common::SleepCurrentThread(1); } // Update info per second u32 ElapseTime = (u32)Timer.GetTimeDifference(); if ((ElapseTime >= 1000 && DrawnVideo > 0) || g_requestRefreshInfo) { UpdateTitle(); // Reset counter Timer.Update(); Common::AtomicStore(DrawnFrame, 0); DrawnVideo = 0; } DrawnVideo++; } // Executed from GPU thread // reports if a frame should be skipped or not // depending on the framelimit set bool ShouldSkipFrame(int skipped) { const u32 TargetFPS = (SConfig::GetInstance().m_Framelimit > 1) ? SConfig::GetInstance().m_Framelimit * 5 : VideoInterface::TargetRefreshRate; const u32 frames = Common::AtomicLoad(DrawnFrame); const bool fps_slow = !(Timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS); return fps_slow; } // --- Callbacks for backends / engine --- // Should be called from GPU thread when a frame is drawn void Callback_VideoCopiedToXFB(bool video_update) { if(video_update) Common::AtomicIncrement(DrawnFrame); Movie::FrameUpdate(); } // Callback_ISOName: Let the DSP emulator get the game name // const char *Callback_ISOName() { SCoreStartupParameter& params = SConfig::GetInstance().m_LocalCoreStartupParameter; if (params.m_strName.length() > 0) return params.m_strName.c_str(); else return ""; } void UpdateTitle() { u32 ElapseTime = (u32)Timer.GetTimeDifference(); g_requestRefreshInfo = false; SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (ElapseTime == 0) ElapseTime = 1; u32 FPS = Common::AtomicLoad(DrawnFrame) * 1000 / ElapseTime; u32 VPS = DrawnVideo * 1000 / ElapseTime; u32 Speed = DrawnVideo * (100 * 1000) / (VideoInterface::TargetRefreshRate * ElapseTime); // Settings are shown the same for both extended and summary info std::string SSettings = StringFromFormat("%s %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC"); // Use extended or summary information. The summary information does not print the ticks data, // that's more of a debugging interest, it can always be optional of course if someone is interested. //#define EXTENDED_INFO #ifdef EXTENDED_INFO u64 newTicks = CoreTiming::GetTicks(); u64 newIdleTicks = CoreTiming::GetIdleTicks(); u64 diff = (newTicks - ticks) / 1000000; u64 idleDiff = (newIdleTicks - idleTicks) / 1000000; ticks = newTicks; idleTicks = newIdleTicks; float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100; std::string SFPS = StringFromFormat("FPS: %u - VPS: %u - SPEED: %u%%", FPS, VPS, Speed); SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)", _CoreParameter.bSkipIdle ? "~" : "", (int)(diff), (int)(diff - idleDiff), (int)(idleDiff), SystemTimers::GetTicksPerSecond() / 1000000, _CoreParameter.bSkipIdle ? "~" : "", TicksPercentage); #else // Summary information std::string SFPS; if (Movie::IsPlayingInput()) SFPS = StringFromFormat("VI: %u/%u - Frame: %u/%u - FPS: %u - VPS: %u - SPEED: %u%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed); else if (Movie::IsRecordingInput()) SFPS = StringFromFormat("VI: %u - Frame: %u - FPS: %u - VPS: %u - SPEED: %u%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed); else SFPS = StringFromFormat("FPS: %u - VPS: %u - SPEED: %u%%", FPS, VPS, Speed); #endif // This is our final "frame counter" string std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str()); std::string TMessage = StringFromFormat("%s | ", scm_rev_str) + SMessage; // Show message g_video_backend->UpdateFPSDisplay(SMessage.c_str()); // Update the audio timestretcher with the current speed if (soundStream) { CMixer* pMixer = soundStream->GetMixer(); pMixer->UpdateSpeed((float)Speed / 100); } if (_CoreParameter.bRenderToMain && SConfig::GetInstance().m_InterfaceStatusbar) { Host_UpdateStatusBar(SMessage.c_str()); Host_UpdateTitle(scm_rev_str); } else Host_UpdateTitle(TMessage.c_str()); } } // Core
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { SetUpCurrentDirectory(); //create window CustomWindow* pWindow = new CustomWindow; pWindow->setSize(800, 600); pWindow->setTitle(L"NFEngine Demo - Initializing engine..."); pWindow->open(); //initialize engine if (EngineInit() != Result::OK) return 1; Demo_InitEditorBar(); //create scene and camera g_pScene = EngineCreateScene(); // -------------------------------- // Build scene // -------------------------------- #ifdef DEMO_SEC_CAMERA_ON InitSecondaryCamera(); #endif //set ambient & background color EnviromentDesc envDesc; envDesc.ambientLight = Vector(0.02f, 0.03f, 0.04f, 0.0f); //envDesc.m_BackgroundColor = Vector(0.04f, 0.05f, 0.07f, 0.03f); envDesc.backgroundColor = Vector(0.02f, 0.03f, 0.04f, 0.01f); g_pScene->SetEnvironment(&envDesc); CollisionShape* pFloorShape = ENGINE_GET_COLLISION_SHAPE("shape_floor"); pFloorShape->SetCallbacks(OnLoadCustomShapeResource, NULL); pFloorShape->Load(); pFloorShape->AddRef(); CollisionShape* pFrameShape = ENGINE_GET_COLLISION_SHAPE("shape_frame"); pFrameShape->SetCallbacks(OnLoadCustomShapeResource, NULL); pFrameShape->Load(); pFrameShape->AddRef(); CollisionShape* pBoxShape = ENGINE_GET_COLLISION_SHAPE("shape_box"); pBoxShape->SetCallbacks(OnLoadCustomShapeResource, NULL); pBoxShape->Load(); pBoxShape->AddRef(); CollisionShape* pBarrelShape = ENGINE_GET_COLLISION_SHAPE("shape_barrel"); pBarrelShape->SetCallbacks(OnLoadCustomShapeResource, NULL); pBarrelShape->Load(); pBarrelShape->AddRef(); CollisionShape* pChamberShape = ENGINE_GET_COLLISION_SHAPE("chamber_collision_shape.nfcs"); pChamberShape->Load(); pChamberShape->AddRef(); pWindow->InitCamera(); pWindow->setTitle(L"NFEngine Demo"); #ifdef SCENE_MINECRAFT // SUNLIGHT Entity* pDirLightEnt = g_pScene->CreateEntity(); XOrientation orient; orient.x = Vector(0.0f, -0.0f, -0.0f, 0.0f); orient.z = Vector(-1.5f, -1.0f, 0.5f, 0.0f); orient.y = Vector(0.0f, 1.0f, 0.0f, 0.0f); pDirLightEnt->SetOrientation(&orient); DirLightDesc dirLight; dirLight.m_Far = 100.0f; dirLight.m_Splits = 4; dirLight.m_LightDist = 1000.0f; LightComponent* pDirLight = new LightComponent(pDirLightEnt); pDirLight->SetDirLight(&dirLight); pDirLight->SetColor(Float3(2.2, 2, 1.8)); pDirLight->SetShadowMap(1024); // MINECRAFT Entity* pEnt = g_pScene->CreateEntity(); pEnt->SetPosition(Vector(0, -70.0f, 0)); MeshComponent* pMesh = new MeshComponent(pEnt); pMesh->SetMeshResource("minecraft.nfm"); #endif #ifdef SCENE_SPONZA // SPONZA Entity* pEnt = g_pScene->CreateEntity(); pEnt->SetPosition(Vector(0, 0, 0)); MeshComponent* pMesh = new MeshComponent(pEnt); pMesh->SetMeshResource("sponza.nfm"); CollisionShape* pSponzaShape = ENGINE_GET_COLLISION_SHAPE("sponza_collision_shape.nfcs"); //pSponzaShape->Load(); BodyComponent* pFloorBody = new BodyComponent(pEnt); pFloorBody->EnablePhysics(pSponzaShape); pFloorBody->SetMass(0.0); pEnt = g_pScene->CreateEntity(); pEnt->SetPosition(Vector(0.0f, 3.5f, 0.0f)); LightComponent* pLight = new LightComponent(pEnt); OmniLightDesc omni; omni.m_ShadowFadeStart = 12.0f; omni.m_ShadowFadeEnd = 120.0f; omni.m_Radius = 90.0f; pLight->SetOmniLight(&omni); pLight->SetColor(Float3(50, 50, 50)); pLight->SetShadowMap(512); /* // SUNLIGHT Entity* pDirLightEnt = g_pScene->CreateEntity(); XOrientation orient; orient.x = Vector(0.0f, -0.0f, -0.0f, 0.0f); orient.z = Vector(0.1, -2.3f, 1.05, 0.0f); orient.y = Vector(0.0f, 1.0f, 0.0f, 0.0f); pDirLightEnt->SetOrientation(&orient); DirLightDesc dirLight; dirLight.m_Far = 100.0f; dirLight.m_Splits = 4; dirLight.m_LightDist = 1000.0; LightComponent* pDirLight = new LightComponent(pDirLightEnt); pDirLight->SetDirLight(&dirLight); pDirLight->SetColor(Float3(2.2, 1.3, 0.8)); pDirLight->SetShadowMap(2048); */ #endif //performance test (many objects and shadowmaps) #ifdef SCENE_SEGMENTS_PERF_TEST for (int x = -4; x < 5; x++) { for (int z = -4; z < 5; z++) { Entity* pEntity = g_pScene->CreateEntity(); pEntity->SetPosition(12.0f * Vector((float)x, 0, (float)z)); MeshComponent* pMesh = new MeshComponent(pEntity); pMesh->SetMeshResource("chamber.nfm"); BodyComponent* pBody = new BodyComponent(pEntity); pBody->EnablePhysics(pChamberShape); LightComponent* pLight; OmniLightDesc omni; pEntity = g_pScene->CreateEntity(); pEntity->SetPosition(12.0f * Vector(x, 0, z) + Vector(0.0f, 3.5f, 0.0f)); pLight = new LightComponent(pEntity); omni.shadowFadeStart = 80.0f; omni.shadowFadeEnd = 120.0f; omni.radius = 8.0f; pLight->SetOmniLight(&omni); pLight->SetColor(Float3(50, 50, 50)); pLight->SetShadowMap(32); pEntity = g_pScene->CreateEntity(); pEntity->SetPosition(12.0f * Vector(x, 0, z) + Vector(6.0f, 1.8f, 0.0f)); pLight = new LightComponent(pEntity); omni.radius = 3.0f; pLight->SetOmniLight(&omni); pLight->SetColor(Float3(5.0f, 0.5f, 0.25f)); pEntity = g_pScene->CreateEntity(); pEntity->SetPosition(12.0f * Vector(x, 0, z) + Vector(0.0f, 1.8f, 6.0f)); pLight = new LightComponent(pEntity); omni.radius = 3.0f; pLight->SetOmniLight(&omni); pLight->SetColor(Float3(5.0f, 0.5f, 0.25f)); /* for (int i = -3; i<=3; i++) { for (int j = 0; j<4; j++) { for (int k = -3; k<=3; k++) { Entity* pCube = g_pScene->CreateEntity(); pCube->SetPosition(12.0f * Vector(x,0,z) + 0.6f * Vector(i,j,k) + Vector(0.0f, 0.25f, 0.0f)); MeshComponent* pMesh = new MeshComponent(pCube); pMesh->SetMeshResource("cube.nfm"); BodyComponent* pBody = new BodyComponent(pCube); pBody->SetMass(0.0f); pBody->EnablePhysics((CollisionShape*)Engine_GetResource(Mesh::COLLISION_SHAPE, "shape_box")); } } }*/ } } #endif // infinite looped scene #ifdef SCENE_SEGMENTS BufferOutputStream segmentDesc; Matrix mat = MatrixRotationNormal(Vector(0, 1, 0), NFE_MATH_PI / 4.0f); // create segments description buffer { OmniLightDesc omni; LightComponent* pLight; Entity entity; entity.SetPosition(Vector()); //pEntity->SetMatrix(mat); MeshComponent* pMesh = new MeshComponent(&entity); pMesh->SetMeshResource("chamber.nfm"); BodyComponent* pBody = new BodyComponent(&entity); pBody->EnablePhysics(pChamberShape); entity.Serialize(&segmentDesc, Vector()); entity.RemoveAllComponents(); entity.SetPosition(Vector(0.0f, 3.5f, 0.0f)); /* pLight = new LightComponent(&entity); omni.m_ShadowFadeEnd = 12.0f; omni.m_ShadowFadeStart = 8.0f; omni.m_Radius = 9.0f; pLight->SetOmniLight(&omni); pLight->SetColor(Float3(50, 50, 50)); pLight->SetShadowMap(512); entity.Serialize(&segmentDesc, Vector()); */ entity.RemoveAllComponents(); entity.SetPosition(Vector(6.0f, 1.8f, 0.0f)); pLight = new LightComponent(&entity); omni.m_Radius = 3.0f; pLight->SetOmniLight(&omni); pLight->SetColor(Float3(5.0f, 0.5f, 0.25f)); entity.Serialize(&segmentDesc, Vector()); entity.RemoveAllComponents(); entity.SetPosition(Vector(0.0f, 1.8f, 6.0f)); pLight = new LightComponent(&entity); omni.m_Radius = 3.0f; pLight->SetOmniLight(&omni); pLight->SetColor(Float3(5.0f, 0.5f, 0.25f)); entity.Serialize(&segmentDesc, Vector()); } #define SEG_AXIS_NUM 12 Segment* pSegments[SEG_AXIS_NUM][SEG_AXIS_NUM]; // create segments array for (int i = 0; i < SEG_AXIS_NUM; i++) { for (int j = 0; j < SEG_AXIS_NUM; j++) { char segName[32]; sprintf_s(segName, "seg_%i_%i", i, j); pSegments[i][j] = g_pScene->CreateSegment(segName, Vector(5.99f, 1000.0f, 5.99f)); pSegments[i][j]->AddEntityFromRawBuffer(segmentDesc.GetData(), segmentDesc.GetSize()); } } // create links for (int x = 0; x < SEG_AXIS_NUM; x++) { for (int z = 0; z < SEG_AXIS_NUM; z++) { //make inifinite loop for (int depth = 1; depth <= 5; depth++) { g_pScene->CreateLink(pSegments[x][z], pSegments[(x + depth) % SEG_AXIS_NUM][z], Vector(depth * 12.0f, 0.0f, 0.0f)); g_pScene->CreateLink(pSegments[x][z], pSegments[x][(z + depth) % SEG_AXIS_NUM], Vector(0.0, 0.0f, depth * 12.0f)); } } } // Set focus g_pScene->SetFocusSegment(pSegments[0][0]); #endif /* for (int i = -4; i<4; i++) { for (int j = -10; j<10; j++) { for (int k = -4; k<4; k++) { Entity* pCube = g_pScene->CreateEntity(); pCube->SetPosition(0.75f * Vector(i,j,k)); MeshComponent* pMesh = new MeshComponent(pCube); pMesh->SetMeshResource("cube.nfm"); BodyComponent* pBody = new BodyComponent(pCube); pBody->SetMass(1.0f); pBody->EnablePhysics((CollisionShape*)Engine_GetResource(Mesh::COLLISION_SHAPE, "shape_box")); } } } //set ambient & background color envDesc.m_AmbientLight = Vector(0.001f, 0.001f, 0.001f, 0.0f); envDesc.m_BackgroundColor = Vector(0.0f, 0.0f, 0.0f, 0.0f); g_pScene->SetEnvironment(&envDesc); // SUNLIGHT Entity* pDirLightEnt = g_pScene->CreateEntity(); XOrientation orient; orient.x = Vector(0.0f, -0.0f, -0.0f, 0.0f); orient.z = Vector(-0.5f, -1.1f, 1.2f, 0.0f); orient.y = Vector(0.0f, 1.0f, 0.0f, 0.0f); pDirLightEnt->SetOrientation(&orient); DirLightDesc dirLight; dirLight.m_Far = 100.0f; dirLight.m_Splits = 4; dirLight.m_LightDist = 1000.0f; LightComponent* pDirLight = new LightComponent(pDirLightEnt); pDirLight->SetDirLight(&dirLight); pDirLight->SetColor(Float3(2.2, 2, 1.8)); pDirLight->SetShadowMap(1024); pFirstWindow->cameraEntity->SetPosition(Vector(0.0f, 1.6f, -20.0f, 0.0f)); */ // message loop DrawRequest drawRequests[2]; Common::Timer timer; timer.Start(); while (!pWindow->isClosed()) { //measure delta time g_DeltaTime = (float)timer.Stop(); timer.Start(); UpdateRequest updateReq; updateReq.pScene = g_pScene; updateReq.deltaTime = g_DeltaTime; pWindow->processMessages(); pWindow->UpdateCamera(); drawRequests[0].deltaTime = g_DeltaTime; drawRequests[0].pView = pWindow->view; drawRequests[1].deltaTime = g_DeltaTime; drawRequests[1].pView = g_pSecondaryCameraView; EngineAdvance(drawRequests, 2, &updateReq, 1); ProcessSceneEvents(); // print focus segment name wchar_t str[128]; Segment* pFocus = g_pScene->GetFocusSegment(); swprintf(str, L"NFEngine Demo (%S) - focus: %S", PLATFORM_STR, (pFocus != 0) ? pFocus->GetName() : "NONE"); pWindow->setTitle(str); } // for testing purposes Common::FileOutputStream test_stream("test.xml"); g_pScene->Serialize(&test_stream, SerializationFormat::Xml, false); EngineDeleteScene(g_pScene); delete pWindow; EngineRelease(); //detect memory leaks #ifdef _DEBUG _CrtDumpMemoryLeaks(); #endif return 0; }
// Apply Frame Limit and Display FPS info // This should only be called from VI void VideoThrottle() { u32 TargetVPS = (SConfig::GetInstance().m_Framelimit > 2) ? (SConfig::GetInstance().m_Framelimit - 1) * 5 : VideoInterface::TargetRefreshRate; // Disable the frame-limiter when the throttle (Tab) key is held down. Audio throttle: m_Framelimit = 2 if (SConfig::GetInstance().m_Framelimit && SConfig::GetInstance().m_Framelimit != 2 && !Host_GetKeyState('\t')) { u32 frametime = ((SConfig::GetInstance().b_UseFPS)? Common::AtomicLoad(DrawnFrame) : DrawnVideo) * 1000 / TargetVPS; u32 timeDifference = (u32)Timer.GetTimeDifference(); if (timeDifference < frametime) { Common::SleepCurrentThread(frametime - timeDifference - 1); } while ((u32)Timer.GetTimeDifference() < frametime) Common::YieldCPU(); //Common::SleepCurrentThread(1); } // Update info per second u32 ElapseTime = (u32)Timer.GetTimeDifference(); if ((ElapseTime >= 1000 && DrawnVideo > 0) || g_requestRefreshInfo) { g_requestRefreshInfo = false; SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; if (ElapseTime == 0) ElapseTime = 1; u32 FPS = Common::AtomicLoad(DrawnFrame) * 1000 / ElapseTime; u32 VPS = DrawnVideo * 1000 / ElapseTime; u32 Speed = DrawnVideo * (100 * 1000) / (VideoInterface::TargetRefreshRate * ElapseTime); // Settings are shown the same for both extended and summary info std::string SSettings = StringFromFormat("%s %s", cpu_core_base->GetName(), _CoreParameter.bCPUThread ? "DC" : "SC"); // Use extended or summary information. The summary information does not print the ticks data, // that's more of a debugging interest, it can always be optional of course if someone is interested. //#define EXTENDED_INFO #ifdef EXTENDED_INFO u64 newTicks = CoreTiming::GetTicks(); u64 newIdleTicks = CoreTiming::GetIdleTicks(); u64 diff = (newTicks - ticks) / 1000000; u64 idleDiff = (newIdleTicks - idleTicks) / 1000000; ticks = newTicks; idleTicks = newIdleTicks; float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100; std::string SFPS = StringFromFormat("FPS: %u - VPS: %u - SPEED: %u%%", FPS, VPS, Speed); SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)", _CoreParameter.bSkipIdle ? "~" : "", (int)(diff), (int)(diff - idleDiff), (int)(idleDiff), SystemTimers::GetTicksPerSecond() / 1000000, _CoreParameter.bSkipIdle ? "~" : "", TicksPercentage); #else // Summary information std::string SFPS; if (Movie::IsPlayingInput()) SFPS = StringFromFormat("VI: %u/%u - Frame: %u/%u - FPS: %u - VPS: %u - SPEED: %u%%", (u32)Movie::g_currentFrame, (u32)Movie::g_totalFrames, (u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount, FPS, VPS, Speed); else if (Movie::IsRecordingInput()) SFPS = StringFromFormat("VI: %u - Frame: %u - FPS: %u - VPS: %u - SPEED: %u%%", (u32)Movie::g_currentFrame, (u32)Movie::g_currentInputCount, FPS, VPS, Speed); else SFPS = StringFromFormat("FPS: %u - VPS: %u - SPEED: %u%%", FPS, VPS, Speed); #endif // This is our final "frame counter" string std::string SMessage = StringFromFormat("%s | %s", SSettings.c_str(), SFPS.c_str()); std::string TMessage = StringFromFormat("%s | ", scm_rev_str) + SMessage; // Show message g_video_backend->UpdateFPSDisplay(SMessage.c_str()); if (_CoreParameter.bRenderToMain && SConfig::GetInstance().m_InterfaceStatusbar) { Host_UpdateStatusBar(SMessage.c_str()); Host_UpdateTitle(scm_rev_str); } else Host_UpdateTitle(TMessage.c_str()); // Reset counter Timer.Update(); Common::AtomicStore(DrawnFrame, 0); DrawnVideo = 0; } DrawnVideo++; }
// Update the time void Pause() { ReRecTimer.Update(); }