From 2065ac5f635e48a6bf01ac480236783f46bcf0de Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 16 Nov 2024 12:51:58 +0100 Subject: [PATCH 1/5] GfxPack: Better logging messages for diagnosing problems in rules.txt --- src/Cafe/GraphicPack/GraphicPack2.cpp | 42 ++++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index 4b6cb095..f21bb89d 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -345,7 +345,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) const auto preset_name = rules.FindOption("name"); if (!preset_name) { - cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", m_name, rules.GetCurrentSectionLineNumber()); + cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", GetNormalizedPathString(), rules.GetCurrentSectionLineNumber()); continue; } @@ -369,7 +369,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) } catch (const std::exception & ex) { - cemuLog_log(LogType::Force, "Graphic pack \"{}\": Can't parse preset \"{}\": {}", m_name, *preset_name, ex.what()); + cemuLog_log(LogType::Force, "Graphic pack \"{}\": Can't parse preset \"{}\": {}", GetNormalizedPathString(), *preset_name, ex.what()); } } else if (boost::iequals(currentSectionName, "RAM")) @@ -383,7 +383,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) { if (m_version <= 5) { - cemuLog_log(LogType::Force, "Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", m_name, optionNameBuf); + cemuLog_log(LogType::Force, "Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", GetNormalizedPathString(), optionNameBuf); throw std::exception(); } @@ -393,12 +393,12 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) { if (addrEnd <= addrStart) { - cemuLog_log(LogType::Force, "Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", m_name, addrStart, addrEnd, optionNameBuf); + cemuLog_log(LogType::Force, "Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", GetNormalizedPathString(), addrStart, addrEnd, optionNameBuf); throw std::exception(); } else if ((addrStart & 0xFFF) != 0 || (addrEnd & 0xFFF) != 0) { - cemuLog_log(LogType::Force, "Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", m_name, optionNameBuf); + cemuLog_log(LogType::Force, "Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", GetNormalizedPathString(), optionNameBuf); throw std::exception(); } else @@ -408,7 +408,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) } else { - cemuLog_log(LogType::Force, "Graphic pack \"{}\": has invalid syntax for option {}", m_name, optionNameBuf); + cemuLog_log(LogType::Force, "Graphic pack \"{}\": has invalid syntax for option {}", GetNormalizedPathString(), optionNameBuf); throw std::exception(); } } @@ -422,24 +422,32 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) std::unordered_map> tmp_map; // all vars must be defined in the default preset vars before - for (const auto& entry : m_presets) + std::vector> mismatchingPresetVars; + for (const auto& presetEntry : m_presets) { - tmp_map[entry->category].emplace_back(entry); + tmp_map[presetEntry->category].emplace_back(presetEntry); - for (auto& kv : entry->variables) + for (auto& presetVar : presetEntry->variables) { - const auto it = m_preset_vars.find(kv.first); + const auto it = m_preset_vars.find(presetVar.first); if (it == m_preset_vars.cend()) { - cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains preset variables which are not defined in the default section", m_name); - throw std::exception(); + mismatchingPresetVars.emplace_back(presetEntry->name, presetVar.first); + continue; } - // overwrite var type with default var type - kv.second.first = it->second.first; + presetVar.second.first = it->second.first; } } + if(!mismatchingPresetVars.empty()) + { + cemuLog_log(LogType::Force, "Graphic pack \"{}\" contains preset variables which are not defined in the [Default] section:", GetNormalizedPathString()); + for (const auto& [presetName, varName] : mismatchingPresetVars) + cemuLog_log(LogType::Force, "Preset: {} Variable: {}", presetName, varName); + throw std::exception(); + } + // have first entry be default active for every category if no default= is set for(auto entry : get_values(tmp_map)) { @@ -469,7 +477,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) auto& p2 = kv.second[i + 1]; if (p1->variables.size() != p2->variables.size()) { - cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", m_name); + cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString()); throw std::exception(); } @@ -477,14 +485,14 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) std::set keys2(get_keys(p2->variables).begin(), get_keys(p2->variables).end()); if (keys1 != keys2) { - cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", m_name); + cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString()); throw std::exception(); } if(p1->is_default) { if(has_default) - cemuLog_log(LogType::Force, "Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", m_name, p1->name); + cemuLog_log(LogType::Force, "Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", GetNormalizedPathString(), p1->name); p1->active = true; has_default = true; } From c3e29fb6199cfd297a86dd0f6bc0d92fe7dab3de Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 16 Nov 2024 13:45:33 +0100 Subject: [PATCH 2/5] Latte: Add support for shader instructions MIN_UINT and MAX_UINT Seen in the eShop version of Fatal Frame Also made some warnings less spammy since this game seems to trigger it a lot --- .../LatteDecompiler.cpp | 2 ++ .../LatteDecompilerAnalyzer.cpp | 2 ++ .../LatteDecompilerEmitGLSL.cpp | 20 +++++++++++-------- .../LatteDecompilerInstructions.h | 2 ++ src/Cafe/OS/libs/gx2/GX2_Shader.cpp | 2 +- src/input/emulated/VPADController.cpp | 2 +- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp index c3f7c19e..5972aacc 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp @@ -370,6 +370,8 @@ bool LatteDecompiler_IsALUTransInstruction(bool isOP3, uint32 opcode) opcode == ALU_OP2_INST_LSHR_INT || opcode == ALU_OP2_INST_MAX_INT || opcode == ALU_OP2_INST_MIN_INT || + opcode == ALU_OP2_INST_MAX_UINT || + opcode == ALU_OP2_INST_MIN_UINT || opcode == ALU_OP2_INST_MOVA_FLOOR || opcode == ALU_OP2_INST_MOVA_INT || opcode == ALU_OP2_INST_SETE_DX10 || diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp index 19604e0c..ff64988c 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp @@ -140,6 +140,8 @@ bool _isIntegerInstruction(const LatteDecompilerALUInstruction& aluInstruction) case ALU_OP2_INST_SUB_INT: case ALU_OP2_INST_MAX_INT: case ALU_OP2_INST_MIN_INT: + case ALU_OP2_INST_MAX_UINT: + case ALU_OP2_INST_MIN_UINT: case ALU_OP2_INST_SETE_INT: case ALU_OP2_INST_SETGT_INT: case ALU_OP2_INST_SETGE_INT: diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp index 7a6605f8..e7ebcf3a 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp @@ -1415,19 +1415,23 @@ void _emitALUOP2InstructionCode(LatteDecompilerShaderContext* shaderContext, Lat } else if( aluInstruction->opcode == ALU_OP2_INST_ADD_INT ) _emitALUOperationBinary(shaderContext, aluInstruction, " + "); - else if( aluInstruction->opcode == ALU_OP2_INST_MAX_INT || aluInstruction->opcode == ALU_OP2_INST_MIN_INT ) + else if( aluInstruction->opcode == ALU_OP2_INST_MAX_INT || aluInstruction->opcode == ALU_OP2_INST_MIN_INT || + aluInstruction->opcode == ALU_OP2_INST_MAX_UINT || aluInstruction->opcode == ALU_OP2_INST_MIN_UINT) { // not verified + bool isUnsigned = aluInstruction->opcode == ALU_OP2_INST_MAX_UINT || aluInstruction->opcode == ALU_OP2_INST_MIN_UINT; + auto opType = isUnsigned ? LATTE_DECOMPILER_DTYPE_UNSIGNED_INT : LATTE_DECOMPILER_DTYPE_SIGNED_INT; _emitInstructionOutputVariableName(shaderContext, aluInstruction); - if( aluInstruction->opcode == ALU_OP2_INST_MAX_INT ) - src->add(" = max("); + src->add(" = "); + _emitTypeConversionPrefix(shaderContext, opType, outputType); + if( aluInstruction->opcode == ALU_OP2_INST_MAX_INT || aluInstruction->opcode == ALU_OP2_INST_MAX_UINT ) + src->add("max("); else - src->add(" = min("); - _emitTypeConversionPrefix(shaderContext, LATTE_DECOMPILER_DTYPE_SIGNED_INT, outputType); - _emitOperandInputCode(shaderContext, aluInstruction, 0, LATTE_DECOMPILER_DTYPE_SIGNED_INT); + src->add("min("); + _emitOperandInputCode(shaderContext, aluInstruction, 0, opType); src->add(", "); - _emitOperandInputCode(shaderContext, aluInstruction, 1, LATTE_DECOMPILER_DTYPE_SIGNED_INT); - _emitTypeConversionSuffix(shaderContext, LATTE_DECOMPILER_DTYPE_SIGNED_INT, outputType); + _emitOperandInputCode(shaderContext, aluInstruction, 1, opType); + _emitTypeConversionSuffix(shaderContext, opType, outputType); src->add(");" _CRLF); } else if( aluInstruction->opcode == ALU_OP2_INST_SUB_INT ) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h index 4cb1982e..6c029b46 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h @@ -60,6 +60,8 @@ #define ALU_OP2_INST_SUB_INT (0x035) // integer instruction #define ALU_OP2_INST_MAX_INT (0x036) // integer instruction #define ALU_OP2_INST_MIN_INT (0x037) // integer instruction +#define ALU_OP2_INST_MAX_UINT (0x038) // integer instruction +#define ALU_OP2_INST_MIN_UINT (0x039) // integer instruction #define ALU_OP2_INST_SETE_INT (0x03A) // integer instruction #define ALU_OP2_INST_SETGT_INT (0x03B) // integer instruction #define ALU_OP2_INST_SETGE_INT (0x03C) // integer instruction diff --git a/src/Cafe/OS/libs/gx2/GX2_Shader.cpp b/src/Cafe/OS/libs/gx2/GX2_Shader.cpp index dfbbfcff..7a153737 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Shader.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Shader.cpp @@ -421,7 +421,7 @@ namespace GX2 { if(aluRegisterOffset&0x8000) { - cemuLog_logDebug(LogType::Force, "_GX2SubmitUniformReg(): Unhandled loop const special case or invalid offset"); + cemuLog_logDebugOnce(LogType::Force, "_GX2SubmitUniformReg(): Unhandled loop const special case or invalid offset"); return; } if((aluRegisterOffset+sizeInU32s) > 0x400) diff --git a/src/input/emulated/VPADController.cpp b/src/input/emulated/VPADController.cpp index f1ab1bc4..81615c9b 100644 --- a/src/input/emulated/VPADController.cpp +++ b/src/input/emulated/VPADController.cpp @@ -408,7 +408,7 @@ bool VPADController::push_rumble(uint8* pattern, uint8 length) std::scoped_lock lock(m_rumble_mutex); if (m_rumble_queue.size() >= 5) { - cemuLog_logDebug(LogType::Force, "too many cmds"); + cemuLog_logDebugOnce(LogType::Force, "VPADControlMotor(): Pattern too long"); return false; } From 7b513f1744a7952abf2fb927c27b2c09a2c992f4 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 17 Nov 2024 09:52:44 +0100 Subject: [PATCH 3/5] Latte: Add workaround for infinite loop in Fatal Frame shaders --- src/config/ActiveSettings.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/config/ActiveSettings.cpp b/src/config/ActiveSettings.cpp index 560f2986..f81f8336 100644 --- a/src/config/ActiveSettings.cpp +++ b/src/config/ActiveSettings.cpp @@ -198,14 +198,20 @@ bool ActiveSettings::ShaderPreventInfiniteLoopsEnabled() { const uint64 titleId = CafeSystem::GetForegroundTitleId(); // workaround for NSMBU (and variants) having a bug where shaders can get stuck in infinite loops - // update: As of Cemu 1.20.0 this should no longer be required + // Fatal Frame has an actual infinite loop in shader 0xb6a67c19f6472e00 encountered during a cutscene for the second drop (eShop version only?) + // update: As of Cemu 1.20.0 this should no longer be required for NSMBU/NSLU due to fixes with uniform handling. But we leave it here for good measure + // todo - Once we add support for loop config registers this workaround should become unnecessary return /* NSMBU JP */ titleId == 0x0005000010101C00 || /* NSMBU US */ titleId == 0x0005000010101D00 || /* NSMBU EU */ titleId == 0x0005000010101E00 || /* NSMBU+L US */ titleId == 0x000500001014B700 || /* NSMBU+L EU */ titleId == 0x000500001014B800 || /* NSLU US */ titleId == 0x0005000010142300 || - /* NSLU EU */ titleId == 0x0005000010142400; + /* NSLU EU */ titleId == 0x0005000010142400 || + /* Project Zero: Maiden of Black Water (EU) */ titleId == 0x00050000101D0300 || + /* Fatal Frame: Maiden of Black Water (US) */ titleId == 0x00050000101D0600 || + /* Project Zero: Maiden of Black Water (JP) */ titleId == 0x000500001014D200 || + /* Project Zero: Maiden of Black Water (Trial, EU) */ titleId == 0x00050000101D3F00; } bool ActiveSettings::FlushGPUCacheOnSwap() From 409f12b13a2d67f18465c2920ab04a4d5ab27ae6 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:38:39 +0100 Subject: [PATCH 4/5] coreinit: Fix calculation of thread total awake time --- src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 2f3808b7..db457047 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -1114,13 +1114,13 @@ namespace coreinit thread->requestFlags = (OSThread_t::REQUEST_FLAG_BIT)(thread->requestFlags & OSThread_t::REQUEST_FLAG_CANCEL); // remove all flags except cancel flag // update total cycles - uint64 remainingCycles = std::min((uint64)hCPU->remainingCycles, (uint64)thread->quantumTicks); - uint64 executedCycles = thread->quantumTicks - remainingCycles; - if (executedCycles < hCPU->skippedCycles) + sint64 executedCycles = (sint64)thread->quantumTicks - (sint64)hCPU->remainingCycles; + executedCycles = std::max(executedCycles, 0); + if (executedCycles < (sint64)hCPU->skippedCycles) executedCycles = 0; else executedCycles -= hCPU->skippedCycles; - thread->totalCycles += executedCycles; + thread->totalCycles += (uint64)executedCycles; // store context and set current thread to null __OSThreadStoreContext(hCPU, thread); OSSetCurrentThread(OSGetCoreId(), nullptr); From 90eb2e01f405e502ed33fea330352e167c4a14db Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Fri, 22 Nov 2024 12:43:12 +0000 Subject: [PATCH 5/5] nsyshid/dimensions: add missing return (#1425) --- src/Cafe/OS/libs/nsyshid/Dimensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cafe/OS/libs/nsyshid/Dimensions.cpp b/src/Cafe/OS/libs/nsyshid/Dimensions.cpp index f328dde7..8a2acc76 100644 --- a/src/Cafe/OS/libs/nsyshid/Dimensions.cpp +++ b/src/Cafe/OS/libs/nsyshid/Dimensions.cpp @@ -675,6 +675,7 @@ namespace nsyshid figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13); m_figureAddedRemovedResponses.push(figureChangeResponse); + return true; } bool DimensionsUSB::CancelRemove(uint8 index)