From 783677633797c65d5b7c5c39921c4327d98bbb6e Mon Sep 17 00:00:00 2001 From: iota97 Date: Wed, 13 Nov 2019 18:55:18 +0100 Subject: [PATCH 01/52] Squash commit --- Core/Config.cpp | 6 ++ Core/Config.h | 8 ++ UI/GamepadEmu.cpp | 143 +++++++++++++++++++++++++++- UI/GamepadEmu.h | 21 +++- UI/TouchControlVisibilityScreen.cpp | 62 ++++++++++-- UI/TouchControlVisibilityScreen.h | 6 ++ 6 files changed, 231 insertions(+), 15 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 3c9385a3f1..99989283b3 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -454,6 +454,12 @@ static ConfigSetting generalSettings[] = { ConfigSetting("GridView2", &g_Config.bGridView2, true), ConfigSetting("GridView3", &g_Config.bGridView3, false), ConfigSetting("ComboMode", &g_Config.iComboMode, 0), + ConfigSetting("RightAnalogUp", &g_Config.iRightAnalogUp, 0), + ConfigSetting("RightAnalogDown", &g_Config.iRightAnalogDown, 0), + ConfigSetting("RightAnalogLeft", &g_Config.iRightAnalogLeft, 0), + ConfigSetting("RightAnalogRight", &g_Config.iRightAnalogRight, 0), + ConfigSetting("RightAnalogPress", &g_Config.iRightAnalogPress, 0), + ConfigSetting("RightAnalogCustom", &g_Config.bRightAnalogCustom, false), // "default" means let emulator decide, "" means disable. ConfigSetting("ReportingHost", &g_Config.sReportHost, "default"), diff --git a/Core/Config.h b/Core/Config.h index 6798dc2d31..8b96d075b4 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -271,6 +271,14 @@ public: //Combo key screen flag int iComboMode; + // Right analog binding + int iRightAnalogUp; + int iRightAnalogDown; + int iRightAnalogLeft; + int iRightAnalogRight; + int iRightAnalogPress; + bool bRightAnalogCustom; + // Disable diagonals bool bDisableDpadDiagonals; bool bGamepadOnlyFocused; diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 421914a7b2..90e45f6a1b 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -29,6 +29,7 @@ #include "gfx/texture_atlas.h" #include "math/math_util.h" #include "ui/ui_context.h" +#include "Core/Util/AudioFormat.h" // for clamp_u8 static u32 GetButtonColor() { return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080; @@ -461,6 +462,140 @@ void PSPStick::ProcessTouch(float x, float y, bool down) { } } +PSPCustomStick::PSPCustomStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams) + : PSPStick(bgImg, stickImg, stickDownImg, -1, scale, layoutParams) { + posX_ = clamp_u8((int)ceilf(127.5f)); + posY_ = clamp_u8((int)ceilf(127.5f)); +} + +void PSPCustomStick::Draw(UIContext &dc) { + float opacity = GetButtonOpacity(); + if (opacity <= 0.0f) + return; + + if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) { + opacity *= 1.35f; + } + + uint32_t colorBg = colorAlpha(GetButtonColor(), opacity); + uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f); + uint32_t color = colorAlpha(0x808080, opacity); + + if (centerX_ < 0.0f) { + centerX_ = bounds_.centerX(); + centerY_ = bounds_.centerY(); + } + + float stickX = centerX_; + float stickY = centerY_; + + float dx, dy; + dx = (posX_ - 127.5f) / 127.5f; + dy = -(posY_ - 127.5f) / 127.5f; + + dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER); + if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_) + dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_, downBg, ALIGN_CENTER); + dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_, colorBg, ALIGN_CENTER); +} + +void PSPCustomStick::Touch(const TouchInput &input) { + GamepadView::Touch(input); + if (input.flags & TOUCH_RELEASE_ALL) { + dragPointerId_ = -1; + centerX_ = bounds_.centerX(); + centerY_ = bounds_.centerY(); + posX_ = clamp_u8((int)ceilf(127.5f)); + posY_ = clamp_u8((int)ceilf(127.5f)); + return; + } + if (input.flags & TOUCH_DOWN) { + if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) { + if (g_Config.bAutoCenterTouchAnalog) { + centerX_ = input.x; + centerY_ = input.y; + } else { + centerX_ = bounds_.centerX(); + centerY_ = bounds_.centerY(); + } + dragPointerId_ = input.id; + ProcessTouch(input.x, input.y, true); + } + } + if (input.flags & TOUCH_MOVE) { + if (input.id == dragPointerId_) { + ProcessTouch(input.x, input.y, true); + } + } + if (input.flags & TOUCH_UP) { + if (input.id == dragPointerId_) { + dragPointerId_ = -1; + centerX_ = bounds_.centerX(); + centerY_ = bounds_.centerY(); + ProcessTouch(input.x, input.y, false); + } + } +} + +void PSPCustomStick::ProcessTouch(float x, float y, bool down) { + static const int button[16] = {CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_SQUARE, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, CTRL_START, CTRL_SELECT}; + + if (down && centerX_ >= 0.0f) { + float inv_stick_size = 1.0f / (stick_size_ * scale_); + + float dx = (x - centerX_) * inv_stick_size; + float dy = (y - centerY_) * inv_stick_size; + + dx = std::min(1.0f, std::max(-1.0f, dx)); + dy = std::min(1.0f, std::max(-1.0f, dy)); + + if (g_Config.iRightAnalogRight != 0) { + if (dx > 0.5f) + __CtrlButtonDown(button[g_Config.iRightAnalogRight-1]); + else + __CtrlButtonUp(button[g_Config.iRightAnalogRight-1]); + } + if (g_Config.iRightAnalogLeft != 0) { + if (dx < -0.5f) + __CtrlButtonDown(button[g_Config.iRightAnalogLeft-1]); + else + __CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]); + } + if (g_Config.iRightAnalogUp != 0) { + if (dy < -0.5f) + __CtrlButtonDown(button[g_Config.iRightAnalogUp-1]); + else + __CtrlButtonUp(button[g_Config.iRightAnalogUp-1]); + } + if (g_Config.iRightAnalogDown != 0) { + if (dy > 0.5f) + __CtrlButtonDown(button[g_Config.iRightAnalogDown-1]); + else + __CtrlButtonUp(button[g_Config.iRightAnalogDown-1]); + } + if (g_Config.iRightAnalogPress != 0) + __CtrlButtonDown(button[g_Config.iRightAnalogPress-1]); + + posX_ = clamp_u8((int)ceilf(dx * 127.5f + 127.5f)); + posY_ = clamp_u8((int)ceilf(dy * 127.5f + 127.5f)); + + } else { + if (g_Config.iRightAnalogUp != 0) + __CtrlButtonUp(button[g_Config.iRightAnalogUp-1]); + if (g_Config.iRightAnalogDown != 0) + __CtrlButtonUp(button[g_Config.iRightAnalogDown-1]); + if (g_Config.iRightAnalogLeft != 0) + __CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]); + if (g_Config.iRightAnalogRight != 0) + __CtrlButtonUp(button[g_Config.iRightAnalogRight-1]); + if (g_Config.iRightAnalogPress != 0) + __CtrlButtonUp(button[g_Config.iRightAnalogPress-1]); + + posX_ = clamp_u8((int)ceilf(127.5f)); + posY_ = clamp_u8((int)ceilf(127.5f)); + } +} + void InitPadLayout(float xres, float yres, float globalScale) { const float scale = globalScale; const int halfW = xres / 2; @@ -679,8 +814,12 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { if (g_Config.touchAnalogStick.show) root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick))); - if (g_Config.touchRightAnalogStick.show) - root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); + if (g_Config.touchRightAnalogStick.show) { + if (g_Config.bRightAnalogCustom) + root->Add(new PSPCustomStick(stickBg, stickImage, ImageID("I_STICK"), g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); + else + root->Add(new PSPStick(stickBg, stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); + } addComboKey(g_Config.iCombokey0, g_Config.bComboToggle0, roundImage, ImageID("I_ROUND"), comboKeyImages[0], g_Config.touchCombo0); addComboKey(g_Config.iCombokey1, g_Config.bComboToggle1, roundImage, ImageID("I_ROUND"), comboKeyImages[1], g_Config.touchCombo1); diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h index 83c985cba9..50aef8f526 100644 --- a/UI/GamepadEmu.h +++ b/UI/GamepadEmu.h @@ -147,9 +147,7 @@ public: void Draw(UIContext &dc) override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; -private: - void ProcessTouch(float x, float y, bool down); - +protected: int dragPointerId_; ImageID bgImg_; ImageID stickImageIndex_; @@ -161,6 +159,23 @@ private: float centerX_; float centerY_; + +private: + void ProcessTouch(float x, float y, bool down); +}; + +class PSPCustomStick : public PSPStick { +public: + PSPCustomStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams); + + void Touch(const TouchInput &input) override; + void Draw(UIContext &dc) override; + +private: + void ProcessTouch(float x, float y, bool down); + + u8 posX_; + u8 posY_; }; //initializes the layout from Config. if a default layout does not exist, diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 04b68b4abd..ef80c0b1c9 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -70,6 +70,7 @@ void TouchControlVisibilityScreen::CreateViews() { gridsettings.fillCells = true; GridLayout *grid = vert->Add(new GridLayout(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); + static const char* rightAnalogKey = "Right Analog Stick (tap to customize)"; toggles_.clear(); toggles_.push_back({ "Circle", &g_Config.bShowTouchCircle, ImageID("I_CIRCLE") }); toggles_.push_back({ "Cross", &g_Config.bShowTouchCross, ImageID("I_CROSS") }); @@ -81,7 +82,7 @@ void TouchControlVisibilityScreen::CreateViews() { toggles_.push_back({ "Select", &g_Config.touchSelectKey.show, ImageID("I_SELECT") }); toggles_.push_back({ "Dpad", &g_Config.touchDpad.show, ImageID::invalid() }); toggles_.push_back({ "Analog Stick", &g_Config.touchAnalogStick.show, ImageID::invalid() }); - toggles_.push_back({ "Right Analog Stick\n(not used by most games)", &g_Config.touchRightAnalogStick.show, ImageID::invalid() }); + toggles_.push_back({ rightAnalogKey, &g_Config.touchRightAnalogStick.show, ImageID::invalid() }); toggles_.push_back({ "Unthrottle", &g_Config.touchUnthrottleKey.show, ImageID::invalid() }); toggles_.push_back({ "Combo0", &g_Config.touchCombo0.show, ImageID("I_1") }); toggles_.push_back({ "Combo1", &g_Config.touchCombo1.show, ImageID("I_2") }); @@ -93,7 +94,6 @@ void TouchControlVisibilityScreen::CreateViews() { toggles_.push_back({ "RapidFire", &g_Config.touchRapidFireKey.show, ImageID::invalid() }); auto mc = GetI18NCategory("MappableControls"); - for (auto toggle : toggles_) { LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); row->SetSpacing(0); @@ -101,16 +101,20 @@ void TouchControlVisibilityScreen::CreateViews() { CheckBox *checkbox = new CheckBox(toggle.show, "", "", new LinearLayoutParams(50, WRAP_CONTENT)); row->Add(checkbox); - Choice *choice; - if (toggle.img.isValid()) { - choice = new CheckBoxChoice(toggle.img, checkbox, new LinearLayoutParams(1.0f)); + if (toggle.key == rightAnalogKey) { + Choice *rightAnalog = new Choice(co->T(rightAnalogKey), "", false, new LinearLayoutParams(1.0f)); + rightAnalog->SetCentered(true); + row->Add(rightAnalog)->OnClick.Handle(this, &TouchControlVisibilityScreen::RightAnalogBindScreen); } else { - choice = new CheckBoxChoice(mc->T(toggle.key), checkbox, new LinearLayoutParams(1.0f)); + Choice *choice; + if (toggle.img.isValid()) { + choice = new CheckBoxChoice(toggle.img, checkbox, new LinearLayoutParams(1.0f)); + } else { + choice = new CheckBoxChoice(mc->T(toggle.key), checkbox, new LinearLayoutParams(1.0f)); + } + choice->SetCentered(true); + row->Add(choice); } - - choice->SetCentered(true); - - row->Add(choice); grid->Add(row); } } @@ -119,6 +123,38 @@ void TouchControlVisibilityScreen::onFinish(DialogResult result) { g_Config.Save("TouchControlVisibilityScreen::onFinish"); } +void RightAnalogMappingScreen::CreateViews() { + using namespace UI; + + auto di = GetI18NCategory("Dialog"); + auto co = GetI18NCategory("Controls"); + + root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 10)); + root_->Add(back)->OnClick.Handle(this, &UIScreen::OnBack); + TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, new AnchorLayoutParams(10, 0, 10, 0, false)); + root_->Add(tabHolder); + ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL); + tabHolder->AddTab(co->T("Binds"), rightPanel); + LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT))); + vert->SetSpacing(0); + + static const char *rightAnalogButton[] = {"None", "Left trigger", "Right trigger", "Square", "Triangle", "Circle", "Cross", "D-PAD Up", "D-PAD Down", "D-PAD Left", "D-PAD Right", "Start", "Select"}; + + vert->Add(new CheckBox(&g_Config.bRightAnalogCustom, co->T("Use custom right analog"))); + PopupMultiChoice *rightAnalogUp = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogUp, co->T("Right analog up"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager())); + PopupMultiChoice *rightAnalogDown = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogDown, co->T("Right analog down"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager())); + PopupMultiChoice *rightAnalogLeft = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogLeft, co->T("Right analog left"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager())); + PopupMultiChoice *rightAnalogRight = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogRight, co->T("Right analog right"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager())); + PopupMultiChoice *rightAnalogPress = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogPress, co->T("Keep this button pressed when right analog is pressed"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), co->GetName(), screenManager())); + vert->Add(new CheckBox(&g_Config.touchRightAnalogStick.show, co->T("Show right analog"))); + rightAnalogUp->SetEnabledPtr(&g_Config.bRightAnalogCustom); + rightAnalogDown->SetEnabledPtr(&g_Config.bRightAnalogCustom); + rightAnalogLeft->SetEnabledPtr(&g_Config.bRightAnalogCustom); + rightAnalogRight->SetEnabledPtr(&g_Config.bRightAnalogCustom); + rightAnalogPress->SetEnabledPtr(&g_Config.bRightAnalogCustom); +} + UI::EventReturn TouchControlVisibilityScreen::OnToggleAll(UI::EventParams &e) { for (auto toggle : toggles_) { *toggle.show = nextToggleAll_; @@ -128,6 +164,12 @@ UI::EventReturn TouchControlVisibilityScreen::OnToggleAll(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn TouchControlVisibilityScreen::RightAnalogBindScreen(UI::EventParams &e) { + screenManager()->push(new RightAnalogMappingScreen()); + + return UI::EVENT_DONE; +} + UI::EventReturn CheckBoxChoice::HandleClick(UI::EventParams &e) { checkbox_->Toggle(); diff --git a/UI/TouchControlVisibilityScreen.h b/UI/TouchControlVisibilityScreen.h index 82efd442b3..895078626b 100644 --- a/UI/TouchControlVisibilityScreen.h +++ b/UI/TouchControlVisibilityScreen.h @@ -37,8 +37,14 @@ public: protected: UI::EventReturn OnToggleAll(UI::EventParams &e); + UI::EventReturn RightAnalogBindScreen(UI::EventParams &e); private: std::vector toggles_; bool nextToggleAll_ = true; }; + +class RightAnalogMappingScreen : public UIDialogScreenWithBackground { +public: + void CreateViews() override; +}; From f460151f39b123af6fc102b582275e3b4054a04b Mon Sep 17 00:00:00 2001 From: iota97 Date: Thu, 5 Mar 2020 18:29:35 +0100 Subject: [PATCH 02/52] u8_clamp clean up --- UI/GamepadEmu.cpp | 19 ++++++++----------- UI/GamepadEmu.h | 4 ++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 90e45f6a1b..355475908a 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -29,7 +29,6 @@ #include "gfx/texture_atlas.h" #include "math/math_util.h" #include "ui/ui_context.h" -#include "Core/Util/AudioFormat.h" // for clamp_u8 static u32 GetButtonColor() { return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080; @@ -464,8 +463,6 @@ void PSPStick::ProcessTouch(float x, float y, bool down) { PSPCustomStick::PSPCustomStick(ImageID bgImg, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams) : PSPStick(bgImg, stickImg, stickDownImg, -1, scale, layoutParams) { - posX_ = clamp_u8((int)ceilf(127.5f)); - posY_ = clamp_u8((int)ceilf(127.5f)); } void PSPCustomStick::Draw(UIContext &dc) { @@ -490,8 +487,8 @@ void PSPCustomStick::Draw(UIContext &dc) { float stickY = centerY_; float dx, dy; - dx = (posX_ - 127.5f) / 127.5f; - dy = -(posY_ - 127.5f) / 127.5f; + dx = posX_; + dy = -posY_; dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER); if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_) @@ -505,8 +502,8 @@ void PSPCustomStick::Touch(const TouchInput &input) { dragPointerId_ = -1; centerX_ = bounds_.centerX(); centerY_ = bounds_.centerY(); - posX_ = clamp_u8((int)ceilf(127.5f)); - posY_ = clamp_u8((int)ceilf(127.5f)); + posX_ = 0.0f; + posY_ = 0.0f; return; } if (input.flags & TOUCH_DOWN) { @@ -576,8 +573,8 @@ void PSPCustomStick::ProcessTouch(float x, float y, bool down) { if (g_Config.iRightAnalogPress != 0) __CtrlButtonDown(button[g_Config.iRightAnalogPress-1]); - posX_ = clamp_u8((int)ceilf(dx * 127.5f + 127.5f)); - posY_ = clamp_u8((int)ceilf(dy * 127.5f + 127.5f)); + posX_ = dx; + posY_ = dy; } else { if (g_Config.iRightAnalogUp != 0) @@ -591,8 +588,8 @@ void PSPCustomStick::ProcessTouch(float x, float y, bool down) { if (g_Config.iRightAnalogPress != 0) __CtrlButtonUp(button[g_Config.iRightAnalogPress-1]); - posX_ = clamp_u8((int)ceilf(127.5f)); - posY_ = clamp_u8((int)ceilf(127.5f)); + posX_ = 0.0f; + posY_ = 0.0f; } } diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h index 50aef8f526..f306c09163 100644 --- a/UI/GamepadEmu.h +++ b/UI/GamepadEmu.h @@ -174,8 +174,8 @@ public: private: void ProcessTouch(float x, float y, bool down); - u8 posX_; - u8 posY_; + float posX_ = 0.0f; + float posY_ = 0.0f; }; //initializes the layout from Config. if a default layout does not exist, From e673d746465704836fd949ef36767a4c66489463 Mon Sep 17 00:00:00 2001 From: AreaScout Date: Mon, 11 Mar 2019 15:06:06 +0000 Subject: [PATCH 03/52] Fix: touchscreen evens on upcomming SDL2 versions which includes the same functionality but inside the library --- SDL/SDLMain.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 82651123ea..5a8cde620f 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -474,6 +474,8 @@ int main(int argc, char *argv[]) { printf("DEBUG: Vulkan is not available, not using Vulkan.\n"); } + SDL_version compiled; + SDL_version linked; int set_xres = -1; int set_yres = -1; int w = 0, h = 0; @@ -531,6 +533,15 @@ int main(int argc, char *argv[]) { } } + SDL_VERSION(&compiled); + SDL_GetVersion(&linked); + printf("Info: We compiled against SDL version %d.%d.%d", compiled.major, compiled.minor, compiled.patch); + if (compiled.minor != linked.minor || compiled.patch != linked.patch) { + printf(", but we are linking against SDL version %d.%d.%d., be aware that this can lead to unexpected behaviors\n", linked.major, linked.minor, linked.patch); + } else { + printf(" and we are linking against SDL version %d.%d.%d. :)\n", linked.major, linked.minor, linked.patch); + } + // Get the video info before doing anything else, so we don't get skewed resolution results. // TODO: support multiple displays correctly SDL_DisplayMode displayMode; @@ -842,6 +853,7 @@ int main(int argc, char *argv[]) { NativeKey(key); break; } +#if !SDL_VERSION_ATLEAST(2, 0, 10) // This behavior doesn't feel right on a macbook with a touchpad. #if !PPSSPP_PLATFORM(MAC) case SDL_FINGERMOTION: @@ -851,7 +863,6 @@ int main(int argc, char *argv[]) { touchEvent.motion.type = SDL_MOUSEMOTION; touchEvent.motion.timestamp = event.tfinger.timestamp; touchEvent.motion.windowID = SDL_GetWindowID(window); - touchEvent.motion.which = SDL_TOUCH_MOUSEID; touchEvent.motion.state = SDL_GetMouseState(NULL, NULL); touchEvent.motion.x = event.tfinger.x * w; touchEvent.motion.y = event.tfinger.y * h; @@ -868,7 +879,6 @@ int main(int argc, char *argv[]) { touchEvent.button.type = SDL_MOUSEBUTTONDOWN; touchEvent.button.timestamp = SDL_GetTicks(); touchEvent.button.windowID = SDL_GetWindowID(window); - touchEvent.button.which = SDL_TOUCH_MOUSEID; touchEvent.button.button = SDL_BUTTON_LEFT; touchEvent.button.state = SDL_PRESSED; touchEvent.button.clicks = 1; @@ -878,7 +888,6 @@ int main(int argc, char *argv[]) { touchEvent.motion.type = SDL_MOUSEMOTION; touchEvent.motion.timestamp = SDL_GetTicks(); touchEvent.motion.windowID = SDL_GetWindowID(window); - touchEvent.motion.which = SDL_TOUCH_MOUSEID; touchEvent.motion.x = event.tfinger.x * w; touchEvent.motion.y = event.tfinger.y * h; // Any real mouse cursor should also move @@ -897,7 +906,6 @@ int main(int argc, char *argv[]) { touchEvent.button.type = SDL_MOUSEBUTTONUP; touchEvent.button.timestamp = SDL_GetTicks(); touchEvent.button.windowID = SDL_GetWindowID(window); - touchEvent.button.which = SDL_TOUCH_MOUSEID; touchEvent.button.button = SDL_BUTTON_LEFT; touchEvent.button.state = SDL_RELEASED; touchEvent.button.clicks = 1; @@ -906,6 +914,7 @@ int main(int argc, char *argv[]) { SDL_PushEvent(&touchEvent); break; } +#endif #endif case SDL_MOUSEBUTTONDOWN: switch (event.button.button) { From a666635fa2a916180e380dca17667528e9485c6f Mon Sep 17 00:00:00 2001 From: iota97 Date: Fri, 15 May 2020 11:15:38 +0200 Subject: [PATCH 04/52] Post shader setting uniform --- Core/Config.cpp | 24 ++++++++++++++ Core/Config.h | 20 ++++++++++++ GPU/Common/PostShader.cpp | 41 ++++++++++++++++++++++- GPU/Common/PostShader.h | 21 ++++++++++++ GPU/Common/PresentationCommon.cpp | 9 ++++++ GPU/Common/PresentationCommon.h | 7 +++- GPU/Common/ShaderTranslation.cpp | 12 +++++++ UI/GameSettingsScreen.cpp | 14 ++++++-- UI/MiscScreens.cpp | 24 ++++++++++++++ assets/shaders/bloom.fsh | 12 +++---- assets/shaders/cartoon.fsh | 4 +-- assets/shaders/colorcorrection.fsh | 23 +++++++++++++ assets/shaders/defaultshaders.ini | 52 ++++++++++++++++++++++++++++++ assets/shaders/scanlines.fsh | 8 ++--- assets/shaders/sharpen.fsh | 6 ++-- 15 files changed, 258 insertions(+), 19 deletions(-) create mode 100644 assets/shaders/colorcorrection.fsh diff --git a/Core/Config.cpp b/Core/Config.cpp index 4096e3ca41..e402ff0783 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -768,6 +768,30 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("HardwareTessellation", &g_Config.bHardwareTessellation, false, true, true), ReportedConfigSetting("PostShader", &g_Config.sPostShaderName, "Off", true, true), + ReportedConfigSetting("PostShaderSettingName1", &g_Config.sPostShaderSettingName1, "", true, true), + ReportedConfigSetting("PostShaderSettingValue1", &g_Config.fPostShaderSettingValue1, 0.0f, true, true), + ConfigSetting("PostShaderSettingMaxValue1", &g_Config.fPostShaderMaxSettingValue1, 1.0f, true, true), + ConfigSetting("PostShaderSettingMinValue1", &g_Config.fPostShaderMinSettingValue1, -1.0f, true, true), + ConfigSetting("PostShaderSettingStep1", &g_Config.fPostShaderSettingStep1, 0.01f, true, true), + + ReportedConfigSetting("PostShaderSettingName2", &g_Config.sPostShaderSettingName2, "", true, true), + ReportedConfigSetting("PostShaderSettingValue2", &g_Config.fPostShaderSettingValue2, 0.0f, true, true), + ConfigSetting("PostShaderSettingMaxValue2", &g_Config.fPostShaderMaxSettingValue2, 1.0f, true, true), + ConfigSetting("PostShaderSettingMinValue2", &g_Config.fPostShaderMinSettingValue2, -1.0f, true, true), + ConfigSetting("PostShaderSettingStep2", &g_Config.fPostShaderSettingStep2, 0.01f, true, true), + + ReportedConfigSetting("PostShaderSettingName3", &g_Config.sPostShaderSettingName3, "", true, true), + ReportedConfigSetting("PostShaderSettingValue3", &g_Config.fPostShaderSettingValue3, 0.0f, true, true), + ConfigSetting("PostShaderSettingMaxValue3", &g_Config.fPostShaderMaxSettingValue3, 1.0f, true, true), + ConfigSetting("PostShaderSettingMinValue3", &g_Config.fPostShaderMinSettingValue3, -1.0f, true, true), + ConfigSetting("PostShaderSettingStep3", &g_Config.fPostShaderSettingStep3, 0.01f, true, true), + + ReportedConfigSetting("PostShaderSettingName4", &g_Config.sPostShaderSettingName4, "", true, true), + ReportedConfigSetting("PostShaderSettingValue4", &g_Config.fPostShaderSettingValue4, 0.0f, true, true), + ConfigSetting("PostShaderSettingMaxValue4", &g_Config.fPostShaderMaxSettingValue4, 1.0f, true, true), + ConfigSetting("PostShaderSettingMinValue4", &g_Config.fPostShaderMinSettingValue4, -1.0f, true, true), + ConfigSetting("PostShaderSettingStep4", &g_Config.fPostShaderSettingStep4, 0.01f, true, true), + ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true), ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false, true, true), ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true), diff --git a/Core/Config.h b/Core/Config.h index ebc2cfbaf6..b6917e44af 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -205,6 +205,26 @@ public: int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High bool bHardwareTessellation; std::string sPostShaderName; // Off for off. + std::string sPostShaderSettingName1; + float fPostShaderSettingValue1; + float fPostShaderMaxSettingValue1; + float fPostShaderMinSettingValue1; + float fPostShaderSettingStep1; + std::string sPostShaderSettingName2; + float fPostShaderSettingValue2; + float fPostShaderMaxSettingValue2; + float fPostShaderMinSettingValue2; + float fPostShaderSettingStep2; + std::string sPostShaderSettingName3; + float fPostShaderSettingValue3; + float fPostShaderMaxSettingValue3; + float fPostShaderMinSettingValue3; + float fPostShaderSettingStep3; + std::string sPostShaderSettingName4; + float fPostShaderSettingValue4; + float fPostShaderMaxSettingValue4; + float fPostShaderMinSettingValue4; + float fPostShaderSettingStep4; bool bGfxDebugOutput; bool bGfxDebugSplitSubmit; int iInflightFrames; diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index df30f76df7..1bbf62592c 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -44,6 +44,26 @@ void LoadPostShaderInfo(std::vector directories) { off.isUpscalingFilter = false; off.SSAAFilterLevel = 0; off.requires60fps = false; + off.settingName1 = ""; + off.settingValue1 = 0.0f; + off.minSettingValue1 = -1.0f; + off.maxSettingValue1 = 1.0f; + off.settingStep1 = 0.01f; + off.settingName2 = ""; + off.settingValue2 = 0.0f; + off.minSettingValue2 = -1.0f; + off.maxSettingValue2 = 1.0f; + off.settingStep2 = 0.01f; + off.settingName3 = ""; + off.settingValue3 = 0.0f; + off.minSettingValue3 = -1.0f; + off.maxSettingValue3 = 1.0f; + off.settingStep3 = 0.01f; + off.settingName4 = ""; + off.settingValue4 = 0.0f; + off.minSettingValue4 = -1.0f; + off.maxSettingValue4 = 1.0f; + off.settingStep4 = 0.01f; shaderInfo.push_back(off); for (size_t d = 0; d < directories.size(); d++) { @@ -90,7 +110,26 @@ void LoadPostShaderInfo(std::vector directories) { section.Get("Upscaling", &info.isUpscalingFilter, false); section.Get("SSAA", &info.SSAAFilterLevel, 0); section.Get("60fps", &info.requires60fps, false); - + section.Get("SettingName1", &info.settingName1, ""); + section.Get("SettingDefaultValue1", &info.settingValue1, 0.0f); + section.Get("SettingMinValue1", &info.minSettingValue1, -1.0f); + section.Get("SettingMaxValue1", &info.maxSettingValue1, 1.0f); + section.Get("SettingStep1", &info.settingStep1, 0.01f); + section.Get("SettingName2", &info.settingName2, ""); + section.Get("SettingDefaultValue2", &info.settingValue2, 0.0f); + section.Get("SettingMinValue2", &info.minSettingValue2, -1.0f); + section.Get("SettingMaxValue2", &info.maxSettingValue2, 1.0f); + section.Get("SettingStep2", &info.settingStep2, 0.01f); + section.Get("SettingName3", &info.settingName3, ""); + section.Get("SettingDefaultValue3", &info.settingValue3, 0.0f); + section.Get("SettingMinValue3", &info.minSettingValue3, -1.0f); + section.Get("SettingMaxValue3", &info.maxSettingValue3, 1.0f); + section.Get("SettingStep3", &info.settingStep3, 0.01f); + section.Get("SettingName4", &info.settingName4, ""); + section.Get("SettingDefaultValue4", &info.settingValue4, 0.0f); + section.Get("SettingMinValue4", &info.minSettingValue4, -1.0f); + section.Get("SettingMaxValue4", &info.maxSettingValue4, 1.0f); + section.Get("SettingStep4", &info.settingStep4, 0.01f); // Let's ignore shaders we can't support. TODO: Not a very good check if (gl_extensions.IsGLES && !gl_extensions.GLES3) { bool requiresIntegerSupport; diff --git a/GPU/Common/PostShader.h b/GPU/Common/PostShader.h index f848e14802..9be8063d77 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -41,6 +41,27 @@ struct ShaderInfo { // Force constant/max refresh for animated filters bool requires60fps; + std::string settingName1; + float settingValue1; + float maxSettingValue1; + float minSettingValue1; + float settingStep1; + std::string settingName2; + float settingValue2; + float maxSettingValue2; + float minSettingValue2; + float settingStep2; + std::string settingName3; + float settingValue3; + float maxSettingValue3; + float minSettingValue3; + float settingStep3; + std::string settingName4; + float settingValue4; + float maxSettingValue4; + float minSettingValue4; + float settingStep4; + // TODO: Add support for all kinds of fun options like mapping the depth buffer, // SRGB texture reads, multiple shaders chained, etc. diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index c107c56815..1f3a61aa3e 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -163,6 +163,11 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer // The shader translator tacks this onto our shaders, if we don't set it they render garbage. uniforms->gl_HalfPixel[0] = u_pixel_delta * 0.5f; uniforms->gl_HalfPixel[1] = v_pixel_delta * 0.5f; + + uniforms->setting1 = g_Config.fPostShaderSettingValue1; + uniforms->setting2 = g_Config.fPostShaderSettingValue2; + uniforms->setting3 = g_Config.fPostShaderSettingValue3; + uniforms->setting4 = g_Config.fPostShaderSettingValue4; } static std::string ReadShaderSrc(const std::string &filename) { @@ -214,6 +219,10 @@ bool PresentationCommon::UpdatePostShader() { { "u_pixelDelta", 2, 2, Draw::UniformType::FLOAT2, offsetof(PostShaderUniforms, pixelDelta) }, { "u_time", 3, 3, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, time) }, { "u_video", 4, 4, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, video) }, + { "u_setting1", 5, 5, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting1) }, + { "u_setting2", 6, 6, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting2) }, + { "u_setting3", 7, 7, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting3) }, + { "u_setting4", 8, 8, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting4) }, } }; Draw::Pipeline *pipeline = CreatePipeline({ vs, fs }, true, &postShaderDesc); if (!pipeline) diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index 967a6b0f2b..4b83b95442 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -31,7 +31,12 @@ struct CardboardSettings { struct PostShaderUniforms { float texelDelta[2]; float pixelDelta[2]; float time[4]; - float video; float pad[3]; + float video; + float setting1; + float setting2; + float setting3; + float setting4; + float pad[3]; // Used on Direct3D9. float gl_HalfPixel[4]; }; diff --git a/GPU/Common/ShaderTranslation.cpp b/GPU/Common/ShaderTranslation.cpp index 7f42a7b2b7..975652f3e2 100644 --- a/GPU/Common/ShaderTranslation.cpp +++ b/GPU/Common/ShaderTranslation.cpp @@ -87,6 +87,10 @@ cbuffer data : register(b0) { float2 u_pixelDelta; float4 u_time; float u_video; + float u_setting1; + float u_setting2; + float u_setting3; + float u_setting4; }; )"; @@ -102,6 +106,10 @@ layout (std140, set = 0, binding = 0) uniform Data { vec2 u_pixelDelta; vec4 u_time; float u_video; + float u_setting1; + float u_setting2; + float u_setting3; + float u_setting4; }; )"; @@ -111,6 +119,10 @@ float2 u_texelDelta : register(c1); float2 u_pixelDelta : register(c2); float4 u_time : register(c3); float u_video : register(c4); +float u_setting1 : register(c5); +float u_setting2 : register(c6); +float u_setting3 : register(c7); +float u_setting4 : register(c8); )"; // SPIRV-Cross' HLSL output has some deficiencies we need to work around. diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index b8486e6cbe..178ae2adac 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -290,6 +290,15 @@ void GameSettingsScreen::CreateViews() { return g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; }); + if (g_Config.sPostShaderSettingName1 != "") + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue1, g_Config.fPostShaderMinSettingValue1, g_Config.fPostShaderMaxSettingValue1, g_Config.sPostShaderSettingName1, g_Config.fPostShaderSettingStep1, screenManager())); + if (g_Config.sPostShaderSettingName2 != "") + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue2, g_Config.fPostShaderMinSettingValue2, g_Config.fPostShaderMaxSettingValue2, g_Config.sPostShaderSettingName2, g_Config.fPostShaderSettingStep2, screenManager())); + if (g_Config.sPostShaderSettingName3 != "") + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue3, g_Config.fPostShaderMinSettingValue3, g_Config.fPostShaderMaxSettingValue3, g_Config.sPostShaderSettingName3, g_Config.fPostShaderSettingStep3, screenManager())); + if (g_Config.sPostShaderSettingName4 != "") + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue4, g_Config.fPostShaderMinSettingValue4, g_Config.fPostShaderMaxSettingValue4, g_Config.sPostShaderSettingName4, g_Config.fPostShaderSettingStep4, screenManager())); + #if !defined(MOBILE_DEVICE) graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) { @@ -1413,12 +1422,13 @@ UI::EventReturn GameSettingsScreen::OnPostProcShader(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnPostProcShaderChange(UI::EventParams &e) { NativeMessageReceived("gpu_resized", ""); + RecreateViews(); // Update setting name return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnDeveloperTools(UI::EventParams &e) { -screenManager()->push(new DeveloperToolsScreen()); -return UI::EVENT_DONE; + screenManager()->push(new DeveloperToolsScreen()); + return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnRemoteISO(UI::EventParams &e) { diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 27fadc85fd..917e84d694 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -317,6 +317,30 @@ void PostProcScreen::OnCompleted(DialogResult result) { if (result != DR_OK) return; g_Config.sPostShaderName = shaders_[listView_->GetSelected()].section; + + g_Config.sPostShaderSettingName1 = shaders_[listView_->GetSelected()].settingName1; + g_Config.fPostShaderSettingValue1 = shaders_[listView_->GetSelected()].settingValue1; + g_Config.fPostShaderMaxSettingValue1 = shaders_[listView_->GetSelected()].maxSettingValue1; + g_Config.fPostShaderMinSettingValue1 = shaders_[listView_->GetSelected()].minSettingValue1; + g_Config.fPostShaderSettingStep1 = shaders_[listView_->GetSelected()].settingStep1; + + g_Config.sPostShaderSettingName2 = shaders_[listView_->GetSelected()].settingName2; + g_Config.fPostShaderSettingValue2 = shaders_[listView_->GetSelected()].settingValue2; + g_Config.fPostShaderMaxSettingValue2 = shaders_[listView_->GetSelected()].maxSettingValue2; + g_Config.fPostShaderMinSettingValue2 = shaders_[listView_->GetSelected()].minSettingValue2; + g_Config.fPostShaderSettingStep2 = shaders_[listView_->GetSelected()].settingStep2; + + g_Config.sPostShaderSettingName3 = shaders_[listView_->GetSelected()].settingName3; + g_Config.fPostShaderSettingValue3 = shaders_[listView_->GetSelected()].settingValue3; + g_Config.fPostShaderMaxSettingValue3 = shaders_[listView_->GetSelected()].maxSettingValue3; + g_Config.fPostShaderMinSettingValue3 = shaders_[listView_->GetSelected()].minSettingValue3; + g_Config.fPostShaderSettingStep3 = shaders_[listView_->GetSelected()].settingStep3; + + g_Config.sPostShaderSettingName4 = shaders_[listView_->GetSelected()].settingName4; + g_Config.fPostShaderSettingValue4 = shaders_[listView_->GetSelected()].settingValue4; + g_Config.fPostShaderMaxSettingValue4 = shaders_[listView_->GetSelected()].maxSettingValue4; + g_Config.fPostShaderMinSettingValue4 = shaders_[listView_->GetSelected()].minSettingValue4; + g_Config.fPostShaderSettingStep4 = shaders_[listView_->GetSelected()].settingStep4; } NewLanguageScreen::NewLanguageScreen(const std::string &title) : ListPopupScreen(title) { diff --git a/assets/shaders/bloom.fsh b/assets/shaders/bloom.fsh index c2450c2709..fd4cee62fa 100644 --- a/assets/shaders/bloom.fsh +++ b/assets/shaders/bloom.fsh @@ -10,8 +10,8 @@ precision mediump int; uniform sampler2D sampler0; varying vec2 v_texcoord0; -float amount = 0.60; // suitable range = 0.00 - 1.00 -float power = 0.5; // suitable range = 0.0 - 1.0 +uniform float u_setting1; // float amount = 0.60; // suitable range = 0.00 - 1.00 +uniform float u_setting2; // float power = 0.5; // suitable range = 0.0 - 1.0 void main() { @@ -21,9 +21,9 @@ void main() for(int i= -3 ;i < 3; i++) { - sum += texture2D(sampler0, v_texcoord0 + vec2(-1, i)*0.004) * amount; - sum += texture2D(sampler0, v_texcoord0 + vec2(0, i)*0.004) * amount; - sum += texture2D(sampler0, v_texcoord0 + vec2(1, i)*0.004) * amount; + sum += texture2D(sampler0, v_texcoord0 + vec2(-1, i)*0.004) * u_setting1; + sum += texture2D(sampler0, v_texcoord0 + vec2(0, i)*0.004) * u_setting1; + sum += texture2D(sampler0, v_texcoord0 + vec2(1, i)*0.004) * u_setting1; } if (color.r < 0.3 && color.g < 0.3 && color.b < 0.3) @@ -42,7 +42,7 @@ void main() } } - bloom = mix(color, bloom, power); + bloom = mix(color, bloom, u_setting2); gl_FragColor.rgb = bloom; gl_FragColor.a = 1.0; } diff --git a/assets/shaders/cartoon.fsh b/assets/shaders/cartoon.fsh index 3c7be597a0..75147281db 100644 --- a/assets/shaders/cartoon.fsh +++ b/assets/shaders/cartoon.fsh @@ -10,7 +10,7 @@ precision mediump float; precision mediump int; #endif -const float bb = 0.5; // effects black border sensitivity; from 0.0 to 1.0 +uniform float u_setting1; // const float bb = 0.5; // effects black border sensitivity; from 0.0 to 1.0 uniform sampler2D sampler0; @@ -39,7 +39,7 @@ void main() float d2=dot(abs(c20-c02),dt); float hl=dot(abs(c01-c21),dt); float vl=dot(abs(c10-c12),dt); - float d = bb*(d1+d2+hl+vl)/(dot(c11,dt)+0.15); + float d = u_setting1*(d1+d2+hl+vl)/(dot(c11,dt)+0.15); float lc = 4.0*length(c11); float f = fract(lc); f*=f; diff --git a/assets/shaders/colorcorrection.fsh b/assets/shaders/colorcorrection.fsh new file mode 100644 index 0000000000..1f432bf064 --- /dev/null +++ b/assets/shaders/colorcorrection.fsh @@ -0,0 +1,23 @@ +// Color correction + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D sampler0; +varying vec2 v_texcoord0; + +uniform float u_setting1; // brightness - default: 1.0, range = 0.1 - 2.0 +uniform float u_setting2; // saturation - default: 1.0, range = 0.0 - 2.0 +uniform float u_setting3; // contrast - default: 1.0, range = 0.1 - 3.0 +uniform float u_setting4; // gamma - default: 1.0, range = 0.1 - 2.0 + +void main() +{ + vec3 rgb = texture2D( sampler0, v_texcoord0 ).xyz; + rgb = vec3(mix(vec3(dot(rgb, vec3(0.299, 0.587, 0.114))), rgb, u_setting2)); + rgb = (rgb-0.5)*u_setting3+0.5+u_setting1-1.0; + gl_FragColor.rgb = pow(rgb, vec3(1.0/u_setting4)); + gl_FragColor.a = 1.0; +} diff --git a/assets/shaders/defaultshaders.ini b/assets/shaders/defaultshaders.ini index f000589e5b..2fe8c9499b 100644 --- a/assets/shaders/defaultshaders.ini +++ b/assets/shaders/defaultshaders.ini @@ -32,10 +32,23 @@ Vertex=fxaa.vsh Name=Bloom Fragment=bloom.fsh Vertex=fxaa.vsh +SettingName1=Amount +SettingDefaultValue1=0.6 +SettingMaxValue1=1.0 +SettingMinValue1=0.0 +SettingName2=Power +SettingDefaultValue2=0.5 +SettingMaxValue2=1.0 +SettingMinValue2=0.0 [Sharpen] Name=Sharpen Fragment=sharpen.fsh Vertex=fxaa.vsh +SettingName1=Amount +SettingDefaultValue1=1.5 +SettingMaxValue1=3.0 +SettingMinValue1=1.0 +SettingStep1=0.1 [InverseColors] Name=Inverse Colors Author=Henrik @@ -46,10 +59,24 @@ Name=Scanlines (CRT) Fragment=scanlines.fsh Vertex=fxaa.vsh OutputResolution=True +SettingName1=Amount +SettingDefaultValue1=1.0 +SettingMaxValue1=1.0 +SettingMinValue1=0.0 +SettingStep1=0.1 +SettingName2=Intensity +SettingDefaultValue2=0.5 +SettingMaxValue2=1.0 +SettingMinValue2=0.0 +SettingStep2=0.05 [Cartoon] Name=Cartoon Fragment=cartoon.fsh Vertex=cartoon.vsh +SettingName1=Black border +SettingDefaultValue1=0.5 +SettingMaxValue1=1.0 +SettingMinValue1=0.0 [4xHqGLSL] Name=4xHqGLSL Upscaler Fragment=4xhqglsl.fsh @@ -93,3 +120,28 @@ Fragment=GaussianDownscale.fsh Vertex=fxaa.vsh OutputResolution=True SSAA=2 +[ColorCorrection] +Name=Color correction +Fragment=colorcorrection.fsh +Vertex=fxaa.vsh +SettingName1=Brightness +SettingDefaultValue1=1.0 +SettingMaxValue1=2.0 +SettingMinValue1=0.1 +SettingStep1=0.05 +SettingName2=Saturation +SettingDefaultValue2=1.0 +SettingMaxValue2=2.0 +SettingMinValue2=0.0 +SettingStep2=0.1 +SettingName3=Constrast +SettingDefaultValue3=1.0 +SettingMaxValue3=3.0 +SettingMinValue3=0.1 +SettingStep3=0.1 +SettingName4=Gamma +SettingDefaultValue4=1.0 +SettingMaxValue4=2.0 +SettingMinValue4=0.1 +SettingStep4=0.05 + diff --git a/assets/shaders/scanlines.fsh b/assets/shaders/scanlines.fsh index 77a839a37b..1733dea7bd 100644 --- a/assets/shaders/scanlines.fsh +++ b/assets/shaders/scanlines.fsh @@ -8,13 +8,13 @@ precision mediump int; uniform sampler2D sampler0; varying vec2 v_texcoord0; -float amount = 1.0; // suitable range = 0.0 - 1.0 -float intensity = 0.5; // suitable range = 0.0 - 1.0 +uniform float u_setting1; // float amount = 1.0; // suitable range = 0.0 - 1.0 +uniform float u_setting2; // float intensity = 0.5; // suitable range = 0.0 - 1.0 void main() { - float pos0 = ((v_texcoord0.y + 1.0) * 170.0*amount); - float pos1 = cos((fract( pos0 ) - 0.5)*3.1415926*intensity)*1.5; + float pos0 = ((v_texcoord0.y + 1.0) * 170.0*u_setting1); + float pos1 = cos((fract( pos0 ) - 0.5)*3.1415926*u_setting2)*1.5; vec4 rgb = texture2D( sampler0, v_texcoord0 ); // slight contrast curve diff --git a/assets/shaders/sharpen.fsh b/assets/shaders/sharpen.fsh index d5ad04de54..40feabf02b 100644 --- a/assets/shaders/sharpen.fsh +++ b/assets/shaders/sharpen.fsh @@ -8,12 +8,12 @@ precision mediump int; uniform sampler2D sampler0; varying vec2 v_texcoord0; -float amount = 1.5; +uniform float u_setting1; float amount = 1.5; void main() { vec3 color = texture2D(sampler0, v_texcoord0.xy).xyz; - color -= texture2D(sampler0, v_texcoord0.xy+0.0001).xyz*7.0*amount; - color += texture2D(sampler0, v_texcoord0.xy-0.0001).xyz*7.0*amount; + color -= texture2D(sampler0, v_texcoord0.xy+0.0001).xyz*7.0*u_setting1; + color += texture2D(sampler0, v_texcoord0.xy-0.0001).xyz*7.0*u_setting1; gl_FragColor.rgb = color; } From 6c9eb4764c3fe7c79ed74dbbad9e0277c6b9caec Mon Sep 17 00:00:00 2001 From: iota97 Date: Sat, 16 May 2020 00:52:38 +0200 Subject: [PATCH 05/52] Use map for settings --- Core/Config.cpp | 26 ++++++++++++++++++++++++++ Core/Config.h | 1 + UI/GameSettingsScreen.cpp | 37 ++++++++++++++++++++++++++++--------- UI/MiscScreens.cpp | 17 +++++++++++++---- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index e402ff0783..50d45c1598 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1214,6 +1214,12 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { } } + auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap(); + mPostShaderSetting.clear(); + for (auto it = postShaderSetting.begin(), end = postShaderSetting.end(); it != end; ++it) { + mPostShaderSetting.insert(std::pair(it->first, std::stof(it->second))); + } + // This caps the exponent 4 (so 16x.) if (iAnisotropyLevel > 4) { iAnisotropyLevel = 4; @@ -1325,6 +1331,14 @@ void Config::Save(const char *saveReason) { pinnedPaths->Set(keyName, vPinnedPaths[i]); } + if (!bGameSpecific) { + IniFile::Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting"); + postShaderSetting->Clear(); + for (auto it = mPostShaderSetting.begin(), end = mPostShaderSetting.end(); it != end; ++it) { + postShaderSetting->Set(it->first.c_str(), it->second); + } + } + IniFile::Section *control = iniFile.GetOrCreateSection("Control"); control->Delete("DPadRadius"); @@ -1575,6 +1589,12 @@ bool Config::saveGameConfig(const std::string &pGameId, const std::string &title } }); + IniFile::Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting"); + postShaderSetting->Clear(); + for (auto it = mPostShaderSetting.begin(), end = mPostShaderSetting.end(); it != end; ++it) { + postShaderSetting->Set(it->first.c_str(), it->second); + } + KeyMap::SaveToIni(iniFile); iniFile.Save(fullIniFilePath); @@ -1593,6 +1613,12 @@ bool Config::loadGameConfig(const std::string &pGameId, const std::string &title IniFile iniFile; iniFile.Load(iniFileNameFull); + auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap(); + mPostShaderSetting.clear(); + for (auto it = postShaderSetting.begin(), end = postShaderSetting.end(); it != end; ++it) { + mPostShaderSetting.insert(std::pair(it->first, std::stof(it->second))); + } + IterateSettings(iniFile, [](IniFile::Section *section, ConfigSetting *setting) { if (setting->perGame_) { setting->Get(section); diff --git a/Core/Config.h b/Core/Config.h index b6917e44af..9df3c8179d 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -225,6 +225,7 @@ public: float fPostShaderMaxSettingValue4; float fPostShaderMinSettingValue4; float fPostShaderSettingStep4; + std::map mPostShaderSetting; bool bGfxDebugOutput; bool bGfxDebugSplitSubmit; int iInflightFrames; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 178ae2adac..48e8b1cc2e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -290,15 +290,34 @@ void GameSettingsScreen::CreateViews() { return g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; }); - if (g_Config.sPostShaderSettingName1 != "") - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue1, g_Config.fPostShaderMinSettingValue1, g_Config.fPostShaderMaxSettingValue1, g_Config.sPostShaderSettingName1, g_Config.fPostShaderSettingStep1, screenManager())); - if (g_Config.sPostShaderSettingName2 != "") - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue2, g_Config.fPostShaderMinSettingValue2, g_Config.fPostShaderMaxSettingValue2, g_Config.sPostShaderSettingName2, g_Config.fPostShaderSettingStep2, screenManager())); - if (g_Config.sPostShaderSettingName3 != "") - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue3, g_Config.fPostShaderMinSettingValue3, g_Config.fPostShaderMaxSettingValue3, g_Config.sPostShaderSettingName3, g_Config.fPostShaderSettingStep3, screenManager())); - if (g_Config.sPostShaderSettingName4 != "") - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue4, g_Config.fPostShaderMinSettingValue4, g_Config.fPostShaderMaxSettingValue4, g_Config.sPostShaderSettingName4, g_Config.fPostShaderSettingStep4, screenManager())); - + if (g_Config.sPostShaderSettingName1 != "") { + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue1, g_Config.fPostShaderMinSettingValue1, g_Config.fPostShaderMaxSettingValue1, + g_Config.sPostShaderSettingName1, g_Config.fPostShaderSettingStep1, screenManager()))->OnChange.Add([=](EventParams &e) { + g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"] = g_Config.fPostShaderSettingValue1; + return UI::EVENT_CONTINUE; + });; + } + if (g_Config.sPostShaderSettingName2 != "") { + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue2, g_Config.fPostShaderMinSettingValue2, g_Config.fPostShaderMaxSettingValue2, + g_Config.sPostShaderSettingName2, g_Config.fPostShaderSettingStep2, screenManager()))->OnChange.Add([=](EventParams &e) { + g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"] = g_Config.fPostShaderSettingValue2; + return UI::EVENT_CONTINUE; + });; + } + if (g_Config.sPostShaderSettingName3 != "") { + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue3, g_Config.fPostShaderMinSettingValue3, g_Config.fPostShaderMaxSettingValue3, + g_Config.sPostShaderSettingName3, g_Config.fPostShaderSettingStep3, screenManager()))->OnChange.Add([=](EventParams &e) { + g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"] = g_Config.fPostShaderSettingValue3; + return UI::EVENT_CONTINUE; + });; + } + if (g_Config.sPostShaderSettingName4 != "") { + graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue4, g_Config.fPostShaderMinSettingValue4, g_Config.fPostShaderMaxSettingValue4, + g_Config.sPostShaderSettingName4, g_Config.fPostShaderSettingStep4, screenManager()))->OnChange.Add([=](EventParams &e) { + g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"] = g_Config.fPostShaderSettingValue4; + return UI::EVENT_CONTINUE; + });; + } #if !defined(MOBILE_DEVICE) graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) { diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 917e84d694..6d6c7988a1 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -318,26 +318,35 @@ void PostProcScreen::OnCompleted(DialogResult result) { return; g_Config.sPostShaderName = shaders_[listView_->GetSelected()].section; + if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue1") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue1", shaders_[listView_->GetSelected()].settingValue1)); + if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue2") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue2", shaders_[listView_->GetSelected()].settingValue2)); + if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue3") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue3", shaders_[listView_->GetSelected()].settingValue3)); + if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue4") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue4", shaders_[listView_->GetSelected()].settingValue4)); + g_Config.sPostShaderSettingName1 = shaders_[listView_->GetSelected()].settingName1; - g_Config.fPostShaderSettingValue1 = shaders_[listView_->GetSelected()].settingValue1; + g_Config.fPostShaderSettingValue1 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"]; g_Config.fPostShaderMaxSettingValue1 = shaders_[listView_->GetSelected()].maxSettingValue1; g_Config.fPostShaderMinSettingValue1 = shaders_[listView_->GetSelected()].minSettingValue1; g_Config.fPostShaderSettingStep1 = shaders_[listView_->GetSelected()].settingStep1; g_Config.sPostShaderSettingName2 = shaders_[listView_->GetSelected()].settingName2; - g_Config.fPostShaderSettingValue2 = shaders_[listView_->GetSelected()].settingValue2; + g_Config.fPostShaderSettingValue2 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"]; g_Config.fPostShaderMaxSettingValue2 = shaders_[listView_->GetSelected()].maxSettingValue2; g_Config.fPostShaderMinSettingValue2 = shaders_[listView_->GetSelected()].minSettingValue2; g_Config.fPostShaderSettingStep2 = shaders_[listView_->GetSelected()].settingStep2; g_Config.sPostShaderSettingName3 = shaders_[listView_->GetSelected()].settingName3; - g_Config.fPostShaderSettingValue3 = shaders_[listView_->GetSelected()].settingValue3; + g_Config.fPostShaderSettingValue3 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"]; g_Config.fPostShaderMaxSettingValue3 = shaders_[listView_->GetSelected()].maxSettingValue3; g_Config.fPostShaderMinSettingValue3 = shaders_[listView_->GetSelected()].minSettingValue3; g_Config.fPostShaderSettingStep3 = shaders_[listView_->GetSelected()].settingStep3; g_Config.sPostShaderSettingName4 = shaders_[listView_->GetSelected()].settingName4; - g_Config.fPostShaderSettingValue4 = shaders_[listView_->GetSelected()].settingValue4; + g_Config.fPostShaderSettingValue4 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"]; g_Config.fPostShaderMaxSettingValue4 = shaders_[listView_->GetSelected()].maxSettingValue4; g_Config.fPostShaderMinSettingValue4 = shaders_[listView_->GetSelected()].minSettingValue4; g_Config.fPostShaderSettingStep4 = shaders_[listView_->GetSelected()].settingStep4; From b07874cd009220cd36d0569e61293dcdd58cde4f Mon Sep 17 00:00:00 2001 From: iota97 Date: Sat, 16 May 2020 09:27:53 +0200 Subject: [PATCH 06/52] Fix per game setting and pack setting in vec4 --- Core/Config.cpp | 38 ++++++++---------------------- Core/Config.h | 20 ---------------- GPU/Common/PostShader.cpp | 10 ++++++++ GPU/Common/PresentationCommon.cpp | 15 +++++------- GPU/Common/PresentationCommon.h | 8 ++----- GPU/Common/ShaderTranslation.cpp | 17 ++++--------- UI/GameSettingsScreen.cpp | 38 +++++++++++------------------- UI/MiscScreens.cpp | 33 -------------------------- assets/shaders/bloom.fsh | 11 ++++----- assets/shaders/cartoon.fsh | 4 ++-- assets/shaders/colorcorrection.fsh | 11 ++++----- assets/shaders/scanlines.fsh | 7 +++--- assets/shaders/sharpen.fsh | 6 ++--- 13 files changed, 63 insertions(+), 155 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 50d45c1598..f16c03e538 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -768,30 +768,6 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("HardwareTessellation", &g_Config.bHardwareTessellation, false, true, true), ReportedConfigSetting("PostShader", &g_Config.sPostShaderName, "Off", true, true), - ReportedConfigSetting("PostShaderSettingName1", &g_Config.sPostShaderSettingName1, "", true, true), - ReportedConfigSetting("PostShaderSettingValue1", &g_Config.fPostShaderSettingValue1, 0.0f, true, true), - ConfigSetting("PostShaderSettingMaxValue1", &g_Config.fPostShaderMaxSettingValue1, 1.0f, true, true), - ConfigSetting("PostShaderSettingMinValue1", &g_Config.fPostShaderMinSettingValue1, -1.0f, true, true), - ConfigSetting("PostShaderSettingStep1", &g_Config.fPostShaderSettingStep1, 0.01f, true, true), - - ReportedConfigSetting("PostShaderSettingName2", &g_Config.sPostShaderSettingName2, "", true, true), - ReportedConfigSetting("PostShaderSettingValue2", &g_Config.fPostShaderSettingValue2, 0.0f, true, true), - ConfigSetting("PostShaderSettingMaxValue2", &g_Config.fPostShaderMaxSettingValue2, 1.0f, true, true), - ConfigSetting("PostShaderSettingMinValue2", &g_Config.fPostShaderMinSettingValue2, -1.0f, true, true), - ConfigSetting("PostShaderSettingStep2", &g_Config.fPostShaderSettingStep2, 0.01f, true, true), - - ReportedConfigSetting("PostShaderSettingName3", &g_Config.sPostShaderSettingName3, "", true, true), - ReportedConfigSetting("PostShaderSettingValue3", &g_Config.fPostShaderSettingValue3, 0.0f, true, true), - ConfigSetting("PostShaderSettingMaxValue3", &g_Config.fPostShaderMaxSettingValue3, 1.0f, true, true), - ConfigSetting("PostShaderSettingMinValue3", &g_Config.fPostShaderMinSettingValue3, -1.0f, true, true), - ConfigSetting("PostShaderSettingStep3", &g_Config.fPostShaderSettingStep3, 0.01f, true, true), - - ReportedConfigSetting("PostShaderSettingName4", &g_Config.sPostShaderSettingName4, "", true, true), - ReportedConfigSetting("PostShaderSettingValue4", &g_Config.fPostShaderSettingValue4, 0.0f, true, true), - ConfigSetting("PostShaderSettingMaxValue4", &g_Config.fPostShaderMaxSettingValue4, 1.0f, true, true), - ConfigSetting("PostShaderSettingMinValue4", &g_Config.fPostShaderMinSettingValue4, -1.0f, true, true), - ConfigSetting("PostShaderSettingStep4", &g_Config.fPostShaderSettingStep4, 0.01f, true, true), - ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true), ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false, true, true), ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true), @@ -1216,8 +1192,8 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap(); mPostShaderSetting.clear(); - for (auto it = postShaderSetting.begin(), end = postShaderSetting.end(); it != end; ++it) { - mPostShaderSetting.insert(std::pair(it->first, std::stof(it->second))); + for (auto it : postShaderSetting) { + mPostShaderSetting[it.first] = std::stof(it.second); } // This caps the exponent 4 (so 16x.) @@ -1615,8 +1591,8 @@ bool Config::loadGameConfig(const std::string &pGameId, const std::string &title auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap(); mPostShaderSetting.clear(); - for (auto it = postShaderSetting.begin(), end = postShaderSetting.end(); it != end; ++it) { - mPostShaderSetting.insert(std::pair(it->first, std::stof(it->second))); + for (auto it : postShaderSetting) { + mPostShaderSetting[it.first] = std::stof(it.second); } IterateSettings(iniFile, [](IniFile::Section *section, ConfigSetting *setting) { @@ -1643,6 +1619,12 @@ void Config::unloadGameConfig() { } }); + auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap(); + mPostShaderSetting.clear(); + for (auto it : postShaderSetting) { + mPostShaderSetting[it.first] = std::stof(it.second); + } + LoadStandardControllerIni(); } } diff --git a/Core/Config.h b/Core/Config.h index 9df3c8179d..2b1437d3b4 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -205,26 +205,6 @@ public: int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High bool bHardwareTessellation; std::string sPostShaderName; // Off for off. - std::string sPostShaderSettingName1; - float fPostShaderSettingValue1; - float fPostShaderMaxSettingValue1; - float fPostShaderMinSettingValue1; - float fPostShaderSettingStep1; - std::string sPostShaderSettingName2; - float fPostShaderSettingValue2; - float fPostShaderMaxSettingValue2; - float fPostShaderMinSettingValue2; - float fPostShaderSettingStep2; - std::string sPostShaderSettingName3; - float fPostShaderSettingValue3; - float fPostShaderMaxSettingValue3; - float fPostShaderMinSettingValue3; - float fPostShaderSettingStep3; - std::string sPostShaderSettingName4; - float fPostShaderSettingValue4; - float fPostShaderMaxSettingValue4; - float fPostShaderMinSettingValue4; - float fPostShaderSettingStep4; std::map mPostShaderSetting; bool bGfxDebugOutput; bool bGfxDebugSplitSubmit; diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index 1bbf62592c..a1b5ace7e4 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -130,6 +130,16 @@ void LoadPostShaderInfo(std::vector directories) { section.Get("SettingMinValue4", &info.minSettingValue4, -1.0f); section.Get("SettingMaxValue4", &info.maxSettingValue4, 1.0f); section.Get("SettingStep4", &info.settingStep4, 0.01f); + + if (g_Config.mPostShaderSetting.find(info.section + "SettingValue1") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue1", info.settingValue1)); + if (g_Config.mPostShaderSetting.find(info.section + "SettingValue2") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue2", info.settingValue2)); + if (g_Config.mPostShaderSetting.find(info.section + "SettingValue3") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue3", info.settingValue3)); + if (g_Config.mPostShaderSetting.find(info.section + "SettingValue4") == g_Config.mPostShaderSetting.end()) + g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue4", info.settingValue4)); + // Let's ignore shaders we can't support. TODO: Not a very good check if (gl_extensions.IsGLES && !gl_extensions.GLES3) { bool requiresIntegerSupport; diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 1f3a61aa3e..ad2ff7a684 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -164,10 +164,10 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer uniforms->gl_HalfPixel[0] = u_pixel_delta * 0.5f; uniforms->gl_HalfPixel[1] = v_pixel_delta * 0.5f; - uniforms->setting1 = g_Config.fPostShaderSettingValue1; - uniforms->setting2 = g_Config.fPostShaderSettingValue2; - uniforms->setting3 = g_Config.fPostShaderSettingValue3; - uniforms->setting4 = g_Config.fPostShaderSettingValue4; + uniforms->setting[0] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"];; + uniforms->setting[1] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"]; + uniforms->setting[2] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"]; + uniforms->setting[3] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"]; } static std::string ReadShaderSrc(const std::string &filename) { @@ -218,11 +218,8 @@ bool PresentationCommon::UpdatePostShader() { { "u_texelDelta", 1, 1, Draw::UniformType::FLOAT2, offsetof(PostShaderUniforms, texelDelta) }, { "u_pixelDelta", 2, 2, Draw::UniformType::FLOAT2, offsetof(PostShaderUniforms, pixelDelta) }, { "u_time", 3, 3, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, time) }, - { "u_video", 4, 4, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, video) }, - { "u_setting1", 5, 5, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting1) }, - { "u_setting2", 6, 6, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting2) }, - { "u_setting3", 7, 7, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting3) }, - { "u_setting4", 8, 8, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, setting4) }, + { "u_setting", 4, 4, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, setting) }, + { "u_video", 5, 5, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, video) }, } }; Draw::Pipeline *pipeline = CreatePipeline({ vs, fs }, true, &postShaderDesc); if (!pipeline) diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index 4b83b95442..7744d9b1e0 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -31,12 +31,8 @@ struct CardboardSettings { struct PostShaderUniforms { float texelDelta[2]; float pixelDelta[2]; float time[4]; - float video; - float setting1; - float setting2; - float setting3; - float setting4; - float pad[3]; + float setting[4]; + float video; float pad[3]; // Used on Direct3D9. float gl_HalfPixel[4]; }; diff --git a/GPU/Common/ShaderTranslation.cpp b/GPU/Common/ShaderTranslation.cpp index 975652f3e2..192e59f186 100644 --- a/GPU/Common/ShaderTranslation.cpp +++ b/GPU/Common/ShaderTranslation.cpp @@ -86,11 +86,8 @@ cbuffer data : register(b0) { float2 u_texelDelta; float2 u_pixelDelta; float4 u_time; + float4 u_setting; float u_video; - float u_setting1; - float u_setting2; - float u_setting3; - float u_setting4; }; )"; @@ -105,11 +102,8 @@ layout (std140, set = 0, binding = 0) uniform Data { vec2 u_texelDelta; vec2 u_pixelDelta; vec4 u_time; + vec4 u_setting; float u_video; - float u_setting1; - float u_setting2; - float u_setting3; - float u_setting4; }; )"; @@ -118,11 +112,8 @@ float4 gl_HalfPixel : register(c0); float2 u_texelDelta : register(c1); float2 u_pixelDelta : register(c2); float4 u_time : register(c3); -float u_video : register(c4); -float u_setting1 : register(c5); -float u_setting2 : register(c6); -float u_setting3 : register(c7); -float u_setting4 : register(c8); +float4 u_setting : register(c4); +float u_video : register(c5); )"; // SPIRV-Cross' HLSL output has some deficiencies we need to work around. diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 48e8b1cc2e..5429fb08ef 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -290,34 +290,24 @@ void GameSettingsScreen::CreateViews() { return g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; }); - if (g_Config.sPostShaderSettingName1 != "") { - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue1, g_Config.fPostShaderMinSettingValue1, g_Config.fPostShaderMaxSettingValue1, - g_Config.sPostShaderSettingName1, g_Config.fPostShaderSettingStep1, screenManager()))->OnChange.Add([=](EventParams &e) { - g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"] = g_Config.fPostShaderSettingValue1; - return UI::EVENT_CONTINUE; - });; + const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sPostShaderName); + if (shaderInfo && !shaderInfo->settingName1.empty()) { + auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"]; + graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue1, shaderInfo->maxSettingValue1, shaderInfo->settingName1, shaderInfo->settingStep1, screenManager())); } - if (g_Config.sPostShaderSettingName2 != "") { - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue2, g_Config.fPostShaderMinSettingValue2, g_Config.fPostShaderMaxSettingValue2, - g_Config.sPostShaderSettingName2, g_Config.fPostShaderSettingStep2, screenManager()))->OnChange.Add([=](EventParams &e) { - g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"] = g_Config.fPostShaderSettingValue2; - return UI::EVENT_CONTINUE; - });; + if (shaderInfo && !shaderInfo->settingName2.empty()) { + auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"]; + graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue2, shaderInfo->maxSettingValue2, shaderInfo->settingName2, shaderInfo->settingStep2, screenManager())); } - if (g_Config.sPostShaderSettingName3 != "") { - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue3, g_Config.fPostShaderMinSettingValue3, g_Config.fPostShaderMaxSettingValue3, - g_Config.sPostShaderSettingName3, g_Config.fPostShaderSettingStep3, screenManager()))->OnChange.Add([=](EventParams &e) { - g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"] = g_Config.fPostShaderSettingValue3; - return UI::EVENT_CONTINUE; - });; + if (shaderInfo && !shaderInfo->settingName3.empty()) { + auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"]; + graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue3, shaderInfo->maxSettingValue3, shaderInfo->settingName3, shaderInfo->settingStep3, screenManager())); } - if (g_Config.sPostShaderSettingName4 != "") { - graphicsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fPostShaderSettingValue4, g_Config.fPostShaderMinSettingValue4, g_Config.fPostShaderMaxSettingValue4, - g_Config.sPostShaderSettingName4, g_Config.fPostShaderSettingStep4, screenManager()))->OnChange.Add([=](EventParams &e) { - g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"] = g_Config.fPostShaderSettingValue4; - return UI::EVENT_CONTINUE; - });; + if (shaderInfo && !shaderInfo->settingName4.empty()) { + auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"]; + graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue4, shaderInfo->maxSettingValue4, shaderInfo->settingName4, shaderInfo->settingStep4, screenManager())); } + #if !defined(MOBILE_DEVICE) graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) { diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 6d6c7988a1..27fadc85fd 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -317,39 +317,6 @@ void PostProcScreen::OnCompleted(DialogResult result) { if (result != DR_OK) return; g_Config.sPostShaderName = shaders_[listView_->GetSelected()].section; - - if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue1") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue1", shaders_[listView_->GetSelected()].settingValue1)); - if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue2") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue2", shaders_[listView_->GetSelected()].settingValue2)); - if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue3") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue3", shaders_[listView_->GetSelected()].settingValue3)); - if (g_Config.mPostShaderSetting.find(g_Config.sPostShaderName + "SettingValue4") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(g_Config.sPostShaderName + "SettingValue4", shaders_[listView_->GetSelected()].settingValue4)); - - g_Config.sPostShaderSettingName1 = shaders_[listView_->GetSelected()].settingName1; - g_Config.fPostShaderSettingValue1 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"]; - g_Config.fPostShaderMaxSettingValue1 = shaders_[listView_->GetSelected()].maxSettingValue1; - g_Config.fPostShaderMinSettingValue1 = shaders_[listView_->GetSelected()].minSettingValue1; - g_Config.fPostShaderSettingStep1 = shaders_[listView_->GetSelected()].settingStep1; - - g_Config.sPostShaderSettingName2 = shaders_[listView_->GetSelected()].settingName2; - g_Config.fPostShaderSettingValue2 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"]; - g_Config.fPostShaderMaxSettingValue2 = shaders_[listView_->GetSelected()].maxSettingValue2; - g_Config.fPostShaderMinSettingValue2 = shaders_[listView_->GetSelected()].minSettingValue2; - g_Config.fPostShaderSettingStep2 = shaders_[listView_->GetSelected()].settingStep2; - - g_Config.sPostShaderSettingName3 = shaders_[listView_->GetSelected()].settingName3; - g_Config.fPostShaderSettingValue3 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"]; - g_Config.fPostShaderMaxSettingValue3 = shaders_[listView_->GetSelected()].maxSettingValue3; - g_Config.fPostShaderMinSettingValue3 = shaders_[listView_->GetSelected()].minSettingValue3; - g_Config.fPostShaderSettingStep3 = shaders_[listView_->GetSelected()].settingStep3; - - g_Config.sPostShaderSettingName4 = shaders_[listView_->GetSelected()].settingName4; - g_Config.fPostShaderSettingValue4 = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"]; - g_Config.fPostShaderMaxSettingValue4 = shaders_[listView_->GetSelected()].maxSettingValue4; - g_Config.fPostShaderMinSettingValue4 = shaders_[listView_->GetSelected()].minSettingValue4; - g_Config.fPostShaderSettingStep4 = shaders_[listView_->GetSelected()].settingStep4; } NewLanguageScreen::NewLanguageScreen(const std::string &title) : ListPopupScreen(title) { diff --git a/assets/shaders/bloom.fsh b/assets/shaders/bloom.fsh index fd4cee62fa..e0636297a2 100644 --- a/assets/shaders/bloom.fsh +++ b/assets/shaders/bloom.fsh @@ -10,8 +10,7 @@ precision mediump int; uniform sampler2D sampler0; varying vec2 v_texcoord0; -uniform float u_setting1; // float amount = 0.60; // suitable range = 0.00 - 1.00 -uniform float u_setting2; // float power = 0.5; // suitable range = 0.0 - 1.0 +uniform vec4 u_setting; void main() { @@ -21,9 +20,9 @@ void main() for(int i= -3 ;i < 3; i++) { - sum += texture2D(sampler0, v_texcoord0 + vec2(-1, i)*0.004) * u_setting1; - sum += texture2D(sampler0, v_texcoord0 + vec2(0, i)*0.004) * u_setting1; - sum += texture2D(sampler0, v_texcoord0 + vec2(1, i)*0.004) * u_setting1; + sum += texture2D(sampler0, v_texcoord0 + vec2(-1, i)*0.004) * u_setting.x; + sum += texture2D(sampler0, v_texcoord0 + vec2(0, i)*0.004) * u_setting.x; + sum += texture2D(sampler0, v_texcoord0 + vec2(1, i)*0.004) * u_setting.x; } if (color.r < 0.3 && color.g < 0.3 && color.b < 0.3) @@ -42,7 +41,7 @@ void main() } } - bloom = mix(color, bloom, u_setting2); + bloom = mix(color, bloom, u_setting.y); gl_FragColor.rgb = bloom; gl_FragColor.a = 1.0; } diff --git a/assets/shaders/cartoon.fsh b/assets/shaders/cartoon.fsh index 75147281db..de8c0a07fd 100644 --- a/assets/shaders/cartoon.fsh +++ b/assets/shaders/cartoon.fsh @@ -10,7 +10,7 @@ precision mediump float; precision mediump int; #endif -uniform float u_setting1; // const float bb = 0.5; // effects black border sensitivity; from 0.0 to 1.0 +uniform vec4 u_setting; uniform sampler2D sampler0; @@ -39,7 +39,7 @@ void main() float d2=dot(abs(c20-c02),dt); float hl=dot(abs(c01-c21),dt); float vl=dot(abs(c10-c12),dt); - float d = u_setting1*(d1+d2+hl+vl)/(dot(c11,dt)+0.15); + float d = u_setting.x*(d1+d2+hl+vl)/(dot(c11,dt)+0.15); float lc = 4.0*length(c11); float f = fract(lc); f*=f; diff --git a/assets/shaders/colorcorrection.fsh b/assets/shaders/colorcorrection.fsh index 1f432bf064..cf62884f5a 100644 --- a/assets/shaders/colorcorrection.fsh +++ b/assets/shaders/colorcorrection.fsh @@ -8,16 +8,13 @@ precision mediump int; uniform sampler2D sampler0; varying vec2 v_texcoord0; -uniform float u_setting1; // brightness - default: 1.0, range = 0.1 - 2.0 -uniform float u_setting2; // saturation - default: 1.0, range = 0.0 - 2.0 -uniform float u_setting3; // contrast - default: 1.0, range = 0.1 - 3.0 -uniform float u_setting4; // gamma - default: 1.0, range = 0.1 - 2.0 +uniform vec4 u_setting; void main() { vec3 rgb = texture2D( sampler0, v_texcoord0 ).xyz; - rgb = vec3(mix(vec3(dot(rgb, vec3(0.299, 0.587, 0.114))), rgb, u_setting2)); - rgb = (rgb-0.5)*u_setting3+0.5+u_setting1-1.0; - gl_FragColor.rgb = pow(rgb, vec3(1.0/u_setting4)); + rgb = vec3(mix(vec3(dot(rgb, vec3(0.299, 0.587, 0.114))), rgb, u_setting.y)); + rgb = (rgb-0.5)*u_setting.z+0.5+u_setting.x-1.0; + gl_FragColor.rgb = pow(rgb, vec3(1.0/u_setting.w)); gl_FragColor.a = 1.0; } diff --git a/assets/shaders/scanlines.fsh b/assets/shaders/scanlines.fsh index 1733dea7bd..ac568b14a9 100644 --- a/assets/shaders/scanlines.fsh +++ b/assets/shaders/scanlines.fsh @@ -8,13 +8,12 @@ precision mediump int; uniform sampler2D sampler0; varying vec2 v_texcoord0; -uniform float u_setting1; // float amount = 1.0; // suitable range = 0.0 - 1.0 -uniform float u_setting2; // float intensity = 0.5; // suitable range = 0.0 - 1.0 +uniform vec4 u_setting; void main() { - float pos0 = ((v_texcoord0.y + 1.0) * 170.0*u_setting1); - float pos1 = cos((fract( pos0 ) - 0.5)*3.1415926*u_setting2)*1.5; + float pos0 = ((v_texcoord0.y + 1.0) * 170.0*u_setting.x); + float pos1 = cos((fract( pos0 ) - 0.5)*3.1415926*u_setting.y)*1.5; vec4 rgb = texture2D( sampler0, v_texcoord0 ); // slight contrast curve diff --git a/assets/shaders/sharpen.fsh b/assets/shaders/sharpen.fsh index 40feabf02b..3c528c7602 100644 --- a/assets/shaders/sharpen.fsh +++ b/assets/shaders/sharpen.fsh @@ -8,12 +8,12 @@ precision mediump int; uniform sampler2D sampler0; varying vec2 v_texcoord0; -uniform float u_setting1; float amount = 1.5; +uniform vec4 u_setting; void main() { vec3 color = texture2D(sampler0, v_texcoord0.xy).xyz; - color -= texture2D(sampler0, v_texcoord0.xy+0.0001).xyz*7.0*u_setting1; - color += texture2D(sampler0, v_texcoord0.xy-0.0001).xyz*7.0*u_setting1; + color -= texture2D(sampler0, v_texcoord0.xy+0.0001).xyz*7.0*u_setting.x; + color += texture2D(sampler0, v_texcoord0.xy-0.0001).xyz*7.0*u_setting.x; gl_FragColor.rgb = color; } From 5e7a837ff9ef4ba1f8b7b56a1255bc0050c14210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 15:20:22 +0200 Subject: [PATCH 07/52] Display the savestate slot number when saving and loading state through shortcuts. --- Core/SaveState.cpp | 34 +++++++++++++++++++--------------- Core/SaveState.h | 4 ++-- UI/EmuScreen.cpp | 2 +- UI/NativeApp.cpp | 2 +- Windows/MainWindowMenu.cpp | 4 ++-- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 198113e3a4..d3472b5e1a 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -70,14 +70,16 @@ namespace SaveState struct Operation { - Operation(OperationType t, const std::string &f, Callback cb, void *cbUserData_) - : type(t), filename(f), callback(cb), cbUserData(cbUserData_) + // The slot number is for visual purposes only. Set to -1 for operations where we don't display a message for example. + Operation(OperationType t, const std::string &f, int slot_, Callback cb, void *cbUserData_) + : type(t), filename(f), callback(cb), slot(slot_), cbUserData(cbUserData_) { } OperationType type; std::string filename; Callback callback; + int slot; void *cbUserData; }; @@ -316,29 +318,29 @@ namespace SaveState Core_UpdateSingleStep(); } - void Load(const std::string &filename, Callback callback, void *cbUserData) + void Load(const std::string &filename, int slot, Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_LOAD, filename, callback, cbUserData)); + Enqueue(Operation(SAVESTATE_LOAD, filename, slot, callback, cbUserData)); } - void Save(const std::string &filename, Callback callback, void *cbUserData) + void Save(const std::string &filename, int slot, Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_SAVE, filename, callback, cbUserData)); + Enqueue(Operation(SAVESTATE_SAVE, filename, slot, callback, cbUserData)); } void Verify(Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), callback, cbUserData)); + Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), -1, callback, cbUserData)); } void Rewind(Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_REWIND, std::string(""), callback, cbUserData)); + Enqueue(Operation(SAVESTATE_REWIND, std::string(""), -1, callback, cbUserData)); } void SaveScreenshot(const std::string &filename, Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, callback, cbUserData)); + Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, callback, cbUserData)); } bool CanRewind() @@ -435,7 +437,7 @@ namespace SaveState { std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); if (!fn.empty()) { - Load(fn, callback, cbUserData); + Load(fn, slot, callback, cbUserData); } else { auto sy = GetI18NCategory("System"); if (callback) @@ -492,7 +494,7 @@ namespace SaveState RenameIfExists(shot, shotUndo); } SaveScreenshot(shot, Callback(), 0); - Save(fn + ".tmp", renameCallback, cbUserData); + Save(fn + ".tmp", slot, renameCallback, cbUserData); } else { auto sy = GetI18NCategory("System"); if (callback) @@ -737,6 +739,8 @@ namespace SaveState if (strlen(i18nSaveFailure) == 0) i18nSaveFailure = sc->T("Failed to save state"); + std::string slot_prefix = op.slot >= 0 ? StringFromFormat("(%d) ", op.slot + 1) : ""; + switch (op.type) { case SAVESTATE_LOAD: @@ -744,7 +748,7 @@ namespace SaveState // Use the state's latest version as a guess for saveStateInitialGitVersion. result = CChunkFileReader::Load(op.filename, &saveStateInitialGitVersion, state, &reason); if (result == CChunkFileReader::ERROR_NONE) { - callbackMessage = sc->T("Loaded State"); + callbackMessage = slot_prefix + sc->T("Loaded State"); callbackResult = Status::SUCCESS; hasLoadedState = true; @@ -753,11 +757,11 @@ namespace SaveState // Using save states instead of saves simulates many hour play sessions. // Sometimes this exposes game bugs that were rarely seen on real devices, // because few people played on a real PSP for 10 hours straight. - callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs."); + callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs."); callbackResult = Status::WARNING; } else if (!g_Config.bHideStateWarnings && IsOldVersion()) { // Save states also preserve bugs from old PPSSPP versions, so warn. - callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs."); + callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs."); callbackResult = Status::WARNING; } @@ -794,7 +798,7 @@ namespace SaveState } result = CChunkFileReader::Save(op.filename, title, PPSSPP_GIT_VERSION, state); if (result == CChunkFileReader::ERROR_NONE) { - callbackMessage = sc->T("Saved State"); + callbackMessage = slot_prefix + sc->T("Saved State"); callbackResult = Status::SUCCESS; #ifndef MOBILE_DEVICE if (g_Config.bSaveLoadResetsAVdumping) { diff --git a/Core/SaveState.h b/Core/SaveState.h index e190e6e65d..8a205ee579 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -62,11 +62,11 @@ namespace SaveState // Load the specified file into the current state (async.) // Warning: callback will be called on a different thread. - void Load(const std::string &filename, Callback callback = Callback(), void *cbUserData = 0); + void Load(const std::string &filename, int slot, Callback callback = Callback(), void *cbUserData = 0); // Save the current state to the specified file (async.) // Warning: callback will be called on a different thread. - void Save(const std::string &filename, Callback callback = Callback(), void *cbUserData = 0); + void Save(const std::string &filename, int slot, Callback callback = Callback(), void *cbUserData = 0); CChunkFileReader::Error SaveToRam(std::vector &state); CChunkFileReader::Error LoadFromRam(std::vector &state); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index d3fd56050d..b2c5f9afd8 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -412,7 +412,7 @@ void EmuScreen::sendMessage(const char *message, const char *value) { } else if (!strcmp(message, "boot")) { const char *ext = strrchr(value, '.'); if (ext != nullptr && !strcmp(ext, ".ppst")) { - SaveState::Load(value, &AfterStateBoot); + SaveState::Load(value, -1, &AfterStateBoot); } else { PSP_Shutdown(); bootPending_ = true; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 3c29f900b2..9e3ea05102 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -710,7 +710,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #endif if (!boot_filename.empty() && stateToLoad != NULL) { - SaveState::Load(stateToLoad, [](SaveState::Status status, const std::string &message, void *) { + SaveState::Load(stateToLoad, -1, [](SaveState::Status status, const std::string &message, void *) { if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) { osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 978a73db5b..31de1a4833 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -632,14 +632,14 @@ namespace MainWindow { case ID_FILE_LOADSTATEFILE: if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) { SetCursor(LoadCursor(0, IDC_WAIT)); - SaveState::Load(fn, SaveStateActionFinished); + SaveState::Load(fn, -1, SaveStateActionFinished); } break; case ID_FILE_SAVESTATEFILE: if (W32Util::BrowseForFileName(false, hWnd, L"Save state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) { SetCursor(LoadCursor(0, IDC_WAIT)); - SaveState::Save(fn, SaveStateActionFinished); + SaveState::Save(fn, -1, SaveStateActionFinished); } break; From caf8e5496d87751adfd971a4a5addfce5b502245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 17:46:21 +0200 Subject: [PATCH 08/52] SDL: Show the actually used sample rate in system info. Right now it'll always be the requested 44100 though anyway. --- SDL/SDLMain.cpp | 25 +++++++++++++++---------- UI/DevScreens.cpp | 6 ++++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 82651123ea..a5d98345c1 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -68,6 +68,8 @@ static int g_DesktopWidth = 0; static int g_DesktopHeight = 0; static float g_RefreshRate = 60.f; +static SDL_AudioSpec g_retFmt; + int getDisplayNumber(void) { int displayNumber = 0; char * displayNumberStr; @@ -90,7 +92,7 @@ static SDL_AudioDeviceID audioDev = 0; // Must be called after NativeInit(). static void InitSDLAudioDevice(const std::string &name = "") { - SDL_AudioSpec fmt, ret_fmt; + SDL_AudioSpec fmt; memset(&fmt, 0, sizeof(fmt)); fmt.freq = 44100; fmt.format = AUDIO_S16; @@ -106,24 +108,25 @@ static void InitSDLAudioDevice(const std::string &name = "") { audioDev = 0; if (!startDevice.empty()) { - audioDev = SDL_OpenAudioDevice(startDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + audioDev = SDL_OpenAudioDevice(startDevice.c_str(), 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (audioDev <= 0) { WLOG("Failed to open audio device: %s", startDevice.c_str()); } } if (audioDev <= 0) { - audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + ILOG("SDL: Trying a different sample rate"); + audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); } if (audioDev <= 0) { ELOG("Failed to open audio: %s", SDL_GetError()); } else { - if (ret_fmt.samples != fmt.samples) // Notify, but still use it - ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples); - if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) { + if (g_retFmt.samples != fmt.samples) // Notify, but still use it + ELOG("Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples); + if (g_retFmt.freq != fmt.freq || g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) { ELOG("Sound buffer format does not match requested format."); - ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq); - ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format); - ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels); + ELOG("Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq); + ELOG("Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format); + ELOG("Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels); ELOG("Provided output format does not match requirement, turning audio off"); SDL_CloseAudioDevice(audioDev); } @@ -319,7 +322,9 @@ std::string System_GetProperty(SystemProperty prop) { int System_GetPropertyInt(SystemProperty prop) { switch (prop) { case SYSPROP_AUDIO_SAMPLE_RATE: - return 44100; + return g_retFmt.freq; + case SYSPROP_AUDIO_FRAMES_PER_BUFFER: + return g_retFmt.samples; case SYSPROP_DEVICE_TYPE: #if defined(MOBILE_DEVICE) return DEVICE_TYPE_MOBILE; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index e39888d041..37ffa7ff17 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -502,7 +502,7 @@ void SystemInfoScreen::CreateViews() { deviceSpecs->Add(new ItemHeader(si->T("OS Information"))); deviceSpecs->Add(new InfoItem(si->T("Memory Page Size"), StringFromFormat(si->T("%d bytes"), GetMemoryProtectPageSize()))); deviceSpecs->Add(new InfoItem(si->T("RW/RX exclusive"), PlatformIsWXExclusive() ? di->T("Active") : di->T("Inactive"))); -#ifdef ANDROID +#if PPSSPP_PLATFORM(ANDROID) deviceSpecs->Add(new InfoItem(si->T("Sustained perf mode"), System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE) ? di->T("Supported") : di->T("Unsupported"))); #endif @@ -512,14 +512,16 @@ void SystemInfoScreen::CreateViews() { #endif deviceSpecs->Add(new InfoItem(si->T("PPSSPP build"), build)); -#ifdef __ANDROID__ deviceSpecs->Add(new ItemHeader(si->T("Audio Information"))); deviceSpecs->Add(new InfoItem(si->T("Sample rate"), StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE)))); deviceSpecs->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER)))); +#if PPSSPP_PLATFORM(ANDROID) deviceSpecs->Add(new InfoItem(si->T("Optimal sample rate"), StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE)))); deviceSpecs->Add(new InfoItem(si->T("Optimal frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER)))); +#endif deviceSpecs->Add(new ItemHeader(si->T("Display Information"))); +#if PPSSPP_PLATFORM(ANDROID) deviceSpecs->Add(new InfoItem(si->T("Native Resolution"), StringFromFormat("%dx%d", System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)))); From f77b78d42974445c709aeb2c887ebfb2a781dbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 18:38:30 +0200 Subject: [PATCH 09/52] Oops, need to pass an (invalid) slot number in headless too. --- headless/Headless.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 02b65dd53c..3b755cbd03 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -428,7 +428,7 @@ int main(int argc, const char* argv[]) #endif if (stateToLoad != NULL) - SaveState::Load(stateToLoad); + SaveState::Load(stateToLoad, -1); std::vector failedTests; std::vector passedTests; From b11c7774bc3f2e367911484ad8664bd663af886b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 19:03:15 +0200 Subject: [PATCH 10/52] Only show frames per buffer if the number is available through sysinfo --- UI/DevScreens.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 37ffa7ff17..f146d099e9 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -514,7 +514,10 @@ void SystemInfoScreen::CreateViews() { deviceSpecs->Add(new ItemHeader(si->T("Audio Information"))); deviceSpecs->Add(new InfoItem(si->T("Sample rate"), StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE)))); - deviceSpecs->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER)))); + int framesPerBuffer = System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER); + if (framesPerBuffer > 0) { + deviceSpecs->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", framesPerBuffer))); + } #if PPSSPP_PLATFORM(ANDROID) deviceSpecs->Add(new InfoItem(si->T("Optimal sample rate"), StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE)))); deviceSpecs->Add(new InfoItem(si->T("Optimal frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER)))); From c4f1143adcf8c5fdf8d7de81ed8a07fe8f1f6c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 19:04:41 +0200 Subject: [PATCH 11/52] Qt fix --- Qt/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 35a4b2e278..b1c303f434 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -182,7 +182,7 @@ void MainWindow::lstateAct() if (dialog.exec()) { QStringList fileNames = dialog.selectedFiles(); - SaveState::Load(fileNames[0].toStdString(), SaveStateActionFinished, this); + SaveState::Load(fileNames[0].toStdString(), -1, SaveStateActionFinished, this); } } @@ -197,7 +197,7 @@ void MainWindow::sstateAct() if (dialog.exec()) { QStringList fileNames = dialog.selectedFiles(); - SaveState::Save(fileNames[0].toStdString(), SaveStateActionFinished, this); + SaveState::Save(fileNames[0].toStdString(), -1, SaveStateActionFinished, this); } } From b6a051dd494ff48b6e07cac2f2b269eaa514b120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 18:13:42 +0200 Subject: [PATCH 12/52] Fix comment --- SDL/SDLMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index a5d98345c1..087478a081 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -114,7 +114,7 @@ static void InitSDLAudioDevice(const std::string &name = "") { } } if (audioDev <= 0) { - ILOG("SDL: Trying a different sample rate"); + ILOG("SDL: Trying a different device"); audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); } if (audioDev <= 0) { From fce09f1865fc2acc5d13fcaf39d831887205183b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 19:44:33 +0200 Subject: [PATCH 13/52] Simplify how to get the debug stats from StereoResampler --- Core/HLE/__sceAudio.cpp | 8 +++---- Core/HLE/__sceAudio.h | 3 ++- Core/HW/StereoResampler.cpp | 47 ++++++++++++++++++++++--------------- Core/HW/StereoResampler.h | 25 +++++++++++--------- UI/EmuScreen.cpp | 15 ++---------- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 31573526ad..1d31185348 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -47,7 +47,6 @@ #include "Core/Util/AudioFormat.h" StereoResampler resampler; -AudioDebugStats g_AudioDebugStats; // Should be used to lock anything related to the outAudioQueue. // atomic locks are used on the lock. TODO: make this lock-free @@ -100,7 +99,7 @@ static void __AudioCPUMHzChange() { void __AudioInit() { - memset(&g_AudioDebugStats, 0, sizeof(g_AudioDebugStats)); + resampler.ResetStatCounters(); mixFrequency = 44100; srcFrequency = 0; @@ -459,9 +458,8 @@ int __AudioMix(short *outstereo, int numFrames, int sampleRate) { return resampler.Mix(outstereo, numFrames, false, sampleRate); } -const AudioDebugStats *__AudioGetDebugStats() { - resampler.GetAudioDebugStats(&g_AudioDebugStats); - return &g_AudioDebugStats; +void __AudioGetDebugStats(char *buf, size_t bufSize) { + resampler.GetAudioDebugStats(buf, bufSize); } void __PushExternalAudio(const s32 *audio, int numSamples) { diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index c836bd3f19..f6619ece7d 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -26,6 +26,7 @@ struct AudioDebugStats { int underrunCount; int overrunCount; int instantSampleRate; + int targetSampleRate; int lastPushSize; }; @@ -44,7 +45,7 @@ void __AudioWakeThreads(AudioChannel &chan, int result, int step); void __AudioWakeThreads(AudioChannel &chan, int result); int __AudioMix(short *outstereo, int numSamples, int sampleRate); -const AudioDebugStats *__AudioGetDebugStats(); +void __AudioGetDebugStats(char *buf, size_t bufSize); void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu! // Audio Dumping stuff diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 3894e6d771..cf049c5a13 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -56,16 +56,7 @@ StereoResampler::StereoResampler() : m_bufsize(MAX_SAMPLES_DEFAULT) - , m_lowwatermark(LOW_WATERMARK_DEFAULT) - , m_input_sample_rate(44100) - , m_indexW(0) - , m_indexR(0) - , m_numLeftI(0.0f) - , m_frac(0) - , underrunCount_(0) - , overrunCount_(0) - , sample_rate_(0.0f) - , lastBufSize_(0) { + , m_lowwatermark(LOW_WATERMARK_DEFAULT) { // Need to have space for the worst case in case it changes. m_buffer = new int16_t[MAX_SAMPLES_EXTRA * 2](); @@ -75,7 +66,9 @@ StereoResampler::StereoResampler() // If framerate is "close"... if (refresh != 60.0f && refresh > 50.0f && refresh < 70.0f) { - SetInputSampleRate((int)(44100 * (refresh / 60.0f))); + int input_sample_rate = (int)(44100 * (refresh / 60.0f)); + ILOG("StereoResampler: Adjusting target sample rate to %dHz", input_sample_rate); + SetInputSampleRate(input_sample_rate); } UpdateBufferSize(); @@ -273,16 +266,32 @@ void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) lastPushSize_ = num_samples; } -void StereoResampler::GetAudioDebugStats(AudioDebugStats *stats) { - stats->buffered = lastBufSize_; - stats->underrunCount += underrunCount_; +void StereoResampler::GetAudioDebugStats(char *buf, size_t bufSize) { + snprintf(buf, bufSize, + "Audio buffer: %d/%d (low watermark: %d)\n" + "Underruns: %d\n" + "Overruns: %d\n" + "Sample rate: %d (input: %d)\n" + "Push size: %d\n", + lastBufSize_, + m_bufsize * 2, + m_lowwatermark, + underrunCountTotal_, + overrunCountTotal_, + (int)sample_rate_, + m_input_sample_rate, + lastPushSize_); + underrunCountTotal_ += underrunCount_; + overrunCountTotal_ += overrunCount_; underrunCount_ = 0; - stats->overrunCount += overrunCount_; overrunCount_ = 0; - stats->watermark = m_lowwatermark; - stats->bufsize = m_bufsize * 2; - stats->instantSampleRate = (int)sample_rate_; - stats->lastPushSize = lastPushSize_; +} + +void StereoResampler::ResetStatCounters() { + underrunCount_ = 0; + overrunCount_ = 0; + underrunCountTotal_ = 0; + overrunCountTotal_ = 0; } void StereoResampler::SetInputSampleRate(unsigned int rate) { diff --git a/Core/HW/StereoResampler.h b/Core/HW/StereoResampler.h index 57dbe37773..64fd374302 100644 --- a/Core/HW/StereoResampler.h +++ b/Core/HW/StereoResampler.h @@ -42,7 +42,8 @@ public: void DoState(PointerWrap &p); - void GetAudioDebugStats(AudioDebugStats *stats); + void GetAudioDebugStats(char *buf, size_t bufSize); + void ResetStatCounters(); protected: void UpdateBufferSize(); @@ -50,15 +51,17 @@ protected: int m_bufsize; int m_lowwatermark; - unsigned int m_input_sample_rate; + unsigned int m_input_sample_rate = 44100; int16_t *m_buffer; - volatile u32 m_indexW; - volatile u32 m_indexR; - float m_numLeftI; - u32 m_frac; - int underrunCount_; - int overrunCount_; - float sample_rate_; - int lastBufSize_; - int lastPushSize_; + volatile u32 m_indexW = 0; + volatile u32 m_indexR = 0; + float m_numLeftI = 0.0f; + u32 m_frac = 0; + int underrunCount_ = 0; + int overrunCount_ = 0; + int underrunCountTotal_ = 0; + int overrunCountTotal_ = 0; + float sample_rate_ = 0.0; + int lastBufSize_ = 0; + int lastPushSize_ = 0; }; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index d3fd56050d..d32353f71d 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1265,19 +1265,8 @@ static void DrawDebugStats(DrawBuffer *draw2d, const Bounds &bounds) { static void DrawAudioDebugStats(DrawBuffer *draw2d, const Bounds &bounds) { FontID ubuntu24("UBUNTU24"); - char statbuf[1024] = { 0 }; - const AudioDebugStats *stats = __AudioGetDebugStats(); - snprintf(statbuf, sizeof(statbuf), - "Audio buffer: %d/%d (low watermark: %d)\n" - "Underruns: %d\n" - "Overruns: %d\n" - "Sample rate: %d\n" - "Push size: %d\n", - stats->buffered, stats->bufsize, stats->watermark, - stats->underrunCount, - stats->overrunCount, - stats->instantSampleRate, - stats->lastPushSize); + char statbuf[4096] = { 0 }; + __AudioGetDebugStats(statbuf, sizeof(statbuf)); draw2d->SetFontScale(0.7f, 0.7f); draw2d->DrawTextRect(ubuntu24, statbuf, bounds.x + 11, bounds.y + 31, bounds.w - 20, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT); draw2d->DrawTextRect(ubuntu24, statbuf, bounds.x + 10, bounds.y + 30, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT); From 31ffbbf44fec32638f6b082912f1876fba8c4236 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 15 May 2020 22:24:55 -0700 Subject: [PATCH 14/52] GPU: Move things around to be per postshader. --- GPU/Common/PresentationCommon.cpp | 86 ++++++++++++++++++++++--------- GPU/Common/PresentationCommon.h | 4 +- 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 85ad7e0bae..f57f7d9c6e 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -146,7 +146,7 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer float v_pixel_delta = 1.0f / renderHeight_; if (postShaderAtOutputResolution_) { float x, y, w, h; - CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, g_Config.iInternalScreenRotation); u_pixel_delta = 1.0f / w; v_pixel_delta = 1.0f / h; } @@ -194,6 +194,18 @@ bool PresentationCommon::UpdatePostShader() { if (shaderInfo == nullptr) return false; + bool pipeline = BuildPostShader(shaderInfo, nullptr); + if (!pipeline) { + DestroyPostShader(); + return false; + } + + usePostShader_ = true; + postShaderAtOutputResolution_ = shaderInfo->outputResolution; + return true; +} + +bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const ShaderInfo *next) { std::string vsSourceGLSL = ReadShaderSrc(shaderInfo->vertexShaderFile); std::string fsSourceGLSL = ReadShaderSrc(shaderInfo->fragmentShaderFile); if (vsSourceGLSL.empty() || fsSourceGLSL.empty()) { @@ -226,15 +238,38 @@ bool PresentationCommon::UpdatePostShader() { if (!pipeline) return false; - // No depth/stencil for post processing - Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ (int)renderWidth_, (int)renderHeight_, 1, 1, false, Draw::FBO_8888 }); - if (fbo) + if (!shaderInfo->outputResolution || next) { + int nextWidth = renderWidth_; + int nextHeight = renderHeight_; + + if (next && next->isUpscalingFilter) { + // Force 1x for this shader, so the next can upscale. + const bool isPortrait = g_Config.IsPortrait(); + nextWidth = isPortrait ? 272 : 480; + nextHeight = isPortrait ? 480 : 272; + } else if (next && next->SSAAFilterLevel >= 2) { + // Increase the resolution this shader outputs for the next to SSAA. + nextWidth *= next->SSAAFilterLevel; + nextHeight *= next->SSAAFilterLevel; + } else if (shaderInfo->outputResolution) { + // If the current shader uses output res (not next), we will use output res for it. + float x, y, w, h; + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, g_Config.iInternalScreenRotation); + nextWidth = (int)w; + nextHeight = (int)h; + } + + // No depth/stencil for post processing + Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ nextWidth, nextHeight, 1, 1, false, Draw::FBO_8888 }); + if (!fbo) { + pipeline->Release(); + return false; + } postShaderFramebuffers_.push_back(fbo); + } - usePostShader_ = true; postShaderPipelines_.push_back(pipeline); - postShaderAtOutputResolution_ = shaderInfo->outputResolution; - postShaderIsUpscalingFilter_ = shaderInfo->isUpscalingFilter; + postShaderInfo_.push_back(*shaderInfo); return true; } @@ -268,10 +303,6 @@ void PresentationCommon::ShowPostShaderError(const std::string &errorString) { } } -void PresentationCommon::UpdateShaderInfo(const ShaderInfo *shaderInfo) { - postShaderAtOutputResolution_ = shaderInfo->outputResolution; -} - void PresentationCommon::DeviceLost() { DestroyDeviceObjects(); } @@ -365,16 +396,17 @@ void PresentationCommon::DestroyDeviceObjects() { DoRelease(srcTexture_); DoRelease(srcFramebuffer_); + restorePostShader_ = usePostShader_; DestroyPostShader(); } void PresentationCommon::DestroyPostShader() { - restorePostShader_ = usePostShader_; usePostShader_ = false; DoReleaseVector(postShaderModules_); DoReleaseVector(postShaderPipelines_); DoReleaseVector(postShaderFramebuffers_); + postShaderInfo_.clear(); } Draw::ShaderModule *PresentationCommon::CompileShaderModule(Draw::ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString) { @@ -452,12 +484,10 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u // This should auto-disable usePostShader_ and call ShowPostShaderError(). bool useNearest = flags & OutputFlags::NEAREST; - bool usePostShader = usePostShader_ && !(flags & OutputFlags::RB_SWIZZLE); + const bool usePostShader = usePostShader_ && !(flags & OutputFlags::RB_SWIZZLE); + const bool isFinalAtOutputResolution = usePostShader && postShaderFramebuffers_.size() < postShaderPipelines_.size(); bool usePostShaderOutput = false; - CardboardSettings cardboardSettings; - GetCardboardSettings(&cardboardSettings); - // These are the output coordinates. float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, uvRotation); @@ -517,7 +547,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u } } - if (usePostShader && postShaderAtOutputResolution_) { + if (isFinalAtOutputResolution) { // In this mode, we ignore the g_display_rot_matrix. Apply manually. if (g_display_rotation != DisplayRotation::ROTATE_0) { for (int i = 0; i < 4; i++) { @@ -530,17 +560,21 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u } } - if (usePostShader && postShaderFramebuffers_.size() == 1 && !postShaderAtOutputResolution_) { - draw_->BindFramebufferAsRenderTarget(postShaderFramebuffers_[0], { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); + Draw::Pipeline *postShaderPipeline = usePostShader ? postShaderPipelines_.front() : nullptr; + const ShaderInfo *shaderInfo = usePostShader ? &postShaderInfo_[0] : nullptr; + Draw::Framebuffer *postShaderFramebuffer = usePostShader && postShaderFramebuffers_.size() >= 1 ? postShaderFramebuffers_.front() : nullptr; + + if (usePostShader && postShaderFramebuffer) { + draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); BindSource(); int fbo_w, fbo_h; - draw_->GetFramebufferDimensions(postShaderFramebuffers_[0], &fbo_w, &fbo_h); + draw_->GetFramebufferDimensions(postShaderFramebuffer, &fbo_w, &fbo_h); Draw::Viewport viewport{ 0, 0, (float)fbo_w, (float)fbo_h, 0.0f, 1.0f }; draw_->SetViewports(1, &viewport); draw_->SetScissorRect(0, 0, fbo_w, fbo_h); - draw_->BindPipeline(postShaderPipelines_.front()); + draw_->BindPipeline(postShaderPipeline); draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms)); Draw::SamplerState *sampler = useNearest ? samplerNearest_ : samplerLinear_; @@ -566,12 +600,12 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u draw_->UpdateBuffer(vdata_, (const uint8_t *)verts, 0, postVertsOffset, Draw::UPDATE_DISCARD); } - if (postShaderIsUpscalingFilter_) + if (usePostShader && shaderInfo->isUpscalingFilter) useNearest = true; Draw::Pipeline *pipeline = flags & OutputFlags::RB_SWIZZLE ? texColorRBSwizzle_ : texColor_; - if (usePostShader && postShaderAtOutputResolution_) { - pipeline = postShaderPipelines_.front(); + if (isFinalAtOutputResolution) { + pipeline = postShaderPipeline; } draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); @@ -585,7 +619,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u BindSource(); } - if (usePostShader && postShaderAtOutputResolution_) { + if (isFinalAtOutputResolution) { draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms)); } else { Draw::VsTexColUB ub{}; @@ -604,6 +638,8 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u draw_->SetViewports(1, &viewport); }; + CardboardSettings cardboardSettings; + GetCardboardSettings(&cardboardSettings); if (cardboardSettings.enabled) { // This is what the left eye sees. setViewport(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index 7744d9b1e0..eda944be01 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -91,7 +91,6 @@ public: } bool UpdatePostShader(); - void UpdateShaderInfo(const ShaderInfo *shaderInfo); void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); @@ -112,6 +111,7 @@ protected: Draw::ShaderModule *CompileShaderModule(Draw::ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString); Draw::Pipeline *CreatePipeline(std::vector shaders, bool postShader, const Draw::UniformBufferDesc *uniformDesc); + bool BuildPostShader(const ShaderInfo *shaderInfo, const ShaderInfo *next); void BindSource(); @@ -126,6 +126,7 @@ protected: std::vector postShaderModules_; std::vector postShaderPipelines_; std::vector postShaderFramebuffers_; + std::vector postShaderInfo_; Draw::Texture *srcTexture_ = nullptr; Draw::Framebuffer *srcFramebuffer_ = nullptr; @@ -138,6 +139,5 @@ protected: bool usePostShader_ = false; bool restorePostShader_ = false; bool postShaderAtOutputResolution_ = false; - bool postShaderIsUpscalingFilter_ = false; ShaderLanguage lang_; }; From 5612f9b0af6ef7266424e3067f8fb5fa4828715d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 15 May 2020 22:39:27 -0700 Subject: [PATCH 15/52] GPU: Cleanup a comment, we use shorts now. --- GPU/Common/PresentationCommon.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index f57f7d9c6e..342758ea93 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -355,9 +355,9 @@ void PresentationCommon::CreateDeviceObjects() { using namespace Draw; vdata_ = draw_->CreateBuffer(sizeof(Vertex) * 8, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA); - // TODO: Use 4 and a strip? shorts? - idata_ = draw_->CreateBuffer(sizeof(uint16_t) * 6, BufferUsageFlag::DYNAMIC | BufferUsageFlag::INDEXDATA); + // TODO: Use 4 and a strip? + idata_ = draw_->CreateBuffer(sizeof(uint16_t) * 6, BufferUsageFlag::DYNAMIC | BufferUsageFlag::INDEXDATA); uint16_t indexes[] = { 0, 1, 2, 0, 2, 3 }; draw_->UpdateBuffer(idata_, (const uint8_t *)indexes, 0, sizeof(indexes), Draw::UPDATE_DISCARD); From 2d8ea9576a338599fccdb6a3c6a4e8da507bd576 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 15 May 2020 23:05:05 -0700 Subject: [PATCH 16/52] GPU: Process all postshader pipelines. --- GPU/Common/PresentationCommon.cpp | 64 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 342758ea93..9a28a2d2b5 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -560,26 +560,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u } } - Draw::Pipeline *postShaderPipeline = usePostShader ? postShaderPipelines_.front() : nullptr; - const ShaderInfo *shaderInfo = usePostShader ? &postShaderInfo_[0] : nullptr; - Draw::Framebuffer *postShaderFramebuffer = usePostShader && postShaderFramebuffers_.size() >= 1 ? postShaderFramebuffers_.front() : nullptr; - - if (usePostShader && postShaderFramebuffer) { - draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); - BindSource(); - - int fbo_w, fbo_h; - draw_->GetFramebufferDimensions(postShaderFramebuffer, &fbo_w, &fbo_h); - Draw::Viewport viewport{ 0, 0, (float)fbo_w, (float)fbo_h, 0.0f, 1.0f }; - draw_->SetViewports(1, &viewport); - draw_->SetScissorRect(0, 0, fbo_w, fbo_h); - - draw_->BindPipeline(postShaderPipeline); - draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms)); - - Draw::SamplerState *sampler = useNearest ? samplerNearest_ : samplerLinear_; - draw_->BindSamplerStates(0, 1, &sampler); - + if (usePostShader) { bool flipped = flags & OutputFlags::POSITION_FLIPPED; float post_v0 = !flipped ? 1.0f : 0.0f; float post_v1 = !flipped ? 0.0f : 1.0f; @@ -587,25 +568,48 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u verts[5] = { -1, 1, 0, 0, post_v0, 0xFFFFFFFF }; // BL verts[6] = { 1, 1, 0, 1, post_v0, 0xFFFFFFFF }; // BR verts[7] = { 1, -1, 0, 1, post_v1, 0xFFFFFFFF }; // TR - draw_->UpdateBuffer(vdata_, (const uint8_t *)verts, 0, sizeof(verts), Draw::UPDATE_DISCARD); - draw_->BindVertexBuffers(0, 1, &vdata_, &postVertsOffset); - draw_->BindIndexBuffer(idata_, 0); - draw_->DrawIndexed(6, 0); - draw_->BindIndexBuffer(nullptr, 0); + for (size_t i = 0; i < postShaderFramebuffers_.size(); ++i) { + Draw::Pipeline *postShaderPipeline = postShaderPipelines_[i]; + const ShaderInfo *shaderInfo = &postShaderInfo_[i]; + Draw::Framebuffer *postShaderFramebuffer = postShaderFramebuffers_[i]; - usePostShaderOutput = true; + draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); + if (usePostShaderOutput) { + draw_->BindFramebufferAsTexture(postShaderFramebuffers_[i - 1], 0, Draw::FB_COLOR_BIT, 0); + } else { + BindSource(); + } + + int fbo_w, fbo_h; + draw_->GetFramebufferDimensions(postShaderFramebuffer, &fbo_w, &fbo_h); + Draw::Viewport viewport{ 0, 0, (float)fbo_w, (float)fbo_h, 0.0f, 1.0f }; + draw_->SetViewports(1, &viewport); + draw_->SetScissorRect(0, 0, fbo_w, fbo_h); + + draw_->BindPipeline(postShaderPipeline); + draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms)); + + Draw::SamplerState *sampler = useNearest ? samplerNearest_ : samplerLinear_; + draw_->BindSamplerStates(0, 1, &sampler); + + draw_->BindVertexBuffers(0, 1, &vdata_, &postVertsOffset); + draw_->BindIndexBuffer(idata_, 0); + draw_->DrawIndexed(6, 0); + draw_->BindIndexBuffer(nullptr, 0); + + usePostShaderOutput = true; + if (shaderInfo->isUpscalingFilter) + useNearest = true; + } } else { draw_->UpdateBuffer(vdata_, (const uint8_t *)verts, 0, postVertsOffset, Draw::UPDATE_DISCARD); } - if (usePostShader && shaderInfo->isUpscalingFilter) - useNearest = true; - Draw::Pipeline *pipeline = flags & OutputFlags::RB_SWIZZLE ? texColorRBSwizzle_ : texColor_; if (isFinalAtOutputResolution) { - pipeline = postShaderPipeline; + pipeline = postShaderPipelines_.back(); } draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); From b79ecc159f9233c9b9b470987a321a12ec49b478 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 15 May 2020 23:41:13 -0700 Subject: [PATCH 17/52] GPU: Update postshader uniforms for each. --- GPU/Common/FramebufferCommon.cpp | 14 ++++------ GPU/Common/PresentationCommon.cpp | 46 +++++++++++++++++++------------ GPU/Common/PresentationCommon.h | 17 +++++++----- GPU/Software/SoftGpu.cpp | 6 ++-- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 52be6bce2d..8d3b0d42c4 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -791,10 +791,9 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu flags |= OutputFlags::POSITION_FLIPPED; } - PostShaderUniforms uniforms{}; - presentation_->CalculatePostShaderUniforms(512, 272, textureCache_->VideoIsPlaying(), &uniforms); - presentation_->SourceTexture(pixelsTex); - presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1, uniforms); + presentation_->UpdateUniforms(textureCache_->VideoIsPlaying()); + presentation_->SourceTexture(pixelsTex, 512, 272); + presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1); pixelsTex->Release(); // PresentationCommon sets all kinds of state, we can't rely on anything. @@ -954,12 +953,11 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) { flags |= OutputFlags::POSITION_FLIPPED; } - PostShaderUniforms uniforms{}; int actualWidth = (vfb->bufferWidth * vfb->renderWidth) / vfb->width; int actualHeight = (vfb->bufferHeight * vfb->renderHeight) / vfb->height; - presentation_->CalculatePostShaderUniforms(actualWidth, actualHeight, textureCache_->VideoIsPlaying(), &uniforms); - presentation_->SourceFramebuffer(vfb->fbo); - presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1, uniforms); + presentation_->UpdateUniforms(textureCache_->VideoIsPlaying()); + presentation_->SourceFramebuffer(vfb->fbo, actualWidth, actualHeight); + presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1); } else if (useBufferedRendering_) { WARN_LOG(FRAMEBUF, "Current VFB lacks an FBO: %08x", vfb->fb_address); } diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 9a28a2d2b5..4074e93806 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -139,17 +139,11 @@ void PresentationCommon::GetCardboardSettings(CardboardSettings *cardboardSettin cardboardSettings->screenHeight = cardboardScreenHeight; } -void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, bool hasVideo, PostShaderUniforms *uniforms) { +void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, int targetWidth, int targetHeight, const ShaderInfo *shaderInfo, PostShaderUniforms *uniforms) { float u_delta = 1.0f / bufferWidth; float v_delta = 1.0f / bufferHeight; - float u_pixel_delta = 1.0f / renderWidth_; - float v_pixel_delta = 1.0f / renderHeight_; - if (postShaderAtOutputResolution_) { - float x, y, w, h; - CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, g_Config.iInternalScreenRotation); - u_pixel_delta = 1.0f / w; - v_pixel_delta = 1.0f / h; - } + float u_pixel_delta = 1.0f / targetWidth; + float v_pixel_delta = 1.0f / targetHeight; int flipCount = __DisplayGetFlipCount(); int vCount = __DisplayGetVCount(); float time[4] = { time_now(), (vCount % 60) * 1.0f / 60.0f, (float)vCount, (float)(flipCount % 60) }; @@ -159,7 +153,7 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer uniforms->pixelDelta[0] = u_pixel_delta; uniforms->pixelDelta[1] = v_pixel_delta; memcpy(uniforms->time, time, 4 * sizeof(float)); - uniforms->video = hasVideo; + uniforms->video = hasVideo_ ? 1.0f : 0.0f; // The shader translator tacks this onto our shaders, if we don't set it they render garbage. uniforms->gl_HalfPixel[0] = u_pixel_delta * 0.5f; @@ -201,7 +195,6 @@ bool PresentationCommon::UpdatePostShader() { } usePostShader_ = true; - postShaderAtOutputResolution_ = shaderInfo->outputResolution; return true; } @@ -449,20 +442,24 @@ Draw::ShaderModule *PresentationCommon::CompileShaderModule(Draw::ShaderStage st return shader; } -void PresentationCommon::SourceTexture(Draw::Texture *texture) { +void PresentationCommon::SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight) { DoRelease(srcTexture_); DoRelease(srcFramebuffer_); texture->AddRef(); srcTexture_ = texture; + srcWidth_ = bufferWidth; + srcHeight_ = bufferHeight; } -void PresentationCommon::SourceFramebuffer(Draw::Framebuffer *fb) { +void PresentationCommon::SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight) { DoRelease(srcTexture_); DoRelease(srcFramebuffer_); fb->AddRef(); srcFramebuffer_ = fb; + srcWidth_ = bufferWidth; + srcHeight_ = bufferHeight; } void PresentationCommon::BindSource() { @@ -475,7 +472,11 @@ void PresentationCommon::BindSource() { } } -void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1, const PostShaderUniforms &uniforms) { +void PresentationCommon::UpdateUniforms(bool hasVideo) { + hasVideo_ = hasVideo; +} + +void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1) { // Make sure Direct3D 11 clears state, since we set shaders outside Draw. draw_->BindPipeline(nullptr); @@ -487,6 +488,8 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u const bool usePostShader = usePostShader_ && !(flags & OutputFlags::RB_SWIZZLE); const bool isFinalAtOutputResolution = usePostShader && postShaderFramebuffers_.size() < postShaderPipelines_.size(); bool usePostShaderOutput = false; + int lastWidth = srcWidth_; + int lastHeight = srcHeight_; // These are the output coordinates. float x, y, w, h; @@ -582,11 +585,14 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u BindSource(); } - int fbo_w, fbo_h; - draw_->GetFramebufferDimensions(postShaderFramebuffer, &fbo_w, &fbo_h); - Draw::Viewport viewport{ 0, 0, (float)fbo_w, (float)fbo_h, 0.0f, 1.0f }; + int nextWidth, nextHeight; + draw_->GetFramebufferDimensions(postShaderFramebuffer, &nextWidth, &nextHeight); + Draw::Viewport viewport{ 0, 0, (float)nextWidth, (float)nextHeight, 0.0f, 1.0f }; draw_->SetViewports(1, &viewport); - draw_->SetScissorRect(0, 0, fbo_w, fbo_h); + draw_->SetScissorRect(0, 0, nextWidth, nextHeight); + + PostShaderUniforms uniforms; + CalculatePostShaderUniforms(lastWidth, lastHeight, nextWidth, nextHeight, shaderInfo, &uniforms); draw_->BindPipeline(postShaderPipeline); draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms)); @@ -600,6 +606,8 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u draw_->BindIndexBuffer(nullptr, 0); usePostShaderOutput = true; + lastWidth = nextWidth; + lastHeight = nextHeight; if (shaderInfo->isUpscalingFilter) useNearest = true; } @@ -624,6 +632,8 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u } if (isFinalAtOutputResolution) { + PostShaderUniforms uniforms; + CalculatePostShaderUniforms(lastWidth, lastHeight, (int)w, (int)h, &postShaderInfo_.back(), &uniforms); draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms)); } else { Draw::VsTexColUB ub{}; diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index eda944be01..cff644927d 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -95,12 +95,10 @@ public: void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); - void GetCardboardSettings(CardboardSettings *cardboardSettings); - void CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, bool hasVideo, PostShaderUniforms *uniforms); - - void SourceTexture(Draw::Texture *texture); - void SourceFramebuffer(Draw::Framebuffer *fb); - void CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1, const PostShaderUniforms &uniforms); + void UpdateUniforms(bool hasVideo); + void SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight); + void SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight); + void CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1); protected: void CreateDeviceObjects(); @@ -115,6 +113,9 @@ protected: void BindSource(); + void GetCardboardSettings(CardboardSettings *cardboardSettings); + void CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, int targetWidth, int targetHeight, const ShaderInfo *shaderInfo, PostShaderUniforms *uniforms); + Draw::DrawContext *draw_; Draw::Pipeline *texColor_ = nullptr; Draw::Pipeline *texColorRBSwizzle_ = nullptr; @@ -130,6 +131,9 @@ protected: Draw::Texture *srcTexture_ = nullptr; Draw::Framebuffer *srcFramebuffer_ = nullptr; + int srcWidth_ = 0; + int srcHeight_ = 0; + bool hasVideo_ = false; int pixelWidth_ = 0; int pixelHeight_ = 0; @@ -138,6 +142,5 @@ protected: bool usePostShader_ = false; bool restorePostShader_ = false; - bool postShaderAtOutputResolution_ = false; ShaderLanguage lang_; }; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index a8a0163122..a45b4791dc 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -275,10 +275,8 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { break; } - PostShaderUniforms uniforms{}; - presentation_->CalculatePostShaderUniforms(desc.width, desc.height, false, &uniforms); - presentation_->SourceTexture(fbTex); - presentation_->CopyToOutput(outputFlags, g_Config.iInternalScreenRotation, u0, v0, u1, v1, uniforms); + presentation_->SourceTexture(fbTex, desc.width, desc.height); + presentation_->CopyToOutput(outputFlags, g_Config.iInternalScreenRotation, u0, v0, u1, v1); } void SoftGPU::CopyDisplayToOutput(bool reallyDirty) { From 477e988a687986ba5b8bd06aff6ccc5777212194 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 00:03:39 -0700 Subject: [PATCH 18/52] GPU: Allow postshaders to have parents. --- GPU/Common/PostShader.cpp | 49 +++++++++++++++++++++++++++---- GPU/Common/PostShader.h | 8 +++-- GPU/Common/PresentationCommon.cpp | 16 +++++----- UI/MiscScreens.cpp | 2 ++ Windows/MainWindowMenu.cpp | 2 ++ 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index a1b5ace7e4..66980d0739 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -36,8 +36,11 @@ static std::vector shaderInfo; // Additionally, scan the VFS assets. (TODO) void LoadPostShaderInfo(std::vector directories) { + std::vector notVisible; + shaderInfo.clear(); - ShaderInfo off; + ShaderInfo off{}; + off.visible = true; off.name = "Off"; off.section = "Off"; off.outputResolution = false; @@ -66,6 +69,14 @@ void LoadPostShaderInfo(std::vector directories) { off.settingStep4 = 0.01f; shaderInfo.push_back(off); + auto appendShader = [&](const ShaderInfo &info) { + auto beginErase = std::remove(shaderInfo.begin(), shaderInfo.end(), info.name); + if (beginErase != shaderInfo.end()) { + shaderInfo.erase(beginErase, shaderInfo.end()); + } + shaderInfo.push_back(info); + }; + for (size_t d = 0; d < directories.size(); d++) { std::vector fileInfo; getFilesInDir(directories[d].c_str(), &fileInfo, "ini:"); @@ -102,6 +113,8 @@ void LoadPostShaderInfo(std::vector directories) { std::string temp; info.section = section.name(); section.Get("Name", &info.name, section.name().c_str()); + section.Get("Parent", &info.parent, ""); + section.Get("Visible", &info.visible, true); section.Get("Fragment", &temp, ""); info.fragmentShaderFile = path + "/" + temp; section.Get("Vertex", &temp, ""); @@ -148,15 +161,20 @@ void LoadPostShaderInfo(std::vector directories) { continue; } - auto beginErase = std::find(shaderInfo.begin(), shaderInfo.end(), info.name); - if (beginErase != shaderInfo.end()) { - shaderInfo.erase(beginErase, shaderInfo.end()); + if (info.visible) { + appendShader(info); + } else { + notVisible.push_back(info); } - shaderInfo.push_back(info); } } } } + + // We always want the not visible ones at the end. Makes menus easier. + for (const auto &info : notVisible) { + appendShader(info); + } } // Scans the directories for shader ini files and collects info about all the shaders found. @@ -167,7 +185,7 @@ void ReloadAllPostShaderInfo() { LoadPostShaderInfo(directories); } -const ShaderInfo *GetPostShaderInfo(std::string name) { +const ShaderInfo *GetPostShaderInfo(const std::string &name) { for (size_t i = 0; i < shaderInfo.size(); i++) { if (shaderInfo[i].section == name) return &shaderInfo[i]; @@ -175,6 +193,25 @@ const ShaderInfo *GetPostShaderInfo(std::string name) { return nullptr; } +std::vector GetPostShaderChain(const std::string &name) { + std::vector backwards; + const ShaderInfo *shaderInfo = GetPostShaderInfo(name); + while (shaderInfo) { + backwards.push_back(shaderInfo); + + if (!shaderInfo->parent.empty() && shaderInfo->parent != "Off") { + shaderInfo = GetPostShaderInfo(shaderInfo->parent); + } else { + shaderInfo = nullptr; + } + } + + if (!backwards.empty()) + std::reverse(backwards.begin(), backwards.end()); + // Not backwards anymore. + return backwards; +} + const std::vector &GetAllPostShaderInfo() { return shaderInfo; } diff --git a/GPU/Common/PostShader.h b/GPU/Common/PostShader.h index 9be8063d77..50b36d205d 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -28,10 +28,13 @@ struct ShaderInfo { std::string iniFile; // which ini file was this definition in? So we can write settings back later std::string section; // ini file section. This is saved. std::string name; // Fancy display name. + std::string parent; // Parent shader ini section name. std::string fragmentShaderFile; std::string vertexShaderFile; + // Show this shader in lists (i.e. not just for chaining.) + bool visible; // Run at output instead of input resolution bool outputResolution; // Use x1 rendering res + nearest screen scaling filter @@ -63,7 +66,7 @@ struct ShaderInfo { float settingStep4; // TODO: Add support for all kinds of fun options like mapping the depth buffer, - // SRGB texture reads, multiple shaders chained, etc. + // SRGB texture reads, etc. bool operator == (const std::string &other) { return name == other; @@ -75,5 +78,6 @@ struct ShaderInfo { void ReloadAllPostShaderInfo(); -const ShaderInfo *GetPostShaderInfo(std::string name); +const ShaderInfo *GetPostShaderInfo(const std::string &name); +std::vector GetPostShaderChain(const std::string &name); const std::vector &GetAllPostShaderInfo(); diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 4074e93806..58b954a17a 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -178,20 +178,22 @@ static std::string ReadShaderSrc(const std::string &filename) { // Note: called on resize and settings changes. bool PresentationCommon::UpdatePostShader() { - const ShaderInfo *shaderInfo = nullptr; + std::vector shaderInfo; if (g_Config.sPostShaderName != "Off") { ReloadAllPostShaderInfo(); - shaderInfo = GetPostShaderInfo(g_Config.sPostShaderName); + shaderInfo = GetPostShaderChain(g_Config.sPostShaderName); } DestroyPostShader(); - if (shaderInfo == nullptr) + if (shaderInfo.empty()) return false; - bool pipeline = BuildPostShader(shaderInfo, nullptr); - if (!pipeline) { - DestroyPostShader(); - return false; + for (int i = 0; i < shaderInfo.size(); ++i) { + const ShaderInfo *next = i + 1 < shaderInfo.size() ? shaderInfo[i + 1] : nullptr; + if (!BuildPostShader(shaderInfo[i], next)) { + DestroyPostShader(); + return false; + } } usePostShader_ = true; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 27fadc85fd..7795ffc65e 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -306,6 +306,8 @@ PostProcScreen::PostProcScreen(const std::string &title) : ListPopupScreen(title std::vector items; int selected = -1; for (int i = 0; i < (int)shaders_.size(); i++) { + if (!shaders_[i].visible) + continue; if (shaders_[i].section == g_Config.sPostShaderName) selected = i; items.push_back(ps->T(shaders_[i].section.c_str(), shaders_[i].name.c_str())); diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 31de1a4833..b5e1bb0eb0 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -181,6 +181,8 @@ namespace MainWindow { availableShaders.clear(); for (auto i = info.begin(); i != info.end(); ++i) { + if (!i->visible) + continue; int checkedStatus = MF_UNCHECKED; availableShaders.push_back(i->section); if (g_Config.sPostShaderName == i->section) { From d9195c68b310c08c1df9c2fcef00379521191768 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 00:31:14 -0700 Subject: [PATCH 19/52] GPU: Move calculating render res to presentation. Since it best understands what the first postshader wants. --- GPU/Common/FramebufferCommon.cpp | 40 ++++--------------------- GPU/Common/FramebufferCommon.h | 2 +- GPU/Common/PresentationCommon.cpp | 49 +++++++++++++++++++++++++++++++ GPU/Common/PresentationCommon.h | 2 ++ 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 8d3b0d42c4..e3701b6d48 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1669,41 +1669,13 @@ void FramebufferManagerCommon::SetSafeSize(u16 w, u16 h) { } void FramebufferManagerCommon::Resized() { - // Check if postprocessing shader is doing upscaling as it requires native resolution - const ShaderInfo *shaderInfo = nullptr; - if (g_Config.sPostShaderName != "Off") { - shaderInfo = GetPostShaderInfo(g_Config.sPostShaderName); - } - - postShaderIsUpscalingFilter_ = shaderInfo ? shaderInfo->isUpscalingFilter : false; - postShaderSSAAFilterLevel_ = shaderInfo ? shaderInfo->SSAAFilterLevel : 0; - - // Actually, auto mode should be more granular... - // Round up to a zoom factor for the render size. - int zoom = g_Config.iInternalResolution; - if (zoom == 0 || postShaderSSAAFilterLevel_ >= 2) { - // auto mode, use the longest dimension - if (!g_Config.IsPortrait()) { - zoom = (PSP_CoreParameter().pixelWidth + 479) / 480; - } else { - zoom = (PSP_CoreParameter().pixelHeight + 479) / 480; - } - if (postShaderSSAAFilterLevel_ >= 2) - zoom *= postShaderSSAAFilterLevel_; - } - if (zoom <= 1 || postShaderIsUpscalingFilter_) - zoom = 1; - - if (g_Config.IsPortrait()) { - PSP_CoreParameter().renderWidth = 272 * zoom; - PSP_CoreParameter().renderHeight = 480 * zoom; - } else { - PSP_CoreParameter().renderWidth = 480 * zoom; - PSP_CoreParameter().renderHeight = 272 * zoom; - } - gstate_c.skipDrawReason &= ~SKIPDRAW_NON_DISPLAYED_FB; + int w, h; + presentation_->CalculateRenderResolution(&w, &h, &postShaderIsUpscalingFilter_, &postShaderIsSupersampling_); + PSP_CoreParameter().renderWidth = w; + PSP_CoreParameter().renderHeight = h; + if (UpdateSize()) { DestroyAllFBOs(); } @@ -1785,7 +1757,7 @@ void FramebufferManagerCommon::ShowScreenResolution() { messageStream << PSP_CoreParameter().renderWidth << "x" << PSP_CoreParameter().renderHeight << " "; if (postShaderIsUpscalingFilter_) { messageStream << gr->T("(upscaling)") << " "; - } else if (postShaderSSAAFilterLevel_ >= 2) { + } else if (postShaderIsSupersampling_) { messageStream << gr->T("(supersampling)") << " "; } messageStream << gr->T("Window Size") << ": "; diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 72cd4dd15d..f95a12670f 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -383,7 +383,7 @@ protected: bool useBufferedRendering_ = false; bool postShaderIsUpscalingFilter_ = false; - int postShaderSSAAFilterLevel_ = 0; + bool postShaderIsSupersampling_ = false; std::vector vfbs_; std::vector bvfbs_; // blitting framebuffers (for download) diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 58b954a17a..7f3f08dc69 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -676,3 +676,52 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u draw_->BindPipeline(nullptr); } + +void PresentationCommon::CalculateRenderResolution(int *width, int *height, bool *upscaling, bool *ssaa) { + // Check if postprocessing shader is doing upscaling as it requires native resolution + std::vector shaderInfo; + if (g_Config.sPostShaderName != "Off") { + ReloadAllPostShaderInfo(); + shaderInfo = GetPostShaderChain(g_Config.sPostShaderName); + } + + bool firstIsUpscalingFilter = shaderInfo.empty() ? false : shaderInfo.front()->isUpscalingFilter; + int firstSSAAFilterLevel = shaderInfo.empty() ? 0 : shaderInfo.front()->SSAAFilterLevel; + + // Actually, auto mode should be more granular... + // Round up to a zoom factor for the render size. + int zoom = g_Config.iInternalResolution; + if (zoom == 0 || firstSSAAFilterLevel >= 2) { + // auto mode, use the longest dimension + if (!g_Config.IsPortrait()) { + zoom = (PSP_CoreParameter().pixelWidth + 479) / 480; + } else { + zoom = (PSP_CoreParameter().pixelHeight + 479) / 480; + } + if (firstSSAAFilterLevel >= 2) + zoom *= firstSSAAFilterLevel; + } + if (zoom <= 1 || firstIsUpscalingFilter) + zoom = 1; + + if (upscaling) { + *upscaling = firstIsUpscalingFilter; + for (auto &info : shaderInfo) { + *upscaling = *upscaling || info->isUpscalingFilter; + } + } + if (ssaa) { + *ssaa = firstSSAAFilterLevel >= 2; + for (auto &info : shaderInfo) { + *ssaa = *ssaa || info->SSAAFilterLevel >= 2; + } + } + + if (g_Config.IsPortrait()) { + *width = 272 * zoom; + *height = 480 * zoom; + } else { + *width = 480 * zoom; + *height = 272 * zoom; + } +} diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index cff644927d..eae2834abf 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -100,6 +100,8 @@ public: void SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight); void CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1); + void CalculateRenderResolution(int *width, int *height, bool *upscaling, bool *ssaa); + protected: void CreateDeviceObjects(); void DestroyDeviceObjects(); From 8a626dffc5ceb56eff895945e4c92e8546a64491 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 09:05:53 -0700 Subject: [PATCH 20/52] GPU: Force nearest only for the upscaling filter. So, if we upscale and then apply an effect, we won't use nearest on that effect. --- GPU/Common/PresentationCommon.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 7f3f08dc69..c263b6ab07 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -599,7 +599,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u draw_->BindPipeline(postShaderPipeline); draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms)); - Draw::SamplerState *sampler = useNearest ? samplerNearest_ : samplerLinear_; + Draw::SamplerState *sampler = useNearest || shaderInfo->isUpscalingFilter ? samplerNearest_ : samplerLinear_; draw_->BindSamplerStates(0, 1, &sampler); draw_->BindVertexBuffers(0, 1, &vdata_, &postVertsOffset); @@ -610,9 +610,10 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u usePostShaderOutput = true; lastWidth = nextWidth; lastHeight = nextHeight; - if (shaderInfo->isUpscalingFilter) - useNearest = true; } + + if (isFinalAtOutputResolution && postShaderInfo_.back().isUpscalingFilter) + useNearest = true; } else { draw_->UpdateBuffer(vdata_, (const uint8_t *)verts, 0, postVertsOffset, Draw::UPDATE_DISCARD); } From 978aa897686d287ff4229f1783ed50fb23c159e2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 09:29:45 -0700 Subject: [PATCH 21/52] GPU: Prevent infinite loops in postshader chains. --- GPU/Common/PostShader.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index 66980d0739..ea43ceda3b 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -204,6 +204,11 @@ std::vector GetPostShaderChain(const std::string &name) { } else { shaderInfo = nullptr; } + auto dup = std::find(backwards.begin(), backwards.end(), shaderInfo); + if (dup != backwards.end()) { + // Don't loop forever. + break; + } } if (!backwards.empty()) From 1e0c5db3a9decd57d0aa87f147a70638174b9b81 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 10:21:00 -0700 Subject: [PATCH 22/52] GPU: Keep last output size in chained postshaders. --- GPU/Common/PresentationCommon.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index c263b6ab07..f26da4e031 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -237,6 +237,10 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const Sha int nextWidth = renderWidth_; int nextHeight = renderHeight_; + // When chaining, we use the previous resolution as a base, rather than the render resolution. + if (!postShaderFramebuffers_.empty()) + draw_->GetFramebufferDimensions(postShaderFramebuffers_.back(), &nextWidth, &nextHeight); + if (next && next->isUpscalingFilter) { // Force 1x for this shader, so the next can upscale. const bool isPortrait = g_Config.IsPortrait(); From ff68a0d50fcf1012f3779169470f6834d6e9fe7e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 12:25:52 -0700 Subject: [PATCH 23/52] UI: Translate shader setting names. Also, only save settings in ini that have names. --- GPU/Common/PostShader.cpp | 73 +++++++++++---------------------------- GPU/Common/PostShader.h | 28 +++++---------- UI/GameSettingsScreen.cpp | 21 ++++------- 3 files changed, 35 insertions(+), 87 deletions(-) diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index ea43ceda3b..c8aacbf843 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -43,30 +43,13 @@ void LoadPostShaderInfo(std::vector directories) { off.visible = true; off.name = "Off"; off.section = "Off"; - off.outputResolution = false; - off.isUpscalingFilter = false; - off.SSAAFilterLevel = 0; - off.requires60fps = false; - off.settingName1 = ""; - off.settingValue1 = 0.0f; - off.minSettingValue1 = -1.0f; - off.maxSettingValue1 = 1.0f; - off.settingStep1 = 0.01f; - off.settingName2 = ""; - off.settingValue2 = 0.0f; - off.minSettingValue2 = -1.0f; - off.maxSettingValue2 = 1.0f; - off.settingStep2 = 0.01f; - off.settingName3 = ""; - off.settingValue3 = 0.0f; - off.minSettingValue3 = -1.0f; - off.maxSettingValue3 = 1.0f; - off.settingStep3 = 0.01f; - off.settingName4 = ""; - off.settingValue4 = 0.0f; - off.minSettingValue4 = -1.0f; - off.maxSettingValue4 = 1.0f; - off.settingStep4 = 0.01f; + for (size_t i = 0; i < ARRAY_SIZE(off.settings); ++i) { + off.settings[i].name = ""; + off.settings[i].value = 0.0f; + off.settings[i].minValue = -1.0f; + off.settings[i].maxValue = 1.0f; + off.settings[i].step = 0.01f; + } shaderInfo.push_back(off); auto appendShader = [&](const ShaderInfo &info) { @@ -123,35 +106,21 @@ void LoadPostShaderInfo(std::vector directories) { section.Get("Upscaling", &info.isUpscalingFilter, false); section.Get("SSAA", &info.SSAAFilterLevel, 0); section.Get("60fps", &info.requires60fps, false); - section.Get("SettingName1", &info.settingName1, ""); - section.Get("SettingDefaultValue1", &info.settingValue1, 0.0f); - section.Get("SettingMinValue1", &info.minSettingValue1, -1.0f); - section.Get("SettingMaxValue1", &info.maxSettingValue1, 1.0f); - section.Get("SettingStep1", &info.settingStep1, 0.01f); - section.Get("SettingName2", &info.settingName2, ""); - section.Get("SettingDefaultValue2", &info.settingValue2, 0.0f); - section.Get("SettingMinValue2", &info.minSettingValue2, -1.0f); - section.Get("SettingMaxValue2", &info.maxSettingValue2, 1.0f); - section.Get("SettingStep2", &info.settingStep2, 0.01f); - section.Get("SettingName3", &info.settingName3, ""); - section.Get("SettingDefaultValue3", &info.settingValue3, 0.0f); - section.Get("SettingMinValue3", &info.minSettingValue3, -1.0f); - section.Get("SettingMaxValue3", &info.maxSettingValue3, 1.0f); - section.Get("SettingStep3", &info.settingStep3, 0.01f); - section.Get("SettingName4", &info.settingName4, ""); - section.Get("SettingDefaultValue4", &info.settingValue4, 0.0f); - section.Get("SettingMinValue4", &info.minSettingValue4, -1.0f); - section.Get("SettingMaxValue4", &info.maxSettingValue4, 1.0f); - section.Get("SettingStep4", &info.settingStep4, 0.01f); - if (g_Config.mPostShaderSetting.find(info.section + "SettingValue1") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue1", info.settingValue1)); - if (g_Config.mPostShaderSetting.find(info.section + "SettingValue2") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue2", info.settingValue2)); - if (g_Config.mPostShaderSetting.find(info.section + "SettingValue3") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue3", info.settingValue3)); - if (g_Config.mPostShaderSetting.find(info.section + "SettingValue4") == g_Config.mPostShaderSetting.end()) - g_Config.mPostShaderSetting.insert(std::pair(info.section + "SettingValue4", info.settingValue4)); + for (size_t i = 0; i < ARRAY_SIZE(info.settings); ++i) { + auto &setting = info.settings[i]; + section.Get(StringFromFormat("SettingName%d", i + 1).c_str(), &setting.name, ""); + section.Get(StringFromFormat("SettingDefaultValue%d", i + 1).c_str(), &setting.value, 0.0f); + section.Get(StringFromFormat("SettingMinValue%d", i + 1).c_str(), &setting.minValue, -1.0f); + section.Get(StringFromFormat("SettingMaxValue%d", i + 1).c_str(), &setting.maxValue, 1.0f); + section.Get(StringFromFormat("SettingStep%d", i + 1).c_str(), &setting.step, 0.01f); + + // Populate the default setting value. + std::string section = StringFromFormat("%sSettingValue%d", info.section.c_str(), i + 1); + if (!setting.name.empty() && g_Config.mPostShaderSetting.find(section) == g_Config.mPostShaderSetting.end()) { + g_Config.mPostShaderSetting.insert(std::pair(section, setting.value)); + } + } // Let's ignore shaders we can't support. TODO: Not a very good check if (gl_extensions.IsGLES && !gl_extensions.GLES3) { diff --git a/GPU/Common/PostShader.h b/GPU/Common/PostShader.h index 50b36d205d..86fd7b77da 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -44,26 +44,14 @@ struct ShaderInfo { // Force constant/max refresh for animated filters bool requires60fps; - std::string settingName1; - float settingValue1; - float maxSettingValue1; - float minSettingValue1; - float settingStep1; - std::string settingName2; - float settingValue2; - float maxSettingValue2; - float minSettingValue2; - float settingStep2; - std::string settingName3; - float settingValue3; - float maxSettingValue3; - float minSettingValue3; - float settingStep3; - std::string settingName4; - float settingValue4; - float maxSettingValue4; - float minSettingValue4; - float settingStep4; + struct Setting { + std::string name; + float value; + float maxValue; + float minValue; + float step; + }; + Setting settings[4]; // TODO: Add support for all kinds of fun options like mapping the depth buffer, // SRGB texture reads, etc. diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5429fb08ef..b60d0c7a39 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -291,21 +291,12 @@ void GameSettingsScreen::CreateViews() { }); const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sPostShaderName); - if (shaderInfo && !shaderInfo->settingName1.empty()) { - auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"]; - graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue1, shaderInfo->maxSettingValue1, shaderInfo->settingName1, shaderInfo->settingStep1, screenManager())); - } - if (shaderInfo && !shaderInfo->settingName2.empty()) { - auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"]; - graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue2, shaderInfo->maxSettingValue2, shaderInfo->settingName2, shaderInfo->settingStep2, screenManager())); - } - if (shaderInfo && !shaderInfo->settingName3.empty()) { - auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"]; - graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue3, shaderInfo->maxSettingValue3, shaderInfo->settingName3, shaderInfo->settingStep3, screenManager())); - } - if (shaderInfo && !shaderInfo->settingName4.empty()) { - auto &value = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"]; - graphicsSettings->Add(new PopupSliderChoiceFloat(&value, shaderInfo->minSettingValue4, shaderInfo->maxSettingValue4, shaderInfo->settingName4, shaderInfo->settingStep4, screenManager())); + for (size_t i = 0; shaderInfo && i < ARRAY_SIZE(shaderInfo->settings); ++i) { + auto &setting = shaderInfo->settings[i]; + if (!setting.name.empty()) { + auto &value = g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderInfo->section.c_str(), i + 1)]; + graphicsSettings->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, ps->T(setting.name), setting.step, screenManager())); + } } #if !defined(MOBILE_DEVICE) From cb739fc0b9cf0b6d78899c5b1c57ce11eac04059 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 12:29:22 -0700 Subject: [PATCH 24/52] GPU: Allow each shader in chain to have settings. It'll be up to the author to make the setting names make sense, though. They'll show from first shader to last. --- GPU/Common/PresentationCommon.cpp | 8 ++++---- UI/GameSettingsScreen.cpp | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index f26da4e031..c8b638f3ce 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -159,10 +159,10 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer uniforms->gl_HalfPixel[0] = u_pixel_delta * 0.5f; uniforms->gl_HalfPixel[1] = v_pixel_delta * 0.5f; - uniforms->setting[0] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue1"];; - uniforms->setting[1] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue2"]; - uniforms->setting[2] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue3"]; - uniforms->setting[3] = g_Config.mPostShaderSetting[g_Config.sPostShaderName + "SettingValue4"]; + uniforms->setting[0] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue1"];; + uniforms->setting[1] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue2"]; + uniforms->setting[2] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue3"]; + uniforms->setting[3] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue4"]; } static std::string ReadShaderSrc(const std::string &filename) { diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index b60d0c7a39..451c7b45a1 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -290,12 +290,14 @@ void GameSettingsScreen::CreateViews() { return g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; }); - const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sPostShaderName); - for (size_t i = 0; shaderInfo && i < ARRAY_SIZE(shaderInfo->settings); ++i) { - auto &setting = shaderInfo->settings[i]; - if (!setting.name.empty()) { - auto &value = g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderInfo->section.c_str(), i + 1)]; - graphicsSettings->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, ps->T(setting.name), setting.step, screenManager())); + auto shaderChain = GetPostShaderChain(g_Config.sPostShaderName); + for (auto shaderInfo : shaderChain) { + for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) { + auto &setting = shaderInfo->settings[i]; + if (!setting.name.empty()) { + auto &value = g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderInfo->section.c_str(), i + 1)]; + graphicsSettings->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, ps->T(setting.name), setting.step, screenManager())); + } } } From bab907f7920be7d816c096275da1b815abd70b69 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 21:12:31 -0700 Subject: [PATCH 25/52] arm64jit: Minor cleanup. --- Common/Arm64Emitter.cpp | 3 +-- Common/Arm64Emitter.h | 9 +++------ Common/CodeBlock.h | 13 +++++++------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Common/Arm64Emitter.cpp b/Common/Arm64Emitter.cpp index 3a18a308a2..6740989f04 100644 --- a/Common/Arm64Emitter.cpp +++ b/Common/Arm64Emitter.cpp @@ -278,7 +278,6 @@ static int EncodeSize(int size) { void ARM64XEmitter::SetCodePointer(u8* ptr) { m_code = ptr; - m_startcode = m_code; m_lastCacheFlushEnd = ptr; } @@ -911,7 +910,7 @@ void ARM64XEmitter::SetJumpTarget(FixupBranch const& branch) inst = ((branch.bit & 0x20) << 26) | (0x1B << 25) | (Not << 24) | ((branch.bit & 0x1F) << 19) | (MaskImm14(distance) << 5) | reg; } break; - case 5: // B (uncoditional) + case 5: // B (unconditional) _assert_msg_(DYNA_REC, IsInRangeImm26(distance), "%s(%d): Received too large distance: %llx", __FUNCTION__, branch.type, distance); inst = (0x5 << 26) | MaskImm26(distance); break; diff --git a/Common/Arm64Emitter.h b/Common/Arm64Emitter.h index a6ce09b3fa..99777b9254 100644 --- a/Common/Arm64Emitter.h +++ b/Common/Arm64Emitter.h @@ -340,9 +340,8 @@ class ARM64XEmitter friend class ARM64FloatEmitter; private: - u8* m_code; - u8* m_startcode; - u8* m_lastCacheFlushEnd; + u8 *m_code = nullptr; + u8 *m_lastCacheFlushEnd = nullptr; void EncodeCompareBranchInst(u32 op, ARM64Reg Rt, const void* ptr); void EncodeTestBranchInst(u32 op, ARM64Reg Rt, u8 bits, const void* ptr); @@ -382,14 +381,12 @@ protected: public: ARM64XEmitter() - : m_code(nullptr), m_startcode(nullptr), m_lastCacheFlushEnd(nullptr) { } - ARM64XEmitter(u8* code_ptr) { + ARM64XEmitter(u8 *code_ptr) { m_code = code_ptr; m_lastCacheFlushEnd = code_ptr; - m_startcode = code_ptr; } virtual ~ARM64XEmitter() diff --git a/Common/CodeBlock.h b/Common/CodeBlock.h index a0f5dd55f4..01f7502151 100644 --- a/Common/CodeBlock.h +++ b/Common/CodeBlock.h @@ -15,7 +15,7 @@ class CodeBlockCommon { public: - CodeBlockCommon() : region(nullptr), region_size(0) {} + CodeBlockCommon() {} virtual ~CodeBlockCommon() {} bool IsInSpace(const u8 *ptr) { @@ -34,8 +34,8 @@ public: } protected: - u8 *region; - size_t region_size; + u8 *region = nullptr; + size_t region_size = 0; }; template class CodeBlock : public CodeBlockCommon, public T { @@ -48,14 +48,14 @@ private: virtual void PoisonMemory(int offset) = 0; public: - CodeBlock() : writeStart_(nullptr) {} + CodeBlock() {} virtual ~CodeBlock() { if (region) FreeCodeSpace(); } // Call this before you generate any code. void AllocCodeSpace(int size) { region_size = size; // The protection will be set to RW if PlatformIsWXExclusive. - region = (u8*)AllocateExecutableMemory(region_size); + region = (u8 *)AllocateExecutableMemory(region_size); T::SetCodePointer(region); } @@ -124,6 +124,7 @@ public: } private: - const uint8_t *writeStart_; + // Note: this is a readable pointer. + const uint8_t *writeStart_ = nullptr; }; From d88099e701ce54b9c21726a10bd8ae73b42f77bb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 23:48:56 -0700 Subject: [PATCH 26/52] GLES: Specify buffer type just to be safe. Probably doesn't matter, but maybe some drivers take it as a hint? --- GPU/GLES/DepthBufferGLES.cpp | 3 +-- GPU/GLES/DrawEngineGLES.cpp | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/GPU/GLES/DepthBufferGLES.cpp b/GPU/GLES/DepthBufferGLES.cpp index cf399cff87..534ee73d14 100644 --- a/GPU/GLES/DepthBufferGLES.cpp +++ b/GPU/GLES/DepthBufferGLES.cpp @@ -93,8 +93,7 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address); - // TODO: On desktop, we can just directly download, but for now testing. - const bool useColorPath = true; // gl_extensions.IsGLES; + const bool useColorPath = gl_extensions.IsGLES; bool format16Bit = false; if (useColorPath) { diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 5587c93b54..ca32d8e508 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -426,7 +426,7 @@ void DrawEngineGLES::DoFlush() { // for a very minor speed boost. if (useElements) { size_t esz = sizeof(short) * indexGen.VertexCount(); - vai->ebo = render_->CreateBuffer(GL_ARRAY_BUFFER, esz, GL_STATIC_DRAW); + vai->ebo = render_->CreateBuffer(GL_ELEMENT_ARRAY_BUFFER, esz, GL_STATIC_DRAW); render_->BufferSubdata(vai->ebo, 0, esz, (uint8_t *)decIndex, false); } else { vai->ebo = 0; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 2ea228fcd3..20c5bf7800 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -161,8 +161,8 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps, bool ski case GLRInitStepType::BUFFER_SUBDATA: { GLRBuffer *buffer = step.buffer_subdata.buffer; - glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_); - glBufferSubData(GL_ARRAY_BUFFER, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data); + glBindBuffer(buffer->target_, buffer->buffer_); + glBufferSubData(buffer->target_, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data); if (step.buffer_subdata.deleteData) delete[] step.buffer_subdata.data; CHECK_GL_ERROR_IF_DEBUG(); From 5a5af3871f6c8178a6ffaf837153a9dfe9ba86d0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 23:49:45 -0700 Subject: [PATCH 27/52] GPU: Fix an overflow loading old save states. --- GPU/GPUCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index b183b2e2fd..3b5d220724 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2442,7 +2442,7 @@ void GPUCommon::DoState(PointerWrap &p) { DisplayList_v1 oldDL; p.Do(oldDL); // On 32-bit, they're the same, on 64-bit oldDL is bigger. - memcpy(&dls[i], &oldDL, sizeof(DisplayList)); + memcpy(&dls[i], &oldDL, sizeof(DisplayList_v1)); // Fix the other fields. Let's hope context wasn't important, it was a pointer. dls[i].context = 0; dls[i].offsetAddr = oldDL.offsetAddr; From bdeea1568a8ceb0a168ce429e721149a93864c3f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 23:53:43 -0700 Subject: [PATCH 28/52] cmake: Fix rpi4 build on newer gcc. This was wrong/causing a warning, and now seems to have caused the wrong code to build for some users (presumably a newer gcc.) --- cmake/Toolchains/raspberry.armv7.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Toolchains/raspberry.armv7.cmake b/cmake/Toolchains/raspberry.armv7.cmake index a72d23df46..b9a8b46a65 100644 --- a/cmake/Toolchains/raspberry.armv7.cmake +++ b/cmake/Toolchains/raspberry.armv7.cmake @@ -22,7 +22,7 @@ if(NOT PPSSPP_PI_MODEL4) endif() if(PPSSPP_PI_MODEL4) - set(ARCH_FLAGS "-march=armv7-a -mcpu=cortex-a72 -mtune=cortex-a72 -mfpu=neon-vfpv4 -mfloat-abi=hard") + set(ARCH_FLAGS "-march=armv7-a -mtune=cortex-a72 -mfpu=neon-vfpv4 -mfloat-abi=hard") else() set(ARCH_FLAGS "-mcpu=cortex-a7 -mfpu=neon -mfloat-abi=hard") endif() From 7e70fa1627c3d759ef937c1f56cf3209c16af04e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 May 2020 00:01:06 -0700 Subject: [PATCH 29/52] Mp4: Cleanup some parameters. --- Core/Dialog/PSPOskDialog.cpp | 1 - Core/HLE/sceMp4.cpp | 16 ++++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 958e0cb240..a475d0ec36 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -766,7 +766,6 @@ void PSPOskDialog::RenderKeyboard() float previewLeftSide = (480.0f - (12.0f * drawLimit)) / 2.0f; float title = (480.0f - (0.5f * drawLimit)) / 2.0f; - PPGeDrawText(oskDesc.c_str(), title , 20, PPGE_ALIGN_CENTER, 0.5f, CalcFadedColor(0xFFFFFFFF)); std::u16string result; diff --git a/Core/HLE/sceMp4.cpp b/Core/HLE/sceMp4.cpp index 99dd7ac4c7..ca2c3d3e11 100644 --- a/Core/HLE/sceMp4.cpp +++ b/Core/HLE/sceMp4.cpp @@ -76,16 +76,12 @@ static u32 sceMp4GetMovieInfo(u32 mp4, u32 unknown2) return 0; } -static u32 sceMp4TrackSampleBufAvailableSize(u32 mp4, u32 unknown2) -{ - ERROR_LOG(ME, "UNIMPL sceMp4TrackSampleBufAvailableSize(mp4 %i, unknown2 %08x)", mp4, unknown2); - return 0; +static u32 sceMp4TrackSampleBufAvailableSize(u32 mp4, u32 trackAddr, u32 writableSamplesAddr, u32 writableBytesAddr) { + return hleLogError(ME, 0, "unimplemented"); } -static u32 sceMp4Delete() -{ - ERROR_LOG(ME, "UNIMPL sceMp4Delete()"); - return 0; +static u32 sceMp4Delete(u32 mp4) { + return hleLogError(ME, 0, "unimplemented"); } static u32 sceMp4AacDecodeInitResource(int unknown) @@ -402,7 +398,7 @@ const HLEFunction sceMp4[] = {0X68651CBC, &WrapU_V, "sceMp4Init", 'x', "" }, {0X9042B257, &WrapU_V, "sceMp4Finish", 'x', "" }, {0XB1221EE7, &WrapU_UUUU, "sceMp4Create", 'x', "xxxx" }, - {0X538C2057, &WrapU_V, "sceMp4Delete", 'x', "" }, + {0X538C2057, &WrapU_U, "sceMp4Delete", 'x', "x" }, {0X113E9E7B, &WrapU_V, "sceMp4GetNumberOfMetaData", 'x', "" }, {0X7443AF1D, &WrapU_UU, "sceMp4GetMovieInfo", 'x', "xx" }, {0X5EB65F26, &WrapU_V, "sceMp4GetNumberOfSpecificTrack", 'x', "" }, @@ -418,7 +414,7 @@ const HLEFunction sceMp4[] = {0XF7C51EC1, &WrapU_V, "sceMp4GetSampleInfo", 'x', "" }, {0X74A1CA3E, &WrapU_V, "sceMp4SearchSyncSampleNum", 'x', "" }, {0XD8250B75, nullptr, "sceMp4PutSampleNum", '?', "" }, - {0X8754ECB8, &WrapU_UU, "sceMp4TrackSampleBufAvailableSize", 'x', "xx" }, + {0X8754ECB8, &WrapU_UUUU, "sceMp4TrackSampleBufAvailableSize", 'x', "xppp" }, {0X31BCD7E0, nullptr, "sceMp4TrackSampleBufPut", '?', "" }, {0X5601A6F0, &WrapU_UUUU, "sceMp4GetAacAu", 'x', "xxxx" }, {0X7663CB5C, &WrapU_UUUUU, "sceMp4AacDecode", 'x', "xxxxx" }, From 7910b4029a0d236b6aa2a39e4c4149e80b785330 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 22:12:35 -0700 Subject: [PATCH 30/52] arm64jit: Track writable and non-writable pointers. Switch uses different memory regions. We can handle this, might as well cleanup some const abuse. --- Common/Arm64Emitter.cpp | 24 ++++++++++++----- Common/Arm64Emitter.h | 21 ++++++++------- Common/ArmEmitter.cpp | 2 +- Common/ArmEmitter.h | 2 +- Common/CodeBlock.h | 38 +++++++++++++++++++++------ Common/x64Emitter.cpp | 2 +- Common/x64Emitter.h | 2 +- Core/MIPS/ARM/ArmJit.cpp | 6 ++--- Core/MIPS/ARM64/Arm64Jit.cpp | 10 +++---- Core/MIPS/JitCommon/JitBlockCache.cpp | 5 ++-- Core/MIPS/JitCommon/JitBlockCache.h | 2 +- Core/MIPS/x86/Jit.cpp | 2 +- GPU/Common/VertexDecoderArm.cpp | 2 +- GPU/Common/VertexDecoderArm64.cpp | 2 +- GPU/Common/VertexDecoderX86.cpp | 2 +- GPU/Software/SamplerX86.cpp | 4 +-- unittest/TestArm64Emitter.cpp | 2 +- 17 files changed, 81 insertions(+), 47 deletions(-) diff --git a/Common/Arm64Emitter.cpp b/Common/Arm64Emitter.cpp index 6740989f04..e3e148a60a 100644 --- a/Common/Arm64Emitter.cpp +++ b/Common/Arm64Emitter.cpp @@ -275,9 +275,14 @@ static int EncodeSize(int size) { } } -void ARM64XEmitter::SetCodePointer(u8* ptr) +ARM64XEmitter::ARM64XEmitter(const u8 *ptr, u8 *writePtr) { + SetCodePointer(ptr, writePtr); +} + +void ARM64XEmitter::SetCodePointer(const u8 *ptr, u8 *writePtr) { m_code = ptr; + m_writable = writePtr; m_lastCacheFlushEnd = ptr; } @@ -288,7 +293,7 @@ const u8* ARM64XEmitter::GetCodePointer() const u8* ARM64XEmitter::GetWritableCodePtr() { - return m_code; + return m_writable; } void ARM64XEmitter::ReserveCodeSpace(u32 bytes) @@ -320,11 +325,11 @@ void ARM64XEmitter::FlushIcache() m_lastCacheFlushEnd = m_code; } -void ARM64XEmitter::FlushIcacheSection(u8* start, u8* end) +void ARM64XEmitter::FlushIcacheSection(const u8 *start, const u8 *end) { #if defined(IOS) // Header file says this is equivalent to: sys_icache_invalidate(start, end - start); - sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); + sys_cache_control(kCacheFunctionPrepareForExecution, (void *)start, end - start); #elif PPSSPP_PLATFORM(WINDOWS) FlushInstructionCache(GetCurrentProcess(), start, end - start); #elif PPSSPP_ARCH(ARM64) @@ -919,7 +924,9 @@ void ARM64XEmitter::SetJumpTarget(FixupBranch const& branch) inst = (0x25 << 26) | MaskImm26(distance); break; } - *(u32*)branch.ptr = inst; + + ptrdiff_t writable = m_writable - m_code; + *(u32 *)(branch.ptr + writable) = inst; } FixupBranch ARM64XEmitter::CBZ(ARM64Reg Rt) @@ -3923,8 +3930,11 @@ void ARM64XEmitter::SUBSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) } void ARM64CodeBlock::PoisonMemory(int offset) { - u32* ptr = (u32*)(region + offset); - u32* maxptr = (u32*)(region + region_size - offset); + // So we can adjust region to writable space. Might be zero. + ptrdiff_t writable = m_writable - m_code; + + u32 *ptr = (u32 *)(region + offset + writable); + u32 *maxptr = (u32 *)(region + region_size - offset + writable); // If our memory isn't a multiple of u32 then this won't write the last remaining bytes with anything // Less than optimal, but there would be nothing we could do but throw a runtime warning anyway. // AArch64: 0xD4200000 = BRK 0 diff --git a/Common/Arm64Emitter.h b/Common/Arm64Emitter.h index 99777b9254..eb0fe1f1e5 100644 --- a/Common/Arm64Emitter.h +++ b/Common/Arm64Emitter.h @@ -162,7 +162,8 @@ enum RoundingMode { struct FixupBranch { - u8* ptr; + // Pointer to executable code address. + const u8 *ptr; // Type defines // 0 = CBZ (32bit) // 1 = CBNZ (32bit) @@ -338,10 +339,12 @@ public: class ARM64XEmitter { friend class ARM64FloatEmitter; + friend class ARM64CodeBlock; private: - u8 *m_code = nullptr; - u8 *m_lastCacheFlushEnd = nullptr; + const u8 *m_code = nullptr; + u8 *m_writable = nullptr; + const u8 *m_lastCacheFlushEnd = nullptr; void EncodeCompareBranchInst(u32 op, ARM64Reg Rt, const void* ptr); void EncodeTestBranchInst(u32 op, ARM64Reg Rt, u8 bits, const void* ptr); @@ -375,8 +378,9 @@ private: protected: inline void Write32(u32 value) { - *(u32*)m_code = value; + *(u32 *)m_writable = value; m_code += 4; + m_writable += 4; } public: @@ -384,23 +388,20 @@ public: { } - ARM64XEmitter(u8 *code_ptr) { - m_code = code_ptr; - m_lastCacheFlushEnd = code_ptr; - } + ARM64XEmitter(const u8 *codePtr, u8 *writablePtr); virtual ~ARM64XEmitter() { } - void SetCodePointer(u8* ptr); + void SetCodePointer(const u8 *ptr, u8 *writePtr); const u8* GetCodePointer() const; void ReserveCodeSpace(u32 bytes); const u8* AlignCode16(); const u8* AlignCodePage(); void FlushIcache(); - void FlushIcacheSection(u8* start, u8* end); + void FlushIcacheSection(const u8* start, const u8* end); u8* GetWritableCodePtr(); // FixupBranch branching diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 8626cdc434..dcaab522c5 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -586,7 +586,7 @@ void ARMXEmitter::QuickCallFunction(ARMReg reg, const void *func) { } } -void ARMXEmitter::SetCodePointer(u8 *ptr) +void ARMXEmitter::SetCodePointer(u8 *ptr, u8 *writePtr) { code = ptr; startcode = code; diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 85c2d916b7..b8116129ec 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -443,7 +443,7 @@ public: } virtual ~ARMXEmitter() {} - void SetCodePointer(u8 *ptr); + void SetCodePointer(u8 *ptr, u8 *writePtr); const u8 *GetCodePointer() const; void ReserveCodeSpace(u32 bytes); diff --git a/Common/CodeBlock.h b/Common/CodeBlock.h index 01f7502151..f60c4b9305 100644 --- a/Common/CodeBlock.h +++ b/Common/CodeBlock.h @@ -4,6 +4,7 @@ #pragma once +#include #include "Common.h" #include "MemoryUtil.h" @@ -22,7 +23,6 @@ public: return (ptr >= region) && (ptr < (region + region_size)); } - virtual void SetCodePtr(u8 *ptr) = 0; virtual const u8 *GetCodePtr() const = 0; u8 *GetBasePtr() { @@ -33,7 +33,13 @@ public: return ptr - region; } + virtual const u8 *GetCodePtrFromWritablePtr(u8 *ptr) = 0; + virtual u8 *GetWritablePtrFromCodePtr(const u8 *ptr) = 0; + protected: + virtual void SetCodePtr(u8 *ptr) = 0; + + // Note: this should be the readable/executable side if writable is a different pointer. u8 *region = nullptr; size_t region_size = 0; }; @@ -56,7 +62,8 @@ public: region_size = size; // The protection will be set to RW if PlatformIsWXExclusive. region = (u8 *)AllocateExecutableMemory(region_size); - T::SetCodePointer(region); + writableRegion = region; + T::SetCodePointer(region, writableRegion); } // Always clear code space with breakpoints, so that if someone accidentally executes @@ -104,27 +111,42 @@ public: ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE); FreeMemoryPages(region, region_size); region = nullptr; + writableRegion = nullptr; region_size = 0; } - void SetCodePtr(u8 *ptr) override { - T::SetCodePointer(ptr); - } - const u8 *GetCodePtr() const override { return T::GetCodePointer(); } - void ResetCodePtr(int offset) { - T::SetCodePointer(region + offset); + void ResetCodePtr(size_t offset) { + T::SetCodePointer(region + offset, writableRegion + offset); } size_t GetSpaceLeft() const { return region_size - (T::GetCodePointer() - region); } + const u8 *GetCodePtrFromWritablePtr(u8 *ptr) override { + // So we can adjust region to writable space. Might be zero. + ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer(); + return ptr - writable; + } + + u8 *GetWritablePtrFromCodePtr(const u8 *ptr) override { + // So we can adjust region to writable space. Might be zero. + ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer(); + return (u8 *)ptr + writable; + } + +protected: + void SetCodePtr(u8 *ptr) override { + T::SetCodePointer(ptr, GetWritablePtrFromCodePtr(ptr)); + } + private: // Note: this is a readable pointer. const uint8_t *writeStart_ = nullptr; + uint8_t *writableRegion = nullptr; }; diff --git a/Common/x64Emitter.cpp b/Common/x64Emitter.cpp index 539fc7eeae..df14332f13 100644 --- a/Common/x64Emitter.cpp +++ b/Common/x64Emitter.cpp @@ -96,7 +96,7 @@ enum NormalSSEOps }; -void XEmitter::SetCodePointer(u8 *ptr) +void XEmitter::SetCodePointer(u8 *ptr, u8 *writePtr) { code = ptr; } diff --git a/Common/x64Emitter.h b/Common/x64Emitter.h index 87ae096bb1..30309f71ba 100644 --- a/Common/x64Emitter.h +++ b/Common/x64Emitter.h @@ -372,7 +372,7 @@ public: void WriteModRM(int mod, int rm, int reg); void WriteSIB(int scale, int index, int base); - void SetCodePointer(u8 *ptr); + void SetCodePointer(u8 *ptr, u8 *writePtr); const u8 *GetCodePointer() const; void ReserveCodeSpace(int bytes); diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 5820509c78..f20e5e2600 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -304,17 +304,17 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b) JumpTarget backJump = GetCodePtr(); gpr.SetRegImm(R0, js.blockStart); B((const void *)outerLoopPCInR0); - b->checkedEntry = (u8 *)GetCodePtr(); + b->checkedEntry = GetCodePtr(); SetCC(CC_LT); B(backJump); SetCC(CC_AL); } else if (jo.useForwardJump) { - b->checkedEntry = (u8 *)GetCodePtr(); + b->checkedEntry = GetCodePtr(); SetCC(CC_LT); bail = B(); SetCC(CC_AL); } else { - b->checkedEntry = (u8 *)GetCodePtr(); + b->checkedEntry = GetCodePtr(); SetCC(CC_LT); gpr.SetRegImm(R0, js.blockStart); B((const void *)outerLoopPCInR0); diff --git a/Core/MIPS/ARM64/Arm64Jit.cpp b/Core/MIPS/ARM64/Arm64Jit.cpp index 8501379a0b..330c6b4761 100644 --- a/Core/MIPS/ARM64/Arm64Jit.cpp +++ b/Core/MIPS/ARM64/Arm64Jit.cpp @@ -300,13 +300,13 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) { const u8 *backJump = GetCodePtr(); MOVI2R(SCRATCH1, js.blockStart); B((const void *)outerLoopPCInSCRATCH1); - b->checkedEntry = (u8 *)GetCodePtr(); + b->checkedEntry = GetCodePtr(); B(CC_LT, backJump); } else if (jo.useForwardJump) { - b->checkedEntry = (u8 *)GetCodePtr(); + b->checkedEntry = GetCodePtr(); bail = B(CC_LT); } else if (jo.enableBlocklink) { - b->checkedEntry = (u8 *)GetCodePtr(); + b->checkedEntry = GetCodePtr(); MOVI2R(SCRATCH1, js.blockStart); FixupBranch skip = B(CC_GE); B((const void *)outerLoopPCInSCRATCH1); @@ -442,7 +442,7 @@ void Arm64Jit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) { if (PlatformIsWXExclusive()) { ProtectMemoryPages(exitPoint, 32, MEM_PROT_READ | MEM_PROT_WRITE); } - ARM64XEmitter emit(exitPoint); + ARM64XEmitter emit(GetCodePtrFromWritablePtr(exitPoint), exitPoint); emit.B(checkedEntry); // TODO: Write stuff after, convering up the now-unused instructions. emit.FlushIcache(); @@ -459,7 +459,7 @@ void Arm64Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) { ProtectMemoryPages(checkedEntry, 16, MEM_PROT_READ | MEM_PROT_WRITE); } - ARM64XEmitter emit(checkedEntry); + ARM64XEmitter emit(GetCodePtrFromWritablePtr(checkedEntry), checkedEntry); emit.MOVI2R(SCRATCH1, originalAddress); emit.STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc)); emit.B(MIPSComp::jit->GetDispatcher()); diff --git a/Core/MIPS/JitCommon/JitBlockCache.cpp b/Core/MIPS/JitCommon/JitBlockCache.cpp index 6958fc894a..fa9ca1edd8 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.cpp +++ b/Core/MIPS/JitCommon/JitBlockCache.cpp @@ -186,7 +186,7 @@ void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, cons // Make binary searches and stuff work ok b.normalEntry = codePtr; - b.checkedEntry = (u8 *)codePtr; // Ugh, casting away const.. + b.checkedEntry = codePtr; proxyBlockMap_.insert(std::make_pair(startAddress, num_blocks_)); AddBlockMap(num_blocks_); @@ -541,7 +541,8 @@ void JitBlockCache::DestroyBlock(int block_num, DestroyType type) { if (b->checkedEntry) { // We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive. if (type != DestroyType::CLEAR) { - MIPSComp::jit->UnlinkBlock(b->checkedEntry, b->originalAddress); + u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry); + MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress); } } else { ERROR_LOG(JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num); diff --git a/Core/MIPS/JitCommon/JitBlockCache.h b/Core/MIPS/JitCommon/JitBlockCache.h index ce609b8ada..3c51a67f66 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.h +++ b/Core/MIPS/JitCommon/JitBlockCache.h @@ -59,7 +59,7 @@ enum class DestroyType { struct JitBlock { bool ContainsAddress(u32 em_address); - u8 *checkedEntry; // not const, may need to write through this to unlink + const u8 *checkedEntry; // const, we have to translate to writable. const u8 *normalEntry; u8 *exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 9d3064dddd..a8050ac0ac 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -335,7 +335,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b) { js.PrefixStart(); // We add a check before the block, used when entering from a linked block. - b->checkedEntry = (u8 *)GetCodePtr(); + b->checkedEntry = GetCodePtr(); // Downcount flag check. The last block decremented downcounter, and the flag should still be available. FixupBranch skip = J_CC(CC_NS); MOV(32, MIPSSTATE_VAR(pc), Imm32(js.blockStart)); diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index 5467515e9f..f4fde1e097 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -285,7 +285,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int if (!CompileStep(dec, i)) { EndWrite(); // Reset the code ptr and return zero to indicate that we failed. - SetCodePtr(const_cast(start)); + ResetCodePtr(GetOffset(start)); char temp[1024] = {0}; dec.ToString(temp); INFO_LOG(G3D, "Could not compile vertex decoder: %s", temp); diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index 702dd6ae8d..a4714ee070 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -248,7 +248,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int if (!CompileStep(dec, i)) { EndWrite(); // Reset the code ptr (effectively undoing what we generated) and return zero to indicate that we failed. - SetCodePtr(const_cast(start)); + ResetCodePtr(GetOffset(start)); char temp[1024] = {0}; dec.ToString(temp); ERROR_LOG(G3D, "Could not compile vertex decoder, failed at step %d: %s", i, temp); diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index 1e63145628..e67b50fea0 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -251,7 +251,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int if (!CompileStep(dec, i)) { EndWrite(); // Reset the code ptr and return zero to indicate that we failed. - SetCodePtr(const_cast(start)); + ResetCodePtr(GetOffset(start)); return 0; } } diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index a0c0a3ea91..5fdca729f8 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -79,7 +79,7 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) { if (!Jit_ReadTextureFormat(id)) { EndWrite(); - SetCodePtr(const_cast(start)); + ResetCodePtr(GetOffset(start)); return nullptr; } @@ -106,7 +106,7 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) { if (!Jit_ReadTextureFormat(id)) { EndWrite(); - SetCodePtr(const_cast(nearest)); + ResetCodePtr(GetOffset(nearest)); return nullptr; } diff --git a/unittest/TestArm64Emitter.cpp b/unittest/TestArm64Emitter.cpp index 2efeb77847..af0f37ca45 100644 --- a/unittest/TestArm64Emitter.cpp +++ b/unittest/TestArm64Emitter.cpp @@ -38,7 +38,7 @@ bool TestArm64Emitter() { u32 code[512]; - ARM64XEmitter emitter((u8 *)code); + ARM64XEmitter emitter((u8 *)code, (u8 *)code); ARM64FloatEmitter fp(&emitter); emitter.MOVfromSP(X3); From 06c22497b7fff9ecd22f1833332543e2ee3f79f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 21:15:51 +0200 Subject: [PATCH 31/52] SDL: If we get an audio device with a different freq than 44100, that's actually OK. --- SDL/SDLMain.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 087478a081..382b770115 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -84,8 +84,8 @@ int getDisplayNumber(void) { return displayNumber; } -extern void mixaudio(void *userdata, Uint8 *stream, int len) { - NativeMix((short *)stream, len / 4); +void sdl_mixaudio_callback(void *userdata, Uint8 *stream, int len) { + NativeMix((short *)stream, len / (2 * 2)); } static SDL_AudioDeviceID audioDev = 0; @@ -98,7 +98,7 @@ static void InitSDLAudioDevice(const std::string &name = "") { fmt.format = AUDIO_S16; fmt.channels = 2; fmt.samples = 2048; - fmt.callback = &mixaudio; + fmt.callback = &sdl_mixaudio_callback; fmt.userdata = nullptr; std::string startDevice = name; @@ -122,7 +122,7 @@ static void InitSDLAudioDevice(const std::string &name = "") { } else { if (g_retFmt.samples != fmt.samples) // Notify, but still use it ELOG("Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples); - if (g_retFmt.freq != fmt.freq || g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) { + if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) { ELOG("Sound buffer format does not match requested format."); ELOG("Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq); ELOG("Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format); From 8b5b7d2496bce51699e7bb31ffa941d71040d8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 16 May 2020 22:24:20 +0200 Subject: [PATCH 32/52] Add some vaguely interesting stats --- Core/HW/StereoResampler.cpp | 27 +++++++++++++++++++++++++-- Core/HW/StereoResampler.h | 14 ++++++++++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index cf049c5a13..b8b2a07b7a 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -32,6 +32,7 @@ #include #include "base/logging.h" +#include "base/timeutil.h" #include "base/NativeApp.h" #include "Common/ChunkFile.h" #include "Common/MathUtil.h" @@ -212,6 +213,9 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool if (currentSample < numSamples * 2) underrunCount_++; + // Let's not count the padding here. + outputSampleCount_ += currentSample / 2; + // Padding with the last value to reduce clicking short s[2]; s[0] = clamp_s16(m_buffer[(indexR - 1) & INDEX_MASK]); @@ -233,6 +237,8 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool } void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) { + inputSampleCount_ += num_samples; + UpdateBufferSize(); const int INDEX_MASK = (m_bufsize * 2 - 1); // Cache access in non-volatile variable @@ -241,9 +247,10 @@ void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) u32 indexW = Common::AtomicLoad(m_indexW); u32 cap = m_bufsize * 2; - // If unthottling, no need to fill up the entire buffer, just screws up timing after releasing unthrottle. - if (PSP_CoreParameter().unthrottle) + // If unthrottling, no need to fill up the entire buffer, just screws up timing after releasing unthrottle. + if (PSP_CoreParameter().unthrottle) { cap = m_lowwatermark * 2; + } // Check if we have enough free space // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW @@ -267,11 +274,17 @@ void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) } void StereoResampler::GetAudioDebugStats(char *buf, size_t bufSize) { + double elapsed = real_time_now() - startTime_; + + double effective_input_sample_rate = (double)inputSampleCount_ / elapsed; + double effective_output_sample_rate = (double)outputSampleCount_ / elapsed; snprintf(buf, bufSize, "Audio buffer: %d/%d (low watermark: %d)\n" "Underruns: %d\n" "Overruns: %d\n" "Sample rate: %d (input: %d)\n" + "Effective input sample rate: %0.2f\n" + "Effective output sample rate: %0.2f\n" "Push size: %d\n", lastBufSize_, m_bufsize * 2, @@ -280,11 +293,18 @@ void StereoResampler::GetAudioDebugStats(char *buf, size_t bufSize) { overrunCountTotal_, (int)sample_rate_, m_input_sample_rate, + effective_input_sample_rate, + effective_output_sample_rate, lastPushSize_); underrunCountTotal_ += underrunCount_; overrunCountTotal_ += overrunCount_; underrunCount_ = 0; overrunCount_ = 0; + + // Try to remove the bias from the startup. + if (elapsed > 3.0) { + //ResetStatCounters(); + } } void StereoResampler::ResetStatCounters() { @@ -292,6 +312,9 @@ void StereoResampler::ResetStatCounters() { overrunCount_ = 0; underrunCountTotal_ = 0; overrunCountTotal_ = 0; + inputSampleCount_ = 0; + outputSampleCount_ = 0; + startTime_ = real_time_now(); } void StereoResampler::SetInputSampleRate(unsigned int rate) { diff --git a/Core/HW/StereoResampler.h b/Core/HW/StereoResampler.h index 64fd374302..9d00dd27ea 100644 --- a/Core/HW/StereoResampler.h +++ b/Core/HW/StereoResampler.h @@ -19,7 +19,7 @@ #pragma once -#include +#include #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" @@ -57,11 +57,17 @@ protected: volatile u32 m_indexR = 0; float m_numLeftI = 0.0f; u32 m_frac = 0; + float sample_rate_ = 0.0; + int lastBufSize_ = 0; + int lastPushSize_ = 0; + int underrunCount_ = 0; int overrunCount_ = 0; int underrunCountTotal_ = 0; int overrunCountTotal_ = 0; - float sample_rate_ = 0.0; - int lastBufSize_ = 0; - int lastPushSize_ = 0; + + int64_t inputSampleCount_ = 0; + int64_t outputSampleCount_ = 0; + + double startTime_ = 0.0; }; From c46424afd638ebb9dd4856f88daedb86f43a8802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 10:20:43 +0200 Subject: [PATCH 33/52] StereoResampler: Use C++ atomics --- Core/HW/StereoResampler.cpp | 14 +++++++------- Core/HW/StereoResampler.h | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index b8b2a07b7a..5f67b918c1 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -30,13 +30,13 @@ #define CONTROL_AVG 32 #include +#include #include "base/logging.h" #include "base/timeutil.h" #include "base/NativeApp.h" #include "Common/ChunkFile.h" #include "Common/MathUtil.h" -#include "Common/Atomics.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/HW/StereoResampler.h" @@ -165,8 +165,8 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool // so we will just ignore new written data while interpolating. // Without this cache, the compiler wouldn't be allowed to optimize the // interpolation loop. - u32 indexR = Common::AtomicLoad(m_indexR); - u32 indexW = Common::AtomicLoad(m_indexW); + u32 indexR = m_indexR.load(); + u32 indexW = m_indexW.load(); const int INDEX_MASK = (m_bufsize * 2 - 1); @@ -226,7 +226,7 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool } // Flush cached variable - Common::AtomicStore(m_indexR, indexR); + m_indexR.store(indexR); //if (realSamples != numSamples * 2) { // ILOG("Underrun! %i / %i", realSamples / 2, numSamples); @@ -244,7 +244,7 @@ void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) // Cache access in non-volatile variable // indexR isn't allowed to cache in the audio throttling loop as it // needs to get updates to not deadlock. - u32 indexW = Common::AtomicLoad(m_indexW); + u32 indexW = m_indexW.load(); u32 cap = m_bufsize * 2; // If unthrottling, no need to fill up the entire buffer, just screws up timing after releasing unthrottle. @@ -254,7 +254,7 @@ void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) // Check if we have enough free space // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW - if (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= cap) { + if (num_samples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= cap) { if (!PSP_CoreParameter().unthrottle) overrunCount_++; // TODO: "Timestretch" by doing a windowed overlap with existing buffer content? @@ -269,7 +269,7 @@ void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 2); } - Common::AtomicAdd(m_indexW, num_samples * 2); + m_indexW += num_samples * 2; lastPushSize_ = num_samples; } diff --git a/Core/HW/StereoResampler.h b/Core/HW/StereoResampler.h index 9d00dd27ea..7a95b75917 100644 --- a/Core/HW/StereoResampler.h +++ b/Core/HW/StereoResampler.h @@ -20,6 +20,7 @@ #pragma once #include +#include #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" @@ -53,8 +54,8 @@ protected: int m_lowwatermark; unsigned int m_input_sample_rate = 44100; int16_t *m_buffer; - volatile u32 m_indexW = 0; - volatile u32 m_indexR = 0; + std::atomic m_indexW; + std::atomic m_indexR; float m_numLeftI = 0.0f; u32 m_frac = 0; float sample_rate_ = 0.0; From f81dfa017118d1e6ce53bff50e267895a406c90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 11:05:53 +0200 Subject: [PATCH 34/52] Rename things to make more sense. --- Core/HW/StereoResampler.cpp | 114 +++++++++++++++++++++--------------- Core/HW/StereoResampler.h | 11 +++- 2 files changed, 75 insertions(+), 50 deletions(-) diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 5f67b918c1..14e5a80b4b 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -19,15 +19,16 @@ // 16 bit Stereo -#define MAX_SAMPLES_DEFAULT (4096) // 2*64ms - had to double it for nVidia Shield which has huge buffers -#define MAX_SAMPLES_EXTRA (8192) +// These must be powers of 2. +#define MAX_BUFSIZE_DEFAULT (4096) // 2*64ms - had to double it for nVidia Shield which has huge buffers +#define MAX_BUFSIZE_EXTRA (8192) -#define LOW_WATERMARK_DEFAULT 1680 // 40 ms -#define LOW_WATERMARK_EXTRA 3360 // 80 ms +#define TARGET_BUFSIZE_DEFAULT 1680 // 40 ms +#define TARGET_BUFSIZE_EXTRA 3360 // 80 ms -#define MAX_FREQ_SHIFT 200 // per 32000 Hz +#define MAX_FREQ_SHIFT 600.0f // how far off can we be from 44100 Hz #define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset -#define CONTROL_AVG 32 +#define CONTROL_AVG 32.0f #include #include @@ -56,10 +57,10 @@ #endif StereoResampler::StereoResampler() - : m_bufsize(MAX_SAMPLES_DEFAULT) - , m_lowwatermark(LOW_WATERMARK_DEFAULT) { + : m_maxBufsize(MAX_BUFSIZE_DEFAULT) + , m_targetBufsize(TARGET_BUFSIZE_DEFAULT) { // Need to have space for the worst case in case it changes. - m_buffer = new int16_t[MAX_SAMPLES_EXTRA * 2](); + m_buffer = new int16_t[MAX_BUFSIZE_EXTRA * 2](); // Some Android devices are v-synced to non-60Hz framerates. We simply timestretch audio to fit. // TODO: should only do this if auto frameskip is off? @@ -82,11 +83,11 @@ StereoResampler::~StereoResampler() { void StereoResampler::UpdateBufferSize() { if (g_Config.bExtraAudioBuffering) { - m_bufsize = MAX_SAMPLES_EXTRA; - m_lowwatermark = LOW_WATERMARK_EXTRA; + m_maxBufsize = MAX_BUFSIZE_EXTRA; + m_targetBufsize = TARGET_BUFSIZE_EXTRA; } else { - m_bufsize = MAX_SAMPLES_DEFAULT; - m_lowwatermark = LOW_WATERMARK_DEFAULT; + m_maxBufsize = MAX_BUFSIZE_DEFAULT; + m_targetBufsize = TARGET_BUFSIZE_DEFAULT; } } @@ -148,10 +149,10 @@ inline void ClampBufferToS16WithVolume(s16 *out, const s32 *in, size_t size) { } void StereoResampler::Clear() { - memset(m_buffer, 0, m_bufsize * 2 * sizeof(int16_t)); + memset(m_buffer, 0, m_maxBufsize * 2 * sizeof(int16_t)); } -// Executed from sound stream thread +// Executed from sound stream thread, pulling sound out of the buffer. unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool consider_framelimit, int sample_rate) { if (!samples) return 0; @@ -162,13 +163,18 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool // This is the only function changing the read value, so it's safe to // cache it locally although it's written here. // The writing pointer will be modified outside, but it will only increase, - // so we will just ignore new written data while interpolating. + // so we will just ignore new written data while interpolating (until it wraps...). // Without this cache, the compiler wouldn't be allowed to optimize the // interpolation loop. u32 indexR = m_indexR.load(); u32 indexW = m_indexW.load(); - const int INDEX_MASK = (m_bufsize * 2 - 1); + //if (realSamples != numSamples * 2) { + // ILOG("Underrun! %i / %i", realSamples / 2, numSamples); + //} + + const int INDEX_MASK = (m_maxBufsize * 2 - 1); + lastBufSize_ = (indexR - m_indexW) & INDEX_MASK; // We force on the audio resampler if the output sample rate doesn't match the input. if (!g_Config.bAudioResampler && sample_rate == (int)m_input_sample_rate) { @@ -179,18 +185,32 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool samples[currentSample + 1] = r1; indexR += 2; } - sample_rate_ = (float)sample_rate; + output_sample_rate_ = (float)sample_rate; + droppedSamples_ = 0; } else { - // Drift prevention mechanism + // Drift prevention mechanism. float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2); - m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG - 1)) / CONTROL_AVG; - float offset = (m_numLeftI - m_lowwatermark) * CONTROL_FACTOR; + // If we had to discard samples the last frame due to underrun, + // apply an adjustment here. Otherwise we'll overestimate how many + // samples we need. + numLeft -= droppedSamples_; + droppedSamples_ = 0; + + // m_numLeftI here becomes a lowpass filtered version of numLeft. + m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1.0f)) / CONTROL_AVG; + + // Here we try to keep the buffer size around m_lowwatermark (which is + // really now more like desired_buffer_size) by adjusting the speed. + // Note that the speed of adjustment here does not take the buffer size into + // account. Since this is called once per "output frame", the frame size + // will affect how fast this algorithm reacts, which can't be a good thing. + float offset = (m_numLeftI - (float)m_targetBufsize) * CONTROL_FACTOR; if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT; if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT; - sample_rate_ = (float)(m_input_sample_rate + offset); - const u32 ratio = (u32)(65536.0 * sample_rate_ / (double)sample_rate); - + output_sample_rate_ = (float)(m_input_sample_rate + offset); + const u32 ratio = (u32)(65536.0 * output_sample_rate_ / (double)sample_rate); + ratio_ = ratio; // TODO: consider a higher-quality resampling algorithm. // TODO: Add a fast path for 1:1. for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2) { @@ -228,49 +248,45 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool // Flush cached variable m_indexR.store(indexR); - //if (realSamples != numSamples * 2) { - // ILOG("Underrun! %i / %i", realSamples / 2, numSamples); - //} - lastBufSize_ = (m_indexW - m_indexR) & INDEX_MASK; - return realSamples / 2; } -void StereoResampler::PushSamples(const s32 *samples, unsigned int num_samples) { - inputSampleCount_ += num_samples; +// Executes on the emulator thread, pushing sound into the buffer. +void StereoResampler::PushSamples(const s32 *samples, unsigned int numSamples) { + inputSampleCount_ += numSamples; UpdateBufferSize(); - const int INDEX_MASK = (m_bufsize * 2 - 1); + const int INDEX_MASK = (m_maxBufsize * 2 - 1); // Cache access in non-volatile variable // indexR isn't allowed to cache in the audio throttling loop as it // needs to get updates to not deadlock. u32 indexW = m_indexW.load(); - u32 cap = m_bufsize * 2; + u32 cap = m_maxBufsize * 2; // If unthrottling, no need to fill up the entire buffer, just screws up timing after releasing unthrottle. if (PSP_CoreParameter().unthrottle) { - cap = m_lowwatermark * 2; + cap = m_targetBufsize * 2; } // Check if we have enough free space // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW - if (num_samples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= cap) { + if (numSamples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= cap) { if (!PSP_CoreParameter().unthrottle) overrunCount_++; // TODO: "Timestretch" by doing a windowed overlap with existing buffer content? return; } - int over_bytes = num_samples * 4 - (m_bufsize * 2 - (indexW & INDEX_MASK)) * sizeof(short); + int over_bytes = numSamples * 4 - (m_maxBufsize * 2 - (indexW & INDEX_MASK)) * sizeof(short); if (over_bytes > 0) { - ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, (num_samples * 4 - over_bytes) / 2); - ClampBufferToS16WithVolume(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes / 2); + ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, (numSamples * 4 - over_bytes) / 2); + ClampBufferToS16WithVolume(&m_buffer[0], samples + (numSamples * 4 - over_bytes) / sizeof(short), over_bytes / 2); } else { - ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 2); + ClampBufferToS16WithVolume(&m_buffer[indexW & INDEX_MASK], samples, numSamples * 2); } - m_indexW += num_samples * 2; - lastPushSize_ = num_samples; + m_indexW += numSamples * 2; + lastPushSize_ = numSamples; } void StereoResampler::GetAudioDebugStats(char *buf, size_t bufSize) { @@ -279,23 +295,27 @@ void StereoResampler::GetAudioDebugStats(char *buf, size_t bufSize) { double effective_input_sample_rate = (double)inputSampleCount_ / elapsed; double effective_output_sample_rate = (double)outputSampleCount_ / elapsed; snprintf(buf, bufSize, - "Audio buffer: %d/%d (low watermark: %d)\n" + "Audio buffer: %d/%d (target: %d)\n" + "Filtered: %0.2f\n" "Underruns: %d\n" "Overruns: %d\n" "Sample rate: %d (input: %d)\n" "Effective input sample rate: %0.2f\n" "Effective output sample rate: %0.2f\n" - "Push size: %d\n", + "Push size: %d\n" + "Ratio: %0.6f\n", lastBufSize_, - m_bufsize * 2, - m_lowwatermark, + m_maxBufsize, + m_targetBufsize, + m_numLeftI, underrunCountTotal_, overrunCountTotal_, - (int)sample_rate_, + (int)output_sample_rate_, m_input_sample_rate, effective_input_sample_rate, effective_output_sample_rate, - lastPushSize_); + lastPushSize_, + (float)ratio_ / 65536.0f); underrunCountTotal_ += underrunCount_; overrunCountTotal_ += overrunCount_; underrunCount_ = 0; diff --git a/Core/HW/StereoResampler.h b/Core/HW/StereoResampler.h index 7a95b75917..738c95e59e 100644 --- a/Core/HW/StereoResampler.h +++ b/Core/HW/StereoResampler.h @@ -50,23 +50,28 @@ protected: void UpdateBufferSize(); void SetInputSampleRate(unsigned int rate); - int m_bufsize; - int m_lowwatermark; + int m_maxBufsize; + int m_targetBufsize; + unsigned int m_input_sample_rate = 44100; int16_t *m_buffer; std::atomic m_indexW; std::atomic m_indexR; float m_numLeftI = 0.0f; + u32 m_frac = 0; - float sample_rate_ = 0.0; + float output_sample_rate_ = 0.0; int lastBufSize_ = 0; int lastPushSize_ = 0; + u32 ratio_ = 0; int underrunCount_ = 0; int overrunCount_ = 0; int underrunCountTotal_ = 0; int overrunCountTotal_ = 0; + int droppedSamples_ = 0; + int64_t inputSampleCount_ = 0; int64_t outputSampleCount_ = 0; From 67334e4ae7ef4c928bbf366ab49f3f55a96ced24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 11:46:12 +0200 Subject: [PATCH 35/52] StereoResampler: Make sure the target buffer size is large enough, taking SYSPROP_AUDIO_FRAMES_PER_BUFFER into account. --- Core/HW/StereoResampler.cpp | 67 ++++++++++++++++++++++--------------- Core/HW/StereoResampler.h | 3 +- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 14e5a80b4b..0f74d94341 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -23,6 +23,8 @@ #define MAX_BUFSIZE_DEFAULT (4096) // 2*64ms - had to double it for nVidia Shield which has huge buffers #define MAX_BUFSIZE_EXTRA (8192) +#define TARGET_BUFSIZE_MARGIN 512 + #define TARGET_BUFSIZE_DEFAULT 1680 // 40 ms #define TARGET_BUFSIZE_EXTRA 3360 // 80 ms @@ -70,7 +72,7 @@ StereoResampler::StereoResampler() if (refresh != 60.0f && refresh > 50.0f && refresh < 70.0f) { int input_sample_rate = (int)(44100 * (refresh / 60.0f)); ILOG("StereoResampler: Adjusting target sample rate to %dHz", input_sample_rate); - SetInputSampleRate(input_sample_rate); + m_input_sample_rate = input_sample_rate; } UpdateBufferSize(); @@ -88,6 +90,13 @@ void StereoResampler::UpdateBufferSize() { } else { m_maxBufsize = MAX_BUFSIZE_DEFAULT; m_targetBufsize = TARGET_BUFSIZE_DEFAULT; + + int systemBufsize = System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER); + if (systemBufsize > 0 && m_targetBufsize < systemBufsize + TARGET_BUFSIZE_MARGIN) { + m_targetBufsize = std::min(4096, systemBufsize + TARGET_BUFSIZE_MARGIN); + if (m_targetBufsize * 2 > MAX_BUFSIZE_DEFAULT) + m_maxBufsize = MAX_BUFSIZE_EXTRA; + } } } @@ -157,7 +166,7 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool if (!samples) return 0; - unsigned int currentSample = 0; + unsigned int currentSample; // Cache access in non-volatile variable // This is the only function changing the read value, so it's safe to @@ -169,21 +178,22 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool u32 indexR = m_indexR.load(); u32 indexW = m_indexW.load(); - //if (realSamples != numSamples * 2) { - // ILOG("Underrun! %i / %i", realSamples / 2, numSamples); - //} - const int INDEX_MASK = (m_maxBufsize * 2 - 1); lastBufSize_ = (indexR - m_indexW) & INDEX_MASK; // We force on the audio resampler if the output sample rate doesn't match the input. if (!g_Config.bAudioResampler && sample_rate == (int)m_input_sample_rate) { - for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2) { + for (currentSample = 0; currentSample < numSamples * 2; currentSample += 2) { s16 l1 = m_buffer[indexR & INDEX_MASK]; //current s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current samples[currentSample] = l1; samples[currentSample + 1] = r1; indexR += 2; + if (((indexW - indexR) & INDEX_MASK) == 0) { + // Ran out! + underrunCount_++; + break; + } } output_sample_rate_ = (float)sample_rate; droppedSamples_ = 0; @@ -213,27 +223,32 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool ratio_ = ratio; // TODO: consider a higher-quality resampling algorithm. // TODO: Add a fast path for 1:1. - for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2) { + u32 frac = m_frac; + for (currentSample = 0; currentSample < numSamples * 2; currentSample += 2) { u32 indexR2 = indexR + 2; //next sample s16 l1 = m_buffer[indexR & INDEX_MASK]; //current s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current s16 l2 = m_buffer[indexR2 & INDEX_MASK]; //next s16 r2 = m_buffer[(indexR2 + 1) & INDEX_MASK]; //next - int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16; - int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16; + int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; + int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16; samples[currentSample] = sampleL; samples[currentSample + 1] = sampleR; - m_frac += ratio; - indexR += 2 * (u16)(m_frac >> 16); - m_frac &= 0xffff; + frac += ratio; + indexR += 2 * (frac >> 16); + frac &= 0xffff; + if (((indexW - indexR) & INDEX_MASK) == 0) { + // Ran out! + // int missing = numSamples * 2 - currentSample; + // ILOG("Resampler underrun: %d (numSamples: %d, currentSample: %d)", missing, numSamples, currentSample / 2); + underrunCount_++; + break; + } } + m_frac = frac; } - int realSamples = currentSample; - if (currentSample < numSamples * 2) - underrunCount_++; - - // Let's not count the padding here. + // Let's not count the underrun padding here. outputSampleCount_ += currentSample / 2; // Padding with the last value to reduce clicking @@ -248,7 +263,8 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool // Flush cached variable m_indexR.store(indexR); - return realSamples / 2; + // TODO: What should we actually return here? + return currentSample / 2; } // Executes on the emulator thread, pushing sound into the buffer. @@ -271,8 +287,9 @@ void StereoResampler::PushSamples(const s32 *samples, unsigned int numSamples) { // Check if we have enough free space // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW if (numSamples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= cap) { - if (!PSP_CoreParameter().unthrottle) + if (!PSP_CoreParameter().unthrottle) { overrunCount_++; + } // TODO: "Timestretch" by doing a windowed overlap with existing buffer content? return; } @@ -321,10 +338,10 @@ void StereoResampler::GetAudioDebugStats(char *buf, size_t bufSize) { underrunCount_ = 0; overrunCount_ = 0; - // Try to remove the bias from the startup. - if (elapsed > 3.0) { + // Use this to remove the bias from the startup. + // if (elapsed > 3.0) { //ResetStatCounters(); - } + // } } void StereoResampler::ResetStatCounters() { @@ -337,10 +354,6 @@ void StereoResampler::ResetStatCounters() { startTime_ = real_time_now(); } -void StereoResampler::SetInputSampleRate(unsigned int rate) { - m_input_sample_rate = rate; -} - void StereoResampler::DoState(PointerWrap &p) { auto s = p.Section("resampler", 1); if (!s) diff --git a/Core/HW/StereoResampler.h b/Core/HW/StereoResampler.h index 738c95e59e..451b598d66 100644 --- a/Core/HW/StereoResampler.h +++ b/Core/HW/StereoResampler.h @@ -46,9 +46,8 @@ public: void GetAudioDebugStats(char *buf, size_t bufSize); void ResetStatCounters(); -protected: +private: void UpdateBufferSize(); - void SetInputSampleRate(unsigned int rate); int m_maxBufsize; int m_targetBufsize; From 3f74ffbdf5f3bc2a1df7588d9d4ec543b557762f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 11:50:34 +0200 Subject: [PATCH 36/52] Reduce the SDL audio buffer size, 2048 is a bit excessive. Port the changes to the Qt SDL code. --- Qt/QtMain.cpp | 26 +++++++++++++++----------- SDL/SDLMain.cpp | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 9782032d27..7713b6587d 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -48,6 +48,8 @@ static int browseFileEvent = -1; static int browseFolderEvent = -1; QTCamera *qtcamera = nullptr; +SDL_AudioSpec g_retFmt; + #ifdef SDL static SDL_AudioDeviceID audioDev = 0; @@ -56,35 +58,35 @@ extern void mixaudio(void *userdata, Uint8 *stream, int len) { } static void InitSDLAudioDevice() { - SDL_AudioSpec fmt, ret_fmt; + SDL_AudioSpec fmt; memset(&fmt, 0, sizeof(fmt)); fmt.freq = 44100; fmt.format = AUDIO_S16; fmt.channels = 2; - fmt.samples = 2048; + fmt.samples = 1024; fmt.callback = &mixaudio; fmt.userdata = nullptr; audioDev = 0; if (!g_Config.sAudioDevice.empty()) { - audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (audioDev <= 0) { WLOG("Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str()); } } if (audioDev <= 0) { - audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &ret_fmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); } if (audioDev <= 0) { ELOG("Failed to open audio: %s", SDL_GetError()); } else { - if (ret_fmt.samples != fmt.samples) // Notify, but still use it - ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples); - if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) { + if (g_retFmt.samples != fmt.samples) // Notify, but still use it + ELOG("Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples); + if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) { ELOG("Sound buffer format does not match requested format."); - ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq); - ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format); - ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels); + ELOG("Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq); + ELOG("Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format); + ELOG("Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels); ELOG("Provided output format does not match requirement, turning audio off"); SDL_CloseAudioDevice(audioDev); } @@ -146,7 +148,9 @@ std::string System_GetProperty(SystemProperty prop) { int System_GetPropertyInt(SystemProperty prop) { switch (prop) { case SYSPROP_AUDIO_SAMPLE_RATE: - return 44100; + return g_retFmt.freq; + case SYSPROP_AUDIO_FRAMES_PER_BUFFER: + return g_retFmt.samples; case SYSPROP_DEVICE_TYPE: #if defined(__ANDROID__) return DEVICE_TYPE_MOBILE; diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 382b770115..b74fdd6c86 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -97,7 +97,7 @@ static void InitSDLAudioDevice(const std::string &name = "") { fmt.freq = 44100; fmt.format = AUDIO_S16; fmt.channels = 2; - fmt.samples = 2048; + fmt.samples = 1024; fmt.callback = &sdl_mixaudio_callback; fmt.userdata = nullptr; From af18532095389a7e75871524cfe36a61f2d21e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 12:48:06 +0200 Subject: [PATCH 37/52] Convert atomic code in core and ConsoleListener to use C++ atomics --- Common/ConsoleListener.cpp | 16 ++++++++-------- Common/ConsoleListener.h | 7 +++++-- Core/CoreTiming.cpp | 10 +++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index 2d2851d905..4875aa2b17 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -15,7 +15,7 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ - +#include #include // min #include // System: To be able to add strings with "+" #include @@ -49,8 +49,8 @@ HANDLE ConsoleListener::hTriggerEvent = NULL; CRITICAL_SECTION ConsoleListener::criticalSection; char *ConsoleListener::logPending = NULL; -volatile u32 ConsoleListener::logPendingReadPos = 0; -volatile u32 ConsoleListener::logPendingWritePos = 0; +std::atomic ConsoleListener::logPendingReadPos; +std::atomic ConsoleListener::logPendingWritePos; #endif ConsoleListener::ConsoleListener() : bHidden(true) @@ -188,7 +188,7 @@ void ConsoleListener::Close() { if (hThread != NULL) { - Common::AtomicStoreRelease(logPendingWritePos, (u32) -1); + logPendingWritePos.store((u32)-1, std::memory_order_release); SetEvent(hTriggerEvent); WaitForSingleObject(hThread, LOG_SHUTDOWN_DELAY_MS); @@ -315,7 +315,7 @@ void ConsoleListener::LogWriterThread() WaitForSingleObject(hTriggerEvent, INFINITE); Sleep(LOG_LATENCY_DELAY_MS); - u32 logRemotePos = Common::AtomicLoadAcquire(logPendingWritePos); + u32 logRemotePos = logPendingWritePos.load(std::memory_order_acquire); if (logRemotePos == (u32) -1) break; else if (logRemotePos == logPendingReadPos) @@ -323,7 +323,7 @@ void ConsoleListener::LogWriterThread() else { EnterCriticalSection(&criticalSection); - logRemotePos = Common::AtomicLoadAcquire(logPendingWritePos); + logRemotePos = logPendingWritePos.load(std::memory_order_acquire); int start = 0; if (logRemotePos < logPendingReadPos) @@ -398,7 +398,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text) } EnterCriticalSection(&criticalSection); - u32 logWritePos = Common::AtomicLoad(logPendingWritePos); + u32 logWritePos = logPendingWritePos.load(); u32 prevLogWritePos = logWritePos; if (logWritePos + ColorLen + Len >= LOG_PENDING_MAX) { @@ -448,7 +448,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text) return; } - Common::AtomicStoreRelease(logPendingWritePos, logWritePos); + logPendingWritePos.store(logWritePos, std::memory_order::memory_order_release); LeaveCriticalSection(&criticalSection); SetEvent(hTriggerEvent); diff --git a/Common/ConsoleListener.h b/Common/ConsoleListener.h index e90654ed65..66a612a849 100644 --- a/Common/ConsoleListener.h +++ b/Common/ConsoleListener.h @@ -17,6 +17,8 @@ #pragma once +#include + #include "LogManager.h" #ifdef _WIN32 @@ -44,6 +46,7 @@ public: void Show(bool bShow); bool Hidden() const { return bHidden; } + private: #if defined(USING_WIN_UI) HWND hWnd; @@ -60,8 +63,8 @@ private: static CRITICAL_SECTION criticalSection; static char *logPending; - static volatile u32 logPendingReadPos; - static volatile u32 logPendingWritePos; + static std::atomic logPendingReadPos; + static std::atomic logPendingWritePos; int openWidth_; int openHeight_; diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 743c01dc22..4ec83aa1da 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -15,7 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - +#include #include #include #include @@ -76,7 +76,7 @@ Event *eventPool = 0; Event *eventTsPool = 0; int allocatedTsEvents = 0; // Optimization to skip MoveEvents when possible. -volatile u32 hasTsEvents = 0; +std::atomic hasTsEvents; // Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block // as we can already reach that structure through a register. @@ -255,7 +255,7 @@ void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata tsLast->next = ne; tsLast = ne; - Common::AtomicStoreRelease(hasTsEvents, 1); + hasTsEvents.store(1, std::memory_order::memory_order_release); } // Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread @@ -535,7 +535,7 @@ void ProcessFifoWaitEvents() void MoveEvents() { - Common::AtomicStoreRelease(hasTsEvents, 0); + hasTsEvents.store(0, std::memory_order::memory_order_release); std::lock_guard lk(externalEventLock); // Move events from async queue into main queue @@ -579,7 +579,7 @@ void Advance() globalTimer += cyclesExecuted; currentMIPS->downcount = slicelength; - if (Common::AtomicLoadAcquire(hasTsEvents)) + if (hasTsEvents.load(std::memory_order_acquire)) MoveEvents(); ProcessFifoWaitEvents(); From a9befb5496898e43c4943e5d043e5532fd634d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 13:11:36 +0200 Subject: [PATCH 38/52] Remove our custom atomics code entirely. --- Common/Atomic_GCC.h | 102 ------------------------ Common/Atomic_Win32.h | 101 ----------------------- Common/Atomics.h | 29 ------- Common/Common.vcxproj | 3 - Common/Common.vcxproj.filters | 3 - Common/ConsoleListener.cpp | 1 - Core/CoreTiming.cpp | 1 - Core/HLE/__sceAudio.cpp | 1 - Core/MemMapFunctions.cpp | 1 - UWP/CommonUWP/CommonUWP.vcxproj | 5 +- UWP/CommonUWP/CommonUWP.vcxproj.filters | 5 +- 11 files changed, 2 insertions(+), 250 deletions(-) delete mode 100644 Common/Atomic_GCC.h delete mode 100644 Common/Atomic_Win32.h delete mode 100644 Common/Atomics.h diff --git a/Common/Atomic_GCC.h b/Common/Atomic_GCC.h deleted file mode 100644 index c4c2c4f0f5..0000000000 --- a/Common/Atomic_GCC.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -// IWYU pragma: private, include "Common/Atomic.h" - -#pragma once - -#include "CommonTypes.h" - -// Atomic operations are performed in a single step by the CPU. It is -// impossible for other threads to see the operation "half-done." -// -// Some atomic operations can be combined with different types of memory -// barriers called "Acquire semantics" and "Release semantics", defined below. -// -// Acquire semantics: Future memory accesses cannot be relocated to before the -// operation. -// -// Release semantics: Past memory accesses cannot be relocated to after the -// operation. -// -// These barriers affect not only the compiler, but also the CPU. - -namespace Common -{ - -inline void AtomicAdd(volatile u32& target, u32 value) -{ - __sync_add_and_fetch(&target, value); -} - -inline void AtomicAnd(volatile u32& target, u32 value) -{ - __sync_and_and_fetch(&target, value); -} - -inline void AtomicDecrement(volatile u32& target) -{ - __sync_add_and_fetch(&target, -1); -} - -inline void AtomicIncrement(volatile u32& target) -{ - __sync_add_and_fetch(&target, 1); -} - -inline void AtomicOr(volatile u32& target, u32 value) -{ - __sync_or_and_fetch(&target, value); -} - -// Support clang versions older than 3.4. -#if __clang__ -#if !__has_feature(cxx_atomic) -template -_Atomic(T)* ToC11Atomic(volatile T* loc) -{ - return (_Atomic(T)*) loc; -} - -#define __atomic_load_n(p, m) __c11_atomic_load(ToC11Atomic(p), m) -#define __atomic_store_n(p, v, m) __c11_atomic_store(ToC11Atomic(p), v, m) -#define __atomic_exchange_n(p, v, m) __c11_atomic_exchange(ToC11Atomic(p), v, m) -#endif -#endif - -#ifndef __ATOMIC_RELAXED -#error __ATOMIC_RELAXED not defined; your compiler version is too old. -#endif - -template -inline T AtomicLoad(volatile T& src) -{ - return __atomic_load_n(&src, __ATOMIC_RELAXED); -} - -template -inline T AtomicLoadAcquire(volatile T& src) -{ - return __atomic_load_n(&src, __ATOMIC_ACQUIRE); -} - -template -inline void AtomicStore(volatile T& dest, U value) -{ - __atomic_store_n(&dest, value, __ATOMIC_RELAXED); -} - -template -inline void AtomicStoreRelease(volatile T& dest, U value) -{ - __atomic_store_n(&dest, value, __ATOMIC_RELEASE); -} - -template -inline T* AtomicExchangeAcquire(T* volatile& loc, U newval) -{ - return __atomic_exchange_n(&loc, newval, __ATOMIC_ACQ_REL); -} - -} diff --git a/Common/Atomic_Win32.h b/Common/Atomic_Win32.h deleted file mode 100644 index 7040deb809..0000000000 --- a/Common/Atomic_Win32.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -// IWYU pragma: private, include "Common/Atomic.h" - -#pragma once - -#include - -#include "CommonTypes.h" -#include "CommonWindows.h" - -// Atomic operations are performed in a single step by the CPU. It is -// impossible for other threads to see the operation "half-done." -// -// Some atomic operations can be combined with different types of memory -// barriers called "Acquire semantics" and "Release semantics", defined below. -// -// Acquire semantics: Future memory accesses cannot be relocated to before the -// operation. -// -// Release semantics: Past memory accesses cannot be relocated to after the -// operation. -// -// These barriers affect not only the compiler, but also the CPU. -// -// NOTE: Acquire and Release are not differentiated right now. They perform a -// full memory barrier instead of a "one-way" memory barrier. The newest -// Windows SDK has Acquire and Release versions of some Interlocked* functions. - -namespace Common -{ - -inline void AtomicAdd(volatile u32& target, u32 value) -{ - _InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value); -} - -inline void AtomicAnd(volatile u32& target, u32 value) -{ - _InterlockedAnd((volatile LONG*)&target, (LONG)value); -} - -inline void AtomicIncrement(volatile u32& target) -{ - _InterlockedIncrement((volatile LONG*)&target); -} - -inline void AtomicDecrement(volatile u32& target) -{ - _InterlockedDecrement((volatile LONG*)&target); -} - -inline void AtomicOr(volatile u32& target, u32 value) -{ - _InterlockedOr((volatile LONG*)&target, (LONG)value); -} - -// For the comment below to hold, better only use this with 32-bit types.. -template -inline T AtomicLoad(volatile T& src) -{ - return src; // 32-bit reads are always atomic. -} - -// For the comment below to hold, better only use this with 32-bit types.. -template -inline T AtomicLoadAcquire(volatile T& src) -{ - T result = src; // 32-bit reads are always atomic. - _ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics. - return result; -} - -// For the comment below to hold, better only use this with 32-bit types.. -template -inline void AtomicStore(volatile T& dest, U value) -{ - dest = (T)value; // 32-bit writes are always atomic. -} - -// For the comment below to hold, better only use this with 32-bit types.. -template -inline void AtomicStoreRelease(volatile T& dest, U value) -{ - _WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics. - dest = (T)value; // 32-bit writes are always atomic -} - -#ifdef __MINGW32__ -void *_InterlockedExchangePointer_acq(void *volatile *, void volatile *); -#endif - -template -inline T* AtomicExchangeAcquire(T* volatile& loc, U newval) -{ - return (T*)_InterlockedExchangePointer_acq((void* volatile*)&loc, (void*)newval); -} - -} diff --git a/Common/Atomics.h b/Common/Atomics.h deleted file mode 100644 index bdf2161d7e..0000000000 --- a/Common/Atomics.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#pragma once - -#ifdef _WIN32 - -#include "Atomic_Win32.h" - -#else - -// GCC-compatible compiler assumed! -#include "Atomic_GCC.h" - -#endif diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 037f4258ea..ddc0fd6869 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -384,9 +384,6 @@ - - - diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 65547b8502..9a413fe9a8 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -3,9 +3,6 @@ - - - diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index 4875aa2b17..18472eaa07 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -35,7 +35,6 @@ #include "util/text/utf8.h" #include "Common.h" #include "ConsoleListener.h" // Common -#include "Atomics.h" #if defined(USING_WIN_UI) const int LOG_PENDING_MAX = 120 * 10000; diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 4ec83aa1da..2f0016d511 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -24,7 +24,6 @@ #include "profiler/profiler.h" #include "Common/MsgHandler.h" -#include "Common/Atomics.h" #include "Core/CoreTiming.h" #include "Core/Core.h" #include "Core/Config.h" diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 1d31185348..e462a94efd 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -21,7 +21,6 @@ #include "Common/CommonTypes.h" #include "Common/ChunkFile.h" #include "Common/FixedSizeQueue.h" -#include "Common/Atomics.h" #ifdef _M_SSE #include diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index a463a0e0ae..ac543e36bd 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -16,7 +16,6 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Common.h" -#include "Common/Atomics.h" #include "Core/Core.h" #include "Core/MemMap.h" diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index 4eb97efe92..a5fe599d10 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -383,9 +383,6 @@ - - - @@ -476,4 +473,4 @@ - \ No newline at end of file + diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index 67086f1bc2..566049b279 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -53,9 +53,6 @@ - - - @@ -99,4 +96,4 @@ Crypto - \ No newline at end of file + From 90071fd8684571a5f111a74552449eb9b425deef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 14:44:11 +0200 Subject: [PATCH 39/52] Fix a crash bug in homebrew store --- UI/Store.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/UI/Store.cpp b/UI/Store.cpp index 23f02fd42f..780a7f4c99 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -244,6 +244,7 @@ void ProductView::CreateViews() { auto di = GetI18NCategory("Dialog"); wasInstalled_ = IsGameInstalled(); if (!wasInstalled_) { + launchButton_ = nullptr; installButton_ = Add(new Button(st->T("Install"))); installButton_->OnClick.Handle(this, &ProductView::OnInstall); } else { From 7c8bcb2ee46e2a76f284f129f3ec94e1528fafe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 14:57:12 +0200 Subject: [PATCH 40/52] Fix homebrew store button to work even after changing grid layouts (it lost its click handler) --- UI/MainScreen.cpp | 23 +++++++---------------- UI/MainScreen.h | 19 ++++++++----------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index b5d26b1547..dad5f7cd3c 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -628,7 +628,6 @@ void GameBrowser::Refresh() { lastScale_ = g_Config.fGameGridScale; lastLayoutWasGrid_ = *gridStyle_; - homebrewStoreButton_ = nullptr; // Kill all the contents Clear(); @@ -785,9 +784,7 @@ void GameBrowser::Refresh() { if (browseFlags_ & BrowseFlags::HOMEBREW_STORE) { Add(new Spacer()); - homebrewStoreButton_ = Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT))); - } else { - homebrewStoreButton_ = nullptr; + Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT)))->OnClick.Handle(this, &GameBrowser::OnHomebrewStore); } if (!lastText_.empty() && gameButtons.empty()) { @@ -908,7 +905,12 @@ UI::EventReturn GameBrowser::OnRecentClear(UI::EventParams &e) { return UI::EVENT_DONE; } -MainScreen::MainScreen() : highlightProgress_(0.0f), prevHighlightProgress_(0.0f), backFromStore_(false), lockBackgroundAudio_(false) { +UI::EventReturn GameBrowser::OnHomebrewStore(UI::EventParams &e) { + screenManager_->push(new StoreScreen()); + return UI::EVENT_DONE; +} + +MainScreen::MainScreen() { System_SendMessage("event", "mainscreen"); SetBackgroundAudioGame(""); lastVertical_ = UseVerticalLayout(); @@ -918,7 +920,6 @@ MainScreen::~MainScreen() { SetBackgroundAudioGame(""); } - void MainScreen::CreateViews() { // Information in the top left. // Back button to the bottom left. @@ -974,11 +975,6 @@ void MainScreen::CreateViews() { mm->T("How to get homebrew & demos", "How to get homebrew && demos"), "https://www.ppsspp.org/gethomebrew.html", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); - Choice *hbStore = tabHomebrew->HomebrewStoreButton(); - if (hbStore) { - hbStore->OnClick.Handle(this, &MainScreen::OnHomebrewStore); - } - scrollAllGames->Add(tabAllGames); gameBrowsers_.push_back(tabAllGames); scrollHomebrew->Add(tabHomebrew); @@ -1323,11 +1319,6 @@ UI::EventReturn MainScreen::OnCredits(UI::EventParams &e) { return UI::EVENT_DONE; } -UI::EventReturn MainScreen::OnHomebrewStore(UI::EventParams &e) { - screenManager()->push(new StoreScreen()); - return UI::EVENT_DONE; -} - UI::EventReturn MainScreen::OnSupport(UI::EventParams &e) { #ifdef __ANDROID__ LaunchBrowser("market://details?id=org.ppsspp.ppssppgold"); diff --git a/UI/MainScreen.h b/UI/MainScreen.h index b2052d3474..832d104043 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -53,8 +53,6 @@ public: UI::Event OnHoldChoice; UI::Event OnHighlight; - UI::Choice *HomebrewStoreButton() { return homebrewStoreButton_; } - void FocusGame(const std::string &gamePath); void SetPath(const std::string &path); void Draw(UIContext &dc) override; @@ -81,14 +79,14 @@ private: UI::EventReturn PinToggleClick(UI::EventParams &e); UI::EventReturn GridSettingsClick(UI::EventParams &e); UI::EventReturn OnRecentClear(UI::EventParams &e); + UI::EventReturn OnHomebrewStore(UI::EventParams &e); UI::ViewGroup *gameList_ = nullptr; PathBrowser path_; - bool *gridStyle_; + bool *gridStyle_ = nullptr; BrowseFlags browseFlags_; std::string lastText_; std::string lastLink_; - UI::Choice *homebrewStoreButton_ = nullptr; std::string focusGamePath_; bool listingPending_ = false; float lastScale_ = 1.0f; @@ -131,21 +129,20 @@ protected: UI::EventReturn OnExit(UI::EventParams &e); UI::EventReturn OnDownloadUpgrade(UI::EventParams &e); UI::EventReturn OnDismissUpgrade(UI::EventParams &e); - UI::EventReturn OnHomebrewStore(UI::EventParams &e); UI::EventReturn OnAllowStorage(UI::EventParams &e); - UI::LinearLayout *upgradeBar_; - UI::TabHolder *tabHolder_; + UI::LinearLayout *upgradeBar_ = nullptr; + UI::TabHolder *tabHolder_ = nullptr; std::string restoreFocusGamePath_; std::vector gameBrowsers_; std::string highlightedGamePath_; std::string prevHighlightedGamePath_; - float highlightProgress_; - float prevHighlightProgress_; - bool backFromStore_; - bool lockBackgroundAudio_; + float highlightProgress_ = 0.0f; + float prevHighlightProgress_ = 0.0f; + bool backFromStore_ = false; + bool lockBackgroundAudio_ = false; bool lastVertical_; bool confirmedTemporary_ = false; From 9ed9468d7225941f6b1fccb0d63bb391631fff1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 15:12:38 +0200 Subject: [PATCH 41/52] Make the IsValidPBP DISC_ID check a bit more lenient. Fixes an issue where some homebrew would no longer show in the list. --- UI/MainScreen.cpp | 6 +++--- ext/native/file/file_util.cpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index dad5f7cd3c..43535913d7 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -601,7 +601,7 @@ void GameBrowser::Draw(UIContext &dc) { } } -static bool IsValidPBP(const std::string &path) { +static bool IsValidPBP(const std::string &path, bool allowHomebrew) { if (!File::Exists(path)) return false; @@ -613,7 +613,7 @@ static bool IsValidPBP(const std::string &path) { ParamSFOData sfo; sfo.ReadSFO(sfoData); - if (sfo.GetValueString("DISC_ID").empty()) + if (!allowHomebrew && sfo.GetValueString("DISC_ID").empty()) return false; if (sfo.GetValueString("CATEGORY") == "ME") @@ -705,7 +705,7 @@ void GameBrowser::Refresh() { bool isGame = !fileInfo[i].isDirectory; bool isSaveData = false; // Check if eboot directory - if (!isGame && path_.GetPath().size() >= 4 && IsValidPBP(path_.GetPath() + fileInfo[i].name + "/EBOOT.PBP")) + if (!isGame && path_.GetPath().size() >= 4 && IsValidPBP(path_.GetPath() + fileInfo[i].name + "/EBOOT.PBP", true)) isGame = true; else if (!isGame && File::Exists(path_.GetPath() + fileInfo[i].name + "/PSP_GAME/SYSDIR")) isGame = true; diff --git a/ext/native/file/file_util.cpp b/ext/native/file/file_util.cpp index 57fd61926e..5b1087b425 100644 --- a/ext/native/file/file_util.cpp +++ b/ext/native/file/file_util.cpp @@ -287,6 +287,7 @@ size_t getFilesInDir(const char *directory, std::vector *files, const info.isDirectory = isDirectory(info.fullName); info.exists = true; info.size = 0; + info.isWritable = false; // TODO - implement some kind of check if (!info.isDirectory) { std::string ext = getFileExtension(info.fullName); if (filter) { From 2c3833c28d736ed4d809544944a1365494ab52cc Mon Sep 17 00:00:00 2001 From: sum2012 Date: Sun, 17 May 2020 22:31:37 +0800 Subject: [PATCH 42/52] Add more debug info in scePsmfPlayerStart --- Core/HLE/scePsmf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index 0154e9f085..7d3eee9680 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -1441,8 +1441,6 @@ static int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts) return ERROR_PSMFPLAYER_INVALID_PARAM; } - WARN_LOG(ME, "scePsmfPlayerStart(%08x, %08x, %d)", psmfPlayer, psmfPlayerData, initPts); - psmfplayer->AbortFinish(); psmfplayer->mediaengine->setVideoStream(playerData->videoStreamNum); psmfplayer->videoCodec = playerData->videoCodec; @@ -1455,6 +1453,8 @@ static int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts) psmfplayer->playMode = playerData->playMode; psmfplayer->playSpeed = playerData->playSpeed; + WARN_LOG(ME, "scePsmfPlayerStart(%08x, %08x, %d,(mode %d, speed %d)", psmfPlayer, psmfPlayerData, initPts, playerData->playMode, playerData->playSpeed); + // Does not alter current pts, it just catches up when Update()/etc. get there. int delayUs = psmfplayer->status == PSMF_PLAYER_STATUS_PLAYING ? 3000 : 0; From 0692b6dca6ce9f5bc3d8f056dd660c397423d9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 18:16:26 +0200 Subject: [PATCH 43/52] Revert the loop check in StereoResampler, new one could accidentally keep looping when underrun severely (like if emulation stopped). --- Core/HW/StereoResampler.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 0f74d94341..9f70ae081a 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -188,12 +188,12 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current samples[currentSample] = l1; samples[currentSample + 1] = r1; - indexR += 2; - if (((indexW - indexR) & INDEX_MASK) == 0) { + if (((indexW - indexR) & INDEX_MASK) <= 2) { // Ran out! underrunCount_++; break; } + indexR += 2; } output_sample_rate_ = (float)sample_rate; droppedSamples_ = 0; @@ -225,6 +225,13 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool // TODO: Add a fast path for 1:1. u32 frac = m_frac; for (currentSample = 0; currentSample < numSamples * 2; currentSample += 2) { + if (((indexW - indexR) & INDEX_MASK) <= 2) { + // Ran out! + // int missing = numSamples * 2 - currentSample; + // ILOG("Resampler underrun: %d (numSamples: %d, currentSample: %d)", missing, numSamples, currentSample / 2); + underrunCount_++; + break; + } u32 indexR2 = indexR + 2; //next sample s16 l1 = m_buffer[indexR & INDEX_MASK]; //current s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current @@ -237,13 +244,6 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool frac += ratio; indexR += 2 * (frac >> 16); frac &= 0xffff; - if (((indexW - indexR) & INDEX_MASK) == 0) { - // Ran out! - // int missing = numSamples * 2 - currentSample; - // ILOG("Resampler underrun: %d (numSamples: %d, currentSample: %d)", missing, numSamples, currentSample / 2); - underrunCount_++; - break; - } } m_frac = frac; } From 90fd2d9e3331dc5ef1d3ecd48ca2abd5ca6cc7fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 19:00:15 +0200 Subject: [PATCH 44/52] Remove the Audio Resampling setting (now always on). Having it off is not useful because it'll never sync up perfectly over time. --- Core/Config.cpp | 1 - Core/Config.h | 1 - Core/HW/StereoResampler.cpp | 110 +++++++++++++++--------------------- UI/GameSettingsScreen.cpp | 4 -- 4 files changed, 46 insertions(+), 70 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index f16c03e538..e84e47ea35 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -786,7 +786,6 @@ static ConfigSetting soundSettings[] = { ConfigSetting("Enable", &g_Config.bEnableSound, true, true, true), ConfigSetting("AudioBackend", &g_Config.iAudioBackend, 0, true, true), ConfigSetting("ExtraAudioBuffering", &g_Config.bExtraAudioBuffering, false, true, false), - ConfigSetting("AudioResampler", &g_Config.bAudioResampler, true, true, true), ConfigSetting("GlobalVolume", &g_Config.iGlobalVolume, VOLUME_MAX, true, true), ConfigSetting("AltSpeedVolume", &g_Config.iAltSpeedVolume, -1, true, true), ConfigSetting("AudioDevice", &g_Config.sAudioDevice, "", true, false), diff --git a/Core/Config.h b/Core/Config.h index 2b1437d3b4..15dbf67362 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -261,7 +261,6 @@ public: bool bShowDebugStats; bool bShowAudioDebug; bool bShowGpuProfile; - bool bAudioResampler; //Analog stick tilting //the base x and y tilt. this inclination is treated as (0,0) and the tilt input diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 9f70ae081a..a6e06fafcf 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -181,72 +181,54 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool const int INDEX_MASK = (m_maxBufsize * 2 - 1); lastBufSize_ = (indexR - m_indexW) & INDEX_MASK; - // We force on the audio resampler if the output sample rate doesn't match the input. - if (!g_Config.bAudioResampler && sample_rate == (int)m_input_sample_rate) { - for (currentSample = 0; currentSample < numSamples * 2; currentSample += 2) { - s16 l1 = m_buffer[indexR & INDEX_MASK]; //current - s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current - samples[currentSample] = l1; - samples[currentSample + 1] = r1; - if (((indexW - indexR) & INDEX_MASK) <= 2) { - // Ran out! - underrunCount_++; - break; - } - indexR += 2; + // Drift prevention mechanism. + float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2); + // If we had to discard samples the last frame due to underrun, + // apply an adjustment here. Otherwise we'll overestimate how many + // samples we need. + numLeft -= droppedSamples_; + droppedSamples_ = 0; + + // m_numLeftI here becomes a lowpass filtered version of numLeft. + m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1.0f)) / CONTROL_AVG; + + // Here we try to keep the buffer size around m_lowwatermark (which is + // really now more like desired_buffer_size) by adjusting the speed. + // Note that the speed of adjustment here does not take the buffer size into + // account. Since this is called once per "output frame", the frame size + // will affect how fast this algorithm reacts, which can't be a good thing. + float offset = (m_numLeftI - (float)m_targetBufsize) * CONTROL_FACTOR; + if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT; + if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT; + + output_sample_rate_ = (float)(m_input_sample_rate + offset); + const u32 ratio = (u32)(65536.0 * output_sample_rate_ / (double)sample_rate); + ratio_ = ratio; + // TODO: consider a higher-quality resampling algorithm. + // TODO: Add a fast path for 1:1. + u32 frac = m_frac; + for (currentSample = 0; currentSample < numSamples * 2; currentSample += 2) { + if (((indexW - indexR) & INDEX_MASK) <= 2) { + // Ran out! + // int missing = numSamples * 2 - currentSample; + // ILOG("Resampler underrun: %d (numSamples: %d, currentSample: %d)", missing, numSamples, currentSample / 2); + underrunCount_++; + break; } - output_sample_rate_ = (float)sample_rate; - droppedSamples_ = 0; - } else { - // Drift prevention mechanism. - float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2); - // If we had to discard samples the last frame due to underrun, - // apply an adjustment here. Otherwise we'll overestimate how many - // samples we need. - numLeft -= droppedSamples_; - droppedSamples_ = 0; - - // m_numLeftI here becomes a lowpass filtered version of numLeft. - m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1.0f)) / CONTROL_AVG; - - // Here we try to keep the buffer size around m_lowwatermark (which is - // really now more like desired_buffer_size) by adjusting the speed. - // Note that the speed of adjustment here does not take the buffer size into - // account. Since this is called once per "output frame", the frame size - // will affect how fast this algorithm reacts, which can't be a good thing. - float offset = (m_numLeftI - (float)m_targetBufsize) * CONTROL_FACTOR; - if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT; - if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT; - - output_sample_rate_ = (float)(m_input_sample_rate + offset); - const u32 ratio = (u32)(65536.0 * output_sample_rate_ / (double)sample_rate); - ratio_ = ratio; - // TODO: consider a higher-quality resampling algorithm. - // TODO: Add a fast path for 1:1. - u32 frac = m_frac; - for (currentSample = 0; currentSample < numSamples * 2; currentSample += 2) { - if (((indexW - indexR) & INDEX_MASK) <= 2) { - // Ran out! - // int missing = numSamples * 2 - currentSample; - // ILOG("Resampler underrun: %d (numSamples: %d, currentSample: %d)", missing, numSamples, currentSample / 2); - underrunCount_++; - break; - } - u32 indexR2 = indexR + 2; //next sample - s16 l1 = m_buffer[indexR & INDEX_MASK]; //current - s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current - s16 l2 = m_buffer[indexR2 & INDEX_MASK]; //next - s16 r2 = m_buffer[(indexR2 + 1) & INDEX_MASK]; //next - int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; - int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16; - samples[currentSample] = sampleL; - samples[currentSample + 1] = sampleR; - frac += ratio; - indexR += 2 * (frac >> 16); - frac &= 0xffff; - } - m_frac = frac; + u32 indexR2 = indexR + 2; //next sample + s16 l1 = m_buffer[indexR & INDEX_MASK]; //current + s16 r1 = m_buffer[(indexR + 1) & INDEX_MASK]; //current + s16 l2 = m_buffer[indexR2 & INDEX_MASK]; //next + s16 r2 = m_buffer[(indexR2 + 1) & INDEX_MASK]; //next + int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16; + int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16; + samples[currentSample] = sampleL; + samples[currentSample + 1] = sampleR; + frac += ratio; + indexR += 2 * (frac >> 16); + frac &= 0xffff; } + m_frac = frac; // Let's not count the underrun padding here. outputSampleCount_ += currentSample / 2; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 451c7b45a1..8202870c79 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -548,10 +548,6 @@ void GameSettingsScreen::CreateViews() { CheckBox *extraAudio = audioSettings->Add(new CheckBox(&g_Config.bExtraAudioBuffering, a->T("AudioBufferingForBluetooth", "Bluetooth-friendly buffer (slower)"))); extraAudio->SetEnabledPtr(&g_Config.bEnableSound); #endif - if (System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE) == 44100) { - CheckBox *resampling = audioSettings->Add(new CheckBox(&g_Config.bAudioResampler, a->T("Audio sync", "Audio sync (resampling)"))); - resampling->SetEnabledPtr(&g_Config.bEnableSound); - } // Control ViewGroup *controlsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); From 5e27612efeb505e20c1f5b9d1ebf3c8e4d7b0f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 17 May 2020 19:27:57 +0200 Subject: [PATCH 45/52] Buildfix --- libretro/libretro.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 4df14d381a..9f29455794 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -317,7 +317,6 @@ void retro_init(void) { g_Config.iGlobalVolume = VOLUME_MAX - 1; g_Config.iAltSpeedVolume = -1; g_Config.bEnableSound = true; - g_Config.bAudioResampler = false; g_Config.iCwCheatRefreshRate = 60; g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE; From 32a7e7345e395ca211cde2681a5052acc3288256 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 16:55:21 -0700 Subject: [PATCH 46/52] GPU: Centralize framebuffer download. And try not to use a potentially-null nvfb. Primarily this can happen on Direct3D backends. --- GPU/Common/FramebufferCommon.cpp | 46 ++++++++++++++++++++++----- GPU/Common/FramebufferCommon.h | 2 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 13 -------- GPU/D3D11/FramebufferManagerD3D11.h | 1 - GPU/Directx9/FramebufferDX9.cpp | 13 -------- GPU/Directx9/FramebufferDX9.h | 1 - GPU/GLES/FramebufferManagerGLES.cpp | 28 ---------------- GPU/GLES/FramebufferManagerGLES.h | 1 - GPU/Vulkan/FramebufferVulkan.cpp | 13 -------- GPU/Vulkan/FramebufferVulkan.h | 1 - GPU/Vulkan/GPU_Vulkan.cpp | 1 + 11 files changed, 40 insertions(+), 80 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index e3701b6d48..298f54d8ba 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1351,11 +1351,10 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb) { // For now we'll keep these on the same struct as the ones that can get displayed // (and blatantly copy work already done above while at it). - VirtualFramebuffer *nvfb = 0; + VirtualFramebuffer *nvfb = nullptr; // We maintain a separate vector of framebuffer objects for blitting. - for (size_t i = 0; i < bvfbs_.size(); ++i) { - VirtualFramebuffer *v = bvfbs_[i]; + for (VirtualFramebuffer *v : bvfbs_) { if (v->fb_address == vfb->fb_address && v->format == vfb->format) { if (v->bufferWidth == vfb->bufferWidth && v->bufferHeight == vfb->bufferHeight) { nvfb = v; @@ -1405,6 +1404,34 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram return nvfb; } +bool FramebufferManagerCommon::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { + // When updating VRAM, it need to be exact format. + if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) { + switch (nvfb->format) { + case GE_FORMAT_4444: + nvfb->colorDepth = Draw::FBO_4444; + break; + case GE_FORMAT_5551: + nvfb->colorDepth = Draw::FBO_5551; + break; + case GE_FORMAT_565: + nvfb->colorDepth = Draw::FBO_565; + break; + case GE_FORMAT_8888: + default: + nvfb->colorDepth = Draw::FBO_8888; + break; + } + } + + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, (Draw::FBColorDepth)nvfb->colorDepth }); + if (!nvfb->fbo) { + ERROR_LOG(FRAMEBUF, "Error creating GL FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); + return false; + } + return true; +} + void FramebufferManagerCommon::ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor) { if (currentRenderVfb_) { if ((currentRenderVfb_->usageFlags & FB_USAGE_DOWNLOAD_CLEAR) != 0) { @@ -1969,8 +1996,10 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, PackFramebufferSync_(vfb, x, y, w, h); } else { VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - PackFramebufferSync_(nvfb, x, y, w, h); + if (nvfb) { + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + PackFramebufferSync_(nvfb, x, y, w, h); + } } textureCache_->ForgetLastTexture(); @@ -2014,9 +2043,10 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo // We'll pseudo-blit framebuffers here to get a resized version of vfb. VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferSync_(nvfb, x, y, w, h); + if (nvfb) { + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + PackFramebufferSync_(nvfb, x, y, w, h); + } textureCache_->ForgetLastTexture(); RebindFramebuffer(); diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index f95a12670f..bf92ae189d 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -339,7 +339,7 @@ protected: void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb); void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp); VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb); - virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0; + virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb); virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0; VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 6bca9211b9..70e2b19bfc 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -366,19 +366,6 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr } } -bool FramebufferManagerD3D11::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - nvfb->colorDepth = Draw::FBO_8888; - - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); - if (!(nvfb->fbo)) { - ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); - return false; - } - - draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); - return true; -} - void FramebufferManagerD3D11::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { // Nothing to do here. } diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index aa8641bb14..73b917f326 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -60,7 +60,6 @@ protected: // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; - bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 877b196e64..0300bbf45a 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -379,19 +379,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } } - bool FramebufferManagerDX9::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - nvfb->colorDepth = Draw::FBO_8888; - - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); - if (!(nvfb->fbo)) { - ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); - return false; - } - - draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); - return true; - } - void FramebufferManagerDX9::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { // Nothing to do here. } diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 3de91750c1..bab50d4dfc 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -71,7 +71,6 @@ protected: // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; - bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 0800a8b917..fe597a4d49 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -313,34 +313,6 @@ void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFra } } -bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - // When updating VRAM, it need to be exact format. - if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) { - switch (nvfb->format) { - case GE_FORMAT_4444: - nvfb->colorDepth = Draw::FBO_4444; - break; - case GE_FORMAT_5551: - nvfb->colorDepth = Draw::FBO_5551; - break; - case GE_FORMAT_565: - nvfb->colorDepth = Draw::FBO_565; - break; - case GE_FORMAT_8888: - default: - nvfb->colorDepth = Draw::FBO_8888; - break; - } - } - - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, (Draw::FBColorDepth)nvfb->colorDepth }); - if (!nvfb->fbo) { - ERROR_LOG(FRAMEBUF, "Error creating GL FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); - return false; - } - return true; -} - void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { _assert_msg_(G3D, nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer"); diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 33d364fe7b..25f541fd81 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -64,7 +64,6 @@ protected: // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; - bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 5bb733ddb0..dfef7d47b7 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -325,19 +325,6 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } } -bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - nvfb->colorDepth = Draw::FBO_8888; - - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); - if (!(nvfb->fbo)) { - ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); - return false; - } - - draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); - return true; -} - void FramebufferManagerVulkan::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { // Nothing to do here. } diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index d17d836216..bbd799e1d0 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -71,7 +71,6 @@ protected: // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; - bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 6cd2493f5a..43de4d1078 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -231,6 +231,7 @@ void GPU_Vulkan::CheckGPUFeatures() { features |= GPU_SUPPORTS_INSTANCE_RENDERING; features |= GPU_SUPPORTS_VERTEX_TEXTURE_FETCH; features |= GPU_SUPPORTS_TEXTURE_FLOAT; + features |= GPU_PREFER_CPU_DOWNLOAD; if (vulkan_->GetDeviceFeatures().enabled.wideLines) { features |= GPU_SUPPORTS_WIDE_LINES; From a36239473dd4dbed126e3d0ec1af83a5451feaf0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 16:56:31 -0700 Subject: [PATCH 47/52] GLES: Add dependency tracking for render passes. Let's try to invalidate when it's possible. We move the invalidate to the end of the render when detected. --- CMakeLists.txt | 1 + GPU/GLES/FramebufferManagerGLES.cpp | 2 +- ext/native/native.vcxproj | 1 + ext/native/native.vcxproj.filters | 3 ++ ext/native/thin3d/GLQueueRunner.cpp | 8 ++-- ext/native/thin3d/GLQueueRunner.h | 6 +++ ext/native/thin3d/GLRenderManager.cpp | 19 ++++++++ ext/native/thin3d/VulkanQueueRunner.h | 65 +------------------------- ext/native/util/tiny_set.h | 67 +++++++++++++++++++++++++++ 9 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 ext/native/util/tiny_set.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d96d72320..a40d4334ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1070,6 +1070,7 @@ add_library(native STATIC ext/native/util/text/wrap_text.h ext/native/util/text/wrap_text.cpp ext/native/util/const_map.h + ext/native/util/tiny_set.h ext/native/ext/jpge/jpgd.cpp ext/native/ext/jpge/jpgd.h ext/native/ext/jpge/jpge.cpp diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index fe597a4d49..266b4fde93 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -317,7 +317,7 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) _assert_msg_(G3D, nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer"); // Discard the previous contents of this buffer where possible. - if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { + if (gl_extensions.GLES3) { draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); } else if (gl_extensions.IsGLES) { draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 80b0f68e59..60f361b959 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -489,6 +489,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index 82e64872aa..9c6af13184 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -350,6 +350,9 @@ ui + + util + diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 20c5bf7800..78289b0fcb 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -827,13 +827,15 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { { GLenum attachments[3]; int count = 0; + bool hasDepth = step.render.framebuffer ? step.render.framebuffer->z_stencil_ : false; if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) attachments[count++] = GL_COLOR_ATTACHMENT0; - if (c.clear.clearMask & GL_DEPTH_BUFFER_BIT) + if (hasDepth && (c.clear.clearMask & GL_DEPTH_BUFFER_BIT)) attachments[count++] = GL_DEPTH_ATTACHMENT; - if (c.clear.clearMask & GL_STENCIL_BUFFER_BIT) + if (hasDepth && (c.clear.clearMask & GL_STENCIL_BUFFER_BIT)) attachments[count++] = GL_STENCIL_BUFFER_BIT; - glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments); + if (glInvalidateFramebuffer != nullptr && count != 0) + glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments); CHECK_GL_ERROR_IF_DEBUG(); break; } diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 41bceedb20..813f8c020e 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -6,6 +6,7 @@ #include "gfx/gl_common.h" #include "thin3d/DataFormat.h" +#include "util/tiny_set.h" struct GLRViewport { float x, y, w, h, minZ, maxZ; @@ -295,9 +296,14 @@ struct GLRStep { GLRStep(GLRStepType _type) : stepType(_type) {} GLRStepType stepType; std::vector commands; + TinySet dependencies; union { struct { GLRFramebuffer *framebuffer; + GLRRenderPassAction color; + GLRRenderPassAction depth; + GLRRenderPassAction stencil; + // Note: not accurate. int numDraws; } render; struct { diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 670021cf93..e1d8393162 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -279,6 +279,9 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende GLRStep *step = new GLRStep{ GLRStepType::RENDER }; // This is what queues up new passes, and can end previous ones. step->render.framebuffer = fb; + step->render.color = color; + step->render.depth = depth; + step->render.stencil = stencil; step->render.numDraws = 0; steps_.push_back(step); @@ -308,6 +311,12 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende } curRenderStep_ = step; + if (fb) { + if (color == GLRRenderPassAction::KEEP || depth == GLRRenderPassAction::KEEP || stencil == GLRRenderPassAction::KEEP) { + step->dependencies.insert(fb); + } + } + // Every step clears this state. gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE); } @@ -319,6 +328,7 @@ void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, data.bind_fb_texture.framebuffer = fb; data.bind_fb_texture.aspect = aspectBit; curRenderStep_->commands.push_back(data); + curRenderStep_->dependencies.insert(fb); } void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask) { @@ -328,6 +338,10 @@ void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR step->copy.src = src; step->copy.dst = dst; step->copy.aspectMask = aspectMask; + step->dependencies.insert(src); + bool fillsDst = dst && srcRect.x == 0 && srcRect.y == 0 && srcRect.w == dst->width && srcRect.h == dst->height; + if (dstPos.x != 0 || dstPos.y != 0 || !fillsDst) + step->dependencies.insert(dst); steps_.push_back(step); // Every step clears this state. @@ -342,6 +356,10 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR step->blit.dst = dst; step->blit.aspectMask = aspectMask; step->blit.filter = filter; + step->dependencies.insert(src); + bool fillsDst = dst && dstRect.x == 0 && dstRect.y == 0 && dstRect.w == dst->width && dstRect.h == dst->height; + if (!fillsDst) + step->dependencies.insert(dst); steps_.push_back(step); // Every step clears this state. @@ -356,6 +374,7 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec step->readback.srcRect = { x, y, w, h }; step->readback.aspectMask = aspectBits; step->readback.dstFormat = destFormat; + step->dependencies.insert(src); steps_.push_back(step); // Every step clears this state. diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 129c441b86..1c9e46a5cf 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -6,6 +6,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "math/dataconv.h" #include "thin3d/DataFormat.h" +#include "util/tiny_set.h" class VKRFramebuffer; struct VKRImage; @@ -17,70 +18,6 @@ enum { QUEUE_HACK_RENDERPASS_MERGE = 8, }; -// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded. -template -struct TinySet { - ~TinySet() { delete slowLookup_; } - inline void insert(T t) { - // Fast linear scan. - for (int i = 0; i < fastCount; i++) { - if (fastLookup_[i] == t) - return; // We already have it. - } - // Fast insertion - if (fastCount < MaxFastSize) { - fastLookup_[fastCount++] = t; - return; - } - // Fall back to slow path. - insertSlow(t); - } - bool contains(T t) const { - for (int i = 0; i < fastCount; i++) { - if (fastLookup_[i] == t) - return true; - } - if (slowLookup_) { - for (auto x : *slowLookup_) { - if (x == t) - return true; - } - } - return false; - } - bool contains(const TinySet &otherSet) { - // Awkward, kind of ruins the fun. - for (int i = 0; i < fastCount; i++) { - if (otherSet.contains(fastLookup_[i])) - return true; - } - if (slowLookup_) { - for (auto x : *slowLookup_) { - if (otherSet.contains(x)) - return true; - } - } - return false; - } - -private: - void insertSlow(T t) { - if (!slowLookup_) { - slowLookup_ = new std::vector(); - } else { - for (size_t i = 0; i < slowLookup_->size(); i++) { - if ((*slowLookup_)[i] == t) - return; - } - } - slowLookup_->push_back(t); - } - T fastLookup_[MaxFastSize]; - int fastCount = 0; - int slowCount = 0; - std::vector *slowLookup_ = nullptr; -}; - enum class VKRRenderCommand : uint8_t { REMOVED, BIND_PIPELINE, diff --git a/ext/native/util/tiny_set.h b/ext/native/util/tiny_set.h new file mode 100644 index 0000000000..f82bf35633 --- /dev/null +++ b/ext/native/util/tiny_set.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded. +template +struct TinySet { + ~TinySet() { delete slowLookup_; } + inline void insert(T t) { + // Fast linear scan. + for (int i = 0; i < fastCount; i++) { + if (fastLookup_[i] == t) + return; // We already have it. + } + // Fast insertion + if (fastCount < MaxFastSize) { + fastLookup_[fastCount++] = t; + return; + } + // Fall back to slow path. + insertSlow(t); + } + bool contains(T t) const { + for (int i = 0; i < fastCount; i++) { + if (fastLookup_[i] == t) + return true; + } + if (slowLookup_) { + for (auto x : *slowLookup_) { + if (x == t) + return true; + } + } + return false; + } + bool contains(const TinySet &otherSet) { + // Awkward, kind of ruins the fun. + for (int i = 0; i < fastCount; i++) { + if (otherSet.contains(fastLookup_[i])) + return true; + } + if (slowLookup_) { + for (auto x : *slowLookup_) { + if (otherSet.contains(x)) + return true; + } + } + return false; + } + +private: + void insertSlow(T t) { + if (!slowLookup_) { + slowLookup_ = new std::vector(); + } else { + for (size_t i = 0; i < slowLookup_->size(); i++) { + if ((*slowLookup_)[i] == t) + return; + } + } + slowLookup_->push_back(t); + } + T fastLookup_[MaxFastSize]; + int fastCount = 0; + int slowCount = 0; + std::vector *slowLookup_ = nullptr; +}; From 47bc11a6828ccd031e70698b01d312b07363ea04 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 18:30:59 -0700 Subject: [PATCH 48/52] Vulkan: Generalize dependency tracking. Might as well track on all steps. We also can know if the dst was fully written to here. --- ext/native/thin3d/VulkanQueueRunner.cpp | 17 +++++------------ ext/native/thin3d/VulkanRenderManager.cpp | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 28aa244776..c896b06b57 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -767,11 +767,13 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { TinySet touchedFramebuffers; // must be the same fast-size as the dependencies TinySet for annoying reasons. for (int j = i + 1; j < (int)steps.size(); j++) { // If any other passes are reading from this framebuffer as-is, we cancel the scan. + if (steps[j]->dependencies.contains(fb)) { + // Reading from itself means a KEEP, which is okay. + if (steps[j]->stepType != VKRStepType::RENDER || steps[j]->render.framebuffer != fb) + break; + } switch (steps[j]->stepType) { case VKRStepType::RENDER: - if (steps[j]->dependencies.contains(fb)) { - goto done_fb; - } // Prevent Unknown's example case from https://github.com/hrydgard/ppsspp/pull/12242 if (steps[j]->dependencies.contains(touchedFramebuffers)) { goto done_fb; @@ -793,23 +795,14 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { } break; case VKRStepType::COPY: - if (steps[j]->copy.src == fb || steps[j]->copy.dst == fb) { - goto done_fb; - } touchedFramebuffers.insert(steps[j]->copy.dst); break; case VKRStepType::BLIT: - if (steps[j]->blit.src == fb || steps[j]->blit.dst == fb) { - goto done_fb; - } touchedFramebuffers.insert(steps[j]->blit.dst); break; case VKRStepType::READBACK: // Not sure this has much effect, when executed READBACK is always the last step // since we stall the GPU and wait immediately after. - if (steps[j]->readback.src == fb) { - goto done_fb; - } break; default: break; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 7a639b7d4e..fb39fc3cd0 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -490,6 +490,13 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR step->render.finalColorLayout = !fb ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; steps_.push_back(step); + if (fb) { + // If there's a KEEP, we naturally read from the framebuffer. + if (color == VKRRenderPassAction::KEEP || depth == VKRRenderPassAction::KEEP || stencil == VKRRenderPassAction::KEEP) { + step->dependencies.insert(fb); + } + } + curRenderStep_ = step; if (fb) { curWidth_ = fb->width; @@ -513,6 +520,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a step->readback.src = src; step->readback.srcRect.offset = { x, y }; step->readback.srcRect.extent = { (uint32_t)w, (uint32_t)h }; + step->dependencies.insert(src); steps_.push_back(step); curRenderStep_ = nullptr; @@ -821,6 +829,10 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->copy.srcRect = srcRect; step->copy.dst = dst; step->copy.dstPos = dstPos; + step->dependencies.insert(src); + bool fillsDst = dst && srcRect.offset.x == 0 && srcRect.offset.y == 0 && srcRect.extent.width == dst->width && srcRect.extent.height == dst->height; + if (dstPos.x != 0 || dstPos.y != 0 || !fillsDst) + step->dependencies.insert(dst); std::unique_lock lock(mutex_); steps_.push_back(step); @@ -859,6 +871,10 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->blit.dst = dst; step->blit.dstRect = dstRect; step->blit.filter = filter; + step->dependencies.insert(src); + bool fillsDst = dst && dstRect.offset.x == 0 && dstRect.offset.y == 0 && dstRect.extent.width == dst->width && dstRect.extent.height == dst->height; + if (!fillsDst) + step->dependencies.insert(dst); std::unique_lock lock(mutex_); steps_.push_back(step); From caf9d39fec4a39919415a4ec59795cbac3c28a01 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 May 2020 18:51:23 -0700 Subject: [PATCH 49/52] GLES: Fix invalidation of backbuffer. And of the stencil buffer. --- ext/native/thin3d/GLQueueRunner.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 78289b0fcb..5a297aa974 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -827,13 +827,14 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { { GLenum attachments[3]; int count = 0; - bool hasDepth = step.render.framebuffer ? step.render.framebuffer->z_stencil_ : false; + bool isFBO = step.render.framebuffer != nullptr; + bool hasDepth = isFBO ? step.render.framebuffer->z_stencil_ : false; if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) - attachments[count++] = GL_COLOR_ATTACHMENT0; + attachments[count++] = isFBO ? GL_COLOR_ATTACHMENT0 : GL_COLOR; if (hasDepth && (c.clear.clearMask & GL_DEPTH_BUFFER_BIT)) - attachments[count++] = GL_DEPTH_ATTACHMENT; + attachments[count++] = isFBO ? GL_DEPTH_ATTACHMENT : GL_DEPTH; if (hasDepth && (c.clear.clearMask & GL_STENCIL_BUFFER_BIT)) - attachments[count++] = GL_STENCIL_BUFFER_BIT; + attachments[count++] = isFBO ? GL_STENCIL_ATTACHMENT : GL_STENCIL; if (glInvalidateFramebuffer != nullptr && count != 0) glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments); CHECK_GL_ERROR_IF_DEBUG(); From 112a58cb1a041f62fdadd3014ac74ad262184694 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 May 2020 14:53:37 -0700 Subject: [PATCH 50/52] Vulkan: Make merge pass a bit easier to read. --- ext/native/thin3d/VulkanQueueRunner.cpp | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index c896b06b57..12eb1a4ca2 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1,4 +1,4 @@ -#include +#include #include "base/timeutil.h" #include "DataFormat.h" @@ -752,13 +752,25 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { // First let's count how many times each framebuffer is rendered to. // If it's more than one, let's do our best to merge them. This can help God of War quite a bit. - std::map counts; + std::unordered_map counts; for (int i = 0; i < (int)steps.size(); i++) { if (steps[i]->stepType == VKRStepType::RENDER) { counts[steps[i]->render.framebuffer]++; } } + auto mergeRenderSteps = [](VKRStep *dst, VKRStep *src) { + // OK. Now, if it's a render, slurp up all the commands and kill the step. + // Also slurp up any pretransitions. + dst->preTransitions.insert(dst->preTransitions.end(), src->preTransitions.begin(), src->preTransitions.end()); + dst->commands.insert(dst->commands.end(), src->commands.begin(), src->commands.end()); + src->stepType = VKRStepType::RENDER_SKIP; + }; + auto renderHasNoClear = [](const VKRStep *step) { + const auto &r = step->render; + return r.color != VKRRenderPassAction::CLEAR && r.depth != VKRRenderPassAction::CLEAR && r.stencil != VKRRenderPassAction::CLEAR; + }; + // Now, let's go through the steps. If we find one that is rendered to more than once, // we'll scan forward and slurp up any rendering that can be merged across. for (int i = 0; i < (int)steps.size(); i++) { @@ -778,16 +790,8 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { if (steps[j]->dependencies.contains(touchedFramebuffers)) { goto done_fb; } - if (steps[j]->render.framebuffer == fb && - steps[j]->render.color != VKRRenderPassAction::CLEAR && - steps[j]->render.depth != VKRRenderPassAction::CLEAR && - steps[j]->render.stencil != VKRRenderPassAction::CLEAR) { - // ok. Now, if it's a render, slurp up all the commands - // and kill the step. - // Also slurp up any pretransitions. - steps[i]->preTransitions.insert(steps[i]->preTransitions.end(), steps[j]->preTransitions.begin(), steps[j]->preTransitions.end()); - steps[i]->commands.insert(steps[i]->commands.end(), steps[j]->commands.begin(), steps[j]->commands.end()); - steps[j]->stepType = VKRStepType::RENDER_SKIP; + if (steps[j]->render.framebuffer == fb && renderHasNoClear(steps[j])) { + mergeRenderSteps(steps[i], steps[j]); } // Remember the framebuffer this wrote to. We can't merge with later passes that depend on these. if (steps[j]->render.framebuffer != fb) { From 059e2e5e16fbabb253dc7d6a5a1ddc451e5a2731 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 May 2020 15:02:51 -0700 Subject: [PATCH 51/52] Vulkan: Stop merge at clear/touched read only. Before we would merge on a future render after a clear, which can't be good. But we can safely ignore a draw to another FB that reads from a touched FB. --- ext/native/thin3d/VulkanQueueRunner.cpp | 39 +++++++++++++++++-------- ext/native/util/tiny_set.h | 6 +++- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 12eb1a4ca2..a74a679e6e 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -764,11 +764,13 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { // Also slurp up any pretransitions. dst->preTransitions.insert(dst->preTransitions.end(), src->preTransitions.begin(), src->preTransitions.end()); dst->commands.insert(dst->commands.end(), src->commands.begin(), src->commands.end()); + // So we don't consider it for other things, maybe doesn't matter. + src->dependencies.clear(); src->stepType = VKRStepType::RENDER_SKIP; }; - auto renderHasNoClear = [](const VKRStep *step) { + auto renderHasClear = [](const VKRStep *step) { const auto &r = step->render; - return r.color != VKRRenderPassAction::CLEAR && r.depth != VKRRenderPassAction::CLEAR && r.stencil != VKRRenderPassAction::CLEAR; + return r.color == VKRRenderPassAction::CLEAR || r.depth == VKRRenderPassAction::CLEAR || r.stencil == VKRRenderPassAction::CLEAR; }; // Now, let's go through the steps. If we find one that is rendered to more than once, @@ -786,30 +788,43 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { } switch (steps[j]->stepType) { case VKRStepType::RENDER: - // Prevent Unknown's example case from https://github.com/hrydgard/ppsspp/pull/12242 - if (steps[j]->dependencies.contains(touchedFramebuffers)) { - goto done_fb; - } - if (steps[j]->render.framebuffer == fb && renderHasNoClear(steps[j])) { - mergeRenderSteps(steps[i], steps[j]); - } - // Remember the framebuffer this wrote to. We can't merge with later passes that depend on these. - if (steps[j]->render.framebuffer != fb) { + if (steps[j]->render.framebuffer == fb) { + // Prevent Unknown's example case from https://github.com/hrydgard/ppsspp/pull/12242 + if (renderHasClear(steps[j]) || steps[j]->dependencies.contains(touchedFramebuffers)) { + goto done_fb; + } else { + // Safe to merge, great. + mergeRenderSteps(steps[i], steps[j]); + } + } else { + // Remember the framebuffer this wrote to. We can't merge with later passes that depend on these. touchedFramebuffers.insert(steps[j]->render.framebuffer); } break; case VKRStepType::COPY: + if (steps[j]->copy.dst == fb) { + // Without framebuffer "renaming", we can't merge past a clobbered fb. + goto done_fb; + } touchedFramebuffers.insert(steps[j]->copy.dst); break; case VKRStepType::BLIT: + if (steps[j]->blit.dst == fb) { + // Without framebuffer "renaming", we can't merge past a clobbered fb. + goto done_fb; + } touchedFramebuffers.insert(steps[j]->blit.dst); break; case VKRStepType::READBACK: // Not sure this has much effect, when executed READBACK is always the last step // since we stall the GPU and wait immediately after. break; - default: + case VKRStepType::RENDER_SKIP: + case VKRStepType::READBACK_IMAGE: break; + default: + // We added a new step? Might be unsafe. + goto done_fb; } } done_fb: diff --git a/ext/native/util/tiny_set.h b/ext/native/util/tiny_set.h index f82bf35633..8a7cf89fa6 100644 --- a/ext/native/util/tiny_set.h +++ b/ext/native/util/tiny_set.h @@ -47,6 +47,11 @@ struct TinySet { } return false; } + void clear() { + delete slowLookup_; + slowLookup_ = nullptr; + fastCount = 0; + } private: void insertSlow(T t) { @@ -62,6 +67,5 @@ private: } T fastLookup_[MaxFastSize]; int fastCount = 0; - int slowCount = 0; std::vector *slowLookup_ = nullptr; }; From e216295d32751fd8f2842f471c4d38b0f97d98c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 18 May 2020 00:39:18 +0200 Subject: [PATCH 52/52] Vulkan validation: Switch to the new Khronos validation layer shipping with recent SDKs. --- Common/Vulkan/VulkanContext.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 501690e861..b901388a1c 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -36,15 +36,7 @@ #endif static const char *validationLayers[] = { - "VK_LAYER_LUNARG_standard_validation", - /* - "VK_LAYER_GOOGLE_threading", - "VK_LAYER_LUNARG_draw_state", - "VK_LAYER_LUNARG_image", - "VK_LAYER_LUNARG_mem_tracker", - "VK_LAYER_LUNARG_object_tracker", - "VK_LAYER_LUNARG_param_checker", - */ + "VK_LAYER_KHRONOS_validation", /* // For layers included in the Android NDK. "VK_LAYER_GOOGLE_threading",