From 5e09d57b6c33c9c4a4c2342a2e3d694abf202cdf Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Thu, 18 Jul 2019 03:16:46 +0200 Subject: [PATCH 01/42] Add right analog for touch controls. --- Core/Config.cpp | 2 ++ Core/Config.h | 1 + UI/GamepadEmu.cpp | 12 ++++++++++++ UI/TouchControlLayoutScreen.cpp | 3 +++ UI/TouchControlVisibilityScreen.cpp | 1 + 5 files changed, 19 insertions(+) diff --git a/Core/Config.cpp b/Core/Config.cpp index da0218a9ed..863980f063 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -859,6 +859,7 @@ static ConfigSetting controlSettings[] = { ConfigSetting("LKeyX", "LKeyY", "LKeyScale", "ShowTouchLTrigger", &g_Config.touchLKey, defaultTouchPosShow, true, true), ConfigSetting("RKeyX", "RKeyY", "RKeyScale", "ShowTouchRTrigger", &g_Config.touchRKey, defaultTouchPosShow, true, true), ConfigSetting("AnalogStickX", "AnalogStickY", "AnalogStickScale", "ShowAnalogStick", &g_Config.touchAnalogStick, defaultTouchPosShow, true, true), + ConfigSetting("RightAnalogStickX", "RightAnalogStickY", "RightAnalogStickScale", "ShowRightAnalogStick", &g_Config.touchRightAnalogStick, defaultTouchPosHide, true, true), ConfigSetting("fcombo0X", "fcombo0Y", "comboKeyScale0", "ShowComboKey0", &g_Config.touchCombo0, defaultTouchPosHide, true, true), ConfigSetting("fcombo1X", "fcombo1Y", "comboKeyScale1", "ShowComboKey1", &g_Config.touchCombo1, defaultTouchPosHide, true, true), @@ -1575,6 +1576,7 @@ void Config::ResetControlLayout() { reset(g_Config.touchLKey); reset(g_Config.touchRKey); reset(g_Config.touchAnalogStick); + reset(g_Config.touchRightAnalogStick); reset(g_Config.touchCombo0); reset(g_Config.touchCombo1); reset(g_Config.touchCombo2); diff --git a/Core/Config.h b/Core/Config.h index a81a7f05e8..30b1b99359 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -287,6 +287,7 @@ public: ConfigTouchPos touchLKey; ConfigTouchPos touchRKey; ConfigTouchPos touchAnalogStick; + ConfigTouchPos touchRightAnalogStick; ConfigTouchPos touchCombo0; ConfigTouchPos touchCombo1; diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index a703616178..9a4fc1ceea 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -461,6 +461,9 @@ void InitPadLayout(float xres, float yres, float globalScale) { float Action_button_spacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing; int Action_button_center_X = xres - Action_button_spacing * 2; int Action_button_center_Y = yres - Action_button_spacing * 2; + if (g_Config.touchRightAnalogStick.show) { + Action_button_center_Y -= 150 * scale; + } initTouchPos(g_Config.touchActionButtonCenter, Action_button_center_X, Action_button_center_Y); //D-PAD (up down left right) (aka PSP cross)---------------------------- @@ -480,6 +483,12 @@ void InitPadLayout(float xres, float yres, float globalScale) { int analog_stick_Y = yres - 80 * scale; initTouchPos(g_Config.touchAnalogStick, analog_stick_X, analog_stick_Y); + //right analog stick------------------------------------------------- + //keep the right analog stick right below the face buttons + int right_analog_stick_X = Action_button_center_X; + int right_analog_stick_Y = yres - 80 * scale; + initTouchPos(g_Config.touchRightAnalogStick, right_analog_stick_X, right_analog_stick_Y); + //select, start, throttle-------------------------------------------- //space between the bottom keys (space between select, start and un-throttle) float bottom_key_spacing = 100; @@ -642,6 +651,9 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { if (g_Config.touchAnalogStick.show) root->Add(new PSPStick(stickBg, stickImage, I_STICK, 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick))); + if (g_Config.touchRightAnalogStick.show) + root->Add(new PSPStick(stickBg, stickImage, I_STICK, 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick))); + addComboKey(g_Config.iCombokey0, roundImage, I_ROUND, comboKeyImages[0], g_Config.touchCombo0); addComboKey(g_Config.iCombokey1, roundImage, I_ROUND, comboKeyImages[1], g_Config.touchCombo1); addComboKey(g_Config.iCombokey2, roundImage, I_ROUND, comboKeyImages[2], g_Config.touchCombo2); diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index f75332be81..24574ed951 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -421,6 +421,9 @@ void TouchControlLayoutScreen::CreateViews() { if (g_Config.touchAnalogStick.show) { controls_.push_back(new DragDropButton(g_Config.touchAnalogStick, stickBg, stickImage)); } + if (g_Config.touchRightAnalogStick.show) { + controls_.push_back(new DragDropButton(g_Config.touchRightAnalogStick, stickBg, stickImage)); + } if (g_Config.touchCombo0.show) { controls_.push_back(new DragDropButton(g_Config.touchCombo0, roundImage, comboKeyImages[0])); } diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 20c0407c8b..5dad49c7d9 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -80,6 +80,7 @@ void TouchControlVisibilityScreen::CreateViews() { toggles_.push_back({ "Select", &g_Config.touchSelectKey.show, I_SELECT }); toggles_.push_back({ "Dpad", &g_Config.touchDpad.show, -1 }); toggles_.push_back({ "Analog Stick", &g_Config.touchAnalogStick.show, -1 }); + toggles_.push_back({ "Right Analog Stick", &g_Config.touchRightAnalogStick.show, -1 }); toggles_.push_back({ "Unthrottle", &g_Config.touchUnthrottleKey.show, -1 }); toggles_.push_back({ "Combo0", &g_Config.touchCombo0.show, I_1 }); toggles_.push_back({ "Combo1", &g_Config.touchCombo1.show, I_2 }); From 5e2b4d9f1d67fefe407a5ee8c4e6eb96ce8f4af5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 17 Jul 2019 21:28:21 -0700 Subject: [PATCH 02/42] Config: Specify/save game title on load too. Needed when we load and then save, of course. --- Core/Config.cpp | 6 +++--- Core/Config.h | 2 +- Core/PSPLoaders.cpp | 2 +- UI/EmuScreen.cpp | 2 +- UI/GameSettingsScreen.cpp | 6 +++++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index da0218a9ed..ffdc63c814 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1189,7 +1189,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { //sadly it won't benefit from all the "version conversion" going on up-above //but these configs shouldn't contain older versions anyhow if (bGameSpecific) { - loadGameConfig(gameId_); + loadGameConfig(gameId_, gameIdTitle_); } CleanRecent(); @@ -1508,7 +1508,7 @@ bool Config::saveGameConfig(const std::string &pGameId, const std::string &title return true; } -bool Config::loadGameConfig(const std::string &pGameId) { +bool Config::loadGameConfig(const std::string &pGameId, const std::string &title) { std::string iniFileNameFull = getGameConfigFile(pGameId); if (!hasGameConfig(pGameId)) { @@ -1516,7 +1516,7 @@ bool Config::loadGameConfig(const std::string &pGameId) { return false; } - changeGameSpecific(pGameId); + changeGameSpecific(pGameId, title); IniFile iniFile; iniFile.Load(iniFileNameFull); diff --git a/Core/Config.h b/Core/Config.h index a81a7f05e8..44ce1531d9 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -413,7 +413,7 @@ public: void changeGameSpecific(const std::string &gameId = "", const std::string &title = ""); bool createGameConfig(const std::string &game_id); bool deleteGameConfig(const std::string& pGameId); - bool loadGameConfig(const std::string &game_id); + bool loadGameConfig(const std::string &game_id, const std::string &title); bool saveGameConfig(const std::string &pGameId, const std::string &title); void unloadGameConfig(); std::string getGameConfigFile(const std::string &gameId); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 697a1240a9..78e2a7c417 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -252,7 +252,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { } //in case we didn't go through EmuScreen::boot - g_Config.loadGameConfig(id); + g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE")); host->SendUIMessage("config_loaded", ""); INFO_LOG(LOADER,"Loading %s...", bootpath.c_str()); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index a450595688..f0b86207a4 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -202,7 +202,7 @@ void EmuScreen::bootGame(const std::string &filename) { return; if (!info->id.empty()) { - g_Config.loadGameConfig(info->id); + g_Config.loadGameConfig(info->id, info->GetTitle()); // Reset views in case controls are in a different place. RecreateViews(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 8804597de3..c94c025bd2 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -132,7 +132,8 @@ void GameSettingsScreen::CreateViews() { ReloadAllPostShaderInfo(); if (editThenRestore_) { - g_Config.loadGameConfig(gameID_); + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); + g_Config.loadGameConfig(gameID_, info->GetTitle()); } iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60; @@ -1051,6 +1052,9 @@ void GameSettingsScreen::onFinish(DialogResult result) { Reporting::UpdateConfig(); g_Config.Save("GameSettingsScreen::onFinish"); if (editThenRestore_) { + // In case we didn't have the title yet before, try again. + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0); + g_Config.changeGameSpecific(gameID_, info->GetTitle()); g_Config.unloadGameConfig(); } From 8bb267727e727b9c79eaea6d3a695a2c52951c98 Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Thu, 18 Jul 2019 05:57:28 +0200 Subject: [PATCH 03/42] Make things clear for people that never saw real PSP. --- UI/TouchControlVisibilityScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 5dad49c7d9..1c7f23af4e 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -80,7 +80,7 @@ void TouchControlVisibilityScreen::CreateViews() { toggles_.push_back({ "Select", &g_Config.touchSelectKey.show, I_SELECT }); toggles_.push_back({ "Dpad", &g_Config.touchDpad.show, -1 }); toggles_.push_back({ "Analog Stick", &g_Config.touchAnalogStick.show, -1 }); - toggles_.push_back({ "Right Analog Stick", &g_Config.touchRightAnalogStick.show, -1 }); + toggles_.push_back({ "Right Analog Stick\n(not used by most games)", &g_Config.touchRightAnalogStick.show, -1 }); toggles_.push_back({ "Unthrottle", &g_Config.touchUnthrottleKey.show, -1 }); toggles_.push_back({ "Combo0", &g_Config.touchCombo0.show, I_1 }); toggles_.push_back({ "Combo1", &g_Config.touchCombo1.show, I_2 }); From a7820b6aee19e5879be00340d72b07b1a98ac3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 21 Jul 2019 18:53:56 +0200 Subject: [PATCH 04/42] Upgrade gradle again --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 43f215cd6c..dcf3372b38 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.1' + classpath 'com.android.tools.build:gradle:3.4.2' } } From 3916f8d7b6abfc5a06f3c72cda28c2293f6f41e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 23 Jul 2019 17:45:52 +0200 Subject: [PATCH 05/42] Add a little reminder to self with a link to an interesting article. --- GPU/Vulkan/GPU_Vulkan.cpp | 2 ++ GPU/Vulkan/PipelineManagerVulkan.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 18f6c1b6c0..6507f0f0de 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -136,6 +136,7 @@ void GPU_Vulkan::LoadCache(std::string filename) { // it can just memcpy the finished shader binaries out of the pipeline cache file. bool result = shaderManagerVulkan_->LoadCache(f); if (result) { + // WARNING: See comment in LoadCache if you are tempted to flip the second parameter to true. result = pipelineManager_->LoadCache(f, false, shaderManagerVulkan_, draw_, drawEngine_.GetPipelineLayout()); } fclose(f); @@ -159,6 +160,7 @@ void GPU_Vulkan::SaveCache(std::string filename) { if (!f) return; shaderManagerVulkan_->SaveCache(f); + // WARNING: See comment in LoadCache if you are tempted to flip the second parameter to true. pipelineManager_->SaveCache(f, false, shaderManagerVulkan_, draw_); INFO_LOG(G3D, "Saved Vulkan pipeline cache"); fclose(f); diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 278ea2f9e1..9b253c1acc 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -582,6 +582,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha uint32_t size; if (saveRawPipelineCache) { + // WARNING: See comment in LoadCache before using this path. VkResult result = vkGetPipelineCacheData(vulkan_->GetDevice(), pipelineCache_, &dataSize, nullptr); uint32_t size = (uint32_t)dataSize; if (result != VK_SUCCESS) { @@ -670,6 +671,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha uint32_t size = 0; if (loadRawPipelineCache) { + // WARNING: Do not use this path until after reading and implementing https://zeux.io/2019/07/17/serializing-pipeline-cache/ ! bool success = fread(&size, sizeof(size), 1, file) == 1; if (!size || !success) { WARN_LOG(G3D, "Zero-sized Vulkan pipeline cache."); From 719aebfb8c2bfda69d4d525203e35520e7e63c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 23 Jul 2019 22:57:06 +0200 Subject: [PATCH 06/42] Android: Disable building HLSL support in glslang, works around #12105 --- ext/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index b259a6f059..c9bbff9c47 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -8,6 +8,11 @@ endif() set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "let's not build binaries we don't need" FORCE) set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON CACHE BOOL "let's not use exceptions" FORCE) +# This is really a workaround for an NDK 20 compiler issue (PPSSPP issue #12105), but shouldn't hurt. +if(ANDROID) +set(ENABLE_HLSL OFF CACHE BOOL "let's not build HLSL support we don't need" FORCE) +endif() + add_subdirectory(glslang) add_subdirectory(snappy) add_subdirectory(udis86) From fbcfd774028ff8c1e7315d017415f1266324b637 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 27 Jul 2019 06:17:56 -0700 Subject: [PATCH 07/42] UI: Support zip files with deep folder structures. This will extract the level that has textures.ini, but it doesn't really handle skipping other folders properly. --- Core/Util/GameManager.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 10586b3086..b2fc34767e 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -133,7 +133,7 @@ void GameManager::Update() { } } -void countSlashes(std::string fileName, int *slashLocation, int *slashCount) { +static void countSlashes(const std::string &fileName, int *slashLocation, int *slashCount) { *slashCount = 0; int lastSlashLocation = -1; *slashLocation = -1; @@ -206,9 +206,7 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { isoFileIndex = i; } } else if (zippedName.find("textures.ini") != std::string::npos) { - int slashCount = 0; - int slashLocation = -1; - countSlashes(zippedName, &slashLocation, &slashCount); + int slashLocation = (int)zippedName.find_last_of('/'); if (stripCharsTexturePack == -1 || slashLocation < stripCharsTexturePack + 1) { stripCharsTexturePack = slashLocation + 1; isTexturePack = true; @@ -499,6 +497,9 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, for (int i = 0; i < info.numFiles; i++) { const char *fn = zip_get_name(z, i, 0); std::string zippedName = fn; + if (zippedName.length() < (size_t)info.stripChars) { + continue; + } std::string outFilename = dest + zippedName.substr(info.stripChars); bool isDir = *outFilename.rbegin() == '/'; if (!isDir && outFilename.find("/") != std::string::npos) { @@ -521,8 +522,8 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, for (int i = 0; i < info.numFiles; i++) { const char *fn = zip_get_name(z, i, 0); // Note that we do NOT write files that are not in a directory, to avoid random - // README files etc. - if (fileAllowed(fn)) { + // README files etc. (unless allowRoot is true.) + if (fileAllowed(fn) && strlen(fn) > (size_t)info.stripChars) { fn += info.stripChars; std::string outFilename = dest + fn; bool isDir = *outFilename.rbegin() == '/'; From 0a23fcc18936b4bfc3896543b411ddb9aeebb0be Mon Sep 17 00:00:00 2001 From: Hanbin Sock Date: Sat, 27 Jul 2019 16:38:41 -0400 Subject: [PATCH 08/42] Add Hori Fighting Stick VX to gamecontrollerdb.txt --- assets/gamecontrollerdb.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index 13a4acd5cb..cbc06396c4 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -193,3 +193,4 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,b 03000000380700008532000010010000,MadCatz Madcatz Fightpad,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:b5,righttrigger:b7, 030000000d0f0000ee00000011010000,HORI CO.,LTD. HORIPAD mini4,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, 030000000d0f0000c100000011010000,HORI CO.,LTD. HORIPAD S,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +03000000ad1b000003f5000033050000,Hori Fighting Stick VX,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,-leftx:h0.8,+leftx:h0.2,-lefty:h0.1,+lefty:h0.4,lefttrigger:b6,righttrigger:b7, From 748b33a5613189f196b83539a918b5e9e004dac9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 28 Jul 2019 05:31:49 -0700 Subject: [PATCH 09/42] GPU: Allow upload to temp resized framebuf. Fixes #12045. --- GPU/Common/FramebufferCommon.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index ddf3a61d08..76f1027bfd 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1306,7 +1306,8 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst // Some games use mismatching bitdepths. But make sure the stride matches. // If it doesn't, generally this means we detected the framebuffer with too large a height. - bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->height - dstHeight; + // Use bufferHeight in case of buffers that resize up and down often per frame (Valkyrie Profile.) + bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->bufferHeight - dstHeight; if (match && vfb_byteStride != byteStride) { // Grand Knights History copies with a mismatching stride but a full line at a time. // Makes it hard to detect the wrong transfers in e.g. God of War. @@ -1336,7 +1337,7 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst const u32 byteOffset = srcBasePtr - vfb_address; const u32 byteStride = srcStride * bpp; const u32 yOffset = byteOffset / byteStride; - bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->height - srcHeight; + bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - srcHeight; if (match && vfb_byteStride != byteStride) { if (width != srcStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) { match = false; From 8212236c44414ca5a27662403bc57cccba11385f Mon Sep 17 00:00:00 2001 From: "Tomas Kelemen (vudiq)" Date: Tue, 30 Jul 2019 08:35:45 +0200 Subject: [PATCH 10/42] libretro: don't fail on missing SDL when building libretro core, cmake should not fail on missing SDL, as libretro uses own code for handling the input. the core builds, links and runs fine without SDL --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5d5aebd8d..5c1f155594 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -831,7 +831,7 @@ elseif(TARGET SDL2::SDL2) set(nativeExtraLibs ${nativeExtraLibs} pthread) endif() set(TargetBin PPSSPPSDL) -else() +elseif(NOT LIBRETRO) message(FATAL_ERROR "Could not find SDL2. Failing.") endif() From 6a37389b5edb9f839bdefe2d4234e5e38836ba8f Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Mon, 22 Jul 2019 04:42:07 +0200 Subject: [PATCH 11/42] Move WM_MOUSEWHEEL back to WndProc to fix wheel events not working in Win 7. --- Windows/MainWindow.cpp | 48 +++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index c91112c0ca..4dbba48ade 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -132,7 +132,6 @@ namespace MainWindow // gross hack bool noFocusPause = false; // TOGGLE_PAUSE state to override pause on lost focus bool trapMouse = true; // Handles some special cases(alt+tab, win menu) when game is running and mouse is confined - bool mouseScrollUsed = false; #define MAX_LOADSTRING 100 const TCHAR *szWindowClass = TEXT("PPSSPPWnd"); @@ -260,7 +259,6 @@ namespace MainWindow } void RelaseMouseWheel() { - if (mouseScrollUsed) { // For simplicity release both wheel events KeyInput key; key.deviceId = DEVICE_ID_MOUSE; @@ -269,8 +267,6 @@ namespace MainWindow NativeKey(key); key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP; NativeKey(key); - mouseScrollUsed = false; - } } static void HandleSizeChange(int newSizingType) { @@ -674,27 +670,6 @@ namespace MainWindow } break; - case WM_MOUSEWHEEL: - { - int wheelDelta = (short)(wParam >> 16); - KeyInput key; - key.deviceId = DEVICE_ID_MOUSE; - - if (wheelDelta < 0) { - key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN; - wheelDelta = -wheelDelta; - } else { - key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP; - } - // There's no separate keyup event for mousewheel events, - // so we set mouseScrollUsed here and always release if it's true. - key.flags = KEY_DOWN | KEY_HASWHEELDELTA | (wheelDelta << 16); - mouseScrollUsed = true; - SetTimer(hwndMain, TIMER_WHEELRELEASE, WHEELRELEASE_DELAY_MS, 0); - NativeKey(key); - } - break; - case WM_TOUCH: { touchHandler.handleTouchEvent(hWnd, message, wParam, lParam); @@ -807,6 +782,27 @@ namespace MainWindow } break; + // Wheel events have to stay in WndProc for compatibility with older Windows(7). See #12156 + case WM_MOUSEWHEEL: + { + int wheelDelta = (short)(wParam >> 16); + KeyInput key; + key.deviceId = DEVICE_ID_MOUSE; + + if (wheelDelta < 0) { + key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN; + wheelDelta = -wheelDelta; + } else { + key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP; + } + // There's no separate keyup event for mousewheel events, + // so we release it with a slight delay. + key.flags = KEY_DOWN | KEY_HASWHEELDELTA | (wheelDelta << 16); + SetTimer(hwndMain, TIMER_WHEELRELEASE, WHEELRELEASE_DELAY_MS, 0); + NativeKey(key); + } + break; + case WM_TIMER: // Hack: Take the opportunity to also show/hide the mouse cursor in fullscreen mode. switch (wParam) { @@ -818,7 +814,7 @@ namespace MainWindow hideCursor = true; KillTimer(hWnd, TIMER_CURSORMOVEUPDATE); return 0; - // Hack: need to release wheel event without waiting for another wheel event. + // Hack: need to release wheel event with a delay for games to register it was "pressed down". case TIMER_WHEELRELEASE: RelaseMouseWheel(); return 0; From 3d6bd6f0df14dd4d3c0e9edfe44966b7b71939f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Aug 2019 14:54:54 +0200 Subject: [PATCH 12/42] Vulkan: Using VK_DEPENDENCY_BY_REGION on the backbuffer subpass causes tearing, let's not do it. Fixes #12202. --- ext/native/thin3d/VulkanQueueRunner.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 49d0aa70ab..a0042faf4e 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -158,7 +158,6 @@ void VulkanQueueRunner::InitBackbufferRenderPass() { dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dep.srcAccessMask = 0; dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; rp_info.attachmentCount = 2; From 75e2366a6dbe4ed7e062fc947e98fe699c0562e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Aug 2019 17:46:46 +0200 Subject: [PATCH 13/42] Vulkan: Actually fix #12202, an array was out of scope where it was used, causing garbage flags. --- ext/native/thin3d/VulkanRenderManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 2938590d67..786513eaa7 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -920,10 +920,10 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { cmdBufs[numCmdBufs++] = frameData.mainCmd; VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; + VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; if (triggerFence && !frameData.skipSwap) { submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; - VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; submit_info.pWaitDstStageMask = waitStage; } submit_info.commandBufferCount = (uint32_t)numCmdBufs; From 4d195ce955f95a20e738c65a5679eafc4b8991e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Aug 2019 18:38:40 +0200 Subject: [PATCH 14/42] Workaround for DJ Max Portable's clumsy copy protection. Fixes #9477. --- Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + Core/FileSystems/DirectoryFileSystem.cpp | 20 ++++++++++++++-- assets/compat.ini | 30 ++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index ed396a8a34..71f67ebe4a 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -64,6 +64,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "ForceUMDDelay", &flags_.ForceUMDDelay); CheckSetting(iniFile, gameID, "ForceMax60FPS", &flags_.ForceMax60FPS); CheckSetting(iniFile, gameID, "JitInvalidationHack", &flags_.JitInvalidationHack); + CheckSetting(iniFile, gameID, "HideISOFiles", &flags_.HideISOFiles); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index c80f579994..c85267cf46 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -64,6 +64,7 @@ struct CompatFlags { bool ForceUMDDelay; bool ForceMax60FPS; bool JitInvalidationHack; + bool HideISOFiles; }; class IniFile; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 90589b479e..4e97463300 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -30,6 +30,7 @@ #include "Core/HLE/sceKernel.h" #include "Core/HW/MemoryStick.h" #include "Core/CoreTiming.h" +#include "Core/System.h" #include "Core/Host.h" #include "Core/Replay.h" #include "Core/Reporting.h" @@ -848,6 +849,7 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { return ReplayApplyDiskListing(myVector, CoreTiming::GetGlobalTimeUs()); } + bool hideISOFiles = PSP_CoreParameter().compat.flags().HideISOFiles; while (true) { PSPFileInfo entry; if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) @@ -864,10 +866,16 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { else entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32); entry.name = SimulateVFATBug(ConvertWStringToUTF8(findData.cFileName)); + + bool hideFile = false; + if (hideISOFiles && (endsWithNoCase(entry.name, ".cso") || endsWithNoCase(entry.name, ".iso"))) { + // Workaround for DJ Max Portable, see compat.ini. + hideFile = true; + } tmFromFiletime(entry.atime, findData.ftLastAccessTime); tmFromFiletime(entry.ctime, findData.ftCreationTime); tmFromFiletime(entry.mtime, findData.ftLastWriteTime); - if (!listingRoot || (wcscmp(findData.cFileName, L"..") && wcscmp(findData.cFileName, L"."))) + if (!hideFile && (!listingRoot || (wcscmp(findData.cFileName, L"..") && wcscmp(findData.cFileName, L".")))) myVector.push_back(entry); int retval = FindNextFile(hFind, &findData); @@ -893,6 +901,7 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { return ReplayApplyDiskListing(myVector, CoreTiming::GetGlobalTimeUs()); } + bool hideISOFiles = PSP_CoreParameter().compat.flags().HideISOFiles; while ((dirp = readdir(dp)) != NULL) { PSPFileInfo entry; struct stat s; @@ -905,10 +914,17 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { entry.access = s.st_mode & 0x1FF; entry.name = SimulateVFATBug(dirp->d_name); entry.size = s.st_size; + + bool hideFile = false; + if (hideISOFiles && (endsWithNoCase(entry.name, ".cso") || endsWithNoCase(entry.name, ".iso"))) { + // Workaround for DJ Max Portable, see compat.ini. + hideFile = true; + } + localtime_r((time_t*)&s.st_atime,&entry.atime); localtime_r((time_t*)&s.st_ctime,&entry.ctime); localtime_r((time_t*)&s.st_mtime,&entry.mtime); - if (!listingRoot || (strcmp(dirp->d_name, "..") && strcmp(dirp->d_name, "."))) + if (!hideFile && (!listingRoot || (strcmp(dirp->d_name, "..") && strcmp(dirp->d_name, ".")))) myVector.push_back(entry); } closedir(dp); diff --git a/assets/compat.ini b/assets/compat.ini index f619ca9e02..15aee5a550 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -590,3 +590,33 @@ ULES00034 = true ULES00035 = true # MTX MotoTrax ULUS10138 = true + +[HideISOFiles] +# DJ Max Portable has some crude copy-protection functionality where it looks for ISO/CSO files +# in a few directories. Prevent this by hiding the files from the game. +# To be sure, catch all versions and remixes of the game that's been seen in reports. +# It checks the following directories: +# / +# /PSP/ +# /PSP/COMMON +# /PSP/GAME + +ULKS46116 = true +ULKS46189 = true +ULKS46190 = true +ULKS46236 = true +ULKS46240 = true +ULKS46059 = true +ULUS10403 = true +ULUS10538 = true +ULKS46050 = true +ULJM05836 = true +ULJM46236 = true +ULJM06034 = true +NPHH00260 = true +CF0020046 = true +CF0020074 = true +NPJH50471 = true +ULJM06033 = true +NPJH50559 = true +NPEH00030 = true From 99c4a00dae5595f06e41166044f6cc8a89869812 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Sat, 3 Aug 2019 18:33:43 +0300 Subject: [PATCH 15/42] sceUsbMic --- CMakeLists.txt | 2 ++ Core/Core.vcxproj | 2 ++ Core/Core.vcxproj.filters | 6 +++++ Core/HLE/HLETables.cpp | 2 ++ Core/HLE/sceUsbCam.cpp | 13 +++++++++- Core/HLE/sceUsbMic.cpp | 52 +++++++++++++++++++++++++++++++++++++++ Core/HLE/sceUsbMic.h | 20 +++++++++++++++ android/jni/Android.mk | 1 + 8 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 Core/HLE/sceUsbMic.cpp create mode 100644 Core/HLE/sceUsbMic.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c1f155594..e8d02cdab5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1647,6 +1647,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/sceUsbCam.h Core/HLE/sceUsbGps.cpp Core/HLE/sceUsbGps.h + Core/HLE/sceUsbMic.cpp + Core/HLE/sceUsbMic.h Core/HLE/sceUtility.cpp Core/HLE/sceUtility.h Core/HLE/sceVaudio.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 937ab02ac4..22d258ecad 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -374,6 +374,7 @@ + @@ -901,6 +902,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index b459851f73..7f096c1b50 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -737,6 +737,9 @@ Debugger\WebSocket + + HLE\Libraries + @@ -1367,6 +1370,9 @@ Debugger\WebSocket + + HLE\Libraries + diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index ed1331f3ad..0f4c440c4b 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -69,6 +69,7 @@ #include "sceUsbAcc.h" #include "sceUsbCam.h" #include "sceUsbGps.h" +#include "sceUsbMic.h" #include "sceUtility.h" #include "sceVaudio.h" #include "sceMt19937.h" @@ -298,6 +299,7 @@ void RegisterAllModules() { Register_sceNetIfhandle(); Register_KUBridge(); Register_sceUsbAcc(); + Register_sceUsbMic(); // add new modules here. } diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 2f624cb80e..e1b5986e8d 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -50,6 +50,17 @@ static int sceUsbCamStopMic() { return 0; } +static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) { + INFO_LOG(HLE, "UNIMPL sceUsbCamReadMicBlocking: size: %d", size); + for (unsigned int i = 0; i < size; i++) { + if (Memory::IsValidAddress(bufAddr + i)) { + Memory::Write_U8(i & 0xFF, bufAddr + i); + } + } + hleEatMicro(1000000 / micParam.frequency * (size / 2)); + return size; +} + static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo"); if (Memory::IsValidRange(paramAddr, sizeof(videoParam))) { @@ -115,7 +126,7 @@ const HLEFunction sceUsbCam[] = { 0X2E930264, nullptr, "sceUsbCamSetupMicEx", '?', "" }, { 0X82A64030, &WrapI_V, "sceUsbCamStartMic", 'i', "" }, { 0X5145868A, &WrapI_V, "sceUsbCamStopMic", 'i', "" }, - { 0X36636925, nullptr, "sceUsbCamReadMicBlocking", '?', "" }, + { 0X36636925, &WrapI_UU, "sceUsbCamReadMicBlocking", 'i', "xx" }, { 0X3DC0088E, nullptr, "sceUsbCamReadMic", '?', "" }, { 0XB048A67D, nullptr, "sceUsbCamWaitReadMicEnd", '?', "" }, { 0XF8847F60, nullptr, "sceUsbCamPollReadMicEnd", '?', "" }, diff --git a/Core/HLE/sceUsbMic.cpp b/Core/HLE/sceUsbMic.cpp new file mode 100644 index 0000000000..306f527da1 --- /dev/null +++ b/Core/HLE/sceUsbMic.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2019- PPSSPP 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 git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "base/NativeApp.h" +#include "Common/ChunkFile.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceUsbMic.h" +#include "Core/MemMapHelpers.h" + +static int sceUsbMicInputBlocking(u32 size, u32 samplerate, u32 bufAddr) { + INFO_LOG(HLE, "UNIMPL sceUsbMicInputBlocking: size: %d, samplerate: %d", size, samplerate); + for (unsigned int i = 0; i < size; i++) { + if (Memory::IsValidAddress(bufAddr + i)) { + Memory::Write_U8(i & 0xFF, bufAddr + i); + } + } + hleEatMicro(1000000 / samplerate * (size / 2)); + return size; +} + +const HLEFunction sceUsbMic[] = +{ + {0x06128E42, nullptr, "sceUsbMicPollInputEnd", '?', "" }, + {0x2E6DCDCD, &WrapI_UUU, "sceUsbMicInputBlocking", 'i', "xxx" }, + {0x45310F07, nullptr, "sceUsbMicInputInitEx", '?', "" }, + {0x5F7F368D, nullptr, "sceUsbMicInput", '?', "" }, + {0x63400E20, nullptr, "sceUsbMicGetInputLength", '?', "" }, + {0xB8E536EB, nullptr, "sceUsbMicInputInit", '?', "" }, + {0xF899001C, nullptr, "sceUsbMicWaitInputEnd", '?', "" }, +}; + +void Register_sceUsbMic() +{ + RegisterModule("sceUsbMic", ARRAY_SIZE(sceUsbMic), sceUsbMic); +} diff --git a/Core/HLE/sceUsbMic.h b/Core/HLE/sceUsbMic.h new file mode 100644 index 0000000000..6540246f12 --- /dev/null +++ b/Core/HLE/sceUsbMic.h @@ -0,0 +1,20 @@ +// Copyright (c) 2019- PPSSPP 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 git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +void Register_sceUsbMic(); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 985b8efa8e..3853fa124a 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -394,6 +394,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/sceUsbAcc.cpp \ $(SRC)/Core/HLE/sceUsbCam.cpp \ $(SRC)/Core/HLE/sceUsbGps.cpp \ + $(SRC)/Core/HLE/sceUsbMic.cpp \ $(SRC)/Core/HLE/sceUtility.cpp \ $(SRC)/Core/HLE/sceVaudio.cpp \ $(SRC)/Core/HLE/scePspNpDrm_user.cpp \ From 4d6d12965b4dbd9d4079851b4192f023f2656ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Aug 2019 10:35:51 +0200 Subject: [PATCH 16/42] Update lang submodule --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index 03707d64bf..b2d7f53fa0 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 03707d64bffc594d5707780a34ada77e8f43cdc0 +Subproject commit b2d7f53fa056586504590d271ab5f3198bfc2acd From 96a15cf5253134336efd3ed94f7814e83252fe1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Aug 2019 18:00:33 +0200 Subject: [PATCH 17/42] Fix the indirect postprocessing path. Fixes #12133 (but see the issue for a secondary bug). --- GPU/Common/FramebufferCommon.cpp | 2 +- GPU/Vulkan/FramebufferVulkan.cpp | 2 +- UI/NativeApp.cpp | 1 + android/jni/app-android.cpp | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 76f1027bfd..bdd4490275 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -985,7 +985,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, renderWidth_, renderHeight_, &uniforms); BindPostShader(uniforms); DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST; - flags = flags | DRAWTEX_TO_BACKBUFFER; DrawActiveTexture(0, 0, fbo_w, fbo_h, fbo_w, fbo_h, 0.0f, 0.0f, 1.0f, 1.0f, ROTATION_LOCKED_HORIZONTAL, flags); draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); @@ -1008,6 +1007,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { flags = flags | DRAWTEX_TO_BACKBUFFER; // Fullscreen Image SetViewport2D(0, 0, pixelWidth_, pixelHeight_); + draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); } else { shaderManager_->DirtyLastShader(); // dirty lastShader_ BEFORE drawing diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 918dafdbed..faa104aba7 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -305,8 +305,8 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa if ((flags & DRAWTEX_TO_BACKBUFFER) && g_display_rotation != DisplayRotation::ROTATE_0) { for (int i = 0; i < 4; i++) { - // backwards notation, should fix that... Vec3 v(vtx[i].x, vtx[i].y, 0.0f); + // backwards notation, should fix that... v = v * g_display_rot_matrix; vtx[i].x = v.x; vtx[i].y = v.y; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 65ed16b406..e065035e62 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -982,6 +982,7 @@ void NativeRender(GraphicsContext *graphicsContext) { break; } + // Compensate for rotated display if needed. if (g_display_rotation != DisplayRotation::ROTATE_0) { ortho = ortho * g_display_rot_matrix; } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index c61acb745e..8d6e5f4c44 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -645,7 +645,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv ILOG("Size change detected (previously %d,%d) - calling NativeResized()", old_w, old_h); NativeResized(); } else { - ILOG("Size didn't change."); + ILOG("NativeApp::backbufferReisze: Size didn't change."); } } From 4bff980d342a9119039d7442162e44c17fb248f6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 11 Jun 2019 15:45:20 -0700 Subject: [PATCH 18/42] interp: Use software dot for better accuracy. This gets some vdot/similar tests to give more accurate results compared to hardware. Also added flushing of zero and NaNs. Currently disabled, only enabled with a compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 135 +++++++++++++++++++++++++++--------- Core/MIPS/MIPSVFPUUtils.cpp | 84 ++++++++++++++++++++++ Core/MIPS/MIPSVFPUUtils.h | 2 + 3 files changed, 188 insertions(+), 33 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index bb6c84abe5..9f1d923308 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -70,6 +70,8 @@ #define M_SQRT1_2 0.707106781186547524401f #endif +static const bool USE_VFPU_DOT = false; + union FloatBits { float f[4]; u32 u[4]; @@ -466,20 +468,33 @@ namespace MIPSInt for (int a = 0; a < n; a++) { for (int b = 0; b < n; b++) { - float sum = 0.0f; + union { float f; uint32_t u; } sum = { 0.0f }; if (a == n - 1 && b == n - 1) { // S and T prefixes work on the final (or maybe first, in reverse?) dot. ApplySwizzleS(&s[b * 4], V_Quad); ApplySwizzleT(&t[a * 4], V_Quad); - for (int c = 0; c < 4; c++) { - sum += s[b * 4 + c] * t[a * 4 + c]; + } + + if (USE_VFPU_DOT) { + sum.f = vfpu_dot(&s[b * 4], &t[a * 4]); + if (my_isnan(sum.f)) { + sum.u = 0x7f800001; + } else if ((sum.u & 0x7F800000) == 0) { + sum.u &= 0xFF800000; } } else { - for (int c = 0; c < n; c++) { - sum += s[b * 4 + c] * t[a * 4 + c]; + if (a == n - 1 && b == n - 1) { + for (int c = 0; c < 4; c++) { + sum.f += s[b * 4 + c] * t[a * 4 + c]; + } + } else { + for (int c = 0; c < n; c++) { + sum.f += s[b * 4 + c] * t[a * 4 + c]; + } } } - d[a * 4 + b] = sum; + + d[a * 4 + b] = sum.f; } } @@ -1123,7 +1138,7 @@ namespace MIPSInt void Int_VDot(MIPSOpcode op) { float s[4]{}, t[4]{}; - float d; + union { float f; uint32_t u; } d; int vd = _VD; int vs = _VS; int vt = _VT; @@ -1132,12 +1147,23 @@ namespace MIPSInt ApplySwizzleS(s, V_Quad); ReadVector(t, sz, vt); ApplySwizzleT(t, V_Quad); - d = 0.0f; - for (int i = 0; i < 4; i++) { - d += s[i] * t[i]; + + if (USE_VFPU_DOT) { + d.f = vfpu_dot(s, t); + if (my_isnan(d.f)) { + d.u = 0x7f800001; + } else if ((d.u & 0x7F800000) == 0) { + d.u &= 0xFF800000; + } + } else { + d.f = 0.0f; + for (int i = 0; i < 4; i++) { + d.f += s[i] * t[i]; + } } - ApplyPrefixD(&d, V_Single); - WriteVector(&d, V_Single, vd); + + ApplyPrefixD(&d.f, V_Single); + WriteVector(&d.f, V_Single, vd); PC += 4; EatPrefixes(); } @@ -1565,7 +1591,8 @@ namespace MIPSInt } void Int_Vtfm(MIPSOpcode op) { - float s[16]{}, t[4]{}, d[4]; + float s[16]{}, t[4]{}; + FloatBits d; int vd = _VD; int vs = _VS; int vt = _VT; @@ -1579,13 +1606,36 @@ namespace MIPSInt ReadMatrix(s, msz, vs); ReadVector(t, sz, vt); - for (int i = 0; i < ins; i++) { - d[i] = s[i * 4] * t[0]; - for (int k = 1; k < tn; k++) { - d[i] += s[i * 4 + k] * t[k]; + if (USE_VFPU_DOT) { + float t2[4]; + for (int i = 0; i < 4; i++) { + if (i < tn) { + t2[i] = t[i]; + } else if (i == ins) { + t2[i] = 1.0f; + } else { + t2[i] = 0.0f; + } } - if (ins >= n) { - d[i] += s[i * 4 + ins]; + + for (int i = 0; i < ins; i++) { + d.f[i] = vfpu_dot(&s[i * 4], t2); + + if (my_isnan(d.f[i])) { + d.u[i] = 0x7f800001; + } else if ((d.u[i] & 0x7F800000) == 0) { + d.u[i] &= 0xFF800000; + } + } + } else { + for (int i = 0; i < ins; i++) { + d.f[i] = s[i * 4] * t[0]; + for (int k = 1; k < tn; k++) { + d.f[i] += s[i * 4 + k] * t[k]; + } + if (ins >= n) { + d.f[i] += s[i * 4 + ins]; + } } } @@ -1610,17 +1660,27 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); // Really this is the operation all rows probably use (with constant wiring.) - d[ins] = s[ins * 4] * t[0]; - for (int k = 1; k < 4; k++) { - d[ins] += s[ins * 4 + k] * t[k]; + if (USE_VFPU_DOT) { + d.f[ins] = vfpu_dot(&s[ins * 4], t); + + if (my_isnan(d.f[ins])) { + d.u[ins] = 0x7f800001; + } else if ((d.u[ins] & 0x7F800000) == 0) { + d.u[ins] &= 0xFF800000; + } + } else { + d.f[ins] = s[ins * 4] * t[0]; + for (int k = 1; k < 4; k++) { + d.f[ins] += s[ins * 4 + k] * t[k]; + } } // D prefix applies to the last element only. u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << ins; u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (ins + ins); currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; - ApplyPrefixD(d, sz); - WriteVector(d, sz, vd); + ApplyPrefixD(d.f, sz); + WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } @@ -1967,7 +2027,8 @@ namespace MIPSInt } void Int_VecDo3(MIPSOpcode op) { - float s[4], t[4], d[4]; + float s[4], t[4]; + FloatBits d; int vd = _VD; int vs = _VS; int vt = _VT; @@ -2011,10 +2072,18 @@ namespace MIPSInt for (int i = 0; i < n; i++) { switch (optype) { - case 0: d[i] = s[i] + t[i]; break; //vadd - case 1: d[i] = s[i] - t[i]; break; //vsub - case 7: d[i] = s[i] / t[i]; break; //vdiv - case 8: d[i] = s[i] * t[i]; break; //vmul + case 0: d.f[i] = s[i] + t[i]; break; //vadd + case 1: d.f[i] = s[i] - t[i]; break; //vsub + case 7: d.f[i] = s[i] / t[i]; break; //vdiv + case 8: d.f[i] = s[i] * t[i]; break; //vmul + } + + if (USE_VFPU_DOT) { + if (my_isnan(d.f[i])) { + d.u[i] = 0x7f800001; + } else if ((d.u[i] & 0x7F800000) == 0) { + d.u[i] &= 0xFF800000; + } } } @@ -2023,12 +2092,12 @@ namespace MIPSInt u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; - ApplyPrefixD(d, sz); + ApplyPrefixD(d.f, sz); } else { - RetainInvalidSwizzleST(d, sz); - ApplyPrefixD(d, sz); + RetainInvalidSwizzleST(d.f, sz); + ApplyPrefixD(d.f, sz); } - WriteVector(d, sz, vd); + WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 6c07bfef5a..1ad629d7c2 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -607,3 +607,87 @@ float Float16ToFloat32(unsigned short l) } return f; } + +static uint32_t get_uexp(uint32_t x) { + return (x >> 23) & 0xFF; +} + +static int32_t get_mant(uint32_t x) { + // Note: this returns the hidden 1. + return (x & 0x007FFFFF) | 0x00800000; +} + +static int32_t get_sign(uint32_t x) { + return x & 0x80000000; +} + +float vfpu_dot(float a[4], float b[4]) { + static const int EXTRA_BITS = 2; + union float2int { + uint32_t i; + float f; + } intermed[4]; + + for (int i = 0; i < 4; i++) { + intermed[i].f = a[i] * b[i]; + } + + uint32_t exps[4]; + int32_t mants[4]; + uint32_t max_exp = 0; + + for (int i = 0; i < 4; i++) { + exps[i] = get_uexp(intermed[i].i); + // Preserve extra bits of precision in the mantissa during the add. + mants[i] = get_mant(intermed[i].i) << EXTRA_BITS; + if (exps[i] > max_exp) { + max_exp = exps[i]; + } + } + + int32_t mant_sum = 0; + for (int i = 0; i < 4; i++) { + int exp = max_exp - exps[i]; + if (exp >= 32) { + mants[i] = 0; + } else { + mants[i] >>= max_exp - exps[i]; + } + if (get_sign(intermed[i].i)) { + mants[i] = -mants[i]; + } + mant_sum += mants[i]; + } + + uint32_t sign_sum = 0; + if (mant_sum < 0) { + sign_sum = 0x80000000; + mant_sum = -mant_sum; + } + // Chop off the extra bits. + mant_sum >>= EXTRA_BITS; + + if (mant_sum == 0 || max_exp == 0) { + return 0.0f; + } + + while (mant_sum < 0x00800000) { + mant_sum <<= 1; + max_exp -= 1; + } + while (mant_sum >= 0x01000000) { + mant_sum >>= 1; + max_exp += 1; + } + _dbg_assert_(JIT, (mant_sum & 0x00800000) != 0); + + if (max_exp >= 255) { + max_exp = 255; + mant_sum = 0; + } else if (max_exp == 0) { + return 0.0f; + } + + intermed[0].i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); + return intermed[0].f; +} diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 1489e33fc0..8e3d52bf70 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -97,6 +97,8 @@ inline float vfpu_clamp(float v, float min, float max) { return v >= max ? max : (v <= min ? min : v); } +float vfpu_dot(float a[4], float b[4]); + #define VFPU_FLOAT16_EXP_MAX 0x1f #define VFPU_SH_FLOAT16_SIGN 15 #define VFPU_MASK_FLOAT16_SIGN 0x1 From 39017ea2008c42d69cc4a33722eb5bec927f8c38 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 12 Jun 2019 21:40:05 -0700 Subject: [PATCH 19/42] interp: Use vfpu_dot for vavg, vfad, vhdp. Disabled by default. --- Core/MIPS/MIPSIntVFPU.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 9f1d923308..e22e67fc5f 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1199,8 +1199,12 @@ namespace MIPSInt ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); float sum = 0.0f; - for (int i = 0; i < 4; i++) { - sum += s[i] * t[i]; + if (USE_VFPU_DOT) { + sum = vfpu_dot(s, t); + } else { + for (int i = 0; i < 4; i++) { + sum += s[i] * t[i]; + } } d = my_isnan(sum) ? fabsf(sum) : sum; ApplyPrefixD(&d, V_Single); @@ -1437,9 +1441,13 @@ namespace MIPSInt u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); - d = 0.0f; - for (int i = 0; i < 4; i++) { - d += s[i] * t[i]; + if (USE_VFPU_DOT) { + d = vfpu_dot(s, t); + } else { + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; + } } ApplyPrefixD(&d, V_Single); WriteVector(&d, V_Single, vd); @@ -1471,9 +1479,13 @@ namespace MIPSInt tprefixAdd = 0; ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); - d = 0.0f; - for (int i = 0; i < 4; i++) { - d += s[i] * t[i]; + if (USE_VFPU_DOT) { + d = vfpu_dot(s, t); + } else { + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; + } } ApplyPrefixD(&d, V_Single); WriteVector(&d, V_Single, vd); From 08a894abdea0ac5e7ea3de69df2b6edd80cc11ee Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 12 Jun 2019 22:17:25 -0700 Subject: [PATCH 20/42] interp: Handle NaN better in vfpu_dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 1ad629d7c2..8de54a7834 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -635,6 +635,7 @@ float vfpu_dot(float a[4], float b[4]) { uint32_t exps[4]; int32_t mants[4]; uint32_t max_exp = 0; + uint32_t last_inf = 0; for (int i = 0; i < 4; i++) { exps[i] = get_uexp(intermed[i].i); @@ -643,6 +644,15 @@ float vfpu_dot(float a[4], float b[4]) { if (exps[i] > max_exp) { max_exp = exps[i]; } + if (exps[i] == 255) { + bool diff_sign = last_inf && get_sign(last_inf) != get_sign(intermed[i].i); + bool mant_nan = mants[i] != (0x00800000 << EXTRA_BITS); + if (diff_sign || mant_nan) { + intermed[0].i = 0x7F800001; + return intermed[0].f; + } + last_inf = intermed[i].i; + } } int32_t mant_sum = 0; From 2ba35c639170a8bd04af3a33a2a66c9566452fed Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 14 Jun 2019 22:19:30 -0700 Subject: [PATCH 21/42] interp: Use an integer multiply for dots. --- Core/MIPS/MIPSVFPUUtils.cpp | 84 +++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 8de54a7834..186d458db6 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -15,9 +15,11 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include #include +#include "Common/BitScan.h" #include "Common/CommonFuncs.h" #include "Core/Reporting.h" #include "Core/MIPS/MIPS.h" @@ -626,32 +628,52 @@ float vfpu_dot(float a[4], float b[4]) { union float2int { uint32_t i; float f; - } intermed[4]; + }; + float2int result; + float2int src[2]; - for (int i = 0; i < 4; i++) { - intermed[i].f = a[i] * b[i]; - } - - uint32_t exps[4]; + int32_t exps[4]; int32_t mants[4]; - uint32_t max_exp = 0; - uint32_t last_inf = 0; + int32_t signs[4]; + int32_t max_exp = 0; + int32_t last_inf = -1; for (int i = 0; i < 4; i++) { - exps[i] = get_uexp(intermed[i].i); - // Preserve extra bits of precision in the mantissa during the add. - mants[i] = get_mant(intermed[i].i) << EXTRA_BITS; + src[0].f = a[i]; + src[1].f = b[i]; + + int32_t aexp = get_uexp(src[0].i); + int32_t bexp = get_uexp(src[1].i); + int32_t amant = get_mant(src[0].i) << EXTRA_BITS; + int32_t bmant = get_mant(src[1].i) << EXTRA_BITS; + + bool anan = aexp == 255 && (src[0].i & 0x007FFFFF) != 0; + bool bnan = bexp == 255 && (src[1].i & 0x007FFFFF) != 0; + if (anan || bnan) { + result.i = 0x7F800001; + return result.f; + } + + exps[i] = aexp + bexp - 127; + if (exps[i] >= 255) { + mants[i] = get_mant(0) << EXTRA_BITS; + } else { + // TODO: Adjust precision? + uint64_t adjust = (uint64_t)amant * (uint64_t)bmant; + mants[i] = (adjust >> (23 + EXTRA_BITS)) & 0x7FFFFFFF; + } + signs[i] = get_sign(src[0].i) ^ get_sign(src[1].i); + if (exps[i] > max_exp) { max_exp = exps[i]; } - if (exps[i] == 255) { - bool diff_sign = last_inf && get_sign(last_inf) != get_sign(intermed[i].i); - bool mant_nan = mants[i] != (0x00800000 << EXTRA_BITS); - if (diff_sign || mant_nan) { - intermed[0].i = 0x7F800001; - return intermed[0].f; + if (exps[i] >= 255) { + // Infinity minus infinity is not a real number. + if (last_inf != -1 && signs[i] != last_inf) { + result.i = 0x7F800001; + return result.f; } - last_inf = intermed[i].i; + last_inf = signs[i]; } } @@ -663,7 +685,7 @@ float vfpu_dot(float a[4], float b[4]) { } else { mants[i] >>= max_exp - exps[i]; } - if (get_sign(intermed[i].i)) { + if (signs[i]) { mants[i] = -mants[i]; } mant_sum += mants[i]; @@ -674,20 +696,20 @@ float vfpu_dot(float a[4], float b[4]) { sign_sum = 0x80000000; mant_sum = -mant_sum; } - // Chop off the extra bits. - mant_sum >>= EXTRA_BITS; + // Account for the mantissa actually having more precision. + max_exp -= EXTRA_BITS; - if (mant_sum == 0 || max_exp == 0) { + if (mant_sum == 0 || max_exp <= 0) { return 0.0f; } - while (mant_sum < 0x00800000) { - mant_sum <<= 1; - max_exp -= 1; - } - while (mant_sum >= 0x01000000) { - mant_sum >>= 1; - max_exp += 1; + int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8; + if (shift < 0) { + mant_sum >>= -shift; + max_exp += -shift; + } else { + mant_sum <<= shift; + max_exp -= shift; } _dbg_assert_(JIT, (mant_sum & 0x00800000) != 0); @@ -698,6 +720,6 @@ float vfpu_dot(float a[4], float b[4]) { return 0.0f; } - intermed[0].i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); - return intermed[0].f; + result.i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); + return result.f; } From d5e572b82e3268e295582e6febdc9017c0264990 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 16 Jun 2019 18:27:52 -0700 Subject: [PATCH 22/42] interp: Correct INF * 0 during dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 186d458db6..6215501cd2 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -647,15 +647,19 @@ float vfpu_dot(float a[4], float b[4]) { int32_t amant = get_mant(src[0].i) << EXTRA_BITS; int32_t bmant = get_mant(src[1].i) << EXTRA_BITS; - bool anan = aexp == 255 && (src[0].i & 0x007FFFFF) != 0; - bool bnan = bexp == 255 && (src[1].i & 0x007FFFFF) != 0; - if (anan || bnan) { - result.i = 0x7F800001; - return result.f; - } - exps[i] = aexp + bexp - 127; - if (exps[i] >= 255) { + if (aexp == 255) { + // INF * 0 = NAN + if ((src[0].i & 0x007FFFFF) != 0 || bexp == 0) { + result.i = 0x7F800001; + return result.f; + } + mants[i] = get_mant(0) << EXTRA_BITS; + } else if (bexp == 255) { + if ((src[1].i & 0x007FFFFF) != 0 || aexp == 0) { + result.i = 0x7F800001; + return result.f; + } mants[i] = get_mant(0) << EXTRA_BITS; } else { // TODO: Adjust precision? From c61a24cef01a4eb161ba019129e73c034e181ad5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 26 Jun 2019 21:32:32 -0700 Subject: [PATCH 23/42] interp: Handle rounding in software dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 6215501cd2..edd497d795 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -687,7 +687,7 @@ float vfpu_dot(float a[4], float b[4]) { if (exp >= 32) { mants[i] = 0; } else { - mants[i] >>= max_exp - exps[i]; + mants[i] >>= exp; } if (signs[i]) { mants[i] = -mants[i]; @@ -700,8 +700,9 @@ float vfpu_dot(float a[4], float b[4]) { sign_sum = 0x80000000; mant_sum = -mant_sum; } - // Account for the mantissa actually having more precision. - max_exp -= EXTRA_BITS; + + // Truncate off the extra bits now. We want to zero them for rounding purposes. + mant_sum >>= EXTRA_BITS; if (mant_sum == 0 || max_exp <= 0) { return 0.0f; @@ -709,13 +710,19 @@ float vfpu_dot(float a[4], float b[4]) { int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8; if (shift < 0) { + // Round to even if we'd shift away a 0.5. + uint32_t round_bit = 1 << (-shift - 1); + if ((mant_sum & round_bit) && (mant_sum & (round_bit << 1))) { + mant_sum += round_bit; + shift = (int8_t)clz32_nonzero(mant_sum) - 8; + } mant_sum >>= -shift; max_exp += -shift; } else { mant_sum <<= shift; max_exp -= shift; } - _dbg_assert_(JIT, (mant_sum & 0x00800000) != 0); + _dbg_assert_msg_(JIT, (mant_sum & 0x00800000) != 0, "Mantissa wrong: %08x", mant_sum); if (max_exp >= 255) { max_exp = 255; From 7b84be1479450a555c17a0a5b0cd500eb2e82297 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 20:48:20 -0700 Subject: [PATCH 24/42] interp: Fix adding infinity in software dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index edd497d795..7019429b53 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -655,12 +655,14 @@ float vfpu_dot(float a[4], float b[4]) { return result.f; } mants[i] = get_mant(0) << EXTRA_BITS; + exps[i] = 255; } else if (bexp == 255) { if ((src[1].i & 0x007FFFFF) != 0 || aexp == 0) { result.i = 0x7F800001; return result.f; } mants[i] = get_mant(0) << EXTRA_BITS; + exps[i] = 255; } else { // TODO: Adjust precision? uint64_t adjust = (uint64_t)amant * (uint64_t)bmant; From d2f7594dc5192d9ef4bf54fc634b5f0e8519cc10 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 21:31:36 -0700 Subject: [PATCH 25/42] interp: Use software dot in vcrsp/vqmul. Disabled by default using a compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 48 +++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index e22e67fc5f..441b419340 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2129,27 +2129,61 @@ namespace MIPSInt switch (sz) { case V_Triple: // vcrsp.t - d[0] = s[1]*t[2] - s[2]*t[1]; - d[1] = s[2]*t[0] - s[0]*t[2]; + { + if (USE_VFPU_DOT) { + float t0[4] = { 0.0f, t[2], -t[1], 0.0f }; + float t1[4] = { -t[2], 0.0f, t[0], 0.0f }; + d[0] = vfpu_dot(s, t0); + d[1] = vfpu_dot(s, t1); + } else { + d[0] = s[1] * t[2] - s[2] * t[1]; + d[1] = s[2] * t[0] - s[0] * t[2]; + } // T prefix forces swizzle and negate, can be used to have weird constants. tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2) | VFPU_NEGATE(0, 1, 0, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); ApplySwizzleS(s, V_Quad); - d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; + if (USE_VFPU_DOT) { + // TODO: But flush any infs to 0? This seems sketchy. + for (int i = 0; i < 4; ++i) { + if (my_isinf(s[i])) + s[i] = 0.0f; + if (my_isinf(t[i])) + t[i] = 0.0f; + } + d[2] = vfpu_dot(s, t); + } else { + d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; + } break; + } case V_Quad: // vqmul.q - d[0] = s[0]*t[3] + s[1]*t[2] - s[2]*t[1] + s[3]*t[0]; - d[1] = -s[0]*t[2] + s[1]*t[3] + s[2]*t[0] + s[3]*t[1]; - d[2] = s[0]*t[1] - s[1]*t[0] + s[2]*t[3] + s[3]*t[2]; + { + if (USE_VFPU_DOT) { + float t0[4] = { t[3], t[2], -t[1], t[0] }; + float t1[4] = { -t[2], t[3], t[0], t[1] }; + float t2[4] = { t[1], -t[0], t[3], t[2] }; + d[0] = vfpu_dot(s, t0); + d[1] = vfpu_dot(s, t1); + d[2] = vfpu_dot(s, t2); + } else { + d[0] = s[0] * t[3] + s[1] * t[2] - s[2] * t[1] + s[3] * t[0]; + d[1] = -s[0] * t[2] + s[1] * t[3] + s[2] * t[0] + s[3] * t[1]; + d[2] = s[0] * t[1] - s[1] * t[0] + s[2] * t[3] + s[3] * t[2]; + } // T prefix forces swizzle and negate, can be used to have weird constants. tprefixAdd = VFPU_SWIZZLE(0, 1, 2, 3) | VFPU_NEGATE(1, 1, 1, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); ApplySwizzleS(s, sz); - d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; + if (USE_VFPU_DOT) + d[3] = vfpu_dot(s, t); + else + d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; break; + } case V_Pair: // t swizzles invalid so the multiply is always zero. From ed4f0896db3561a608f91bd93fb7cb9cc0843dd3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 8 Jul 2019 22:35:15 -0700 Subject: [PATCH 26/42] interp: Use vfpu_dot for vdet as well. Improves test accuracy here too. Also keep neg sign on NAN for vdiv. Disabled by default via compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 441b419340..8ecb5fac72 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1419,8 +1419,14 @@ namespace MIPSInt u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 0, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); - d[0] = s[0] * t[0] - s[1] * t[1]; - d[0] += s[2] * t[2] + s[3] * t[3]; + if (USE_VFPU_DOT) { + s[1] = -s[1]; + d[0] = vfpu_dot(s, t); + } else { + d[0] = s[0] * t[0] - s[1] * t[1]; + d[0] += s[2] * t[2] + s[3] * t[3]; + } + ApplyPrefixD(d, sz); WriteVector(d, V_Single, vd); PC += 4; @@ -2092,7 +2098,7 @@ namespace MIPSInt if (USE_VFPU_DOT) { if (my_isnan(d.f[i])) { - d.u[i] = 0x7f800001; + d.u[i] = (d.u[i] & 0xff800001) | 1; } else if ((d.u[i] & 0x7F800000) == 0) { d.u[i] &= 0xFF800000; } From 13278dc1f44788571f90e376f2af6ddc37cdbb75 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 8 Jul 2019 22:35:59 -0700 Subject: [PATCH 27/42] interp: Implement software sqrt to match PSP. This matches the PSP's square root better. Disabled by default using a compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 5 ++-- Core/MIPS/MIPSVFPUUtils.cpp | 54 +++++++++++++++++++++++++++++++++++++ Core/MIPS/MIPSVFPUUtils.h | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 8ecb5fac72..6c1311483e 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -71,6 +71,7 @@ #endif static const bool USE_VFPU_DOT = false; +static const bool USE_VPFU_SQRT = false; union FloatBits { float f[4]; @@ -617,13 +618,13 @@ namespace MIPSInt case 4: if (s[i] <= 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 case 5: if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 case 16: d[i] = 1.0f / s[i]; break; //vrcp - case 17: d[i] = 1.0f / sqrtf(s[i]); break; //vrsq + case 17: d[i] = USE_VPFU_SQRT ? 1.0f / vfpu_sqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq case 18: { d[i] = vfpu_sin(s[i]); } break; //vsin case 19: { d[i] = vfpu_cos(s[i]); } break; //vcos case 20: d[i] = powf(2.0f, s[i]); break; //vexp2 case 21: d[i] = logf(s[i])/log(2.0f); break; //vlog2 - case 22: d[i] = fabsf(sqrtf(s[i])); break; //vsqrt + case 22: d[i] = USE_VPFU_SQRT ? fabsf(vfpu_sqrt(s[i])) : fabsf(sqrtf(s[i])); break; //vsqrt case 23: d[i] = asinf(s[i]) / M_PI_2; break; //vasin case 24: d[i] = -1.0f / s[i]; break; // vnrcp case 26: { d[i] = -vfpu_sin(s[i]); } break; // vnsin diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 7019429b53..8d0d3e5e6f 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -614,6 +614,10 @@ static uint32_t get_uexp(uint32_t x) { return (x >> 23) & 0xFF; } +int32_t get_exp(uint32_t x) { + return get_uexp(x) - 127; +} + static int32_t get_mant(uint32_t x) { // Note: this returns the hidden 1. return (x & 0x007FFFFF) | 0x00800000; @@ -736,3 +740,53 @@ float vfpu_dot(float a[4], float b[4]) { result.i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); return result.f; } + +// TODO: This is still not completely accurate compared to the PSP's vsqrt. +float vfpu_sqrt(float a) { + union float2int { + uint32_t u; + float f; + }; + float2int val; + val.f = a; + + if ((val.u & 0xff800000) == 0x7f800000) { + if ((val.u & 0x007fffff) != 0) { + val.u = 0x7f800001; + } + return val.f; + } + if ((val.u & 0x7fffffff) == 0) { + // Kill any sign. + val.u = 0; + return val.f; + } + if (val.u & 0x80000000) { + val.u = 0x7f800001; + return val.f; + } + + int k = get_exp(val.u); + int sp = get_mant(val.u); + int less_bits; + + if (k & 1) { + less_bits = 1; + k /= 2; + } else { + less_bits = 0; + k /= 2; + } + + int z = 0x00800000 >> less_bits; + int64_t halfsp = sp >> 1; + halfsp <<= 23 - less_bits; + for (int i = 0; i < 6; ++i) { + z = (z >> 1) + (int)(halfsp / z); + } + + val.u = ((k + 127) << 23) | ((z << less_bits) & 0x007FFFFF); + // The lower two bits never end up set on the PSP, it seems like. + val.u &= 0xFFFFFFFC; + return val.f; +} diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 8e3d52bf70..53c729e3c0 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -98,6 +98,7 @@ inline float vfpu_clamp(float v, float min, float max) { } float vfpu_dot(float a[4], float b[4]); +float vfpu_sqrt(float a); #define VFPU_FLOAT16_EXP_MAX 0x1f #define VFPU_SH_FLOAT16_SIGN 15 From 6028b798958f994a6fe780e180eb7910251fc289 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 8 Jul 2019 22:58:59 -0700 Subject: [PATCH 28/42] interp: Oops, fix vdot bug with subnormals. --- Core/MIPS/MIPSVFPUUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 8d0d3e5e6f..67f0bb5798 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -733,7 +733,7 @@ float vfpu_dot(float a[4], float b[4]) { if (max_exp >= 255) { max_exp = 255; mant_sum = 0; - } else if (max_exp == 0) { + } else if (max_exp <= 0) { return 0.0f; } From c1c869df2774422fe1c808d428c90b247c2768c0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Jul 2019 14:09:20 -0700 Subject: [PATCH 29/42] interp: Implement software inverse square root. --- Core/MIPS/MIPSIntVFPU.cpp | 4 +- Core/MIPS/MIPSVFPUUtils.cpp | 88 +++++++++++++++++++++++++++++++------ Core/MIPS/MIPSVFPUUtils.h | 1 + 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 6c1311483e..cf5edba112 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -618,13 +618,13 @@ namespace MIPSInt case 4: if (s[i] <= 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 case 5: if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 case 16: d[i] = 1.0f / s[i]; break; //vrcp - case 17: d[i] = USE_VPFU_SQRT ? 1.0f / vfpu_sqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq + case 17: d[i] = USE_VPFU_SQRT ? vfpu_rsqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq case 18: { d[i] = vfpu_sin(s[i]); } break; //vsin case 19: { d[i] = vfpu_cos(s[i]); } break; //vcos case 20: d[i] = powf(2.0f, s[i]); break; //vexp2 case 21: d[i] = logf(s[i])/log(2.0f); break; //vlog2 - case 22: d[i] = USE_VPFU_SQRT ? fabsf(vfpu_sqrt(s[i])) : fabsf(sqrtf(s[i])); break; //vsqrt + case 22: d[i] = USE_VPFU_SQRT ? vfpu_sqrt(s[i]) : fabsf(sqrtf(s[i])); break; //vsqrt case 23: d[i] = asinf(s[i]) / M_PI_2; break; //vasin case 24: d[i] = -1.0f / s[i]; break; // vnrcp case 26: { d[i] = -vfpu_sin(s[i]); } break; // vnsin diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 67f0bb5798..7e46209c76 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -717,10 +717,13 @@ float vfpu_dot(float a[4], float b[4]) { int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8; if (shift < 0) { // Round to even if we'd shift away a 0.5. - uint32_t round_bit = 1 << (-shift - 1); + const uint32_t round_bit = 1 << (-shift - 1); if ((mant_sum & round_bit) && (mant_sum & (round_bit << 1))) { mant_sum += round_bit; shift = (int8_t)clz32_nonzero(mant_sum) - 8; + } else if ((mant_sum & round_bit) && (mant_sum & (round_bit - 1))) { + mant_sum += round_bit; + shift = (int8_t)clz32_nonzero(mant_sum) - 8; } mant_sum >>= -shift; max_exp += -shift; @@ -756,7 +759,7 @@ float vfpu_sqrt(float a) { } return val.f; } - if ((val.u & 0x7fffffff) == 0) { + if ((val.u & 0x7f800000) == 0) { // Kill any sign. val.u = 0; return val.f; @@ -767,26 +770,83 @@ float vfpu_sqrt(float a) { } int k = get_exp(val.u); - int sp = get_mant(val.u); - int less_bits; + uint32_t sp = get_mant(val.u); + int less_bits = k & 1; + k >>= 1; - if (k & 1) { - less_bits = 1; - k /= 2; - } else { - less_bits = 0; - k /= 2; - } - - int z = 0x00800000 >> less_bits; + uint32_t z = 0x00C00000 >> less_bits; int64_t halfsp = sp >> 1; halfsp <<= 23 - less_bits; for (int i = 0; i < 6; ++i) { - z = (z >> 1) + (int)(halfsp / z); + z = (z >> 1) + (uint32_t)(halfsp / z); } val.u = ((k + 127) << 23) | ((z << less_bits) & 0x007FFFFF); // The lower two bits never end up set on the PSP, it seems like. val.u &= 0xFFFFFFFC; + + return val.f; +} + +static inline uint32_t mant_mul(uint32_t a, uint32_t b) { + uint64_t m = (uint64_t)a * (uint64_t)b; + if (m & 0x007FFFFF) { + m += 0x01437000; + } + return m >> 23; +} + +float vfpu_rsqrt(float a) { + union float2int { + uint32_t u; + float f; + }; + float2int val; + val.f = a; + + if (val.u == 0x7f800000) { + return 0.0f; + } + if ((val.u & 0x7fffffff) > 0x7f800000) { + val.u = (val.u & 0x80000000) | 0x7f800001; + return val.f; + } + if ((val.u & 0x7f800000) == 0) { + val.u = (val.u & 0x80000000) | 0x7f800000; + return val.f; + } + if (val.u & 0x80000000) { + val.u = 0xff800001; + return val.f; + } + + int k = get_exp(val.u); + uint32_t sp = get_mant(val.u); + int less_bits = k & 1; + k = -(k >> 1); + + uint32_t z = 0x00800000 >> less_bits; + uint32_t halfsp = sp >> (1 + less_bits); + for (int i = 0; i < 6; ++i) { + uint32_t oldz = z; + uint32_t zsq = mant_mul(z, z); + uint32_t correction = 0x00C00000 - mant_mul(halfsp, zsq); + z = mant_mul(z, correction); + } + + int8_t shift = (int8_t)clz32_nonzero(z) - 8 + less_bits; + if (shift < 1) { + z >>= -shift; + k += -shift; + } else if (shift > 0) { + z <<= shift; + k -= shift; + } + + z >>= less_bits; + + val.u = ((k + 127) << 23) | (z & 0x007FFFFF); + val.u &= 0xFFFFFFFC; + return val.f; } diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 53c729e3c0..cf7c1e7426 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -99,6 +99,7 @@ inline float vfpu_clamp(float v, float min, float max) { float vfpu_dot(float a[4], float b[4]); float vfpu_sqrt(float a); +float vfpu_rsqrt(float a); #define VFPU_FLOAT16_EXP_MAX 0x1f #define VFPU_SH_FLOAT16_SIGN 15 From f65a71d6d81e1aa4c820abb5976aabf2987ddc6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Aug 2019 21:37:33 -0700 Subject: [PATCH 30/42] Compat: Option to use accurate dotprod for VMMUL. Eliminates Tekken 6 leg shaking. --- Common/BitScan.h | 1 + Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + Core/MIPS/ARM/ArmCompVFPU.cpp | 14 ++++++++++---- Core/MIPS/ARM64/Arm64CompVFPU.cpp | 13 ++++++++++--- Core/MIPS/IR/IRCompVFPU.cpp | 8 ++++++++ Core/MIPS/MIPSIntVFPU.cpp | 8 ++++++-- Core/MIPS/x86/CompVFPU.cpp | 15 +++++++++++---- assets/compat.ini | 6 ++++++ 9 files changed, 54 insertions(+), 13 deletions(-) diff --git a/Common/BitScan.h b/Common/BitScan.h index 729c320759..4a8a8c28ac 100644 --- a/Common/BitScan.h +++ b/Common/BitScan.h @@ -1,6 +1,7 @@ #pragma once #include "ppsspp_config.h" +#include #if PPSSPP_PLATFORM(WINDOWS) #include "Common/CommonWindows.h" diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 71f67ebe4a..4763cf4c25 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -65,6 +65,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "ForceMax60FPS", &flags_.ForceMax60FPS); CheckSetting(iniFile, gameID, "JitInvalidationHack", &flags_.JitInvalidationHack); CheckSetting(iniFile, gameID, "HideISOFiles", &flags_.HideISOFiles); + CheckSetting(iniFile, gameID, "MoreAccurateVMMUL", &flags_.MoreAccurateVMMUL); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index c85267cf46..8632813e26 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -65,6 +65,7 @@ struct CompatFlags { bool ForceMax60FPS; bool JitInvalidationHack; bool HideISOFiles; + bool MoreAccurateVMMUL; }; class IniFile; diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 4893820081..94cd0d811c 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -21,14 +21,16 @@ #include #include "math/math_util.h" +#include "Core/Compatibility.h" +#include "Core/Config.h" #include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Common/CPUDetect.h" -#include "Core/Config.h" -#include "Core/Reporting.h" #include "Core/MIPS/ARM/ArmJit.h" #include "Core/MIPS/ARM/ArmRegCache.h" @@ -1468,12 +1470,16 @@ namespace MIPSComp void ArmJit::Comp_Vmmul(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_MTX_VMMUL); - if (js.HasUnknownPrefix()) { + if (!js.HasNoPrefix()) { DISABLE; } NEON_IF_AVAILABLE(CompNEON_Vmmul); - // TODO: This probably ignores prefixes? + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } MatrixSize sz = GetMtxSize(op); int n = GetMatrixSide(sz); diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 243890f1ea..69d6a302c6 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -21,15 +21,16 @@ #include #include "math/math_util.h" +#include "Core/Compatibility.h" +#include "Core/Config.h" #include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Common/CPUDetect.h" -#include "Core/Config.h" -#include "Core/Reporting.h" - #include "Common/Arm64Emitter.h" #include "Core/MIPS/ARM64/Arm64Jit.h" #include "Core/MIPS/ARM64/Arm64RegCache.h" @@ -1219,6 +1220,12 @@ namespace MIPSComp { DISABLE; } + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } + MatrixSize sz = GetMtxSize(op); int n = GetMatrixSide(sz); diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index fe69d4f7e1..13fa39b2c2 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -20,6 +20,7 @@ #include "math/math_util.h" #include "Common/CPUDetect.h" +#include "Core/Compatibility.h" #include "Core/Config.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" @@ -29,6 +30,7 @@ #include "Core/MIPS/IR/IRFrontend.h" #include "Core/MIPS/IR/IRRegCache.h" #include "Core/Reporting.h" +#include "Core/System.h" // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. @@ -1242,6 +1244,12 @@ namespace MIPSComp { DISABLE; } + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } + // Matrix multiply (weird prefixes) // D[0 .. N, 0 .. M] = S[0 .. N, 0 .. M]' * T[0 .. N, 0 .. M] // Note: Behaves as if it's implemented through a series of vdots. diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index cf5edba112..8e3ad0ba01 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -23,9 +23,11 @@ #include "math/math_util.h" +#include "Core/Compatibility.h" #include "Core/Core.h" -#include "Core/Reporting.h" #include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSInt.h" @@ -467,6 +469,8 @@ namespace MIPSInt ReadMatrix(s, sz, vs); ReadMatrix(t, sz, vt); + // TODO: Always use the more accurate path in interpreter? + bool useAccurateDot = USE_VFPU_DOT || PSP_CoreParameter().compat.flags().MoreAccurateVMMUL; for (int a = 0; a < n; a++) { for (int b = 0; b < n; b++) { union { float f; uint32_t u; } sum = { 0.0f }; @@ -476,7 +480,7 @@ namespace MIPSInt ApplySwizzleT(&t[a * 4], V_Quad); } - if (USE_VFPU_DOT) { + if (useAccurateDot) { sum.f = vfpu_dot(&s[b * 4], &t[a * 4]); if (my_isnan(sum.f)) { sum.u = 0x7f800001; diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 0b87ef5ea0..ff7bd79f2b 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -29,9 +29,11 @@ #include "math/math_util.h" #include "Common/CPUDetect.h" -#include "Core/MemMap.h" +#include "Core/Compatibility.h" #include "Core/Config.h" +#include "Core/MemMap.h" #include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSVFPUUtils.h" @@ -2803,10 +2805,15 @@ void Jit::Comp_VScl(MIPSOpcode op) { void Jit::Comp_Vmmul(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_MTX_VMMUL); - - // TODO: This probably ignores prefixes? - if (js.HasUnknownPrefix()) + if (!js.HasNoPrefix()) { DISABLE; + } + + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } MatrixSize sz = GetMtxSize(op); VectorSize vsz = GetVectorSize(sz); diff --git a/assets/compat.ini b/assets/compat.ini index 15aee5a550..74cd2406d5 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -620,3 +620,9 @@ NPJH50471 = true ULJM06033 = true NPJH50559 = true NPEH00030 = true + +[MoreAccurateVMMUL] +# Fixes leg shaking in Tekken 6. The potential for slowdown in other games is large enough +# that we will not generally apply this accurate mode where not needed. +ULES01376 = true +ULUS10466 = true From 85b9ad8fe3565a225dbbd94636a6bb5b9feaaab9 Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Sun, 4 Aug 2019 21:41:25 -0700 Subject: [PATCH 31/42] Compat: Add more Tekken game IDs to leg shake fix. --- assets/compat.ini | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/assets/compat.ini b/assets/compat.ini index 74cd2406d5..f1917c5882 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -624,5 +624,10 @@ NPEH00030 = true [MoreAccurateVMMUL] # Fixes leg shaking in Tekken 6. The potential for slowdown in other games is large enough # that we will not generally apply this accurate mode where not needed. -ULES01376 = true ULUS10466 = true +ULES01376 = true +ULJS00224 = true +NPUH10047 = true +ULAS42214 = true +ULJS19054 = true +NPJH50184 = true From 641bb72b40d344c72b86925af1cef79666a2d5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Aug 2019 15:33:11 +0200 Subject: [PATCH 32/42] Memchecks: Avoid taking the mutex lock if no memchecks active. Possible minor speedup in GoW and other block-copy-heavy games. --- Core/Debugger/Breakpoints.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index dd6184d2ce..920bd8955f 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include #include "Common/Log.h" @@ -28,6 +29,8 @@ #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/CoreTiming.h" +std::atomic anyMemChecks_ = false; + static std::mutex breakPointsMutex_; std::vector CBreakPoints::breakPoints_; u32 CBreakPoints::breakSkipFirstAt_ = 0; @@ -388,6 +391,7 @@ void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, Break check.result = result; memChecks_.push_back(check); + anyMemChecks_ = true; guard.unlock(); Update(); } @@ -395,6 +399,7 @@ void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, Break { memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond); memChecks_[mc].result = (BreakAction)(memChecks_[mc].result | result); + anyMemChecks_ = true; guard.unlock(); Update(); } @@ -410,6 +415,7 @@ void CBreakPoints::RemoveMemCheck(u32 start, u32 end) if (mc != INVALID_MEMCHECK) { memChecks_.erase(memChecks_.begin() + mc); + anyMemChecks_ = !memChecks_.empty(); guard.unlock(); Update(); } @@ -499,6 +505,8 @@ MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) { BreakAction CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc) { + if (!anyMemChecks_) + return BREAK_ACTION_IGNORE; std::unique_lock guard(memCheckMutex_); auto check = GetMemCheckLocked(address, size); if (check) { From 9b30e5393d41055c537362aba6ea916d89103018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Aug 2019 16:09:39 +0200 Subject: [PATCH 33/42] Buildfix (oops) --- Core/Debugger/Breakpoints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index 920bd8955f..96f52ae0ba 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -29,7 +29,7 @@ #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/CoreTiming.h" -std::atomic anyMemChecks_ = false; +std::atomic anyMemChecks_(false); static std::mutex breakPointsMutex_; std::vector CBreakPoints::breakPoints_; From 86421344191cba850f21dcebc4ea08d43196b6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Aug 2019 14:53:11 +0200 Subject: [PATCH 34/42] VFPU-Int: Add a few fast-paths to ReadMatrix/WriteMatrix. Should gain back part of the speed lost in #12217 (which fixed #5399). --- Core/MIPS/MIPSVFPUUtils.cpp | 102 +++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 7e46209c76..57d5c4cad8 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -17,7 +17,8 @@ #include #include -#include +#include +#include #include "Common/BitScan.h" #include "Common/CommonFuncs.h" @@ -245,14 +246,44 @@ void ReadMatrix(float *rd, MatrixSize size, int reg) { default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); } - for (int i = 0; i < side; i++) { - for (int j = 0; j < side; j++) { - int index = mtx * 4; - if (transpose) - index += ((row+i)&3) + ((col+j)&3)*32; - else - index += ((col+j)&3) + ((row+i)&3)*32; - rd[j*4 + i] = V(index); + // The voffset ordering is now integrated in these formulas, + // eliminating a table lookup. + const float *v = currentMIPS->v + (size_t)mtx * 16; + if (transpose) { + if (side == 4 && col == 0 && row == 0) { + // Simple 4x4 transpose. TODO: Optimize. + for (int j = 0; j < 4; j++) { + for (int i = 0; i < 4; i++) { + rd[j * 4 + i] = v[i * 4 + j]; + } + } + } else { + for (int j = 0; j < side; j++) { + for (int i = 0; i < side; i++) { + int index = ((row + i) & 3) * 4 + ((col + j) & 3); + rd[j * 4 + i] = v[index]; + } + } + } + } else { + // Fast path + if (side == 4 && col == 0 && row == 0) { + memcpy(rd, v, sizeof(float) * 16); + /* + for (int j = 0; j < side; j++) { + for (int i = 0; i < side; i++) { + int index = mtx * 16 + j * 4 + i; + rd[j * 4 + i] = v[index]; + } + } + */ + } else { + for (int j = 0; j < side; j++) { + for (int i = 0; i < side; i++) { + int index = ((col + j) & 3) * 4 + ((row + i) & 3); + rd[j * 4 + i] = v[index]; + } + } } } } @@ -277,16 +308,49 @@ void WriteMatrix(const float *rd, MatrixSize size, int reg) { ERROR_LOG_REPORT(CPU, "Write mask used with vfpu matrix instruction."); } - for (int i = 0; i < side; i++) { - for (int j = 0; j < side; j++) { - // Hm, I wonder if this should affect matrices at all. - if (j != side -1 || !currentMIPS->VfpuWriteMask(i)) { - int index = mtx * 4; - if (transpose) - index += ((row+i)&3) + ((col+j)&3)*32; - else - index += ((col+j)&3) + ((row+i)&3)*32; - V(index) = rd[j*4+i]; + // The voffset ordering is now integrated in these formulas, + // eliminating a table lookup. + float *v = currentMIPS->v + (size_t)mtx * 16; + if (transpose) { + // Fast path + if (side == 4 && row == 0 && col == 0 && currentMIPS->VfpuWriteMask() == 0x0) { + for (int j = 0; j < side; j++) { + for (int i = 0; i < side; i++) { + int index = i * 4 + j; + v[index] = rd[j * 4 + i]; + } + } + } else { + for (int j = 0; j < side; j++) { + for (int i = 0; i < side; i++) { + // Hm, I wonder if this should affect matrices at all. + if (j != side - 1 || !currentMIPS->VfpuWriteMask(i)) { + int index = ((row + i) & 3) * 4 + ((col + j) & 3); + v[index] = rd[j * 4 + i]; + } + } + } + } + } else { + if (side == 4 && row == 0 && col == 0 && currentMIPS->VfpuWriteMask() == 0x0) { + memcpy(v, rd, sizeof(float) * 16); + /* + for (int j = 0; j < side; j++) { + for (int i = 0; i < side; i++) { + int index = j * 4 + i; + v[index] = rd[j * 4 + i]; + } + } + */ + } else { + for (int j = 0; j < side; j++) { + for (int i = 0; i < side; i++) { + // Hm, I wonder if this should affect matrices at all. + if (j != side - 1 || !currentMIPS->VfpuWriteMask(i)) { + int index = ((col + j) & 3) * 4 + ((row + i) & 3); + v[index] = rd[j * 4 + i]; + } + } } } } From f9ede96344e18641b20c9d40e29d5a7120cb43ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Aug 2019 16:39:28 +0200 Subject: [PATCH 35/42] Address feedback --- Core/MIPS/MIPSVFPUUtils.cpp | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 57d5c4cad8..280dec90e4 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -251,7 +251,7 @@ void ReadMatrix(float *rd, MatrixSize size, int reg) { const float *v = currentMIPS->v + (size_t)mtx * 16; if (transpose) { if (side == 4 && col == 0 && row == 0) { - // Simple 4x4 transpose. TODO: Optimize. + // Fast path: Simple 4x4 transpose. TODO: Optimize. for (int j = 0; j < 4; j++) { for (int i = 0; i < 4; i++) { rd[j * 4 + i] = v[i * 4 + j]; @@ -266,17 +266,9 @@ void ReadMatrix(float *rd, MatrixSize size, int reg) { } } } else { - // Fast path if (side == 4 && col == 0 && row == 0) { - memcpy(rd, v, sizeof(float) * 16); - /* - for (int j = 0; j < side; j++) { - for (int i = 0; i < side; i++) { - int index = mtx * 16 + j * 4 + i; - rd[j * 4 + i] = v[index]; - } - } - */ + // Fast path + memcpy(rd, v, sizeof(float) * 16); // rd[j * 4 + i] = v[j * 4 + i]; } else { for (int j = 0; j < side; j++) { for (int i = 0; i < side; i++) { @@ -312,18 +304,16 @@ void WriteMatrix(const float *rd, MatrixSize size, int reg) { // eliminating a table lookup. float *v = currentMIPS->v + (size_t)mtx * 16; if (transpose) { - // Fast path if (side == 4 && row == 0 && col == 0 && currentMIPS->VfpuWriteMask() == 0x0) { + // Fast path: Simple 4x4 transpose. TODO: Optimize. for (int j = 0; j < side; j++) { for (int i = 0; i < side; i++) { - int index = i * 4 + j; - v[index] = rd[j * 4 + i]; + v[i * 4 + j] = rd[j * 4 + i]; } } } else { for (int j = 0; j < side; j++) { for (int i = 0; i < side; i++) { - // Hm, I wonder if this should affect matrices at all. if (j != side - 1 || !currentMIPS->VfpuWriteMask(i)) { int index = ((row + i) & 3) * 4 + ((col + j) & 3); v[index] = rd[j * 4 + i]; @@ -333,19 +323,10 @@ void WriteMatrix(const float *rd, MatrixSize size, int reg) { } } else { if (side == 4 && row == 0 && col == 0 && currentMIPS->VfpuWriteMask() == 0x0) { - memcpy(v, rd, sizeof(float) * 16); - /* - for (int j = 0; j < side; j++) { - for (int i = 0; i < side; i++) { - int index = j * 4 + i; - v[index] = rd[j * 4 + i]; - } - } - */ + memcpy(v, rd, sizeof(float) * 16); // v[j * 4 + i] = rd[j * 4 + i]; } else { for (int j = 0; j < side; j++) { for (int i = 0; i < side; i++) { - // Hm, I wonder if this should affect matrices at all. if (j != side - 1 || !currentMIPS->VfpuWriteMask(i)) { int index = ((col + j) & 3) * 4 + ((row + i) & 3); v[index] = rd[j * 4 + i]; From ba7ea39a1c8eb728c2b7b3c3ca3f10a5a124a731 Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Tue, 6 Aug 2019 16:12:19 +0100 Subject: [PATCH 36/42] Move NKCodeFrom*.h into the relevant platform folders --- {ext/native/base => Qt}/NKCodeFromQt.h | 4 +++- Qt/QtMain.h | 2 +- {ext/native/base => SDL}/NKCodeFromSDL.h | 2 +- SDL/SDLMain.cpp | 2 +- UWP/NativeUWP/NativeUWP.vcxproj.filters | 6 ------ 5 files changed, 6 insertions(+), 10 deletions(-) rename {ext/native/base => Qt}/NKCodeFromQt.h (98%) rename {ext/native/base => SDL}/NKCodeFromSDL.h (98%) diff --git a/ext/native/base/NKCodeFromQt.h b/Qt/NKCodeFromQt.h similarity index 98% rename from ext/native/base/NKCodeFromQt.h rename to Qt/NKCodeFromQt.h index ee09d63417..2a87141720 100644 --- a/ext/native/base/NKCodeFromQt.h +++ b/Qt/NKCodeFromQt.h @@ -1,7 +1,9 @@ +#pragma once + #include "util/const_map.h" #include -// TODO: Add any missing keys +// TODO: Add any missing keys static const std::map KeyMapRawQttoNative = InitConstMap (Qt::Key_P, NKCODE_P) (Qt::Key_O, NKCODE_O) diff --git a/Qt/QtMain.h b/Qt/QtMain.h index 3e165a8d9f..bf351c84f7 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -33,7 +33,7 @@ QTM_USE_NAMESPACE #include "thin3d/thin3d.h" #include "base/NativeApp.h" #include "net/resolve.h" -#include "base/NKCodeFromQt.h" +#include "NKCodeFromQt.h" #include "Common/GraphicsContext.h" #include "Core/Core.h" diff --git a/ext/native/base/NKCodeFromSDL.h b/SDL/NKCodeFromSDL.h similarity index 98% rename from ext/native/base/NKCodeFromSDL.h rename to SDL/NKCodeFromSDL.h index 7485786d3a..d71654c945 100644 --- a/ext/native/base/NKCodeFromSDL.h +++ b/SDL/NKCodeFromSDL.h @@ -3,7 +3,7 @@ #include "util/const_map.h" #include -// TODO: Add any missing keys +// TODO: Add any missing keys static const std::map KeyMapRawSDLtoNative = InitConstMap (SDLK_UNKNOWN, NKCODE_UNKNOWN) (SDLK_p, NKCODE_P) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 4c4a8677eb..0d001f32ad 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -27,7 +27,7 @@ SDLJoystick *joystick = NULL; #include "input/input_state.h" #include "input/keycodes.h" #include "net/resolve.h" -#include "base/NKCodeFromSDL.h" +#include "NKCodeFromSDL.h" #include "util/const_map.h" #include "util/text/utf8.h" #include "math/math_util.h" diff --git a/UWP/NativeUWP/NativeUWP.vcxproj.filters b/UWP/NativeUWP/NativeUWP.vcxproj.filters index 2bda07e6a3..c1658f0e62 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj.filters +++ b/UWP/NativeUWP/NativeUWP.vcxproj.filters @@ -505,12 +505,6 @@ base - - base - - - base - base From ebe64c60e9c557645a5e6f44b9d3de3c2d2483f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 8 Aug 2019 11:49:35 +0200 Subject: [PATCH 37/42] Vulkan: Improve the Metal Gear Acid 2 performance hack. --- assets/compat.ini | 26 +++++---- ext/native/thin3d/VulkanQueueRunner.cpp | 74 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 12 deletions(-) diff --git a/assets/compat.ini b/assets/compat.ini index f1917c5882..74e11597fd 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -403,13 +403,13 @@ NPJH50036 = true # Burnout games have problems with this on Mali, and have no use for it # Legends -ULES00125 = true -ULUS10025 = true -ULJM05228 = true -NPJH50305 = true -ULJM05049 = true -ULKS46027 = true -ULAS42019 = true +#ULES00125 = true +#ULUS10025 = true +#ULJM05228 = true +#NPJH50305 = true +#ULJM05049 = true +#ULKS46027 = true +#ULAS42019 = true # Dominator ULUS10236 = true @@ -441,15 +441,17 @@ ULES01376 = true ULUS10466 = true [MGS2AcidHack] +ULUS10006 = true # Metal Gear Acid ULES00008 = true +ULJM05001 = true +ULAS42007 = true +ULJM08001 = true +ULUS10077 = true # Metal Gear Acid 2 +ULAS42035 = true ULES00284 = true ULJM05047 = true ULKS46065 = true -ULJM08001 = true -ULJM05001 = true -ULAS42007 = true -ULUS10006 = true -ULUS10077 = true +ULJM08011 = true [SonicRivalsHack] ULES00622 = true # SR1 diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index a0042faf4e..1a36d139cf 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -463,6 +463,8 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st } void VulkanQueueRunner::ApplyMGSHack(std::vector &steps) { + // Really need a sane way to express transforms of steps. + // We want to turn a sequence of copy,render(1),copy,render(1),copy,render(1) to copy,copy,copy,render(n). for (int i = 0; i < (int)steps.size() - 3; i++) { @@ -530,6 +532,78 @@ void VulkanQueueRunner::ApplyMGSHack(std::vector &steps) { break; } } + + // There's also a post processing effect using depals that's just brutal in some parts + // of the game. + for (int i = 0; i < (int)steps.size() - 3; i++) { + int last = -1; + if (!(steps[i]->stepType == VKRStepType::RENDER && + steps[i + 1]->stepType == VKRStepType::RENDER && + steps[i + 2]->stepType == VKRStepType::RENDER && + steps[i]->render.numDraws == 1 && + steps[i + 1]->render.numDraws == 1 && + steps[i + 2]->render.numDraws == 1 && + steps[i]->render.color == VKRRenderPassAction::DONT_CARE && + steps[i + 1]->render.color == VKRRenderPassAction::KEEP && + steps[i + 2]->render.color == VKRRenderPassAction::DONT_CARE)) + continue; + VKRFramebuffer *depalFramebuffer = steps[i]->render.framebuffer; + VKRFramebuffer *targetFramebuffer = steps[i + 1]->render.framebuffer; + // OK, found the start of a post-process sequence. Let's scan until we find the end. + for (int j = i; j < steps.size() - 3; j++) { + if (((j - i) & 1) == 0) { + // This should be a depal draw. + if (steps[j]->render.numDraws != 1) + break; + if (steps[j]->render.color != VKRRenderPassAction::DONT_CARE) + break; + if (steps[j]->render.framebuffer != depalFramebuffer) + break; + last = j; + } else { + // This should be a target draw. + if (steps[j]->render.numDraws != 1) + break; + if (steps[j]->render.color != VKRRenderPassAction::KEEP) + break; + if (steps[j]->render.framebuffer != targetFramebuffer) + break; + last = j; + } + } + + if (last == -1) + continue; + + // Combine the depal renders. + for (int j = i + 2; j <= last + 1; j += 2) { + for (int k = 0; k < (int)steps[j]->commands.size(); k++) { + switch (steps[j]->commands[k].cmd) { + case VKRRenderCommand::DRAW: + case VKRRenderCommand::DRAW_INDEXED: + steps[i]->commands.push_back(steps[j]->commands[k]); + break; + } + } + steps[j]->stepType = VKRStepType::RENDER_SKIP; + } + + // Combine the target renders. + for (int j = i + 3; j <= last; j += 2) { + for (int k = 0; k < (int)steps[j]->commands.size(); k++) { + switch (steps[j]->commands[k].cmd) { + case VKRRenderCommand::DRAW: + case VKRRenderCommand::DRAW_INDEXED: + steps[i + 1]->commands.push_back(steps[j]->commands[k]); + break; + } + } + steps[j]->stepType = VKRStepType::RENDER_SKIP; + } + + // We're done - we only expect one of these sequences per frame. + break; + } } void VulkanQueueRunner::ApplySonicHack(std::vector &steps) { From 3b0624ff9f88ae862e0b16163cfefbc52b984f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 8 Aug 2019 14:07:53 +0200 Subject: [PATCH 38/42] Some changes and asserts. --- ext/native/thin3d/VulkanQueueRunner.cpp | 13 ++++++++++++- ext/native/thin3d/VulkanRenderManager.cpp | 11 ++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 1a36d139cf..6948891c45 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -379,14 +379,18 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st // Planned optimizations: // * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes // as early as possible in the frame (Wipeout billboards). + // * Merge subsequent render passes to the same target that are interspersed with unrelated draws to other render targets (God of War). - for (int j = 0; j < (int)steps.size() - 1; j++) { + for (int j = 0; j < (int)steps.size(); j++) { if (steps[j]->stepType == VKRStepType::RENDER && + steps[j]->render.framebuffer && steps[j]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { // Just leave it at color_optimal. steps[j]->render.finalColorLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } + } + for (int j = 0; j < (int)steps.size() - 1; j++) { // Push down empty "Clear/Store" renderpasses, and merge them with the first "Load/Store" to the same framebuffer. // Actually let's just bother with the first one for now. This affects Wipeout Pure. if (steps.size() > 1 && steps[j]->stepType == VKRStepType::RENDER && @@ -458,6 +462,11 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st case VKRStepType::RENDER_SKIP: break; } + } + + // Deleting all in one go should be easier on the instruction cache than deleting + // them as we go - and easier to debug because we can look backwards in the frame. + for (size_t i = 0; i < steps.size(); i++) { delete steps[i]; } } @@ -947,6 +956,8 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step int w; int h; if (step.render.framebuffer) { + _dbg_assert_(G3D, step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED); + VKRFramebuffer *fb = step.render.framebuffer; framebuf = fb->framebuf; w = fb->width; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 786513eaa7..39701d7ad6 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -412,7 +412,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR // 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.depth = depth; step->render.stencil = stencil; step->render.clearColor = clearColor; step->render.clearDepth = clearDepth; @@ -423,8 +423,13 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR steps_.push_back(step); curRenderStep_ = step; - curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth(); - curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); + if (fb) { + curWidth_ = fb->width; + curHeight_ = fb->height; + } else { + curWidth_ = vulkan_->GetBackbufferWidth(); + curHeight_ = vulkan_->GetBackbufferHeight(); + } } bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { From 213e2ccefceb23e37cda64cc0ea9232541beb241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 8 Aug 2019 15:15:52 +0200 Subject: [PATCH 39/42] Vulkan: Add missing barrier when rendering to a target that's already in COLOR_OPTIMAL layout. Fixes #12215 --- .gitignore | 3 +++ ext/native/thin3d/VulkanQueueRunner.cpp | 19 +++++++++++++++++++ ext/native/thin3d/VulkanRenderManager.cpp | 3 ++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index becbb37a0d..01c0268ea7 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,6 @@ debian/ppsspp/ # YouCompleteMe file .ycm_extra_conf.pyc + +# RenderDoc +*.rdc diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 6948891c45..fdbdf3ebe9 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -797,6 +797,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VkPipelineStageFlags dstStage{}; switch (barrier.oldLayout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; case VK_IMAGE_LAYOUT_UNDEFINED: barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; @@ -838,6 +841,22 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c return; } + if (step.render.framebuffer && step.render.framebuffer->color.layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = step.render.framebuffer->color.image; + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + // This is supposed to bind a vulkan render pass to the command buffer. PerformBindFramebufferAsRenderTarget(step, cmd); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 39701d7ad6..1f8795a34e 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -397,6 +397,8 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR return; } } + + // More redundant bind elimination. if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == VKRRenderPassAction::KEEP && curRenderStep_->render.depth == VKRRenderPassAction::KEEP && curRenderStep_->render.stencil == VKRRenderPassAction::KEEP) { // Can trivially kill the last empty render step. assert(steps_.back() == curRenderStep_); @@ -409,7 +411,6 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR } VKRStep *step = new VKRStep{ VKRStepType::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; From caabc9023f5b3e03be69bab45ec354792146b28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 8 Aug 2019 23:13:47 +0200 Subject: [PATCH 40/42] Vulkan: Set all four coordinates to NaN instead of just W. Fixes range culling on ARM. Fixes #11999. --- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index c6aa3644bd..88382f3b81 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -47,9 +47,6 @@ static const char *vulkan_glsl_preamble = // texcoord = 2 // fog = 3 - - - #undef WRITE #define WRITE p+=sprintf @@ -634,7 +631,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { const char *outMin = "projPos.x < base.cullRangeMin.x || projPos.y < base.cullRangeMin.y || projPos.z < base.cullRangeMin.z"; const char *outMax = "projPos.x > base.cullRangeMax.x || projPos.y > base.cullRangeMax.y || projPos.z > base.cullRangeMax.z"; WRITE(p, " if (%s || %s) {\n", outMin, outMax); - WRITE(p, " outPos.w = base.cullRangeMax.w;\n"); + WRITE(p, " outPos.xyzw = vec4(base.cullRangeMax.w);\n"); WRITE(p, " }\n"); WRITE(p, " }\n"); } From 09b55468286c31a8a49e38ba487073e2364abbb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 9 Aug 2019 00:09:06 +0200 Subject: [PATCH 41/42] Apply the same Mali range culling "fix" in OpenGL. --- GPU/GLES/VertexShaderGeneratorGLES.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/VertexShaderGeneratorGLES.cpp b/GPU/GLES/VertexShaderGeneratorGLES.cpp index f8193617e0..3bbec79002 100644 --- a/GPU/GLES/VertexShaderGeneratorGLES.cpp +++ b/GPU/GLES/VertexShaderGeneratorGLES.cpp @@ -842,7 +842,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y || projPos.z < u_cullRangeMin.z"; const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y || projPos.z > u_cullRangeMax.z"; WRITE(p, " if (%s || %s) {\n", outMin, outMax); - WRITE(p, " outPos.w = u_cullRangeMax.w;\n"); + WRITE(p, " outPos.xyzw = vec4(u_cullRangeMax.w);\n"); WRITE(p, " }\n"); WRITE(p, " }\n"); } From 3bc3f3e969a9cab1c2aaad2711e2b874810610f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 9 Aug 2019 10:02:41 +0200 Subject: [PATCH 42/42] WASAPI: Add some missing error checks. May help #12081? (but will result in silence) --- Windows/WASAPIStream.cpp | 20 +++++++++++--------- Windows/WASAPIStream.h | 3 ++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Windows/WASAPIStream.cpp b/Windows/WASAPIStream.cpp index 728574ecb7..d92442ae37 100644 --- a/Windows/WASAPIStream.cpp +++ b/Windows/WASAPIStream.cpp @@ -141,7 +141,7 @@ private: #define REFTIMES_PER_SEC (10000000/200) #define REFTIMES_PER_MILLISEC (REFTIMES_PER_SEC / 1000) -WASAPIAudioBackend::WASAPIAudioBackend() { +WASAPIAudioBackend::WASAPIAudioBackend() : threadData_(0) { } WASAPIAudioBackend::~WASAPIAudioBackend() { @@ -177,7 +177,7 @@ bool WASAPIAudioBackend::Init(HWND window, StreamCallback callback, int sampleRa // This to be run only on the thread. class WASAPIAudioThread { public: - WASAPIAudioThread(volatile int &threadData, int &sampleRate, StreamCallback &callback) + WASAPIAudioThread(std::atomic &threadData, int &sampleRate, StreamCallback &callback) : threadData_(threadData), sampleRate_(sampleRate), callback_(callback) { } ~WASAPIAudioThread(); @@ -190,7 +190,7 @@ private: void ShutdownAudioDevice(); bool DetectFormat(); - volatile int &threadData_; + std::atomic &threadData_; int &sampleRate_; StreamCallback &callback_; @@ -200,7 +200,7 @@ private: CMMNotificationClient *notificationClient_ = nullptr; WAVEFORMATEXTENSIBLE *deviceFormat_ = nullptr; IAudioRenderClient *renderClient_ = nullptr; - short *shortBuf_ = nullptr; + int16_t *shortBuf_ = nullptr; enum class Format { UNKNOWN = 0, @@ -210,7 +210,7 @@ private: uint32_t numBufferFrames = 0; Format format_ = Format::UNKNOWN; - REFERENCE_TIME actualDuration_; + REFERENCE_TIME actualDuration_{}; }; WASAPIAudioThread::~WASAPIAudioThread() { @@ -264,6 +264,7 @@ void WASAPIAudioThread::ShutdownAudioDevice() { bool WASAPIAudioThread::DetectFormat() { // Don't know if PCM16 ever shows up here, the documentation only talks about float... but let's blindly // try to support it :P + format_ = Format::UNKNOWN; if (deviceFormat_->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { if (!memcmp(&deviceFormat_->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(deviceFormat_->SubFormat))) { @@ -285,10 +286,13 @@ bool WASAPIAudioThread::DetectFormat() { delete [] shortBuf_; shortBuf_ = nullptr; + if (format_ == Format::UNKNOWN) { + return false; + } BYTE *pData; HRESULT hresult = renderClient_->GetBuffer(numBufferFrames, &pData); - int numSamples = numBufferFrames * deviceFormat_->Format.nChannels; + const int numSamples = numBufferFrames * deviceFormat_->Format.nChannels; if (format_ == Format::IEEE_FLOAT) { memset(pData, 0, sizeof(float) * numSamples); shortBuf_ = new short[numBufferFrames * deviceFormat_->Format.nChannels]; @@ -298,15 +302,13 @@ bool WASAPIAudioThread::DetectFormat() { hresult = renderClient_->ReleaseBuffer(numBufferFrames, 0); actualDuration_ = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * numBufferFrames / deviceFormat_->Format.nSamplesPerSec); - return true; } void WASAPIAudioThread::Run() { // Adapted from http://msdn.microsoft.com/en-us/library/windows/desktop/dd316756(v=vs.85).aspx - HRESULT hresult; - hresult = CoCreateInstance(CLSID_MMDeviceEnumerator, + HRESULT hresult = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, /* Object is not created as the part of the aggregate */ CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&deviceEnumerator_); if (FAILED(hresult)) diff --git a/Windows/WASAPIStream.h b/Windows/WASAPIStream.h index 2a708928cb..b9e2169a78 100644 --- a/Windows/WASAPIStream.h +++ b/Windows/WASAPIStream.h @@ -1,5 +1,6 @@ #pragma once +#include #include "WindowsAudio.h" // This should only be included from WindowsAudio.cpp and WASAPIStream.cpp. @@ -20,5 +21,5 @@ private: HANDLE hThread_ = nullptr; StreamCallback callback_ = nullptr; int sampleRate_ = 0; - volatile int threadData_ = 0; + std::atomic threadData_; };