diff --git a/Core/CoreParameter.h b/Core/CoreParameter.h index 2cf0112055..27acc60e36 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -33,7 +33,7 @@ enum GPUCore { // PSP_CoreParameter() struct CoreParameter { - CoreParameter() : collectEmuLog(0), unthrottle(false), fpsLimit(0), updateRecent(true) {} + CoreParameter() : collectEmuLog(0), unthrottle(false), fpsLimit(0), updateRecent(true), freezeNext(false), frozen(false) {} CPUCore cpuCore; GPUCore gpuCore; bool enableSound; // there aren't multiple sound cores. @@ -64,4 +64,8 @@ struct CoreParameter { int fpsLimit; bool updateRecent; + + // Freeze-frame. For nvidia perfhud profiling. Developers only. + bool freezeNext; + bool frozen; }; diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 8af3a5bc0e..cf545f8675 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -65,6 +65,19 @@ namespace SaveState void *cbUserData; }; + CChunkFileReader::Error SaveToRam(std::vector &data) { + SaveStart state; + size_t sz = CChunkFileReader::MeasurePtr(state); + if (data.size() < sz) + data.resize(sz); + return CChunkFileReader::SavePtr(&data[0], state); + } + + CChunkFileReader::Error LoadFromRam(std::vector &data) { + SaveStart state; + return CChunkFileReader::LoadPtr(&data[0], state); + } + struct StateRingbuffer { StateRingbuffer(int size) : first_(0), next_(0), size_(size) @@ -78,12 +91,7 @@ namespace SaveState if ((next_ % size_) == first_) ++first_; - SaveStart state; - size_t sz = CChunkFileReader::MeasurePtr(state); - if (states_[n].size() < sz) - states_[n].resize(sz); - - return CChunkFileReader::SavePtr(&states_[n][0], state); + return SaveToRam(states_[n]); } CChunkFileReader::Error Restore() @@ -96,8 +104,7 @@ namespace SaveState if (states_[n].empty()) return CChunkFileReader::ERROR_BAD_FILE; - SaveStart state; - return CChunkFileReader::LoadPtr(&states_[n][0], state); + return LoadFromRam(states_[n]); } void Clear() diff --git a/Core/SaveState.h b/Core/SaveState.h index 65eccdb9ae..808597580b 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -17,7 +17,7 @@ #include -class PointerWrap; +#include "ChunkFile.h" namespace SaveState { @@ -44,6 +44,9 @@ namespace SaveState // Warning: callback will be called on a different thread. void Save(const std::string &filename, Callback callback = 0, void *cbUserData = 0); + CChunkFileReader::Error SaveToRam(std::vector &state); + CChunkFileReader::Error LoadFromRam(std::vector &state); + // For testing / automated tests. Runs a save state verification pass (async.) // Warning: callback will be called on a different thread. void Verify(Callback callback = 0, void *cbUserData = 0); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 891572d13e..01b1f15c38 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -29,6 +29,7 @@ #include "Common/LogManager.h" #include "Core/MemMap.h" #include "Core/Config.h" +#include "Core/CoreParameter.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "ext/disarm.h" @@ -49,6 +50,7 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { parent->Add(new Choice("Log Channels"))->OnClick.Handle(this, &DevMenu::OnLogConfig); parent->Add(new Choice("Developer Tools"))->OnClick.Handle(this, &DevMenu::OnDeveloperTools); parent->Add(new Choice("Jit Compare"))->OnClick.Handle(this, &DevMenu::OnJitCompare); + parent->Add(new Choice("Toggle Freeze"))->OnClick.Handle(this, &DevMenu::OnFreezeFrame); } UI::EventReturn DevMenu::OnLogConfig(UI::EventParams &e) { @@ -66,6 +68,15 @@ UI::EventReturn DevMenu::OnJitCompare(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DevMenu::OnFreezeFrame(UI::EventParams &e) { + if (PSP_CoreParameter().frozen) { + PSP_CoreParameter().frozen = false; + } else { + PSP_CoreParameter().freezeNext = true; + } + return UI::EVENT_DONE; +} + void DevMenu::dialogFinished(const Screen *dialog, DialogResult result) { // Close when a subscreen got closed. // TODO: a bug in screenmanager causes this not to work here. diff --git a/UI/DevScreens.h b/UI/DevScreens.h index ed634236dd..487b3f445b 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -38,6 +38,7 @@ public: protected: UI::EventReturn OnLogConfig(UI::EventParams &e); UI::EventReturn OnJitCompare(UI::EventParams &e); + UI::EventReturn OnFreezeFrame(UI::EventParams &e); UI::EventReturn OnDeveloperTools(UI::EventParams &e); }; @@ -84,4 +85,4 @@ private: UI::LinearLayout *leftDisasm_; UI::LinearLayout *rightDisasm_; -}; \ No newline at end of file +}; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 1da271fd3d..17fbabbc00 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -454,7 +454,7 @@ void EmuScreen::update(InputState &input) { PSP_CoreParameter().pixelHeight = pixel_yres; UpdateUIState(UISTATE_INGAME); - + if (errorMessage_.size()) { I18NCategory *g = GetI18NCategory("Error"); std::string errLoadingFile = g->T("Error loading file"); @@ -482,8 +482,7 @@ void EmuScreen::update(InputState &input) { // TODO: Make into an axis #ifdef USING_GLES2 if (g_Config.bAccelerometerToAnalogHoriz) { - - //get the "base" coordinate system which is setup by the calibration system + // Get the "base" coordinate system which is setup by the calibration system float base_x = g_Config.fTiltBaseX; float base_y = g_Config.fTiltBaseY; @@ -513,7 +512,6 @@ void EmuScreen::update(InputState &input) { leftstick_y += clamp1(delta_y); __CtrlSetAnalogY(clamp1(leftstick_y), CTRL_STICK_LEFT); - } #endif @@ -533,6 +531,17 @@ void EmuScreen::render() { if (invalid_) return; + if (PSP_CoreParameter().freezeNext) { + PSP_CoreParameter().frozen = true; + PSP_CoreParameter().freezeNext = false; + SaveState::SaveToRam(freezeState_); + } else if (PSP_CoreParameter().frozen) { + if (CChunkFileReader::ERROR_NONE != SaveState::LoadFromRam(freezeState_)) { + ERROR_LOG(HLE, "Failed to load freeze state. Unfreezing."); + PSP_CoreParameter().frozen = false; + } + } + // Reapply the graphics state of the PSP ReapplyGfxState(); diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 890f692b3f..5fe07f38d8 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include "ui/screen.h" @@ -56,7 +57,7 @@ private: void setVKeyAnalogY(int stick, int virtualKeyMin, int virtualKeyMax); void autoLoad(); - + bool booted_; std::string gamePath_; @@ -69,4 +70,7 @@ private: // To track mappable virtual keys. We can have as many as we want. bool virtKeys[VIRTKEY_COUNT]; + + // In-memory save state used for freezeFrame, which is useful for debugging. + std::vector freezeState_; }; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index dbd658f5b9..c97f4ca372 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -589,7 +589,7 @@ void NativeRender() { glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // Clearing the screen at the start of the frame is an optimization for tiled mobile GPUs, as it then doesn't need to keep it around between frames. - glClearColor(0,0,0,1); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glstate.viewport.set(0, 0, pixel_xres, pixel_yres); @@ -601,8 +601,9 @@ void NativeRender() { glUniformMatrix4fv(UIShader_Get()->u_worldviewproj, 1, GL_FALSE, ortho.getReadPtr()); screenManager->render(); - if (screenManager->getUIContext()->Text()) + if (screenManager->getUIContext()->Text()) { screenManager->getUIContext()->Text()->OncePerFrame(); + } if (g_TakeScreenshot) { TakeScreenshot();